Sandbox ↔ Production differences
The sandbox reproduces the production API contract — method addresses, request structure, signing and response format — but not the billing itself. Everything that behaves differently is collected below, so that it does not surface in production instead.
| Area | How the sandbox behaves |
|---|---|
| Money | No charges happen in any scenario: neither the subscriber account nor the card balance changes |
| Method responses | Invoices are stored in the sandbox: GetInvoice returns the real MerchantOrderID, MSISDN and amount from CreateInvoice. Sandbox constants remain templated — OperatorID and NetworkID do not come from a real carrier |
| Protocols | Both protocols — XML Merchant API v2.9 and REST JSON v3 — work in the sandbox and in production. Only the contour addresses differ |
| PIN confirmation | No SMS is sent. POST /invoices/{invoice_id}/authorize and XML AuthorizeInvoice expect the fixed 1234, three attempts, after the third it is rejected permanently. Contract confirmation follows the same rule. The card payment page accepts any 4 digits instead of 3-D Secure |
| 3-D Secure | Switched by the project setting card_3ds_enabled (on by default) so that both modes can be exercised. Production has no such setting — the challenge is enabled per integration, by acquirer, integration type and fraud rules. The only flow where the response payload changes is a charge under a card contract; details and cases — “Flow: 3-D Secure” |
| Sandbox knobs | test_info (JSON) and <TestInfo> (XML) set the billing outcome and the callback delay. These fields do not exist in the production contract — remove them before going live. Details: “Sandbox knobs” |
| Error emulation | Failures are triggered by special values: ERR_NNN inside merchant_order_id and the reserved last 4 card digits 0001–0005. In production these are ordinary values, and such an order will be created for real |
| Billing time | Simulated: for the first 6 seconds after the first status request, XML getInvoice returns Active / NOT_PAID, then Processed / PAID. Real timings differ |
| Confirmation type | A DCB invoice always returns SMS_PIN: the mock does not emulate the carrier choosing a scheme, although the spec allows seven AuthorizationType values |
| SBP and SberPay | Not distinguished — ?useSbp=true and ?payType=Sber both lead to the same QR page, only the screen selected by deviceType differs |
| Payment widget | Works with every method and the /pay redirect route: embed. is published and the token arrives in the CreateInvoice response. There are no real banks, so the payment cannot be confirmed from a banking app — a simulation button inside the widget is what drives the flow to success. Production has no such button |
| Deduplication | Invoices are deduplicated by customer, as in production: while the previous one is open, a new order for the same subscriber gets 409. Mobile contracts are too: a repeated merchant_contract_id gets 409 — CONTRACT_DRAFT_EXISTS while the contract awaits the PIN, and CONTRACT_ALREADY_EXISTS after confirmation |
createInvoice response | Status code 201 — as in the spec and in production. The one difference: the deprecated alias data. is returned alongside data. |
| Storage | Request logs and invoice state are cleaned up on a schedule: an invoice created long ago eventually stops being found |
Callbacks are real. Notifications are actually delivered to your address, with a real signature and real retries. This is the part of the integration you can debug here exactly as in production — see “Callbacks”.
Before going live: remove test_info / <TestInfo> from requests, replace test merchant_order_id values containing ERR_NNN and card numbers with reserved last digits, do not rely on the PIN 1234, and obtain production Login and Secret — sandbox credentials do not work in production.