# BaaS
# Payment Routing
To connect to the service and get started, please contact Support (opens new window) or your supervising manager (opens new window).
# Sub-merchant Registration
A sub-merchant can be a legal entity or a sole proprietor.
To get started, a sub-merchant must independently create a Mandarin Personal Account through the standard registration (opens new window) process, completing steps from (1) submitting an application to (4) completing the application.
In addition to the main Personal Account registration process, bulk Personal Account creation is available through the registry. To use this scenario, please contact your supervising manager.
Once your Personal Account is approved, you can proceed to linking the created Sub-Merchant Personal Account to your Marketplace account.
# Creating a Sub-Merchant Account
To create a Sub-Merchant account, use the token, which is available in the Routing section of the Project Settings in the Sub-Merchant Personal Account.
| Parameter | Type | Required | Description |
|---|---|---|---|
| accountType | string | Yes | Account type. For a legal entity, it is set to business. |
| token | string | Yes | Legal entity account token. |
The synchronous response contains the account id.
Request
POST https://secure.mandarinpay.com/api/v1/accounts/business
{
"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXJjaGFudF9pZCI6MiwiaXNzIjoiMzAg0LzQsNGPIDIwMjIg0LMuIn0.unGctyIBkp4EHXw-7bFqWbbkmfhs2yCJ-jAKJGqRP_1"
}
Response if account is successfully created (200 OK)
{
"id": "01dab7d8-be9b-4f87-ba91-801731787725",
"type": "Business"
}
Response if the request is not created (400 Bad request)
{
"error": "Invalid request"
}
# Payment Acceptance (Routing)
Standard API requests for payment acceptance are used, as described in the documentation. Single-stage payment (api_payments.md#single-stage-payment), recurring payment (auto-debit) (api_payments.md#recurring-payment-auto-debit), and two-stage payment (api_payments.md#two-stage-payment) are possible (in this case, authorization remains standard, and payment distribution occurs upon completion of settlements).
In this case, the routing.destination object containing the payment distribution scheme is added to the pay request. To accept payments, the sum of the amount values from the routing object must equal the top-level amount!
IMPORTANT! When using two-step payment, the routing object is added to the second request with "action": "pay"
The payer can enter card details on the payment page or the embedded Mandarin Custom Pay payment form.
| Parameter | Type | Required | Description |
|---|---|---|---|
| routing | Yes | An object containing routing parameters. | |
| routing.destination | string | Yes | An array containing payment recipients. |
| routing.destination. accountId | string | Yes | Recipient account ID. |
| routing.destination. amount.value | string | Yes | The amount transferred to the recipient's account. For accepting payments, the platform fee will be deducted from this amount. Separator is a period. |
| routing.destination.amount.currency | string | Yes | The currency of the amount transferred to the recipient's account. Currently, it is always RUB. |
| routing.destination.platformFeeAmount.value | string | Yes | The platform fee, deducted from the amount transferred to the recipient's account. Separator is a period. |
| routing.destination.platformFeeAmount.currency | string | Yes | The currency of the platform fee. Currently, it is always RUB. |
| routing.destination.description | string | No | Description |
In the example below, 5,000 rubles are debited from the payer's card and will be credited to the following recipients:
- 3,950 rubles to account 16f90c5e-6bc3-11eb-9439-0242ac130002, which belongs to a legal entity.
- 50 rubles to the platform account.
- 950 rubles to account 8ba85f01-8cc8-4161-b45d-ce6442e678ae, which belongs to another legal entity.
- another 50 rubles to the platform account. Total: 100 rubles to the platform account.
Request one-step payment
POST https://api.mandarin.io/secure/transactions
{
"payment": {
"action": "pay",
"orderId": "your_unique_order_id",
"amount": {
"value": "5000.00",
"currency": "RUB"
},
"orderActualTill": "2020-02-20 12:34:56+00:00"
},
"routing": {
"destination": [{
"accountId": "16f90c5e-6bc3-11eb-9439-0242ac130002",
"amount": {
"value": "4000.00",
"currency": "RUB"
},
"platformFeeAmount": {
"value": "50.00",
"currency": "RUB"
},
"description": ""
},
{
"accountId": "8ba85f01-8cc8-4161-b45d-ce6442e678ae",
"amount": {
"value": "1000.00",
"currency": "RUB"
},
"platformFeeAmount": {
"value": "50.00",
"currency": "RUB"
},
"description": ""
}
]
},
"customerInfo": {
"email": "user@example.com",
"phone": "+79001234567"
}
}
The response to the request matches the standard response for creating transactions.
Response if the transaction is successfully created (200 OK)
{
"id": "43913ddc000c4d3990fddbd3980c1725",
"userWebLink": "https://secure.mandarinpay.com/Pay?transaction=0eb51e74-e704-4c36-b5cb-8f0227621518",
"jsOperationId": "9874694yr87y73e7ey39ed80"
}
Response if the transaction is not created (400 Bad request)
{
"error": "Invalid request"
}
# Tokenization (Routing)
Standard tokenization requests are used for tokenization. The card token can then be used to create recurring charges, payments with a saved card in interactive mode, or payments with a saved card without entering the CVV code and without completing the checkout process. 3d-secure](api_payments.md#payment-by-saved-card-without-entering-cvv-cvc-code-and-without-passing-3d-secure)
# Payment Cancellation (Routing)
Standard API requests for refunds on previously completed payments are used, in accordance with the documentation.
To cancel a successful card debit transaction ("action": "pay"), use "action": "reversal" and the id of the previously completed transaction as target.transaction.
In this case, the routing.source object is added to the pay request, containing the refund distribution scheme between Sub-merchant accounts. To accept payments: the sum of the amount values from the routing object must equal the top-level amount!
Cancellation is possible for the entire The transaction amount, as well as a partial cancellation (partial cancellation). An unlimited number of partial cancellations of a single payment transaction is allowed within the payment amount. Active participation of the payer is not required.
| Parameter | Required | Parameter | Required | |
|---|---|---|---|---|
| routing | Yes | An object containing routing parameters. | ||
| routing.source | string | Yes | An array containing payers. | |
| routing.source.accountId | string | Yes | The payer's account identifier. | |
| routing.source.amount.value | string | Yes | The amount transferred to the payer's account. Separator is a period. | |
| routing.source.amount.currency | string | Yes | The currency of the amount debited from the account Payer's Fee. Currently always RUB. | |
| routing.source.platformFeeAmount. value | string | Yes | Used when refunding the retained fee; can be 0. Separator: period. | |
| routing.source.platformFeeAmount. currency | string | Yes | Platform fee currency. Currently always RUB. | |
| routing.source.description | string | No | Description. |
The synchronous response and asynchronous callback-notification may contain a wider range of parameters than in the example.
Request
POST https://secure.mandarinpay.com/api/transactions
{
"payment": {
"action": "reversal",
"orderId": "your_unique_order_id",
"price": "5000.00"
},
"target": {
"transaction": "43913ddc000c4d3990fddbd3980c1725"
},
"customValues": [
{"name": "first parameter to save and show", "value": "p1"},
{"name": "second parameter to save and show", "value": "p2"}
],
"metadata": {
"first_parameter_to_callback_and_not_to_show": "p1",
"second_parameter_to_callback_and_not_to_show": "p2"
},
"routing": {
"source": [{
"accountId": "16f90c5e-6bc3-11eb-9439-0242ac130002",
"amount": {
"value": "4000.00",
"currency": "RUB"
},
"platformFeeAmount": {
"value": "50.00",
"currency": "RUB"
},
"description": ""
},
{
"accountId": "8ba85f01-8cc8-4161-b45d-ce6442e678ae",
"amount": {
"value": "1000.00",
"currency": "RUB"
},
"platformFeeAmount": {
"value": "50.00",
"currency": "RUB"
},
"description": ""
}
]
},
"urls": {
"callback": "http://...",
"return": "http://..."
}
}
Reply if successful Transaction creation (200 OK)
{
"id": "43913ddc000c4d3990fddbd3980c1725"
}
Response if the transaction is not created (400 Bad request)
{
"error": "Invalid request"
}
# Funds Transfer (Routing)
Funds are transferred to Sub-merchants automatically the next business day after the payment date.
# Working with Virtual Accounts
# Creating and Maintaining Virtual Accounts
# Creating a Virtual Account (ESP)
Production
https://payment-tokens.mandarin.io/api/v1/tokens/generate
Sandbox
https://sandbox-payment-tokens.mandarin.io/api/v1/tokens/generate
Request
curl --location 'https://payment-tokens.mandarin.io/api/v1/tokens/generate'\
--header 'Mid: 395' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer Token' \
--data-raw '{
"first_name": "Ivan",
"last_name": "Ivanov",
"middle_name": "Ivanovich",
"birth_date": "1990-12-22",
"citizenship": "RU",
"registration_address": "Moscow, Petrovka st., 2, apt. 1",
"living_address": "Moscow, Petrovka st., 2, apt. 1",
"document": {
"document_type": "Passport",
"serial": "1234",
"number": "123456",
"issue_date": "2002-04-24T15:23:14.969Z",
"birth_place": "Moscow",
"issuer": "Fili Davydkovo District Department of Internal Affairs",
"issue_code": "404-004",
"expiration_date": null
},
"inn": "1234567890",
"snils": "140-120-150 35",
"phones": [
{
"phone": "79004001234",
"type": "Personal"
}
],
"email": "ivanoff@mail.ru",
"phone_check": true,
"terms_agreement": true,
"is_public_official_person": false,
"presence_of_beneficiary": false,
"beneficiary_information": false,
"exist_fatf_government_bills": false,
"affiliation_with_foreign_taxpayers": false
}'
Response in case of a successful request to create a virtual account (VAT) (200 OK)
{
"id": "06dca2f1-4e1c-44e7-8848-3e9e3adba875",
"status": "Processing",
"created_at": "2025-11-21T10:33:27.021823Z",
"processed_at": null,
"finished_at": null
}
# Checking VAT status
Request
GET https://payment-tokens.mandarin.io/api/v1/tokens/{payment_token_id}/status
Response if the status is successfully received (200 OK)
{
"id": "06dca2f1-4e1c-44e7-8848-3e9e3adba875",
"status": "Success",
"created_at": "2025-11-21T10:33:27.021823Z",
"processed_at": "2025-11-21T10:33:32.431478Z",
"finished_at": "2025-11-21T10:33:33.022295Z"
}
# Balance Receive ESP
Request
curl --location 'https://payment-tokens.mandarin.io/api/v1/tokens/{{payment_token_id}}/wallet/balance' \
--header 'Authorization: Bearer Token' \
--header 'Mid: 3019'
Response if balance is successfully received (200 OK)
{
"wallet_id": "fsdfrg3amt",
"balance": 1500075,
"currency": "RUB"
}
# Payment Transactions
All payment transactions are processed through a single endpoint.
POST https://secure.mandarinpay.com/api/transactions
The operation is asynchronous. The final status is transmitted in a callback notification.
# Internal Operations
Depositing funds to an ESP (business → wallet)
Request
curl --location 'https://secure.mandarinpay.com/api/transactions' \
--header 'Content-Type: application/json' \
--header 'Mid: 777' \
--header 'Authorization: Bearer Token' \
--data-raw '{
"payment": {
"action": "payout",
"orderId": "your_unique_order_id",
"price": "10.00",
"orderActualTill": "2026-02-20 12:34:56+00:00"
},
"target": {
"paymentToken": "payment_token_id"
},
"customerInfo": {
"email": "user@example.com",
"phone": "+79001234567"
},
"customValues": [
{ "name": "first parameter to save and show", "value": "p1" },
{ "name": "second parameter to save and show", "value": "p2" }
],
"metadata": {
"first_parameter_to_callback_and_not_to_show": "p1",
"second_parameter_to_callback_and_not_to_show": "p2"
},
"urls": {
"callback": "http://...",
"return": "http://..."
}
}'
Response in case of successful transaction creation (200 OK)
{
"id": "43913ddc000c4d3990fddbd3980c1725",
"userWebLink": "https://secure.mandarinpay.com/Pay?transaction=0eb51e74-e704-4c36-b5cb-8f0227621518",
"jsOperationId": "9874694yr87y73e7ey39ed80"
}
Response if the transaction is not created (400 Bad Request)
{
"error": "Invalid request"
}
Withdrawing funds from the ESP to a business account (wallet → business)
Request
curl --location 'https://secure.mandarinpay.com/api/transactions'\
--header 'Content-Type: application/json' \
--header 'Mid: 777' \
--header 'Authorization: Bearer Token' \
--data-raw '{
"payment": {
"action": "pay",
"orderId": "your_unique_orde1f313fr_id",
"price": "10.00",
"orderActualTill": "2026-02-20 12:34:56+00:00"
},
"target": {
"paymentToken": "payment_token_id"
},
"customerInfo": {
"email": "user@example.com",
"phone": "+79001234567"
},
"customValues": [
{ "name": "first parameter to save and show", "value": "p1" },
{ "name": "second parameter to save and show", "value": "p2" }
],
"metadata": {
"first_parameter_to_callback_and_not_to_show": "p1",
"second_parameter_to_callback_and_not_to_show": "p2"
},
"urls": {
"callback": "http://...",
"return": "http://..."
}
}'
Response in case of successful transaction creation (200 OK)
{
"id": "9b3c8a1f7e52d4096b0c2f8d3a5e17b4",
"userWebLink": "https://secure.mandarinpay.com/Pay?transaction=f2a85b1c-3e9d-4871-a0b4-6c83d9f1e502",
"jsOperationId": "p3j8m267t1n9f4k5d0q8b3v6"
}
Response if the transaction is not created (400 Bad request)
{
"error": "Invalid request"
}
# External transactions (withdrawals from ESP)
To a card by card number (wallet → knownCardNumber)
Request
curl --location 'https://secure.mandarinpay.com/api/transactions' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer Token' \
--data-raw '{
"payment": {
"action": "payout",
"orderId": "1756292752",
"price": "100"
},
"customerInfo": {
"email": "noreply@example.ru",
"phone": "+72244861047"
},
"source": {
"paymentToken": "63eed817-c56e-4aac-ad38-15113ed13744"
},
"destination": {
"knownCardNumber": "2202202244861047"
}
}'
Response if the transaction was successfully created (200 OK)
{
"id": "e7a2f1b409d83c6a25e0b8d14c9f3a72",
"userWebLink": "https://secure.mandarinpay.com/Pay?transaction=7d14c9a3-0b8e-42f6-95c1-e3a6f0d8b247",
"jsOperationId": "k5h8293xq4m7p1tg6r0n8s2v3"
}
Response if the transaction was not created (400 Bad request)
{
"error": "Invalid request"
}
To a bound card (wallet → bound card)
Request
curl --location 'https://secure.mandarinpay.com/api/transactions' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer Token' \
--data-raw '{
"payment": {
"action": "payout",
"orderId": "1756292807",
"price": "100"
},
"customerInfo": {
"email": "noreply@example.ru",
"phone": "+72244861047"
},
"source": {
"paymentToken": "63eed817-c56e-4aac-ad38-15113ed13744"
},
"destination": {
"card": "63eed817-c56e-4aac-ad38-15113ed13722"
}
}'
Response if the transaction was successfully created (200 OK)
{
"id": "a7f4c2098e3b15d60f8a2e1b7c390d4f",
"userWebLink": "https://secure.mandarinpay.com/Pay?transaction=1d8f3b9a-6e05-4c12-9a7d-0c4a8b5f3e12",
"jsOperationId": "j384m76t9k5f1d2w0n8q7x3r5"
}
Response if the transaction is not created (400 Bad request)
{
"error": "Invalid request"
}
On SBP (1-step)
curl --location 'https://secure.mandarinpay.com/api/transactions' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer Token' \
--data-raw '{
"payment": {
"action": "payout",
"orderId": "1756292895",
"price": "100"
},
"customerInfo": {
"phone": "+7926087626",
"email": "test@example.ru",
"firstName": "Ivan",
"middleName": "Valerievich",
"lastName": "Ivanov"
},
"source": {
"paymentToken": "63eed817-c56e-4aac-ad38-15113ed13744"
},
"destination": {
"sbp": {
"bankId": "100000000008",
"bankBic": "044525593"
}
}
}'
Response if the transaction is successfully created (200 OK)
{
"id": "43913ddc000c4d3990fddbd3980c1725",
"userWebLink": "https://secure.mandarinpay.com/Pay?transaction=0eb51e74-e704-4c36-b5cb-8f0227621518",
"jsOperationId": "9874694yr87y73e7ey39ed80"
}
Response if the transaction is not created (400 Bad Request)
{
"error": "Invalid request"
}
On the FPS with confirmation (2-step)
Creating a payout
curl --location 'https://secure.mandarinpay.com/api/transactions' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer Token' \
--data-raw '{
"payment": {
"action": "payout",
"orderId": "1756291493",
"price": "100"
},
"customerInfo": {
"phone": "+7926087626",
"email": "test@example.ru",
"firstName": "Ivan",
"middleName": "Valerievich",
"lastName": "Ivanov"
},
"source": {
"paymentToken": "63eed817-c56e-4aac-ad38-15113ed13744"
},
"destination": {
"sbp": {
"bankId": "100000000008",
"bankBic": "044525593"
}
}
}'
Response if the transaction was successfully created (200 OK)
{
"id": "e8f2a1b90c7d34f562a8e0b9d1c7f3a5",
"userWebLink": "https://secure.mandarinpay.com/Pay?transaction=9d4c7a1f-3b28-4e65-8f09-2a5b6c0d7e31",
"jsOperationId": "z5q8n2r7k1m4f9t0p3w6b8x2v"
}
Response if the transaction was not created (400 Bad request)
{
"error": "Invalid request"
}
Receiving Full Name
POST https://secure.mandarinpay.com/api/sbp/{id}/status
Payment Confirmation
POST https://secure.mandarinpay.com/api/sbp/{id}/confirm
Payment Rejection
POST https://secure.mandarinpay.com/api/sbp/{id}/reject
# Transit Transactions
Payout to Card with Transit via ESP
Request
curl --location 'https://secure.mandarinpay.com/api/transactions'\
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer Token' \
--data-raw '{
"payment": {
"action": "payout",
"orderId": "1756291886",
"price": "100"
},
"customerInfo": {
"email": "noreply@example.ru",
"phone": "+72244861047"
},
"transit": {
"paymentToken": "63eed817-c56e-4aac-ad38-15113ed13744"
},
"target": {
"card": "63eed817-c56e-4aac-ad38-15113ed13722"
}
}'
Response if the transaction was successfully created (200 OK)
{
"id": "8c3f9a2d1e7b0456f0c8d2e5a1b7f3d9",
"userWebLink": "https://secure.mandarinpay.com/Pay?transaction=0eb51e74-e704-4c36-b5cb-8f0227621518",
"jsOperationId": "9874694yr87y73e7ey39ed80"
}
Response if the transaction was not created (400 Bad request)
{
"error": "Invalid request"
}
# Payment Transactions
Topping up an ESP with a linked card
Request
curl --location 'https://secure.mandarinpay.com/api/transactions' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer Token' \
--data-raw '{
"payment": {
"action": "pay",
"orderId": "1756293713",
"price": "1"
},
"customerInfo": {
"email": "noreply@example.ru",
"phone": "+72244861047"
},
"source": {
"card": "63eed817-c56e-4aac-ad38-15113ed13722"
},
"destination": {
"paymentToken": "63eed817-c56e-4aac-ad38-15113ed13721"
}
}'
Response if the transaction is successfully created (200 OK)
{
"id": "43913ddc000c4d3990fddbd3980c1725",
"userWebLink": "https://secure.mandarinpay.com/Pay?transaction=0eb51e74-e704-4c36-b5cb-8f0227621518",
"jsOperationId": "9874694yr87y73e7ey39ed80"
}
Response if the transaction is not created (400 Bad request)
{
"error": "Invalid request"
}
Important notes for merchants
All scenarios are implemented by a sequence of calls to existing API methods. There are no separate endpoints for "payout," "acceptance," "refinancing," or "funds distribution." Scenarios are generated by platform-side logic. The same API paths are used for sandbox and production. The differences lie in the service domains and the merchant_id and secret values. All operations are asynchronous and require callback notifications.