Payment widget
The widget accepts payments right on the merchant’s own page with the methods enabled for the store: SBP, Alfa Pay, SberPay, T-Pay. The customer picks a method, and on a click the widget starts the payment straight away. It is an alternative to the flow in §5.2 “SBP and SberPay”: the invoice is created by the same CreateInvoice, but instead of sending the customer to AuthorizationUrl the page embeds the widget in place.
The set and order of the buttons come from the store’s setting on the platform side. Methods are enabled through your manager; the merchant cannot pick them through the API. If the store has a single method, there is no choice screen — the widget looks like the former SBP widget. A store that draws the method buttons itself does not need the widget: it has the redirect route (§11.7).
The widget works in the sandbox too — with all four methods and with the redirect route. embed. is published on its domain, and the token arrives in the CreateInvoice response — just as in production. The sandbox demo project has all four methods enabled, so the choice screen shows right away; other projects get only SBP by default, and the set is changed on request. The test environment has no real banks: the Alfa Pay, SberPay or T-Pay button opens a bank app imitation with a confirm button, and the widget still carries the “Simulate payment” button. The AuthorizationUrl redirect flow is described separately — Flow: SBP. A step-by-step sandbox walkthrough — Payment widget in the sandbox.
11.1 How to embed
- Create the invoice server-to-server. Take
invoiceIdand theAuthorizationInfo.token from theAuthorizationToken CreateInvoiceresponse. - Load
embed.from the payment domain and calljs SbpWidget.mount()orSbpWidget.open().
<div id="pay"></div>
<script src="https://pay.digitalspb.com/Card/Sbp/embed.js"></script>
<script>
SbpWidget.mount('#pay', {
invoiceId: 'A1B2C3...', // from the CreateInvoice response
token: 'eyJ...', // AuthorizationInfo.AuthorizationToken (required)
methods: ['sbp', 'alfapay'], // optional: narrow the set of methods
webview: true, // optional: the widget is open in an app WebView
brand: {
name: 'Warm Corner',
logo: '/logo.png',
primary: '#c2410c',
background: '#fff8f3',
textColor: '#1a1f36'
},
onSuccess: function () { location.href = '/thanks'; },
onFail: function () {},
onExpired: function () {}, // the QR has expired, the order must be recreated
onError: function (e) {}
});
</script>mount() renders an <iframe src=".../Card/Sbp/widget.html?...">. The widget itself lives inside that iframe — full isolation of styles and scripts — and runs same-origin with the connector, so its requests to the payment domain carry no CORS. The widget and the merchant’s page talk over postMessage: iframe height auto-resize and the success / fail / expired / error events are forwarded to the callbacks.
Modal window — SbpWidget.open(). The second display mode, with the same capabilities as mount(): the widget appears over the page instead of inside its layout. It takes the same parameters, just without the selector, plus an onClose callback. Only one payment screen is ever open — a new open closes the previous one. The window closes with the cross, the Esc key or a click on the backdrop; SbpWidget.close() closes it from code.
SbpWidget.open({
invoiceId: 'A1B2C3...',
token: 'eyJ...',
onSuccess: function () { location.href = '/thanks'; },
onClose: function () {} // the window is closed
});Without embed. the same is available as a link — open it in an iframe or a WebView yourself:
GET /Card/Sbp/widget.html?invoiceId={invoiceId}&t={token}&methods=sbp,alfapayThe methods parameter of the link is optional and works the same way as in mount().
Creating the invoice stays on the merchant’s backend — in every flow on this page. Credentials and the signature never reach the browser.
11.2 Payment methods
| Code | Method | What happens on a click |
|---|---|---|
sbp | SBP | A QR code on a computer, a deep link into the bank app on a phone |
alfapay | Alfa Pay | The payment is created and the customer goes to the bank |
sberpay | SberPay | The payment is created and the customer goes to the bank |
tpay | T-Pay | The payment is created and the customer goes to the bank |
The codes are the same everywhere — in methods, in the widget. link and in the redirect route. Provider codes are never exposed.
How the widget behaves depending on the set:
- one method enabled — there is no choice screen, everything works as before;
- several methods — the widget shows method buttons. They are styled to the banks’ guidelines, so the store color (
brand.) does not apply to them;primary - a click on a button creates the payment and takes the customer to the bank at once, with no intermediate screen;
- below the payment screen there is a “Choose another method” link;
- coming back from the bank or reloading the tab opens the payment already started instead of asking for the method again.
methods only narrows the set. A method that is not enabled for the store cannot be turned on through it, and the button order still comes from the store setting. If nothing is left of the intersection — say, a method was disabled while the button on the store’s page stayed — the widget does not break: it shows the whole available set and calls onError with code: 'method_unavailable'. The payment carries on after that.
11.3 mount() and open() parameters
| Parameter | Required | Description |
|---|---|---|
invoiceId | Yes | Invoice identifier from the CreateInvoice response |
token | Yes | The invoice token from the CreateInvoice response — AuthorizationInfo. in XML, authorization. in JSON |
methods | No | An array of codes (sbp, alfapay, sberpay, tpay) or a comma-separated string. Only narrows the store’s set, see §11.2 |
webview | No | true — the widget is open in the store app’s WebView. A WebView does not open new tabs, so going to the bank takes over the whole page. Without the flag the widget detects the environment itself, but less reliably |
apiBase | No | Payment domain address. Defaults to the origin embed. was loaded from |
brand | No | Branding object (see below) |
poll | No | false disables automatic status polling; enabled by default |
lang | No | Widget interface language |
onSuccess | No | Successful payment callback |
onFail | No | Decline / failure callback |
onExpired | No | Expired QR code callback — the order must be recreated |
onError | No | Error callback; receives an Error object that may carry a code field (§11.5) |
onClose | No | open() only: the modal window has been closed |
Without token the method throws — it is the only parameter the SDK itself requires.
The token field is an extension of the AuthorizationInfo block in the CreateInvoice response. The token is bound to the invoice and lives for 2 hours.
11.4 Appearance — the brand object
| Field | Description | Default |
|---|---|---|
name | Store or product name | “SBP” |
logo | Logo URL — only http(s):// or data:image/ | none |
primary | Accent color for buttons and highlights: hex, named color or rgb() / rgba(). Does not apply to the payment method buttons | #1a1f36 |
background | Widget background color | #ffffff |
textColor | Text color | #1a1f36 |
Every color value and logo goes through server-side validation and sanitizing: the parameters arrive through the query string of the public widget. page, and without this it would be a reflected XSS. An invalid color is silently replaced with the default, and logo is accepted only over the http(s) or data:image schemes.
11.5 Events and lifecycle
The widget sends the page five kinds of postMessage:
| Event | What it means |
|---|---|
success | The payment succeeded |
fail | Decline or failure |
expired | The QR code lifetime has run out |
error | Widget error; onError receives an Error object |
resize | Iframe height auto-fit — handled by the wrapper automatically |
The Error object may carry a code field. One code is defined so far — method_unavailable (see methods in §11.2); the widget keeps working after it, the payment is not over. If the widget page fails to load — a 5xx response or a network failure — the wrapper also calls onError instead of leaving an empty frame.
The destroy() method on the mount() result removes the postMessage handler and the iframe from the page.
11.6 Ready-made SBP button — renderButton()
A thin helper on top of mount(): the SDK draws the branded SBP button itself and delegates invoice creation to a createInvoice() callback on the merchant’s backend. The button handles its own states — loading, disabled, double-click protection, errors — and mounts the widget once the invoice is created. Convenient when you have no checkout of your own.
<div id="pay"></div>
<script src="https://pay.digitalspb.com/Card/Sbp/embed.js"></script>
<script>
SbpWidget.renderButton('#pay', {
createInvoice: async function () { // on the merchant's backend: CreateInvoice
const r = await fetch('/api/create-invoice', { method: 'POST' });
return r.json(); // returns invoiceId and token
},
text: 'Pay', // optional (defaults to "Pay")
brand: { name: 'Warm Corner', primary: '#c2410c', background: '#fff8f3' },
onSuccess: function () { location.href = '/thanks'; },
onFail: function () {},
onError: function (e) {}
});
</script>| Parameter | Required | Description |
|---|---|---|
createInvoice | Yes | Async callback: creates the invoice on the merchant’s backend (CreateInvoice) and returns an object with invoiceId and token |
text | No | Button caption; defaults to “Pay” |
brand | No | Widget branding (table above). It does not affect the SBP button itself — that one is branded |
apiBase, poll, lang, onSuccess, onFail, onExpired, onError | No | The same as in mount() |
The button looks the same for every merchant: the branded dark blue #1D1346 background, the caption on the left, the official SBP logo on the right — the color mark and the word “сбп” in white for a dark background, with no recoloring. Built to the NSPK brand book (P.231).
The renderButton() result has two methods: reset() brings the button back — for a “pay again”, say — and destroy() removes both the button and the widget from the page.
11.7 The store’s own buttons — the redirect route
If the store draws the method buttons itself, it does not need the widget. The button leads to the redirect route: the server creates the payment with the chosen method and answers 302 to the bank’s page.
GET /Card/Handlers/{invoiceId}/pay?method=alfapay&t={token}| Parameter | Required | Description |
|---|---|---|
invoiceId | Yes | Invoice identifier from the CreateInvoice response (part of the path) |
method | Yes | Method code: sbp, alfapay, sberpay, tpay |
t | Yes | The same AuthorizationInfo. from the CreateInvoice response |
- An unknown
methodcode gets400. The route will not swap the customer’s choice for SBP. sbphas no bank page of its own, so the platform’s QR code page opens. It has no choice screen — the method is already chosen.- The address does not depend on the acquiring the store works through: there is no channel name in it.
There is no separate method listing the methods enabled for the store, and none is planned: only the widget needs the list, and it arrives built into the widget’s page.
11.8 Which one to use
| Option | When |
|---|---|
mount() | The widget sits inside the page layout; the merchant decides where and when to show it |
open() / close() | There is no room to embed: a narrow page, payment from an order list |
renderButton() | A quick drop-in with a ready branded SBP button and state handling |
A widget. link | embed. cannot be loaded — for example, the widget opens in a WebView |
Own buttons + /pay | The store draws the method buttons itself; no widget is loaded |