Skip to main content

Cryptocurrency

This guide covers accepting cryptocurrency payments through the POST /api/v1/order endpoint, and sending cryptocurrency payouts through POST /api/v1/payouts.

Accepting a Crypto Payment​

A crypto payment runs through a crypto wallet:

  1. Create an order with instrumentType: crypto_wallet and the cryptocurrency to accept. The response returns a paymentLink.
  2. Send the customer to the paymentLink, where the wallet address and its QR code are shown.
  3. The customer transfers funds to that address. We send a callback on each order status change and on each incoming transfer (see Configuring Callback).

A minimal request:

curl -L 'https://gateway.demo.vertexgateway.com/api/v1/order' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Api-Access-Token: YOUR_API_KEY' \
--data-raw '{
"shopId": 12345,
"order": {
"orderNumber": "ORD-987654",
"cost": {
"amount": "100.00",
"currency": "USDT",
"tokenStandard": "TRC20"
}
},
"paymentSettings": {
"instrumentType": "crypto_wallet",
"redirectUrls": {
"success": "https://example.com/success",
"failure": "https://example.com/failure"
}
}
}'

The full step-by-step walkthrough with every request and the callback payload is in the CryptoWallet flow.

Supported Cryptocurrencies Table​

asset_codecrypto_currency_codeblockchaincryptoTokenStandardnativedecimal_places
BTCBTCBitcoin-true8
BNBBNBBNB Chain-true18
DOGEDOGEDogecoin-true8
ETHETHEthereum-true18
LTCLTCLitecoin-true8
MATICMATICPolygon-true18
TRXTRXTRON-true6
USDC_ETHUSDCEthereumERC20false6
USDT_TRXUSDTTRONTRC20false6
USDT_ETHUSDTEthereumERC20false6
USDT_BSCUSDTBNB ChainBEP20false18
XRPXRPXRP Ledger-true18
TONTONTON-true9
USDT_SOLUSDTSolanaSPLfalse6
BCHBCHBitcoin Cash-true8
MATIC_ETHMATICEthereumERC20false18
USDC_TRXUSDCTRONTRC20false6
SOLSOLSolana-true9

Column Descriptions​

  • asset_code: A unique identifier that combines the cryptocurrency code with the blockchain/token standard.
  • crypto_currency_code: The symbol or abbreviation of the cryptocurrency.
  • blockchain: The blockchain network on which the cryptocurrency operates.
  • cryptoTokenStandard: The standard protocol for tokens on the blockchain. A dash (-) indicates that the cryptocurrency is native to the blockchain and does not use a token standard.
  • native: Indicates whether the cryptocurrency is native to its blockchain (true) or a token (false).
  • decimal_places: The number of decimal places used by the cryptocurrency. This defines the precision required when specifying amounts.

Specifying the Cryptocurrency​

A crypto payment request carries up to two independent currency values:

  • Customer wallet currency — the cryptocurrency the customer pays with, set in order.cost.currency (plus order.cost.tokenStandard for token-based cryptocurrencies).
  • Merchant balance currency — the currency in which the merchant holds funds and in which the provider denominates the operation, set in recipient.merchantBalance.currency. It may be fiat or crypto and is used only by providers that require it (see Declaring the Merchant Balance Currency).

There are two ways to specify the customer wallet currency.

Method 1: crypto_currency_code with tokenStandard​

Put the crypto_currency_code in order.cost.currency. For a native cryptocurrency (native is true in the table), no token standard is needed:

{
"order": {
"cost": {
"amount": "0.005",
"currency": "BTC"
}
}
}

For a token-based cryptocurrency (native is false), add order.cost.tokenStandard from the cryptoTokenStandard column to identify the blockchain network:

{
"order": {
"cost": {
"amount": "100.00",
"currency": "USDT",
"tokenStandard": "TRC20"
}
}
}

Method 2: asset_code​

Put the asset_code directly in order.cost.currency. The asset code already encodes the token standard, so order.cost.tokenStandard is not needed:

{
"order": {
"cost": {
"amount": "100.00",
"currency": "USDT_TRX"
}
}
}

For a native cryptocurrency, the asset_code equals the crypto_currency_code (for example BTC), so Method 1 and Method 2 are identical.

Required Fields​

Required:

  • shopId — your merchant shop identifier.
  • order.orderNumber — your order reference number.
  • order.cost.amount — the expected amount. The customer may transfer a different amount, and the order settles on the amount actually received.
  • order.cost.currency — the cryptocurrency to accept.
  • paymentSettings.instrumentType — set to crypto_wallet; this routes the order to the crypto-wallet flow.
  • paymentSettings.redirectUrls — at least one of success / failure.

Optional:

  • customer — the customer the wallet is bound to. Pass customerId to use an existing customer. The field is optional for the API, but the crypto-wallet flow binds the wallet to a customer, so the request must carry enough for the system to resolve or create one. With too little data the order does not go through (it is rejected on creation, or ends as failed with the reason "Insufficient data to identify the customer").
  • order.cost.tokenStandard — the token standard, with Method 1 for token-based cryptocurrencies.
  • recipient.merchantBalance — the merchant balance currency, for providers that require it (see Declaring the Merchant Balance Currency).
  • paymentSettings.providerCode, paymentSettings.acquirerCode — a specific provider or acquirer.

The Wallet Address and Incoming Transfers​

The wallet address is presented to the customer on the order's paymentLink.

  • The address is stable and reused. The address bound to a customer for a given cryptocurrency and network is returned again for that same customer's later orders in the same cryptocurrency and network, and stays theirs for the wallet's validity period.
  • The customer can transfer any amount, any number of times. Each transfer is a separate transaction with its own callback.

How an Incoming Transfer Maps to an Order​

An incoming transfer is matched by its destination — the wallet address (plus the memo or destination tag where the network uses one) and the provider. Since each address is bound to one customer for a given cryptocurrency and network, the address identifies the customer. The transfer is then attached to one of that wallet's active orders — an active order is one still in in_progress:

  • One active order — the first transfer in the wallet's currency moves it to completed, and the order's amount is updated to the amount actually received. The amount you sent in POST /api/v1/order is the expected figure; the transfer settles on whatever arrives.
  • Several active orders — the most recently created one moves to completed; the others stay in_progress until their own transfers arrive.
  • No active order — a new order is created for the transfer and completed with the order_by_webhook payment type. Its callback carries its own orderNumber, assigned by the platform.

A repeat transfer to a wallet whose order is already completed therefore creates a new order rather than reopening the old one.

This mapping applies to transfers in the wallet's own currency. A transfer in a different currency is not applied to any order: the active order stays in_progress, and no new order is created.

note

An order that never receives a transfer moves to failed by timeout once the provider timeout window elapses without a status notification (status code 1-01-02, see Order Life Cycle) — the provider-settlement timeout, not the short timeLimit payment-page expiry. The wallet address stays valid, so a later transfer still follows the rules above.

Declaring the Merchant Balance Currency​

Some providers require the merchant balance currency to be declared alongside the currency the customer pays with. Use the recipient.merchantBalance object. The merchant balance currency may be a fiat currency in ISO 4217 format (USD, EUR) or a cryptocurrency.

The object has two fields:

  • currency (required when merchantBalance is used) — the merchant balance currency code, in the same two formats as order.cost.currency: crypto_currency_code (with tokenStandard), asset_code, or an ISO 4217 code for fiat.
  • tokenStandard (optional) — the token standard when currency is a token-based cryptocurrency; ignored for fiat.

Both the customer wallet currency and the merchant balance currency are crypto:

{
"order": {
"cost": {
"amount": "100.00",
"currency": "USDT",
"tokenStandard": "TRC20"
}
},
"recipient": {
"merchantBalance": {
"currency": "USDC",
"tokenStandard": "TRC20"
}
}
}

The merchant balance currency is fiat:

{
"order": {
"cost": {
"amount": "0.005",
"currency": "BTC"
}
},
"recipient": {
"merchantBalance": {
"currency": "EUR"
}
}
}

Full Request Examples​

Method 1 — native cryptocurrency (BTC)​

{
"shopId": 12345,
"order": {
"orderNumber": "ORD-987654",
"cost": {
"amount": "0.005",
"currency": "BTC"
}
},
"customer": {
"customerId": "cust_001"
},
"paymentSettings": {
"instrumentType": "crypto_wallet",
"redirectUrls": {
"success": "https://example.com/success",
"failure": "https://example.com/failure"
}
}
}

Method 1 — token cryptocurrency (USDT on TRON)​

{
"shopId": 12345,
"order": {
"orderNumber": "ORD-987654",
"cost": {
"amount": "100.00",
"currency": "USDT",
"tokenStandard": "TRC20"
}
},
"customer": {
"customerId": "cust_001"
},
"paymentSettings": {
"instrumentType": "crypto_wallet",
"redirectUrls": {
"success": "https://example.com/success",
"failure": "https://example.com/failure"
}
}
}

Method 2 — asset_code (USDT on TRON)​

{
"shopId": 12345,
"order": {
"orderNumber": "ORD-987654",
"cost": {
"amount": "100.00",
"currency": "USDT_TRX"
}
},
"customer": {
"customerId": "cust_001"
},
"paymentSettings": {
"instrumentType": "crypto_wallet",
"redirectUrls": {
"success": "https://example.com/success",
"failure": "https://example.com/failure"
}
}
}

With recipient.merchantBalance​

{
"shopId": 12345,
"order": {
"orderNumber": "ORD-987654",
"cost": {
"amount": "100.00",
"currency": "USDT",
"tokenStandard": "TRC20"
}
},
"customer": {
"customerId": "cust_001"
},
"recipient": {
"merchantBalance": {
"currency": "USDC",
"tokenStandard": "TRC20"
}
},
"paymentSettings": {
"instrumentType": "crypto_wallet",
"redirectUrls": {
"success": "https://example.com/success",
"failure": "https://example.com/failure"
}
}
}

Cryptocurrency Payouts​

Payouts send cryptocurrency out to a recipient through the POST /api/v1/payouts endpoint — the reverse direction from the payments above. The cryptocurrencies available are the same ones listed in the Supported Cryptocurrencies Table.

A full payout request also needs payout.payoutNumber (your identifier for the payout) and the provider fields for your payout method; the examples below show only the currency part. For a complete request body, see Payout Examples.

Specifying the Cryptocurrency​

The currency is specified the same two ways as for a payment — a crypto_currency_code with a token standard, or a single asset_code. The one difference is where the token standard goes: in a payout it is payout.cryptoTokenStandard, a sibling of cost, not cost.tokenStandard.

Method 1 — crypto_currency_code with cryptoTokenStandard. For a native cryptocurrency (native is true in the table), no token standard is needed:

{
"payout": {
"payoutNumber": "PAYOUT-987654",
"cost": {
"amount": "0.005",
"currency": "BTC"
}
}
}

For a token-based cryptocurrency (native is false), add payout.cryptoTokenStandard:

{
"payout": {
"payoutNumber": "PAYOUT-987654",
"cost": {
"amount": "100.00",
"currency": "USDT"
},
"cryptoTokenStandard": "TRC20"
}
}

Method 2 — asset_code. Put the asset_code directly in payout.cost.currency. It already encodes the token standard, so cryptoTokenStandard is not needed. For a native cryptocurrency the asset_code equals the crypto_currency_code (for example BTC), so the two methods are identical:

{
"payout": {
"payoutNumber": "PAYOUT-987654",
"cost": {
"amount": "100.00",
"currency": "USDT_TRX"
}
}
}

Declaring the Source Currency​

Some providers require a sourceCurrency — the currency the sender's or recipient's account is denominated in — set in recipient.payoutData and/or sender.payoutData. It may be a fiat currency in ISO 4217 format (USD, EUR) or a cryptocurrency. Use it when the payout involves currency conversion, or when the provider needs the account's denomination currency even though the payout itself is made in a different cryptocurrency.

{
"recipient": {
"payoutData": {
"sourceCurrency": "USD"
}
}
}