Skip to main content

1. Authentication and Security

RequirementDescription
HTTPSSend API requests over HTTPS.
JSONRequests and responses use JSON.
API keyUse an enabled merchant API key with v2 access and its signing secret.
IP whitelistUse an allowed request source IP.
API prefixExternal v2 endpoints use /api/v2.
Money formatPositive JSON numbers with at most two decimal places, for example 500.25.
ChangelogContract updates are recorded in the v2 changelog.

BDT Merchant API v2 creates deposits (PayIn) and withdrawals (PayOut), and retrieves an individual transaction status. The API base path is /api/v2. This guide uses BDT and an active BDT shop in its examples; use your assigned shop code as method. Use HTTPS and JSON. Ask your integration contact to enable v2 for your API key, provide its signing secret, configure the allowed source IPs, and confirm your active shop codes and their fields schemas.

Confirm with your integration contact that the revised v2 contract is available in your environment and enabled for each intended shop, currency, and payment direction. Agree on the required customer fields, available payer-account data, callback address, and PayIn return-page configuration before live payments.

For POST, send a JSON object in the body and no query string. Query parameters are rejected with HTTP 400, including parameters that repeat signed body fields. A JSON array, scalar, or malformed JSON is also rejected. Send your signing secret only to your own signing implementation; never include it in the request or a browser URL.

Signature examples​

Pass the complete create-request JSON as rawBody; send those same bytes after signing.

import { createHmac } from 'node:crypto';

function v2Signature(rawBody, apiSecret) {
return createHmac('sha256', apiSecret).update(rawBody, 'utf8').digest('hex');
}

// Sign and send the same raw JSON bytes for POST; sign '' for GET.

Version boundaries​

v2 uses order_id, method, fields, and a JSON number for amount. The v1 names external_id, shop_code, and paymentData, its decimal-string amount and timestamp-based signature are different contracts. Unknown top-level create fields are rejected.

method is the exact code of one of your active shops. If omitted, your default active shop is used. fields is a JSON object validated against the selected shop's configured PayIn or PayOut schema. See the shop fields contract and confirm the schema for your shop code and direction before sending a request.

For BDT migration, use the v1-to-v2 request-field mapping. In particular, the deposit mobile number moves to fields.mobile_number. Arbitrary string customer IDs and the v1 customer IP do not currently have equivalent v2 inputs; sharing payment processing does not make the request fields interchangeable.

Payment flow​

  1. Create a deposit or withdrawal with a stable order_id and store the returned transaction_id.
  2. For a deposit, send the customer to the complete redirect_url. The page may initially show payment preparation. A withdrawal has no customer redirect.
  3. Use the status endpoint to read the current result. HTTP 200 from create means that the operation exists, not that money has been received or sent.
  4. When a callback arrives, verify its raw-body signature, durably accept the notification, respond with HTTP 200, and retrieve the current transaction status.

v1 and v2 use the same payment processing rules, balances, and configured payment routes. Their authentication and wire formats remain separate. Keep existing v1 operations on v1; a v2 status request cannot retrieve them.

Available operations​

ActionEndpointGuide
Create withdrawalPOST /api/v2/withdrawalsWithdrawals
Create depositPOST /api/v2/depositsDeposits
Get either transaction's statusGET /api/v2/transactions/{transaction_id}Transaction status

See errors, statuses, and callbacks. Only transactions created through v2 can be retrieved by the v2 status endpoint.

Retrying a create request​

If a create response is lost, retry the same endpoint with the same order_id and original request data, signing each request's exact bytes. An accepted repeat resolves to the same operation and transaction_id; it does not create another operation. Do not change method, amount, recipient, URLs, or fields on a retry. A conflict returns HTTP 409.

Idempotency is scoped to your merchant and operation type across shops. Switching shops or API versions is not a way to retry the same payment. Do not use a new ID merely because a response, callback, or payment page is delayed. Check status, or contact your integration contact if the outcome remains uncertain.

Because callbacks contain only order_id, prefer IDs unique across both deposits and withdrawals when using a shared callback endpoint. If you reuse an ID across directions, use separate callback URLs or another unambiguous server-side order mapping.