# BaaS

# Payment routing

# Authentication

Requests to the routing API are authenticated by a header x-auth. Formation of the value is in the Request Authentication.

To connect the service and get started, you need to contact Support Service (opens new window) or your supervising manager (opens new window).

# Sub-merchant registration

A sub-merchant can be a legal entity or an individual entrepreneur.

To get started, the Sub-merchant must independently create a Mandarin Personal Account through the standard registration (opens new window) procedure, going through the steps from (1) submitting an application to (4) completing the processing of the questionnaire.

In addition to the main process of registering a Personal Account, mass establishment of Personal Accounts through the registry is available. To use this script, please contact your supervising manager.

After approval of your Personal Account, you can proceed to the procedure of linking the created Sub-merchant Personal Account to your Marketplace account

# Creating a sub-merchant account

To create a Sub-merchant account, use token, which is available in the Sub-merchant’s Personal Account in the Routing section of the Project Settings.

Parameter Type Required Description
accountType string Yes Account type
for a legal entity takes on the valuebusiness.
token string Yes Legal entity account token.

The synchronous response contains id(account ID).

Request

curl --request POST \
  --url https://secure.mandarinpay.com/api/v1/accounts/business \
  --header 'Content-Type: application/json' \
--header 'x-auth: {{x_auth}}' \
--data-raw '{

	"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXJjaGFudF9pZCI6MiwiaXNzIjoiMzAg0LzQsNGPIDIwMjIg0LMuIn0.unGctyIBkp4EHXw-7bFqWbbkmfhs2yCJ-jAKJGqRP_1"

}'

Response if of successful account creation (200 OK)

{
	"id": "01dab7d8-be9b-4f87-ba91-801731787725",
	"type": "Business"
}

Response if the request has not been created (400 Bad request)

{
	"error": "Invalid request"  
}

# Accepting payments (Routing)

Standard API requests are used to accept payments, in accordance with documentation. Possible single-stage payment, recurrent payment (auto-debit) and two-stage payment (in this case, authorization remains standard, and distribution of payments occurs upon completion of settlements).

In this case, the pay object is added to the requestrouting.destination, containing the payment distribution scheme. To accept payments: sum of values amountfrom object routing``must be equal to the top-levelamount`!

IMPORTANT! When using two-stage payment, the object routing``added to the second request with"action": "pay"`

The payer can enter card details on payment page or 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 The recipient's account ID.
routing.destination. amount.value string Yes The amount transferred to the recipient's account. To accept payments: the platform commission will be deducted from this amount. The separator is a dot.
routing.destination. amount.currency string Yes Currency of the amount transferred to the recipient's account. Now alwaysRUB.
routing.destination. platformFeeAmount. value string Yes Platform commission, based on the amount transferred to the recipient’s account. The separator is a dot.
routing.destination. platformFeeAmount. currency string Yes Platform commission currency. Now alwaysRUB.
routing.destination. description string No Description.

In the example below, 5,000 rubles are debited from the payer’s card, which will be credited to the following recipients:

  • 3950 rubles per account16f90c5e-6bc3-11eb-9439-0242ac130002owned by a legal entity.
  • 50 rubles per platform account.
  • 950 rubles per account8ba85f01-8cc8-4161-b45d-ce6442e678aewhich belongs to another legal entity.
  • another 50 rubles per platform account. Total 100 rubles per platform account.

Request one-step payment

curl --request POST \
  --url https://secure.mandarinpay.com/api/transactions \
  --header 'Content-Type: application/json' \
--header 'x-auth: {{x_auth}}' \
--data-raw '{
	"payment": {
		"action": "pay",
		"orderId": "your_unique_order_id",
		"price": "5000.00"
		},
		"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 corresponds to the standard response for creating transactions.

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"
}

Answer if the transaction is not created (400 Bad request)

{
	"error": "Invalid request"  
}

# Tokenization (Routing)

For tokenization, standard tokenization requests are used. Subsequently, the card token can be used to create recurrent debits, payments with a saved card in interactive mode or payments with a saved card without entering a cvv code and without passing 3d-secure

# Payment cancellation (Routing)

Standard API requests are used to refund a previously completed payment, in accordance with documentation.

To cancel a successful transaction to debit funds from your card ("action": "pay") use"action": "reversal"Andidpreviously completed transaction astarget.transaction

In this case, the pay object is added to the requestrouting.source, containing a scheme for distributing returns between Sub-merchant accounts. To accept payments: sum of values amountfrom object routing``must be equal to the top-levelprice`!

Cancellation is possible both for the entire transaction amount and for part of the amount (partial cancellation). An unlimited number of partial cancellations of one payment transaction is allowed within the payment amount. The 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 Payer account ID.
routing.source.amount.value string Yes The amount transferred to the payer's account. The separator is a dot.
routing.source.amount.currency string Yes Currency of the amount debited from the payer's account. Now alwaysRUB.
routing.source.platformFeeAmount. value string Yes Used when returning the withheld commission, can be equal to 0. The separator is a dot.
routing.source.platformFeeAmount. currency string Yes Platform commission currency. Now alwaysRUB.
routing.source.description string No Description.

Synchronous response and asynchronous callback-notification can contain a wider set of parameters compared to the example.

Request

curl --request POST \
  --url https://secure.mandarinpay.com/api/transactions \
  --header 'Content-Type: application/json' \
  --header 'x-auth: {{x_auth}}' \
  --data-raw '{
	"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://..."
	}
}'

Response in case of successful transaction creation (200 OK)

{
	"id": "43913ddc000c4d3990fddbd3980c1725"
}

Answer if the transaction is not created (400 Bad request)

{
	"error": "Invalid request"
}

# Transfer of funds (Routing)

Funds are transferred to Sub-merchants automatically on the next business day after the payment date.

# Working with virtual accounts

TESTING

Before integration, obtain test credentials and scripts in the BaaS section: OAuth application, Sandbox, test names for creating ESP.

# Environments

Two environments are available:

Environment Base URL
Sandbox https://sandbox-payment-tokens.mandarin.io
Production https://payment-tokens.mandarin.io

Sandbox is used for integration testing.


# Integration process

  1. Getaccess_token2. Create ESP (generate)
  2. Send SMS code (send)
  3. User enters code
  4. Confirm the code (verify)
  5. Get ESP status (status) or via webhook

# Authentication

Requests to the virtual account API are authenticated using the OAuth 2.0 (Bearer) protocol. Token generation is in the Request Authentication.

The examples below use the template--header 'Authorization: Bearer '.

Request for a token

curl --request POST \
  --url https://accounts.mandarin.io/oauth/token/ \
  --form 'grant_type=client_credentials' \
--form 'client_id={{client_id}}' \
--form 'client_secret={{client_secret}}' \
--form 'scope=payment-tokens:tokens.write payment-tokens:tokens.read payment-tokens:otp.write'

In the parameterscopepass rights for called API methods, separated by a space. Example:payment-tokens:tokens.write payment-tokens:tokens.read payment-tokens:otp.write.


# Creating a virtual account (ESP)

# Entry points

Scope required:payment-tokens:tokens.write

Sandbox:POST https://sandbox-payment-tokens.mandarin.io/api/v1/tokens/generateProduction:POST https://payment-tokens.mandarin.io/api/v1/tokens/generate

Request

curl --request POST \
  --url https://payment-tokens.mandarin.io/api/v1/tokens/generate \
  --header 'Mid: {{mid}}' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{access_token}}' \
--data-raw '{
    "first_name": "Николай",
    "last_name": "Николаев",
    "middle_name": "Николаевич",
    "birth_date": "1993-12-22",
    "citizenship": "RU",
    "registration_address": "г. Москва, ул. Петровка, д.2, кв.1",
    "living_address": "г. Москва, ул. Петровка, д.2, кв.1",
    "document": {
        "document_type": "Passport",
        "serial": "1233",
        "number": "123458",
        "issue_date": "2002-04-24T15:23:14.969Z",
        "birth_place": "г. Москва",
        "issuer": "ОВД района Фили Давыдково",
        "issue_code": "404-004",
        "expiration_date": null
    },
    "inn": "777777777777",
    "snils": "140-120-150 35",
    "phones": [
        {
            "phone": "79017636353",
            "type": "Personal"
        }
    ],
    "email": "ivanoff@mail.ru",
    "fax": "string",
    "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 if the creation request was successfully sent (200 OK)

{
  "id": "f289271a-914d-4987-88c4-d7cc66482c75",
  "status": "Processing",
  "created_at": "2026-03-16T10:00:36.0018804Z",
  "processed_at": null,
  "finished_at": null
}

Fieldidused aspayment_token_idin subsequent requests.


# Sending to OTP

Sends an SMS confirmation code.

Info

  • standard code is valid for 2 minutes;
  • if the code is entered incorrectly, then minus the attempt and you need to request a new code;
  • only 10 attempts to enter the code.

POST https://payment-tokens.mandarin.io/api/v1/otp/{payment_token_id}/send

Request

curl --request POST \
  --url https://payment-tokens.mandarin.io/api/v1/otp/{payment_token_id}/send \
  --header 'Authorization: Bearer {{access_token}}'

Scope required:payment-tokens:otp.write

Response in case of successful sending of SMS code (200 OK)```json { "status": "pending", "retries": 10, "channel": "sms", "channel_status": "queued" }


**Description`channel_status`:**

| Meaning | Description |
|----------|----------|
| queued | Message queued for sending |
| delivered | Message delivered successfully |
| unknown | Delivery status unknown or delivery not confirmed |

> **Note:**
> - field`channel_status`is of an informational nature;
> - delivery status does not affect the success of OTP.

---

### OTP verification

Confirms SMS code.

`POST https://payment-tokens.mandarin.io/api/v1/otp/{payment_token_id}/verify`

**Request**

```bash
curl --request POST \
  --url https://payment-tokens.mandarin.io/api/v1/otp/{payment_token_id}/verify \
  --header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{access_token}}' \
--data-raw '{
  "code": "0153"
}'

Scope required:payment-tokens:otp.write

Response in case of successful code confirmation (200 OK)```json { "verified": true, "retries": 9, "channel": "sms", "channel_status": "delivered" }


---

### Obtaining OTP status

`GET https://payment-tokens.mandarin.io/api/v1/otp/{payment_token_id}/status`

The method allows you to get:

- current OTP status;
- number of remaining attempts;
- SMS delivery status.

**Request**

```bash
curl --request GET \
  --url https://payment-tokens.mandarin.io/api/v1/otp/{payment_token_id}/status \
  --header 'Authorization: Bearer {{access_token}}'

Scope required:payment-tokens:otp.write

OTP awaiting confirmation (200 OK):```json { "status": "pending", "retries": 9, "channel_status": "queued" }


**OTP successfully confirmed (`200 OK`):**```json
{
  "status": "correct",
  "retries": 9,
  "channel_status": "delivered"
}

Description of valuesstatus:

Meaning Description
pending Pending status
correct Successful Confirmation Status

Description of valueschannel_status:

Meaning Description
queued Pending confirmation
delivered Successfully confirmed
unknown Status unknown or message not confirmed

# Checking the ESP status

GET https://payment-tokens.mandarin.io/api/v1/tokens/{payment_token_id}/status

Request

curl --request GET \
  --url https://payment-tokens.mandarin.io/api/v1/tokens/{payment_token_id}/status \
  --header 'Authorization: Bearer {{access_token}}'

Scope required:payment-tokens:tokens.read

Response if the creation request was successfully sent (200 OK)```json { "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" }


**Description of values`status`:**

| Status | Description |
|--------|-----------|
| Processing | wallet is created |
| Success | wallet activated |
| Failed | creation error |
| Blocked | wallet blocked |
| Suspended | wallet suspended |
| Deleted | wallet deleted (final status, recovery impossible) |

---

### Webhooks

To receive notifications about the status of the wallet, you can set up a webhook.

Webhook is sent using the POST method.

**Payload:**```json
{
  "event_id": "17c5fdaf-3570-4d77-8966-6ae4505c2454",
  "payment_token_id": "98a4a683-305e-4664-8d96-5f51d9e668f6",
  "status": "Success",
  "created_at": "2026-03-04T14:44:57.079171Z"
}

# Removing a blocked ESP

The method deletes an electronic wallet (ESP) in the statusBlockedand transfers it to final statusDeleted. Deletion is irreversible: ESP cannot be restored.

It is used when the ESP is blocked due to unsuccessful identification (SMEV) and a new wallet with corrected client data needs to be issued. Blocked ESP cannot be used in payments and disbursements; Unlocking via API is not supported.

DELETE https://payment-tokens.mandarin.io/api/v1/tokens/{payment_token_id}/wallet

The request body is not transmitted.payment_token_id— payment token Mandarin identifier (GUID).

Required scope:payment-tokens:tokens.write

Request

curl --request DELETE \
  --url https://payment-tokens.mandarin.io/api/v1/tokens/{{payment_token_id}}/wallet \
  --header 'Authorization: Bearer {{access_token}}' \
  --header 'Mid: {{mid}}'

Response in case of successful deletion (200 OK)

{
  "id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
  "status": "Deleted",
  "created_at": "2026-07-24T12:00:00Z",
  "processed_at": "2026-07-24T12:01:00Z",
  "finished_at": "2026-07-24T15:00:00Z"
}
Field Type Description
id string Payment token ID
status string Final status. If successful -Deleted
created_at string Token creation date (ISO 8601)
processed_at string Date of last processing (ISO 8601)
finished_at string End of life date (ISO 8601)

WARNING

The method is executed only for ESP in the statusBlocked. For any other status, the API returns409 Conflict. The token must belong to the MID from the header Mid.

Response in case of error

HTTP code Description
401 Unauthorized Invalid or expired access token
403 Forbidden No scopepayment-tokens:tokens.writeor the token does not belong to MID
404 Not Found payment_token_idnot found
409 Conflict ESP status is notBlocked
502 / 503 Temporary provider error; ESP status in Mandarin does not change

Upon successful deletion, if a webhook is configured for the project, you will receive a notification with the statusDeleted- see Webhooks.

# Reissue of ESP

  1. Check status:GET /api/v1/tokens/{payment_token_id}/status(scopepayment-tokens:tokens.read). It makes sense to delete and re-release when blocked due to SMEV (SMEV_PASSPORT_INVALID,SMEV_INN_INVALID,SMEV_SNILS_INVALID). AtSANCTIONS_LIST_MATCHorBLOCKED_UNKNOWNContact Customer Service (opens new window).
  2. Call delete - see request above. The operation is irreversible, oldpayment_token_idgoes intoDeletedand is no longer used in operations.
  3. Create a new ESP with corrected data: Creating a virtual account (ESP) → OTP (send/verify) → waitSuccessin Checking ESP status. Newpayment_token_iduse in payments and disbursements as usual.

If the new ESP is blocked again, the client’s data most likely still does not pass the SMEV check. Check your passport details; If you are blocked again, please contact support.


# Obtaining ESP balance

GET https://payment-tokens.mandarin.io/api/v1/tokens/{payment_token_id}/wallet/balance

Request

curl --request GET \
  --url https://payment-tokens.mandarin.io/api/v1/tokens/{{payment_token_id}}/wallet/balance \
  --header 'Authorization: Bearer {{access_token}}' \
  --header 'Mid: {{mid}}'

Response in case of successful receipt of balance (200 OK)

{
  "wallet_id": "fsdfrg3amt",
  "balance": 1500075,
  "currency": "RUB"
}

# Payment transactions

All payment transactions are performed through a single endpoint:POST https://secure.mandarinpay.com/api/transactions.

The operation is asynchronous. The final status is transmitted in the callback notification.

# Internal Operations

Depositing funds to ESP (business → wallet)

Request

curl --request POST \
  --url https://secure.mandarinpay.com/api/transactions \
--header 'Content-Type: application/json' \
--header 'Mid: {{mid}}' \
--header 'Authorization: Bearer {{access_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"
}

Answer if the transaction is not created (400 Bad request)

{
	"error": "Invalid request"  
}

Debiting funds from ESP to business account (wallet → business)

Request

curl --request POST \
  --url https://secure.mandarinpay.com/api/transactions \
--header 'Content-Type: application/json' \
--header 'Mid: {{mid}}' \
--header 'Authorization: Bearer {{access_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"
}

Answer if the transaction is not created (400 Bad request)

{
	"error": "Invalid request"  
}
```---
#### External operations (withdrawal of funds from ESP)

**To card by card number (wallet → knownCardNumber)**

**Request**

```bash
curl --request POST \
  --url https://secure.mandarinpay.com/api/transactions \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{access_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 in case of successful transaction creation (200 OK)

{
	"id": "e7a2f1b409d83c6a25e0b8d14c9f3a72",
	"userWebLink": "https://secure.mandarinpay.com/Pay?transaction=7d14c9a3-0b8e-42f6-95c1-e3a6f0d8b247",
	"jsOperationId": "k5h8293xq4m7p1tg6r0n8s2v3"
}

Answer if the transaction is not created (400 Bad request)

{
	"error": "Invalid request"  
}

To a bound card (wallet → bound card)

Request

curl --request POST \
  --url https://secure.mandarinpay.com/api/transactions \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{access_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 in case of successful transaction creation (200 OK)

{
	"id": "a7f4c2098e3b15d60f8a2e1b7c390d4f",
	"userWebLink": "https://secure.mandarinpay.com/Pay?transaction=1d8f3b9a-6e05-4c12-9a7d-0c4a8b5f3e12",
	"jsOperationId": "j384m76t9k5f1d2w0n8q7x3r5"
}

Answer if the transaction is not created (400 Bad request)

{
	"error": "Invalid request"  
}

On SBP (1-step)

curl --request POST \
  --url https://secure.mandarinpay.com/api/transactions \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{access_token}}' \
--data-raw '{
    "payment": {
        "action": "payout",
        "orderId": "1756292895",
        "price": "100"
    },
    "customerInfo": {
        "phone": "+7926087626",
        "email": "test@example.ru",
        "firstName": "Иван",
        "middleName": "Валерьевич",
        "lastName": "Иванов"
    },
    "source": {
        "paymentToken": "63eed817-c56e-4aac-ad38-15113ed13744"
    },
    "destination": {
        "sbp": {
            "bankId": "100000000008",
            "bankBic": "044525593"
        }
    }
}'

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"
}

Answer if the transaction is not created (400 Bad request)

{
	"error": "Invalid request"  
}

On SBP with confirmation (2-step)

Creating a payout

curl --request POST \
  --url https://secure.mandarinpay.com/api/transactions \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{access_token}}' \
--data-raw '{
    "payment": {
        "action": "payout",
        "orderId": "1756291493",
        "price": "100"
    },
    "customerInfo": {
        "phone": "+7926087626",
        "email": "test@example.ru",
        "firstName": "Иван",
        "middleName": "Валерьевич",
        "lastName": "Иванов"
    },
    "source": {
        "paymentToken": "63eed817-c56e-4aac-ad38-15113ed13744"
    },
    "destination": {
        "sbp": {
            "bankId": "100000000008",
            "bankBic": "044525593"
        }
    }
}'

Response in case of successful transaction creation (200 OK)

{
	"id": "e8f2a1b90c7d34f562a8e0b9d1c7f3a5",
	"userWebLink": "https://secure.mandarinpay.com/Pay?transaction=9d4c7a1f-3b28-4e65-8f09-2a5b6c0d7e31",
	"jsOperationId": "z5q8n2r7k1m4f9t0p3w6b8x2v"
}

Answer if the transaction is not created (400 Bad request)

{
	"error": "Invalid request"  
}

Receiving full name

curl --request POST \
  --url https://secure.mandarinpay.com/api/sbp/{id}/status \
  --header 'Authorization: Bearer {{access_token}}'

Payment confirmation

curl --request POST \
  --url https://secure.mandarinpay.com/api/sbp/{id}/confirm \
  --header 'Authorization: Bearer {{access_token}}'

Payment Rejected

curl --request POST \
  --url https://secure.mandarinpay.com/api/sbp/{id}/reject \
  --header 'Authorization: Bearer {{access_token}}'
```---
#### Transit operations

**Payment to card with transit through ESP**

**Request**

```bash
curl --request POST \
  --url https://secure.mandarinpay.com/api/transactions \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{access_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 in case of successful transaction creation (200 OK)

{
	"id": "8c3f9a2d1e7b0456f0c8d2e5a1b7f3d9",
	"userWebLink": "https://secure.mandarinpay.com/Pay?transaction=0eb51e74-e704-4c36-b5cb-8f0227621518",
	"jsOperationId": "9874694yr87y73e7ey39ed80"
}

Answer if the transaction is not created (400 Bad request)

{
	"error": "Invalid request"  
}
```---
#### Payment transactions

**Replenishment of ESP from a linked card**

**Request**

```bash
curl --request POST \
  --url https://secure.mandarinpay.com/api/transactions \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{access_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 in case of successful transaction creation (200 OK)

{
	"id": "43913ddc000c4d3990fddbd3980c1725",
	"userWebLink": "https://secure.mandarinpay.com/Pay?transaction=0eb51e74-e704-4c36-b5cb-8f0227621518",
	"jsOperationId": "9874694yr87y73e7ey39ed80"
}

Answer if the transaction is not created (400 Bad request)

{
	"error": "Invalid request"  
}

# Replenishment of ESP from a card and routing for sub-merchants (card2wallet2account)

The transit operation combines replenishment of ESP from a card and distribution of funds to sub-merchant accounts in one request.

The amount is debited from the payer's cardpayment.price. The specified part of the funds is distributed to the accounts of sub-merchants throughrouting.destination. The operation takes place through a wallet (ESP), the identifier of which is indicated indestination.paymentToken.

Sum of valuesamount.valueVrouting.destinationmay be less transaction amount (payment.price). The undistributed balance remains on the wallet.

Sum of allrouting.destination.amount.valuemust not exceedpayment.price.

The operation is asynchronous. The final status is transmitted in the callback notification.

Prerequisites

Before calling the method:

  1. Connect BaaS / routing and the card2wallet2account script via Help Desk (opens new window) or Supervising Manager (opens new window).
  2. Create and activate a wallet (ESP) - see Creating a virtual account (ESP). ESP must be in statusSuccess— see Checking ESP status.
  3. Create sub-merchants and get themaccountId— see [Sub-merchant registration](#sub-merchant registration) and [Creating a sub-merchant account](#sub-merchant account creation).

accountIdVrouting.destination— account ID from the responsePOST /api/v1/accounts/business, notmerchantIdfrom your Personal Account.

# Authentication

Requests are authenticated using the OAuth 2.0 protocol (Bearer). Token generation is in the Request Authentication.

Required scopes:

secure:transactions.write payment-tokens:tokens.read

The received token is transmitted in the headers:

Authorization: Bearer {{access_token}}
Mid: {{mid}}
Content-Type: application/json

# Entry point

POST https://secure.mandarinpay.com/api/transactions

# Request parameters

Параметр Тип Обязателен Описание
payment object Да Параметры платежа.
payment.action string Да Действие. Для данной операции: pay.
payment.orderId string Да Уникальный идентификатор заказа на стороне мерчанта.
payment.price string Да Сумма списания с карты. Разделитель — точка.
payment.orderActualTill string Нет Срок актуальности заказа. Формат: 2026-02-20 12:34:56+00:00.
customerInfo object Да Данные плательщика.
customerInfo.email string Да Email плательщика.
customerInfo.phone string Да Телефон плательщика.
destination object Да Назначение транзитного зачисления.
destination.paymentToken string Да Идентификатор кошелька (ЭСП) для пополнения.
routing object Да Объект, содержащий параметры роутинга. См. Прием платежей (Роутинг).
routing.destination array Да Массив получателей (саб-мерчантов).
routing.destination[].accountId string Да Идентификатор аккаунта саб-мерчанта.
routing.destination[].amount.value string Да Сумма, перечисляемая на аккаунт получателя. Разделитель — точка.
routing.destination[].amount.currency string Нет Валюта. Сейчас всегда RUB.
routing.destination[].platformFeeAmount.value string Нет Комиссия платформы с суммы получателя. Разделитель — точка.
routing.destination[].platformFeeAmount.currency string Нет Валюта комиссии. Сейчас всегда RUB.
routing.destination[].description string Нет Описание.
urls object Нет URL для callback и return.
urls.callback string Нет URL для асинхронного callback-уведомления.
urls.return string Нет URL для возврата пользователя после оплаты.
customValues array Нет Пользовательские параметры для отображения.
metadata object Нет Метаданные для callback.

Example: 100 ₽ is debited from the card: 20 ₽ - to sub-merchant 1, 30 ₽ - to sub-merchant 2. The balance 50 ₽ remains in the wallet.

Request

curl --request POST \
  --url https://secure.mandarinpay.com/api/transactions \
  --header 'Mid: {{mid}}' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer {{access_token}}' \
  --data-raw '{
    "payment": {
        "action": "pay",
        "orderId": "test6",
        "price": "100.00"
    },
    "customerInfo": {
        "email": "noreply@test.ru",
        "phone": "+79017636353"
    },
    "destination": {
        "paymentToken": "payment_token_id"
    },
    "routing": {
        "destination": [
            {
                "accountId": "id_сабмерчанта_1",
                "amount": {
                    "value": "20.00",
                    "currency": "RUB"
                },
                "description": "test"
            },
            {
                "accountId": "id_сабмерчанта_2",
                "amount": {
                    "value": "30.00",
                    "currency": "RUB"
                },
                "description": "test"
            }
        ]
    },
    "urls": {
        "callback": "https://example.com/callback",
        "return": "https://example.com/return"
    }
}'

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"
}

Answer if the transaction is not created (400 Bad request)

{
    "error": "Invalid request"
}

What happens after the request

  1. API returnsuserWebLink— link to payment page or embedded payment form.
  2. The payer enters card details.
  3. The final status comes asynchronously tourls.callback.
  4. Transfer of funds to sub-merchants to the current account - on the next business day, see Transfer of funds (Routing).
  5. The balance on your wallet can be checked through Receiving ESP balance.

Important notes

  • For the card2wallet2account operation partial routing is allowed: amount inrouting.destinationmaybe lesspayment.price.
  • If the routing amount exceeds the transaction amount, the request is rejected.
  • There is no separate endpoint for the operation - the scenario is formed by a combinationdestination,routingand a standard request to create a transaction.
  • Do not consider the payment successful based on a synchronous response200 OK— wait for the callback.

Important notes for merchants

All scenarios are implemented by a sequence of calls to existing API methods. There are no separate endpoints for “payment”, “acceptance”, “refinancing” or “distribution of funds”. Scenarios are generated by logic on the platform side. Sandbox and production use the same API paths. The difference lies in the service domains and merchant_id and secret values. All operations are asynchronous and require processing callback notifications.