H2H: Payments with Saved Cards
Create a payment using a saved paymentInstrumentToken instead of sending the card details again. Set tokenPaymentReason to identify whether the customer or the merchant initiates the payment.
Token payments are available for
BankCardRU. The token must belong to the same settlement account and customer as the payment. H2H tokenization must be enabled by the integration team.
To obtain a token, see Card Tokenization via API. To find a customer's saved cards, see Manage Saved Cards.
Choose CIT or MIT
| CIT | MIT | |
|---|---|---|
| Who initiates the payment | The customer | The merchant's system |
| Example | A customer selects a saved card and presses Pay for another purchase | A service charges a saved card after a journey ends |
tokenPaymentReason | CIT | MIT |
| Customer interaction | The customer is present; additional confirmation may be required | The payment runs without the customer taking part |
| Return URL | Required for the BankCardRU CIT request | Not required |
MIT supports unscheduled card-on-file payments: your system initiates a charge when the relevant business event occurs. There is no additional MIT subtype parameter.
You can use the same eligible saved-card token for CIT and MIT, including tokens saved previously through the API or Hosted Checkout.
An
ACTIVEcard status does not guarantee that a particular MIT payment can be processed or approved. The card details endpoint does not return a separate MIT-availability flag.
flowchart TD
A[Saved card token] --> B{Who initiates the payment?}
B -->|Customer| C[CIT request with a return URL]
C --> D{Additional confirmation required?}
D -->|Yes| E[Open the returned URL for the customer]
D -->|No| F[Retrieve the result with GET or receive a webhook]
E --> F
B -->|Merchant system| G[MIT request without customer interaction]
G --> F
Request requirements
Use POST /api/v1/intents, relative to your environment's processing base URL. See Access URLs and API Authentication.
Use the following headers for both CIT and MIT. The examples cover standard card payments. Include any additional fields required by your account, business category or the BankCardRU payment method.
Headers
| Header | Value |
|---|---|
Authorization | Bearer <access_token> |
settlement-account-id | The account in which the card is saved |
Content-Type | application/json |
Do not send the card number, expiry date, holder name or CVV alongside a saved-card token. Omit
savePaymentInstrument: the card is already saved, and combining a token withsavePaymentInstrument: trueis not allowed.
Customer initiated payment (CIT)
Use CIT when the customer chooses to make a payment with their saved card. Include a return URL so the customer can return to your application after any required confirmation.
A CIT payment may complete its authorisation without further interaction, or it may require the customer to follow an authentication flow. Using a saved card does not guarantee that 3DS will be skipped.
Request: POST /api/v1/intents
{
"clientReferenceId": "order-1002",
"payments": [
{
"payer": {
"merchantPayerReference": "12345"
},
"paymentInstrument": {
"paymentMethodName": "BankCardRU",
"paymentInstrumentToken": "pi_01J8ZC4M7QK3T5V9XB2NDR6HAG",
"tokenPaymentReason": "CIT",
"incomingDetails": {
"redirectUrl": "https://merchant.example/payment/return",
"failureUrl": "https://merchant.example/payment/failure"
}
},
"submittedAmount": {
"value": 500,
"currency": "RUB"
},
"authCurrencyCode": "RUB",
"webhookUrl": "https://merchant.example/webhooks/payments"
}
]
}Request body
| Field | Requirement |
|---|---|
clientReferenceId | Your reference for this Intent. Use a new value for each new Intent |
payments | The array of payments to create. The examples contain one payment |
payments[]
| Field | Requirement |
|---|---|
payer | Identifies the customer whose saved card is used |
paymentInstrument | Identifies the saved card, payment method and initiator |
submittedAmount | The payment amount and currency, using the usual payment rules |
authCurrencyCode | The authorisation currency, using the usual payment rules |
webhookUrl | The endpoint that receives payment notifications |
payments[].payer
| Field | Requirement |
|---|---|
merchantPayerReference | Required. The same customer reference used when the card was saved |
Any additional payer information required by the payment method or your account still applies.
payments[].paymentInstrument
| Field | Requirement |
|---|---|
paymentMethodName | Required. BankCardRU; must match the saved card |
paymentInstrumentToken | Required. The customer's saved pi_ token |
tokenPaymentReason | Required. CIT |
incomingDetails | Required. Carries the BankCardRU CIT return URL |
payments[].paymentInstrument.incomingDetails
| Field | Requirement |
|---|---|
redirectUrl | Required for the CIT example. The customer's return URL after confirmation |
failureUrl | Optional. The return URL after an unsuccessful confirmation flow |
Do not put card details in this object when paying by token.
payments[].submittedAmount
| Field | Description |
|---|---|
value | Payment amount. This example uses 500 |
currency | Submitted currency. This example uses RUB |
authCurrencyCode is a sibling field on the payment. The normal currency-conversion rules apply.
Response excerpt requiring customer confirmation. The URL is illustrative; use the value returned for the actual payment.
{
"intentId": "204264682781298710",
"payments": [
{
"id": "204264682781298711",
"status": "ACCEPTED",
"paymentInstrument": {
"paymentMethodName": "BankCardRU",
"paymentInstrumentToken": "pi_01J8ZC4M7QK3T5V9XB2NDR6HAG"
},
"additionalData": {
"details": {
"url": "https://checkout.example/verification/session-1002"
}
}
}
],
"paymentCreationErrors": []
}Response body
| Field | Description |
|---|---|
intentId | The created Intent identifier. Use it to retrieve the result |
payments | Created payment objects |
paymentCreationErrors | Per-payment creation errors. An empty array means none are reported in this example |
payments[]
| Field | Description |
|---|---|
id | The payment identifier |
status | The current payment status. ACCEPTED in this example is an intermediate result |
paymentInstrument | The saved card used for the payment |
additionalData | Data needed to continue the payment flow, when returned |
payments[].paymentInstrument
| Field | Description |
|---|---|
paymentMethodName | BankCardRU in these examples |
paymentInstrumentToken | The saved-card token used for this payment |
Paying by token does not request another save, so savingRequested and isSaved can be absent.
payments[].additionalData.details
| Field | Description |
|---|---|
url | Open this returned URL in the customer's browser or web view to continue the confirmation flow |
This field path describes the create-Intent response illustrated here.
Open the returned URL in the customer's browser or web view and follow the BankCardRU authorisation flow. Retrieve the payment result or receive its webhook after the customer finishes.
A browser return to your application is not itself confirmation of payment success. Check the payment result through GET or a webhook.
Merchant initiated payment (MIT)
Use MIT when your system charges a previously saved card without the customer taking part. Supply the saved token and tokenPaymentReason: "MIT". The standard MIT request can omit incomingDetails, return URLs and threeDSContext.
Request: POST /api/v1/intents
{
"clientReferenceId": "journey-1003",
"payments": [
{
"payer": {
"merchantPayerReference": "12345"
},
"paymentInstrument": {
"paymentMethodName": "BankCardRU",
"paymentInstrumentToken": "pi_01J8ZC4M7QK3T5V9XB2NDR6HAG",
"tokenPaymentReason": "MIT"
},
"submittedAmount": {
"value": 750,
"currency": "RUB"
},
"authCurrencyCode": "RUB",
"webhookUrl": "https://merchant.example/webhooks/payments"
}
]
}Request body
| Field | Requirement |
|---|---|
clientReferenceId | Your reference for this Intent. Use a new value for each new Intent |
payments | The array of payments to create. This example contains one payment |
payments[]
| Field | Requirement |
|---|---|
payer | Identifies the customer whose saved card is used |
paymentInstrument | The saved card, its payment method and the MIT reason |
submittedAmount | The payment amount and currency |
authCurrencyCode | The authorisation currency; RUB in the example |
webhookUrl | The endpoint that receives payment notifications |
threeDSContext is not required for MIT.
payments[].payer
| Field | Requirement |
|---|---|
merchantPayerReference | Required. The same customer reference used when the card was saved |
Any additional payer information required by the payment method or your account still applies.
payments[].paymentInstrument
| Field | Requirement |
|---|---|
paymentMethodName | Required. BankCardRU; must match the saved card |
paymentInstrumentToken | Required. The customer's saved pi_ token |
tokenPaymentReason | Required. MIT |
incomingDetails | Not required for this example. Return URLs are not required; include any noninteractive context required for your account |
Do not send card details or savePaymentInstrument: true when paying by token.
payments[].submittedAmount
| Field | Description |
|---|---|
value | Payment amount. This example uses 750 |
currency | Submitted currency. This example uses RUB |
authCurrencyCode is a sibling field on the payment. The normal currency-conversion rules apply.
Response excerpt after successful authorisation:
{
"intentId": "204264682781298720",
"payments": [
{
"id": "204264682781298721",
"status": "AUTHORIZED",
"paymentInstrument": {
"paymentMethodName": "BankCardRU",
"paymentInstrumentToken": "pi_01J8ZC4M7QK3T5V9XB2NDR6HAG"
}
}
],
"paymentCreationErrors": []
}Response body and payments[]
| Field | Description |
|---|---|
intentId | The created Intent identifier |
payments[].id | The created payment identifier |
payments[].status | The current payment status. AUTHORIZED confirms successful authorisation in this example |
payments[].paymentInstrument.paymentInstrumentToken | The saved token used for this MIT payment |
paymentCreationErrors | Per-payment creation errors, if any |
The response uses the same payment structure as CIT. The example has no customer-confirmation URL.
The initial response may contain an intermediate or an already available result.
MIT does not return an interactive 3DS step for the customer to complete. If the saved card cannot be used for MIT, the payment fails with the applicable error; it is not automatically converted into a CIT payment.
Get the payment result
For either CIT or MIT, use GET /api/v1/intents/{intentId}, GET /api/v1/payments/{paymentId}, or payment webhooks. The normal payment status model applies. See Payment Overview.
Request: GET /api/v1/intents/204264682781298720
No request body is required. Use the same account and API authentication as for the original Intent.
Response excerpt for a completed MIT payment:
{
"id": "204264682781298720",
"payments": [
{
"id": "204264682781298721",
"status": "CAPTURED",
"submittedAmount": {
"value": 750,
"currency": "RUB"
},
"authAmount": {
"value": 750,
"currency": "RUB"
},
"paymentInstrument": {
"paymentMethodName": "BankCardRU",
"paymentInstrumentToken": "pi_01J8ZC4M7QK3T5V9XB2NDR6HAG"
}
}
]
}Response body
| Field | Description |
|---|---|
id | The Intent identifier. GET uses id, while the create response uses intentId |
payments | The Intent's payments and their current results |
payments[]
| Field | Description |
|---|---|
id | The payment identifier |
status | The payment result. This example shows a completed payment with CAPTURED status |
submittedAmount | The amount and currency submitted by the merchant |
authAmount | The resulting authorisation amount and currency |
paymentInstrument | The payment method and saved token used. It does not indicate a new card save |
Both amount objects use value and currency. The example uses RUB for both amounts.
Payment webhook
The equivalent payment webhook uses transactionId for the payment identifier and includes isStoredCredential: true:
{
"transactionType": "PAYMENT",
"transactionId": "204264682781298721",
"intentId": "204264682781298720",
"status": "CAPTURED",
"isStoredCredential": true,
"paymentInstrument": {
"paymentMethodName": "BankCardRU",
"paymentInstrumentToken": "pi_01J8ZC4M7QK3T5V9XB2NDR6HAG"
}
}Webhook body
| Field | Description |
|---|---|
transactionType | PAYMENT |
transactionId | The payment identifier; corresponds to payments[].id in an Intent response |
intentId | The parent Intent identifier |
status | The payment status reported by this notification |
isStoredCredential | true because the payment used a previously saved card |
paymentInstrument
| Field | Description |
|---|---|
paymentMethodName | The payment method, BankCardRU in the example |
paymentInstrumentToken | The saved-card token used for the payment |
These are response excerpts. A payment by a saved token does not request a new save, so its token can be returned without savingRequested or isSaved. See Webhooks for the standard notification contract.
Handle errors
Check the request result, payment creation result and payment status separately.
| Where the problem occurs | Where to read the result |
|---|---|
| Request validation or access | HTTP error response, including errorCode and failureMessage |
| Payment creation | paymentCreationErrors in the Intent response, including createStatusCode and statusMessage. An HTTP success response can still contain creation errors |
| Payment processing | The payment's status and error or decline details. Read platform errors from the Intent's payment object (processingStatusCode and statusMessage) or the payment webhook (errorCode and errorMessage) |
Errors relevant to saved cards
| Code | Meaning | Action |
|---|---|---|
HTTP 400 | Invalid fields, such as a missing customer reference or reason, or card details supplied with a token | Correct the request |
CAPABILITY_DISABLED — HTTP 403 | H2H tokenization is disabled for the settlement account | Contact the integration team |
INSTRUMENT_NOT_FOUND — HTTP 404 | The token is unknown, deleted or unavailable within the requested account and customer scope | Check the token, account and customer; retrieve the customer's saved cards |
CODE_CT0005 | Payment initiator type is not allowed for this account or method | Check CIT/MIT availability with the integration team |
CODE_PT0007 | Payment instrument is invalid or blocked | Ask the customer to use another card |
CODE_PT0009 | Payment instrument is not available for merchant-initiated payments | Ask the customer to complete a new card-saving flow before another MIT payment |
An instrument already recognised as invalid can also be rejected with payment status DECLINED and declineCode: "36" (Restricted card). Handle the payment status and decline details as well as platform error codes.
An existing token without a usable MIT payment capability produces a payment with status ERROR and CODE_PT0009. For example, a GET Intent response may contain this excerpt:
{
"id": "204264682781298720",
"payments": [
{
"id": "204264682781298721",
"status": "ERROR",
"processingStatusCode": "CODE_PT0009",
"statusMessage": "Payment instrument is not available for merchant-initiated payments",
"paymentInstrument": {
"paymentMethodName": "BankCardRU",
"paymentInstrumentToken": "pi_01J8ZC4M7QK3T5V9XB2NDR6HAG"
}
}
]
}Bank declines use the normal payment decline fields. See Decline Reasons, Creation Errors and Processing Errors for the general error model.
Currency conversion and refunds
Token payments use the same amount and currency rules as other payments. Currency conversion remains available: submittedAmount and the resulting authAmount may be in different currencies. The examples use RUB for both to keep the token-specific fields clear.
Where the payment method supports refunds, refund a CIT or MIT payment through the normal refund API, referencing the original payment. A refund does not delete the saved-card token.
Test the integration
Use the test cards listed under BankCardRU Sandbox, an account with H2H tokenization enabled and the appropriate configuration for card binding. Keep sandbox tokens and credentials separate from production.
Cover the complete customer journey: save a card, retrieve its token, make a CIT payment, handle any required confirmation, make a MIT payment, handle an unsuccessful payment, delete the token and save the card again. For binding without a purchase, also cover zero amount authorisation and cancellation of a nonzero hold.
Updated about 7 hours ago
