Quick start
Five requests from zero to a paid invoice, on demo credentials, with no sign-up. The examples use XML Merchant API v2.9. JSON v3 equivalents are in the method sections below: both protocols are production protocols, the choice is yours.
Flow diagram
Merchant Callisto (sandbox) Subscriber
│ │ │
│ 1. CreateInvoice ─────────────►│ │
│◄─── InvoiceID + SMS_PIN ───────│ ~~~ SMS with PIN ~~~~~~~~~►│
│ │ (not sent in sandbox) │
│ 2. AuthorizeInvoice(PIN) ─────►│◄── subscriber tells PIN ───│
│◄─── ResultCode=0 ──────────────│ │
│ │ │
│ 3. GetInvoice ────────────────►│ carrier-side billing │
│◄─── Processed / PAID ──────────│ │
│ │ │
│◄── SendPaymentTransaction ─────│ callback, arrives on its own- Take the credentials. In the sandbox these are the public demo credentials from “Access and test data”; no sign-up is needed, and the credentials are shared by everyone who opens this page.
- Compute the signature. Every request needs one, and it lives in
<Header><Authentication>. The formula and a walkthrough are in “Request signing”. - Create an invoice with
CreateInvoice. The<TestInfo>block is sandbox-only:ExpectedBillingResult=Successforces successful billing andCallbackDelaySeconds=0removes the callback delay. The response carries anInvoiceID— keep it. - Confirm the invoice with
AuthorizeInvoiceand<AuthorizeInfo><PinCode>1234</PinCode></AuthorizeInfo>. The sandbox sends no SMS and the PIN is fixed. The response isResultCode=0with an empty<AuthorizeInvoiceResponse />. - Check the status with
GetInvoice. Right after confirmation you getActive/NOT_PAID— the sandbox simulates billing time. Repeat the request after 6 seconds to getProcessed/PAID. - Wait for the callback.
SendPaymentTransactionis sent to the project callback URL on its own — you do not need to request it. Details and the retry policy are in “Callbacks”. - Next: paying by bank card and via SBP, sandbox knobs for emulating failures, and the differences from production — see the corresponding sections.
The sandbox stores your invoice: GetInvoice returns the same MerchantOrderID, MSISDN and amount you sent. Only sandbox constants such as OperatorID come from templates — see “Sandbox ↔ Production differences”.
Step 2 — signature (bash)
Sandbox
NOW and SIG go into the <Header> of every request
LOGIN=demo_login
SECRET=demo_password
NOW=$(date -u +%Y%m%d%H%M%S)
INNER=$(printf '%s' "$NOW" | openssl dgst -sha256 -hmac "$SECRET" -r | cut -d' ' -f1)
SIG=$(printf '%s' "$LOGIN$INNER" | openssl dgst -sha256 -hmac "$SECRET" -r | cut -d' ' -f1)Step 3 — CreateInvoice
Sandbox
curl -X POST \
https://pay-sandbox.digitalspb.com/API/XML/InvoiceManagementService.ashx \
-H "Content-Type: text/xml; charset=utf-8" \
--data-binary '<?xml version="1.0" encoding="utf-8"?>
<Request>
<Header>
<Authentication>
<Login>'"$LOGIN"'</Login>
<NowUtc>'"$NOW"'</NowUtc>
<Signature>'"$SIG"'</Signature>
</Authentication>
</Header>
<CreateInvoiceRequest>
<ProjectID>1001</ProjectID>
<MobilePhoneCustomer><MSISDN>79600000000</MSISDN></MobilePhoneCustomer>
<InvoiceInfo>
<MerchantOrderID>QS-DEMO-1042</MerchantOrderID>
<CustomerPrice amount="99.00" currency="RUB" includingVAT="true"/>
</InvoiceInfo>
<!-- sandbox knobs, absent in production -->
<TestInfo>
<ExpectedBillingResult>Success</ExpectedBillingResult>
<CallbackDelaySeconds>0</CallbackDelaySeconds>
</TestInfo>
</CreateInvoiceRequest>
</Request>'Response — keep the InvoiceID
<Response>
<CreateInvoiceResponse>
<InvoiceInfo>
<InvoiceStatus>Created</InvoiceStatus>
<PaymentStatus>NOT_PAID</PaymentStatus>
<AuthorizationType>SMS_PIN</AuthorizationType>
<InvoiceID>8e3b885b-b51a-46d2-835d-7b77b3d5cca7</InvoiceID>
<MerchantOrderID>QS-DEMO-1042</MerchantOrderID>
</InvoiceInfo>
<BillingInfo><BillingChannel>MC</BillingChannel></BillingInfo>
</CreateInvoiceResponse>
<ResultCode>0</ResultCode>
</Response>Step 4 — AuthorizeInvoice
Recompute the signature: NowUtc is new
<AuthorizeInvoiceRequest>
<ProjectID>1001</ProjectID>
<InvoiceInfo>
<InvoiceID>8e3b885b-b51a-46d2-835d-7b77b3d5cca7</InvoiceID>
</InvoiceInfo>
<AuthorizeInfo>
<PinCode>1234</PinCode>
</AuthorizeInfo>
</AuthorizeInvoiceRequest>
<!-- response -->
<Response>
<AuthorizeInvoiceResponse />
<ResultCode>0</ResultCode>
<ResultDescription>OK</ResultDescription>
</Response>Step 5 — GetInvoice
<GetInvoiceRequest>
<ProjectID>1001</ProjectID>
<InvoiceInfo>
<InvoiceID>8e3b885b-b51a-46d2-835d-7b77b3d5cca7</InvoiceID>
</InvoiceInfo>
</GetInvoiceRequest>
<!-- right after AuthorizeInvoice -->
<InvoiceStatus>Active</InvoiceStatus>
<PaymentStatus>NOT_PAID</PaymentStatus>
<!-- the same request 6 seconds later -->
<InvoiceStatus>Processed</InvoiceStatus>
<PaymentStatus>PAID</PaymentStatus>Step 6 — callback, arrives on its own
<Request>
<Header><RequestID>bae26b7d-…</RequestID><RequestAttempt>1</RequestAttempt></Header>
<SendPaymentTransaction>
<InvoiceID>8e3b885b-b51a-46d2-835d-7b77b3d5cca7</InvoiceID>
<MerchantOrderID>QS-DEMO-1042</MerchantOrderID>
<Status>Success</Status>
<BillingResult>Success</BillingResult>
<PaymentStatus>SUCCESS</PaymentStatus>
<Amount>99.00</Amount><Currency>RUB</Currency>
</SendPaymentTransaction>
</Request>