How to start accepting cryptocurrency in your online store?

Create a link to pay with cryptocurrency via API in Mitilena Pay in 5 steps

Everything that is written below refers to automatic payments through the online store. If you just need to rarely receive payment through cryptocurrency, then you can manually set a link to pay in cryptocurrency in the interface of our wallet.

  1. You need to get an API key in our system in the “Settings” section. Registration takes approximately 5 seconds. Initial review and confirmation of your store is not necessary. You can start accepting cryptocurrency immediately and withdraw it immediately.
  2. When the order is placed in your online store and the total cost of the order is ready on your part, you create a JSON object, which you send to our server with a POST request.
  3. We register your possible payment request, in response we send you our internal order number and a payment link.
  4. You redirect the user to this payment page, he pays, he is automatically redirected back to your site.
  5. We contact your site with a hidden request and tell you that the order is paid (this is done through the so-called webhook). You put the order in your database as paid (automatically) and then the process is standard for you, like any other paid order.

If your online store is powered by WordPress (WooCommerce), then you don’t need to program anything, we have a ready-made plugin for accepting cryptocurrency, follow the link for instructions and the plugin itself.  

Our payment system now supports the USDT cryptocurrency on the TRC20 protocol in the Tron blockchain, so all examples will be on it. When we add new cryptocurrencies, we will change this manual.

API description:

Step 2, creating a JSON object (JavaScript example):

let createPaymentObj =
        userSecret: 'your_API_key', // required
        userPaymentId: 'your_payment_id_usually_order_id', // required
        cryptoReceiveSymbol: 'USDT', // required
        blockchain: 'tron', // required
        cryptoProtocol: 'TRC20', // required
        fiatCode: '3x_ISO_code_of_your_fiat_currency_for example_EUR', // required
        contractAddress: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t', // required
        customerEmail: 'customer_mail', // required

        fiatAmount: 'digit_amount_in_fiat_currency_for_example_100', // number, required
        userProductId: 'your_product_id_if_any', // not required
        language: 'two_digit_ISO_language_code', // default en, not required [available: en, cz, ru, tr, my, hr, si, ar, swa] - the rest will be an error
        // redirectUrl_yours: 'thank_you_page or success_page',
        redirectUrl_yours: 'url_on_your_site_where_to_send_user_after_successful_payment', // not required, but very desirable
        webHookPostUrl_yours: 'url_on_your_site_where_the_hidden_POST_request_will_go_on_successful_payment,' // required

In the headers of the request, you also need to insert your API key: ‘mitilena-signature’: ‘your_api_key’
It looks something like this:

headers: {
     'Content-Type': 'application/json',
     accept: 'application/json',
     'mitilena-signature': 'your_key',

And send it as a POST request to the address:

If the creation of the invoice was successful, then the server response will be with the code 200 and the response will contain a JSON object, for example, something like this:
From this response you only need 2 fields, invoiceId and paymentPage

  usdtOverAmount: '1.580437',
  invoiceId: 'auto-e80099-ef456c-e0de42',
  invoiceVs: '0437',
  fiatAmount: '1.450',
  fiatCurrency: 'EUR',
  rateLocalCurrencyToNeededCrypto: '0.917',
  rateNeededCryptoToUSD: '0.99959700',
  isTrc20: 1,
  whenInit: '2023-08-22T11:30:05.000Z',
  contractAddress: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t',
  customerEmail: '',
  productId: 85,
  expiresAt: '2023-09-05T11:30:05.000Z',
  blockchainHash: null,
  cryptoShortCode: 'USDT',
  cryptoValue: '1.580437',
  fiatValue: '1.450',
  receivedOnAddress: 'TRHpcBugcdEdD6BdW6k1Grtof6LBvRGWWJ',
  txId: 'auto-e80099-ef456c-e0de42',
  fiatShortCode: 'EUR',
  wasPaid: null,
  redirectUrl: '',
  paymentPage: ''

If an error occurs, the server response will be with a 400 code and the response will contain a String, the error text.
The lifetime of such a link is 14 days. There are plans to make it possible to choose the lifetime of the link.

JavaScript examples (node.js version 16.8)
const https = require('https');
const url = require('url');

const data = JSON.stringify({
    userSecret: '',
    userPaymentId: 'userPaymentdId111',
    cryptoReceiveSymbol: 'USDT',
    blockchain: 'tron',
    cryptoProtocol: 'TRC20',
    fiatCode: 'EUR',
    contractAddress: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t',
    customerEmail: '',

    fiatAmount: 1.45,
    userProductId: 'new_someGoodsOk',
    language: 'en', // default en, not neccessary
    // redirectUrl_yours: '',
    redirectUrl_yours: '',
    webHookPostUrl_yours: ''

const myURL = url.parse('');

const options = {
    hostname: myURL.hostname,
    port: myURL.port,
    path: myURL.pathname,
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        accept: 'application/json',
        'mitilena-signature': '',

requestAsync(options, data)
    .then(dataFObj => {
        if (dataFObj.statusCode === 200) {
            let tokensObj = JSON.parse(dataFObj.dataRaw);
            console.log('success:', tokensObj)
        } else {
            console.log('error dataFObj', dataFObj);
            throw new Error('some errror in postToWebHook ' + dataFObj.dataRaw);
    .catch(e => {
        console.log('error request:', e)

function requestAsync(optionsArr, dataToPost = false) {
    return new Promise((resolve, reject) => {
        const req = https.request(optionsArr, (resp) => {
            // console.log(`statusCode: ${resp.statusCode}`);
            let dataF = '';
            // A chunk of data has been received.
            resp.on('data', (chunk) => {
                dataF += chunk;

            resp.on('end', () => {
                resolve({statusCode: resp.statusCode, dataRaw: dataF});
            }).on('error', (err) => {
                console.log('Error: ' + err);

        if (dataToPost) {


The url module in Node.js above version 16 seems to have changed the syntax, if anything, find information on this topic or enter the address and path manually.

Examples in PHP and WordPress:
Query using a WordPress function wp_remote_post(), check out the pure PHP function, it will look something like this:
$request_body = [
    'userSecret' => $this->mitilenaToken,
    'userPaymentId' => $order->id,
    'cryptoReceiveSymbol' => 'USDT',
    'blockchain' => 'tron',
    'cryptoProtocol' => $mi__protocol,
    'fiatCode' => $order->currency,
    'contractAddress' => $mi__contractAddress,
    'customerEmail' => $order->get_billing_email(),

    'fiatAmount' => $order->get_total(),
    'userProductId' => $orderids,
    'language' => 'en', // default en, not neccessary
    'redirectUrl_yours' => $order->get_checkout_order_received_url(),
    'webHookPostUrl_yours' => ''


$request_headers = [
    'content-type' => 'application/json',
    'mitilena-signature' => $this->mitilenaToken,

$args = [
    'method' => 'POST',
    'httpversion' => '1.0',
    'timeout' => 90,
    'headers' => $request_headers,
    'body' => ''

$environment_url = '';

$result = wp_remote_post($environment_url, array(
        'method' => 'POST',
        'headers' => $request_headers,
        'timeout' => 60, // added
        'redirection' => 5,  // added
        'blocking' => true, // added
        'httpversion' => '1.0',
        'sslverify' => false,
        'body' => json_encode($request_body))

$fullObj = json_decode($result['body']);

if (isset($result['response']) && isset($result['response']['code']) && $result['response']['code'] != 200) {
    $mi__err_message = 'Unknown error';
    if (isset($result['body'])) {
        $mi__err_message = 'Error: ' . $result['body'];
    // error handling + exit

if (!isset($fullObj->paymentPage)) {
    http_response_code(400); // Bad Request
    echo "Error: No payment page specified";
$paymentPage = $fullObj->paymentPage;

if (strlen($paymentPage) < 5) {
    http_response_code(400); // Bad Request
    echo "Error: payment page is too short.";


Step 5, creating a WebHook handler on the side of your site (server):

On the example of Node.js 16.8 version and Express.js 4 version (JavaScript)
const bodyParser = require('body-parser');
const Big = require('big.js')
let endpointSecret = 'copy of the api key value'

// an object called statusObj arrives'/mi_webhook/', bodyParser.json(), (request, response) => {

    try {
        let eventObj = request.body;
        const signature = request.headers['mitilena-signature'];

// compare the incoming secret key with your real one
        if (signature === endpointSecret) {
            if (!eventObj.statusObj) {
                return response.status(400).send('Error, no object.');

            let hookObj = eventObj.statusObj;

            if (
                typeof hookObj.mitilenaInvoiceId === 'undefined' ||
                typeof hookObj.yourPaymentId === 'undefined' ||
                typeof hookObj.yourProductName === 'undefined' ||
                typeof hookObj.yourProductId === 'undefined' ||
                typeof hookObj.secretKey === 'undefined' ||
                typeof hookObj.stableCoinAmount === 'undefined' ||
                typeof hookObj.stableCoinSymbol === 'undefined' ||
                typeof hookObj.stableCoinBlockchain === 'undefined' ||
                typeof hookObj.stableCoinContractAddress === 'undefined' ||
                typeof hookObj.localCurrencyAmount === 'undefined' ||
                typeof hookObj.localCurrencyCode === 'undefined' ||
                typeof hookObj.wasPaid === 'undefined' ||
                typeof hookObj.blockchainHash === 'undefined'
            ) {
                return response.status(400).send('Error, incomplete object.');

            if (hookObj.wasPaid === true) {
                let centsAmount = Big(hookObj.localCurrencyAmount).times(Big(100)).round(0).toNumber();

// put the order in your database as paid and return the status code 200
// this is an example of our code, yours may look different
                    .completeUserPayment(hookObj.mitilenaInvoiceId, centsAmount)
                    .then((okString) => {
                        return response.status(200).send('all good Mitilena token');
                    .catch((e) => {
                        console.log('err in completeUserCardPayment', e.message);
                        return response.status(400).send('error, contact support');
            } else {
                return response.status(400).send('invoice was not paid');
        } else {
            return response.status(400).send('bad Mitilena token');
    } catch (e) {
        console.log('webhook err:', e.message);
        return response.status(400).send('bad request, error');
Using the PHP example:

Create a handler file and put it on your web server (site) so that it is accessible from the outside, i.e. if you write the full path to the file so that it opens, for example (this is an example, you can do it differently, through routing, etc.).

As mentioned above, specify this address in the object in which you create a payment link, i.e.
webHookPostUrl_yours: ‘’

File contents:

$mi__jsonData = file_get_contents('php://input');
if (!$mi__jsonData) {
    http_response_code(400); // Bad Request
    echo 'bad input data';
// Decode the JSON data into a PHP associative array
$mitilena_data = json_decode($mi__jsonData, true);

// Check if decoding was successful
if ($mitilena_data !== null && isset($mitilena_data['statusObj'])) {
    $mitilena_data = $mitilena_data['statusObj'];

    if (!isset($mitilena_data['secretKey'])) {
        http_response_code(400); // Bad Request
        echo "Error: Api key of Mitilena (Mitilena Token) is not specified. We can't verify if the request is genuine. Cancellation of a transaction.";
    $mitilenaToken = $mitilena_data['secretKey'];

    // compare your real API key with the one you received
    $realMitilenaToken = 'your_api_key'

    if ($mitilenaToken != $realMitilenaToken) {
        http_response_code(400); // Bad Request
        echo "Error: API token does not match. The website owner must specify the correct Api Mitilena token.";

    if (!isset($mitilena_data['yourPaymentId'])) {
        http_response_code(400); // Bad Request
        echo "Error: No payment id specified";
    $mi__paymentId = $mitilena_data['yourPaymentId'];

    if (strlen($mi__paymentId) < 1) {
        http_response_code(400); // Bad Request
        echo "Error: too short payment id.";
    function mi__pay_escapeString($var)
        $var = stripslashes($var);
        $var = htmlentities($var);
        $var = strip_tags($var);
        return $var;

    // yourPaymentId
    try {
        $mi__order = '';  // search your database for this order, mi__pay_escapeString($mi__paymentId)
    } catch (Throwable $e) {
        http_response_code(400); // Bad Request
        echo "Error: wrong payment id (order id). {yourPaymentId: \"\"}";

    if (!isset($mitilena_data['wasPaid'])) {
        http_response_code(400); // Bad Request
        echo "Error: No payment status specified";
    $mi__wasPaid = $mitilena_data['wasPaid'];

    if ($mi__wasPaid == true) {

        $blockchainHash = '';
        if (isset($mitilena_data['blockchainHash'])) {
            $blockchainHash = mi__pay_escapeString($mitilena_data['blockchainHash']);

        // set the order as paid in your database
        // reduce inventory if required
        // send the user an email if required

        echo 'success';
    } else {
        echo 'unpaid status received';
 } else {

    // JSON decoding failed
    http_response_code(400); // Bad Request
    echo "Invalid JSON data";


That’s all, if you have any questions, ask in the comments.

Leave a Reply

Your email address will not be published. Required fields are marked *