Skip to Content
One-time paymentsPayment widget

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.js 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

  1. Create the invoice server-to-server. Take invoiceId and the AuthorizationInfo.AuthorizationToken token from the CreateInvoice response.
  2. Load embed.js from the payment domain and call SbpWidget.mount() or SbpWidget.open().
Merchant's payment page
<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.

Modal window
SbpWidget.open({ invoiceId: 'A1B2C3...', token: 'eyJ...', onSuccess: function () { location.href = '/thanks'; }, onClose: function () {} // the window is closed });

Without embed.js 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,alfapay

The 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

CodeMethodWhat happens on a click
sbpSBPA QR code on a computer, a deep link into the bank app on a phone
alfapayAlfa PayThe payment is created and the customer goes to the bank
sberpaySberPayThe payment is created and the customer goes to the bank
tpayT-PayThe payment is created and the customer goes to the bank

The codes are the same everywhere — in methods, in the widget.html 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.primary) does not apply to them;
  • 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

ParameterRequiredDescription
invoiceIdYesInvoice identifier from the CreateInvoice response
tokenYesThe invoice token from the CreateInvoice response — AuthorizationInfo.AuthorizationToken in XML, authorization.token in JSON
methodsNoAn array of codes (sbp, alfapay, sberpay, tpay) or a comma-separated string. Only narrows the store’s set, see §11.2
webviewNotrue — 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
apiBaseNoPayment domain address. Defaults to the origin embed.js was loaded from
brandNoBranding object (see below)
pollNofalse disables automatic status polling; enabled by default
langNoWidget interface language
onSuccessNoSuccessful payment callback
onFailNoDecline / failure callback
onExpiredNoExpired QR code callback — the order must be recreated
onErrorNoError callback; receives an Error object that may carry a code field (§11.5)
onCloseNoopen() 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

FieldDescriptionDefault
nameStore or product name“SBP”
logoLogo URL — only http(s):// or data:image/none
primaryAccent color for buttons and highlights: hex, named color or rgb() / rgba(). Does not apply to the payment method buttons#1a1f36
backgroundWidget background color#ffffff
textColorText 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.html 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:

EventWhat it means
successThe payment succeeded
failDecline or failure
expiredThe QR code lifetime has run out
errorWidget error; onError receives an Error object
resizeIframe 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.

SBP button on the merchant's page
<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>
ParameterRequiredDescription
createInvoiceYesAsync callback: creates the invoice on the merchant’s backend (CreateInvoice) and returns an object with invoiceId and token
textNoButton caption; defaults to “Pay”
brandNoWidget branding (table above). It does not affect the SBP button itself — that one is branded
apiBase, poll, lang, onSuccess, onFail, onExpired, onErrorNoThe 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}
ParameterRequiredDescription
invoiceIdYesInvoice identifier from the CreateInvoice response (part of the path)
methodYesMethod code: sbp, alfapay, sberpay, tpay
tYesThe same AuthorizationInfo.AuthorizationToken from the CreateInvoice response
  • An unknown method code gets 400. The route will not swap the customer’s choice for SBP.
  • sbp has 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

OptionWhen
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.html linkembed.js cannot be loaded — for example, the widget opens in a WebView
Own buttons + /payThe store draws the method buttons itself; no widget is loaded