Connecting crypto payments via API

api connection crypto

API connection instructions for developers

  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. Mitilena Pay is non-custodial: payments go directly from the buyer to your own wallet address.
  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. 

 

API description:

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

Possible values for the blockchain field, take the value from the quotes (for example: tether-trc20):

'tether-erc20' (ERC20 Ethereum USDT, contract: 0xdac17f958d2ee523a2206206994597c13d831ec7)

'tether-bep20' (BEP20 Binance smart chain USDT, contract: 0x55d398326f99059ff775485246999027b3197955)

'tether-trc20' (TRC20 Tron USDT, contract: TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t)

'bitcoin' (Bitcoin native, BTC)
'mitilena-own' (Vanishing Mitilena, VMT token)
'apfcoin' (ERC20 Ethereum APFC, contract: 0x8eD955a2b7d2C3a17a9d05dACa95E01818f8C11e)
'verdan-token-bep20' (BEP20 Binance smart chain VERDT, contract: 0x2ea26d54ba80fe908621428ed7d4aa42ca9b81b5)

Important (Cold Wallet / Non-Custodial): All payments go directly to your own cold wallet. You must pass the receiveAddress parameter with your wallet address corresponding to the selected blockchain (for example, your Tron T... address for tether-trc20, your 0x... address for tether-bep20 / tether-erc20 / apfcoin / verdan-token-bep20, or your Bitcoin address for bitcoin).

 

let createPaymentObj =
    {
        userSecret: 'your_API_key', // required
        userPaymentId: 'your_payment_id_usually_order_id', // required
        blockchain: 'tether-trc20', // required 
        receiveAddress: 'your_wallet_address_for_selected_blockchain', // required (non-custodial: payments go directly to your wallet)
        fiatCode: '3x_ISO_code_of_your_fiat_currency_for example_EUR', // 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, fr, de, zh, tr, my, hr, si, ar, swa]
        // 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, clean URL path without query string (?param=...)
    }

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:
https://mitilena.com/api/generatePayment

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. You must send (redirect) the user to the payment page to the Mitilena Pay website [paymentPage field]!
Note: if you pass a localized language code (for example 'cz', 'de', 'fr', 'ru'), the returned paymentPage URL will use the corresponding language subdomain (for example https://cz.mitilena.com/pay/.../fast/). If your code validates the host of paymentPage, allow both mitilena.com and *.mitilena.com.

{
  usdtOverAmount: '1.580437',
  invoiceId: 'auto-e80099-ef456c-e0de42',
  invoiceVs: '0437',
  fiatAmount: '1.450',
  fiatCurrency: 'EUR',
  rateLocalCurrencyToNeededCrypto: '0.917',
  rateNeededCryptoToUSD: '0.99959700',
  isTrc20: 1,
isErc20: 0,
isBep20: 0, whenInit: '2023-08-22T11:30:05.000Z', contractAddress: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t', customerEmail: 'email_customer@email.com', 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: 'https://google.com', paymentPage: 'https://mitilena.com/pay/auto-e80099-ef456c-e0de42/fast/' // or https://<lang>.mitilena.com/pay/.../fast/ }

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)
Request:
const https = require('https');
const url = require('url');

const data = JSON.stringify({
    userSecret: '',
    userPaymentId: 'userPaymentdId111',
    blockchain: 'tether-trc20',
    receiveAddress: 'TRHpcBugcdEdD6BdW6k1Grtof6LBvRGWWJ', // your wallet address
    fiatCode: 'EUR',
    customerEmail: 'email_customer@email.com',

    fiatAmount: 1.45,
    userProductId: 'new_someGoodsOk',
    language: 'en', // default en, not neccessary
    // redirectUrl_yours: 'https://google.com',
    redirectUrl_yours: 'https://google.com',
    webHookPostUrl_yours: 'https://mitilena.com/mi_webhook/'
});

const myURL = url.parse('https://mitilena.com/api/generatePayment');

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);
                reject(err);
            });
        });

        if (dataToPost) {
            req.write(dataToPost);
        }

        req.end();
    });
}

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:
<?php
$request_body = [
    'userSecret' => $this->mitilenaToken,
    'userPaymentId' => $order->id,
    'blockchain' => 'tether-trc20',
    'receiveAddress' => 'TRHpcBugcdEdD6BdW6k1Grtof6LBvRGWWJ', // your wallet address for this blockchain
    'fiatCode' => $order->currency,
    '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' => 'https://mywebsite.com/post-handler.php'

];

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

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

$environment_url = 'https://mitilena.com/api/generatePayment/';

$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
    exit;
}

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

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

 

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

When a payment is detected (whether the invoice was created via POST /api/generatePayment or via the invoice generator form on mitilena.com/get-payment-eshop/ where you specify Product name, Product ID, and Webhook URL), our server sends a POST request to your webhook URL with header mitilena-signature: your_api_key and the following JSON payload:

{
  statusObj: {
    mitilenaInvoiceId: 'auto-683dc5-03f2c0-e3f06a', // string, never undefined
    yourPaymentId: 'your_order_id', // string (order ID from API, or '' if created via /get-payment-eshop/ form), never undefined
    yourProductName: 'stringOrNull', // string (Product name from /get-payment-eshop/ form), never undefined
    yourProductId: 'stringOrNull', // string (Product ID from /get-payment-eshop/ form or userProductId from API), never undefined
    secretKey: 'your-unique-secret-key', // string, never undefined (must match your API key!)
    stableCoinAmount: 10.55, // number | string, crypto amount paid, never undefined
    stableCoinSymbol: 'USDT', // string ('USDT', 'BTC', 'ETH', etc.), never undefined
    stableCoinBlockchain: 'Tron', // string ('Tron', 'Ethereum', 'Binance Smart Chain (BSC)', 'tron', 'eth', 'bsc', etc.), never undefined
    stableCoinContractAddress: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t', // string | null
    localCurrencyAmount: 10, // number, amount in your fiat currency, never undefined
    localCurrencyCode: 'EUR', // ISO 4217 currency code, never undefined
    wasPaid: true, // boolean true | false, never undefined
    blockchainHash: '0x...' // string, transaction hash in the blockchain, never undefined
  }
}
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
app.post('/mi_webhook/', bodyParser.json(), (request, response) => {

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

        console.log({eventObj});
// 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' ||
                hookObj.secretKey !== endpointSecret
            ) {
                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
                paymentsModel
                    .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 https://youwebsite.com/webhook.php (this is an example, you can do it differently, through routing, etc. Do not include query string parameters like ?action=... in the webhook URL).

As mentioned above, specify this address in the object in which you create a payment link, i.e.
{….
webHookPostUrl_yours: ‘https://youwebsite.com/webhook.php’
…}

File contents:

<?php
$mi__jsonData = file_get_contents('php://input');
if (!$mi__jsonData) {
    http_response_code(400); // Bad Request
    echo 'bad input data';
    exit;
}
// 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']) || !is_string($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.";
        exit;
    }
    $mitilenaToken = $mitilena_data['secretKey'];

    // compare your real API key with the one you received (always use strict comparison / hash_equals!)
    $realMitilenaToken = 'your_api_key';


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


    // For API invoices, your order ID is in 'yourPaymentId'; for invoices created on mitilena.com/get-payment-eshop/, your ID is in 'yourProductId'
    $mi__paymentId = '';
    if (isset($mitilena_data['yourPaymentId']) && is_string($mitilena_data['yourPaymentId']) && strlen($mitilena_data['yourPaymentId']) > 0) {
        $mi__paymentId = $mitilena_data['yourPaymentId'];
    } elseif (isset($mitilena_data['yourProductId']) && is_string($mitilena_data['yourProductId']) && strlen($mitilena_data['yourProductId']) > 0) {
        $mi__paymentId = $mitilena_data['yourProductId'];
    }

    if (strlen($mi__paymentId) < 1) {
        http_response_code(400); // Bad Request
        echo "Error: No payment id or product id specified.";
        exit;
    }
    
    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: \"\"}";
        exit;
    }

    if (!isset($mitilena_data['wasPaid'])) {
        http_response_code(400); // Bad Request
        echo "Error: No payment status specified";
        exit;
    }
    $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";
}

exit;
?>

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

Don’t have a developer to implement this for you? Write to us at support@mitilena.com, we will make you a commercial proposal for the developer’s work on connecting crypto-payments.

Leave a Reply

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