Main
/Host to host (white label)
Copy page
This guide describes accepting crypto payments through direct server-to-server interaction with the Heleket API. Unlike the ready-made payment page, your backend creates invoices, receives payment details, and handles payment status notifications. You can render the payment form on your side or use the URL returned in the API response.
You need two values from your account to accept payments:
| Value | Where to get |
|---|---|
| Merchant ID | Merchant UUID. Go to Business → Merchants → Merchant settings. |
| Payment API key | Generated in the merchant settings after moderation. |
The payout API key is issued separately in Settings → API, requires two-factor authentication, and is not needed to accept payments.
Base endpoint for all requests:
All API requests use POST with JSON and must be signed.
Each request is authenticated with two HTTP headers:
| Header | Value |
|---|---|
| merchant | Your Merchant ID (UUID). |
| sign | Request body signature. |
| Content-Type | application/json |
The signature is an MD5 hash of the base64-encoded JSON request body concatenated with your API key.
$body = json_encode($data);
$sign = md5(base64_encode($body) . $API_KEY);CopyFor requests without body parameters, calculate the signature from an empty string:
$sign = md5(base64_encode('') . $API_KEY);CopyImportant: slash escaping. PHP escapes / in JSON (\/) by default, while many other languages do not. The signature is calculated from the same string sent in the request body, so the string used for signing and the request body must be byte-for-byte identical. In non-PHP stacks, escape slashes manually; otherwise, the signature will not match (the same issue occurs when verifying webhooks; see section 5).
curl https://api.heleket.com/v1/payment \
-X POST \
-H 'merchant: 8b03432e-385b-4670-8d06-064591096795' \
-H 'sign: fe99035f86fa436181717b302b95bacff1' \
-H 'Content-Type: application/json' \
-d '{"amount":"15","currency":"USD","order_id":"1"}'CopyDo not rely only on the webhook: always keep a fallback through /v1/payment/info (section 6) in case the callback is not delivered.
Endpoint:
| Parameter | Type | Required | Description |
|---|---|---|---|
| amount | string | yes | Amount to pay. Use a period as the decimal separator, for example, 10.28. |
| currency | string | yes | Invoice currency code (fiat or cryptocurrency), for example, USD, USDT, or BTC. |
| order_id | string | yes | Your order identifier. It may contain only letters, digits, _ and -. It must be unique. |
| network | string | no | Blockchain network code, for example, tron, bsc, or eth. |
| to_currency | string | no | Target cryptocurrency for amount conversion (always a cryptocurrency code, not fiat). |
| url_callback | string | no | URL to which Heleket sends status webhooks. It is effectively required for H2H. |
| url_return | string | no | Where to return the customer from the payment form before payment. |
| url_success | string | no | Where to return the customer after successful payment. |
| lifetime | integer | no | Invoice lifetime in seconds (300–43200; 3600 by default). |
| subtract | integer | no | Percentage of the fee to pass on to the customer (0–100). |
| accuracy_payment_percent | numeric | no | Allowed underpayment percentage (0–5): the invoice is marked as paid when the underpayment is within this limit. |
| is_payment_multiple | boolean | no | Allow the remaining balance to be paid. The default is true. |
| additional_data | string | no | An arbitrary string for your use (not visible to the customer), up to 255 characters. |
| currencies | array | no | Allowlist of currencies and networks available for payment. |
| except_currencies | array | no | Blocklist of currencies and networks. |
| discount_percent | integer | no | Discount (positive) or surcharge (negative), from -99 to 100. |
| is_refresh | boolean | no | Refresh an expired invoice with the same order_id (new address and expiration time). |
About order_id: if an invoice with this order_id already exists, a new one will not be created—the existing payment details will be returned. This provides idempotency: repeating a request for the same order is safe.
When the wallet address is returned immediately. The address field is populated on creation only when the payment currency is unambiguous: the cryptocurrency and network are specified (to_currency + network), or the cryptocurrency has only one network (for example, BTC). Otherwise, the customer selects the currency and network on the payment page, and the address appears later.
Minimal invoice for 15 USD (the customer selects the cryptocurrency and network):
{ "amount": "15", "currency": "USD", "order_id": "1" }CopyInvoice for 20 USDT on the TRON network—the address is returned immediately:
{ "amount": "20", "currency": "USDT", "order_id": "1", "network": "tron" }CopyInvoice for 25 USD payable only in USDT on any network:
{ "amount": "25", "currency": "USD", "order_id": "1", "to_currency": "USDT" }Copy{
1 "state": 0,
2 "result": {
3 "uuid": "1ec87133-b22d-4643-988f-cac29a6ac85d",
4 "order_id": "3",
5 "amount": "20000.00",
6 "payment_amount": null,
7 "payer_amount": "254.92",
8 "payer_currency": "USDT",
9 "currency": "RUB",
10 "merchant_amount": "249.82816502",
11 "network": "bsc",
12 "address": "0x2b...",
13 "txid": null,
14 "payment_status": "check",
15 "url": "https://pay.heleket.com/pay/1ec87133-b22d-4643-988f-cac29a6ac85d",
16 "expired_at": 1753202502,
17 "is_final": false,
18 "commission": "5.09853397",
19 "address_qr_code": "data:image/png;base64 ..."
20 }
21}Copy| Field | Purpose |
|---|---|
| uuid | Invoice UUID in Heleket. Store it with the order. |
| address | Payment wallet address. It may be null until the currency is selected. |
| payer_amount | Amount to pay in payer_currency, including a discount or surcharge. |
| payer_currency | Payment currency. Null means that the customer has not selected it yet. |
| merchant_amount | Amount credited to your balance after fees. |
| payment_status | Current status (see section 7). |
| url | Link to the Heleket payment page if you do not render the form yourself. |
| address_qr_code | Base64-encoded QR code of the payment address. |
| expired_at | Invoice expiration Unix timestamp. |
| is_final | Whether the invoice is finalized and can no longer be paid. |
state: 0 means success. For validation errors, state: 1 (section 8).
Heleket sends a POST webhook whenever the invoice status changes.
| Field | Description |
|---|---|
| type | Type: payment or wallet. |
| uuid | Payment UUID. |
| order_id | Your order identifier, used to find the order. |
| amount | Invoice amount. |
| payment_amount | Amount actually paid by the customer. |
| payment_amount_usd | Amount actually paid in USD. |
| merchant_amount | Amount credited to the balance after the fee. |
| commission | Heleket fee. |
| is_final | Whether the invoice is finalized. |
| status | Payment status (see section 7). |
| from | Payer wallet address. |
| network | Payment network. |
| currency | Invoice currency. |
| payer_currency | Currency actually used for payment. |
| txid | Blockchain transaction hash (may be absent for P2P or manual closing). |
| sign | Webhook signature used for verification. |
{
1 "type": "payment",
2 "uuid": "62f88b36-a9d5-4fa6-aa26-e040c3dbf26d",
3 "order_id": "97a75bf8eda5cca41ba9d2e104840fcd",
4 "amount": "3.00000000",
5 "payment_amount": "3.00000000",
6 "merchant_amount": "2.94000000",
7 "commission": "0.06000000",
8 "is_final": true,
9 "status": "paid",
10 "from": "THgEWubVc8tPKXLJ4VZ5zbiiAK7AgqSeGH",
11 "network": "tron",
12 "currency": "TRX",
13 "payer_currency": "TRX",
14 "txid": "6f0d9c8374db57cac0d806251473de754f361c83a03cd805f74aa9da3193486b",
15 "sign": "a76c0d77f3e8e1a419b138af04ab600a"
16}CopyBecause you deliver a product or credit a user’s balance based on a webhook, you must ensure that the request came from Heleket. Verify it using both methods:
// 1. Read the raw request body
1$data = json_decode(file_get_contents('php://input'), true);
2
3// 2. Extract and remove the signature from the array
4$sign = $data['sign'];
5unset($data['sign']);
6
7// 3. Calculate the hash from the body (without sign) plus your payment API key
8$hash = md5(base64_encode(json_encode($data, JSON_UNESCAPED_UNICODE)) . $apiPaymentKey);
9
10// 4. Compare
11if (!hash_equals($hash, $sign)) {
12 // invalid signature — reject
13 http_response_code(400);
14 exit;
15}CopyThe same slash issue described in section 2 applies: when encoding JSON outside PHP, escape / manually (JSON.stringify(data).replace(/\//g, "\\/") in JS); otherwise, the signature will not match.
Endpoint:
Pass uuid or order_id (if both are passed, order_id takes priority).
curl https://api.heleket.com/v1/payment/info \
-X POST \
-H 'merchant: 8b03432e-385b-4670-8d06-064591096795' \
-H 'sign: <body signature>' \
-H 'Content-Type: application/json' \
-d '{"order_id":"1"}'CopyThe response contains the same payment object with its current payment_status and is_final values. Use this method for periodic reconciliation of “stuck” orders, not as the primary mechanism (webhooks are faster and require fewer requests).
| Status | Final | Meaning |
|---|---|---|
| check | no | Waiting for the transaction to appear on the blockchain. |
| process | no | The payment is being processed. |
| confirm_check | no | The transaction is visible; waiting for the required number of network confirmations. |
| wrong_amount_waiting | no | Underpayment with the option to pay the remaining balance. |
| paid | yes | The exact required amount was paid. Deliver the product. |
| paid_over | yes | More than the required amount was paid. Deliver the product. |
| wrong_amount | yes | The customer paid less than required. |
| fail | yes | Payment error. |
| cancel | yes | The payment was cancelled; the customer did not pay. |
| system_fail | yes | System error. |
| locked | yes | Funds are locked under the AML program. |
| refund_process | no | The refund is being processed. |
| refund_paid | yes | The refund was completed. |
| refund_fail | yes | Refund error. |
Treat paid and paid_over as successful payments. confirm_check may also appear in webhooks as an intermediate status.
Validation errors — HTTP 422, state: 1:
{ "state": 1, "errors": { "amount": ["validation.required"] } }CopyCommon messages (state: 1, message field):
| Message | Reason |
|---|---|
| The network was not found | An unsupported network code was provided. |
| The currency was not found | An unsupported currency code was provided. |
| Not found service to_currency | No payment service is available for to_currency. |
| Minimum amount 0.5 USDT | The amount is below the minimum for the currency. |
| Maximum amount 10000000 USDT | The amount exceeds the maximum for the currency. |
| Wallet not found | No active merchant wallet is available for the payment currency. |
| You are forbidden | Payments are blocked. Contact support. |
| Gateway error / Server error | A temporary technical issue occurred and the payment is unavailable. |
Internal error — HTTP 500:
{ "message": "Server error, #1", "code": 500, "error": null }CopyTreat Gateway error, Server error, and HTTP 500 as temporary and retry with exponential backoff.
See the related documentation for currency and network codes, refunds, static wallets, and payouts.