Skip to Content
SandboxOne-time paymentsFlow: card binding

Flow: bank card binding

Card-on-file / custom form: card data is entered on the merchant’s website or in the app and passed to the API inside <BankCardCustomer>. The card is bound via a contract with no PIN confirmation; subsequent charges go through without re-entering the card details. The scenario exists only in the XML protocol — JSON v3 has no block for raw card data.

Two hard conditions. The merchant handling raw card data must be PCI DSS certified — without it, use the regular card flow with a redirect to the payment form. The presence of the BankCard block in requests is governed by the integration type and the individual requirements of the acquirer — it is enabled per partner.

Only test card numbers starting with 1111 (e.g. 1111 2222 3333 4444) — never send real card data. The reserved endings 0001–0005 emulate a charge refusal. The sandbox does not store card data (only the last 4 digits of the PAN remain), the CVC is never stored in any form, and card details are masked in the request log. Sandbox contracts are cleaned up by the regular retention, not by the two-year term from the spec.

  1. Bind the card: CreateCustomerContract with <BankCardCustomer> containing the full <BankCard> block (PAN, expiration, CVC, cardholder name) plus MerchantContractID. Headers for this scenario: Service-Method: CreateCustomerContract, Callisto-Protocol-Version: 2, SignatureVersion: 4. The response returns ContractID, AuthorizationType: SKIP and the contract ExpirationTimeUtc: there is no PIN confirmation as in the mobile phone flow, the AuthorizeCustomerContract step is not needed.
  2. Charge under the contract: a regular CreateInvoice with the same <BankCardCustomer> and <CustomerContractInfo> (MerchantContractID); CreateIfNotExists=true may create the contract together with the first invoice. The response contains the CustomerContractInfo block and AuthorizationInfo with an AuthorizationUrl — the 3-D Secure confirmation page (any 4-digit code works in the sandbox); the card form itself stays on the merchant side. With 3-D Secure off the response looks different — see the note below.
  3. Re-binding the same active MerchantContractID returns ResultCode 230 (Business_CustomerContractAlreadyExists — see ResultCode) with the existing ContractID in ResultDescription.

Step 1 — CreateCustomerContract with card details (XML)

Request — headers Service-Method: CreateCustomerContract, SignatureVersion: 4
<Request> <Header><Authentication> <Login>demo_login</Login> <NowUtc>20260625120000</NowUtc> <Signature>abcdef1234...</Signature> </Authentication></Header> <CreateCustomerContractRequest> <ProjectID>1001</ProjectID> <ClientIP>10.20.30.40</ClientIP> <BankCardCustomer> <Source>my_source</Source> <Country>ru</Country> <MerchantCustomerId>user_123</MerchantCustomerId> <Email>test@test.com</Email> <BankCard> <PAN>1111222233334444</PAN> <ExpirationMonth>08</ExpirationMonth> <ExpirationYear>28</ExpirationYear> <SecurityCode>123</SecurityCode> <Cardholder>IVANOV IVAN</Cardholder> </BankCard> </BankCardCustomer> <CustomerContractInfo> <MerchantContractID>CARD-CONTRACT-001</MerchantContractID> </CustomerContractInfo> </CreateCustomerContractRequest> </Request>
Response — contract created without PIN confirmation
<Response> <CreateCustomerContractResponse> <BankCardCustomer> <Source>my_source</Source> <Country>ru</Country> <MerchantCustomerId>user_123</MerchantCustomerId> <Email>test@test.com</Email> </BankCardCustomer> <CustomerContractInfo> <ContractID>1c6495bb-0002-005f-f54e-d286731d55b9</ContractID> <AuthorizationType>SKIP</AuthorizationType> <ExpirationTimeUtc>27.04.2028 21:00:06</ExpirationTimeUtc> <MerchantContractID>CARD-CONTRACT-001</MerchantContractID> </CustomerContractInfo> </CreateCustomerContractResponse> <ResultCode>0</ResultCode> <ResultDescription>OK</ResultDescription> </Response>

Step 2 — charge under the contract (XML)

Request — headers Service-Method: CreateInvoice, SignatureVersion: 4
<Request> <Header><Authentication> <Login>demo_login</Login> <NowUtc>20260625120000</NowUtc> <Signature>abcdef1234...</Signature> </Authentication></Header> <CreateInvoiceRequest> <ProjectID>1001</ProjectID> <ClientIP>10.20.30.40</ClientIP> <BankCardCustomer> <Source>my_source</Source> <Country>ru</Country> <MerchantCustomerId>user_123</MerchantCustomerId> <Email>test@test.com</Email> </BankCardCustomer> <InvoiceInfo> <MerchantOrderID>ORDER-001</MerchantOrderID> <CustomerPrice amount="99.00" currency="RUB" includingVAT="true"/> </InvoiceInfo> <CustomerContractInfo> <CreateIfNotExists>false</CreateIfNotExists> <MerchantContractID>CARD-CONTRACT-001</MerchantContractID> </CustomerContractInfo> </CreateInvoiceRequest> </Request>
Response — invoice AuthorizationType is AUTH_PAGE (the contract stays SKIP), AuthorizationUrl is the 3-D Secure page
<Response> <CreateInvoiceResponse> <BankCardCustomer> <Source>my_source</Source> <MerchantCustomerId>user_123</MerchantCustomerId> <Country>ru</Country> <Email>test@test.com</Email> </BankCardCustomer> <InvoiceInfo> <CustomerPrice amount="99.00" currency="RUB" includingVAT="true" /> <CreatedTimeUtc>31.08.2026 20:00:10</CreatedTimeUtc> <ExpirationTimeUtc>31.08.2026 22:00:10</ExpirationTimeUtc> <InvoiceStatus>WaitPaymentConfirmation</InvoiceStatus> <PaymentStatus>NOT_PAID</PaymentStatus> <AuthorizationType>AUTH_PAGE</AuthorizationType> <IsAuthorized>false</IsAuthorized> <InvoiceID>3c8d95c6-0006-0062-6727-4ea756dba332</InvoiceID> <MerchantOrderID>ORDER-001</MerchantOrderID> </InvoiceInfo> <BillingInfo> <BillingChannel>BankRtp</BillingChannel> </BillingInfo> <CustomerContractInfo> <ContractID>1c6495bb-0002-005f-f54e-d286731d55b9</ContractID> <AuthorizationType>SKIP</AuthorizationType> <ExpirationTimeUtc>27.04.2028 21:00:06</ExpirationTimeUtc> <MerchantContractID>CARD-CONTRACT-001</MerchantContractID> </CustomerContractInfo> <AuthorizationInfo> <AuthorizationUrl>https://pay-sandbox.digitalspb.com/checkout/a038a5ce-c580-4ff1-b480-e364b21a7474/pin?last4=4444</AuthorizationUrl> </AuthorizationInfo> </CreateInvoiceResponse> <ResultCode>0</ResultCode> <ResultDescription>OK</ResultDescription> </Response>

The response depends on the card_3ds_enabled setting. This is the only flow where 3-D Secure changes not just the number of screens the payer sees but the response payload as well. With the setting off the charge is settled synchronously inside CreateInvoice: there is no AuthorizationInfo block (and so no payment link), the invoice AuthorizationType is SKIP, and InvoiceStatus / PaymentStatus already carry the outcome (Processed / PAID, or Cancelled / NOT_PAID for a reserved card ending). The payment notification is sent as usual. Both responses side by side and how to test them — Flow: 3-D Secure.

Variant — card details directly in CreateInvoice (XML)

Depending on the integration, the <BankCard> block is passed not only in CreateCustomerContract but also in CreateInvoice — inside the same <BankCardCustomer>, with the same fields as in step 1. The same conditions apply: PCI DSS certification and individual enablement by the acquirer (see the callout at the top of the page).

Request — headers Service-Method: CreateInvoice, SignatureVersion: 4
<Request> <Header><Authentication> <Login>demo_login</Login> <NowUtc>20260625120000</NowUtc> <Signature>abcdef1234...</Signature> </Authentication></Header> <CreateInvoiceRequest> <ProjectID>1001</ProjectID> <ClientIP>10.20.30.40</ClientIP> <BankCardCustomer> <Source>my_source</Source> <Country>ru</Country> <MerchantCustomerId>user_123</MerchantCustomerId> <Email>test@test.com</Email> <BankCard> <PAN>1111222233334444</PAN> <ExpirationMonth>08</ExpirationMonth> <ExpirationYear>28</ExpirationYear> <SecurityCode>123</SecurityCode> <Cardholder>IVANOV IVAN</Cardholder> </BankCard> </BankCardCustomer> <InvoiceInfo> <MerchantOrderID>ORDER-002</MerchantOrderID> <CustomerPrice amount="99.00" currency="RUB" includingVAT="true"/> </InvoiceInfo> </CreateInvoiceRequest> </Request>

Recurring payments by phone number (a contract with PIN confirmation) are a separate flow: Flow: recurring. The full method description — Create contract and the createContract reference.