Skip to Content
SubscriptionsPayment widget

Payment widget for subscriptions

The widget lets the payer take out a subscription on the partner’s page via SBP, Alfa Pay or SberPay, depending on the project settings and the subscription terms. T-Pay is temporarily disabled.

If the first period is paid at sign-up, the chosen method starts the first payment and saves the binding for later charges. If the first payment is deferred, only SBP is available in this version: the payer confirms a binding without a charge, and the platform later starts the first payment on schedule. Free binding must be enabled for the project and supported by the payment provider.

The success event reports that the initial step completed successfully: the first payment, or a binding without a charge when payment is deferred. It does not replace the server notifications about subscription activation and charges. Payment accounting on the partner side must rely on server notifications, even if the payer closed the page and never received success in the browser.

The widget works in the sandbox too — embed.js is published on its domain and the token comes from AuthorizeUrl exactly as in production. The sandbox demo project has SBP, Alfa Pay and SberPay enabled, so the choice screen shows right away; other projects get only SBP by default, and the set is changed on request. In the sandbox a deferred first payment is switched on by a future BillingStartTimeUtc — the widget then offers SBP only. The test environment has no real banks: the Alfa Pay or SberPay button opens a bank app imitation with a confirm button, and the widget carries a simulation button — that is what drives the flow to success. A step-by-step walkthrough — Subscription payment widget in the sandbox.

6.1 How to embed

  1. Call CreateSubscription from your own server. The response carries AuthorizeUrl, which contains the token — 32 hexadecimal characters. That token is what the widget needs.
  2. Load embed.js from the payment domain and call SbpWidget.mount() or SbpWidget.open().
Partner page
<div id="pay"></div> <script src="https://subs.digitalspb.com/Cards/Sbp/embed.js"></script> <script> const widget = SbpWidget.mount('#pay', { token: 'TOKEN_FROM_CREATE_SUBSCRIPTION', brand: { name: 'CoinKeeper', logo: 'https://partner.example/logo.png', primary: '#1756ef', background: '#ffffff', textColor: '#182045' }, onSuccess: function () { location.href = '/thanks'; }, onFail: function () {}, onExpired: function () {}, onError: function (error) { console.error(error.message); } }); </script>

Here TOKEN_FROM_CREATE_SUBSCRIPTION marks the place for the real token from the CreateSubscription response; it is not a valid token value. The request signature and the project password stay on the partner backend only.

mount() inserts an <iframe src=".../Cards/Sbp/widget.html?...">. The widget itself lives inside the iframe — the partner page’s styles and scripts do not affect it and vice versa — and runs same-origin with the payment domain, so its requests carry no CORS. It talks to the parent page over postMessage: iframe height auto-resize plus the events delivered to the mount() 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 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.

The SDK passes the token in the token query parameter of the iframe address: /Cards/Sbp/widget.html?token=…. The widget’s requests to the payment methods, payment creation and state check APIs carry that token in an Authorization: Bearer header. Treat the token as sensitive data: do not publish it or include it in open logs. Since it is present in the iframe URL, its absence from web server logs is not guaranteed.

If you embed the widget, do not open AuthorizeUrl. That is a separate payment flow on our page, and it conflicts with the widget over the same token.

Creating the subscription stays on the partner backend — both here and in the button flow below. The project password and the signature never reach the browser.

6.2 mount() and open() parameters

ParameterRequiredDescription
tokenYesThe token from AuthorizeUrl of the CreateSubscription response. Used for the initial step with the chosen method and for the state check
apiBaseNoPayment domain address. Defaults to the origin embed.js was loaded from
brandNoBranding object (see below)
pollNofalse disables automatic state polling; enabled by default
methodsNoMethod filter: an array of codes or a comma-separated string of codes. Codes: sbp, alfapay, sberpay. By default the server set is shown. The filter does not enable methods the server forbids. The order stays the server’s
webviewNoBoolean. true turns WebView mode on, false turns it off; without the parameter the widget detects the mode automatically. In WebView mode the bank payment opens on a button press, without opening a new tab automatically
onSuccessNoThe initial step completed successfully: the first payment, or a binding without a charge when payment is deferred. Does not replace server notifications
onFailNoThe initial step ended in a decline
onExpiredNoThe waiting time for the initial step in the widget has run out. The event itself does not confirm that the payment was cancelled or that nothing was charged
onErrorNoA widget loading or runtime error. Receives an Error object; the message is in error.message. Some events also carry error.code; its presence is not guaranteed
onCloseNoopen() only: the modal window has been closed. Closing the window does not cancel the subscription or the payment (§6.11)

The destroy() method on the mount() result removes the postMessage handler and deletes the iframe from the page.

Method filter:

SbpWidget.mount('#pay', { token: 'TOKEN_FROM_CREATE_SUBSCRIPTION', methods: ['sbp', 'sberpay'], onError: function (error) { console.error(error.message, error.code); } });

The token value in the example marks the place for the real 32 hex characters from AuthorizeUrl; it is not a working token.

6.3 Appearance — the brand object

FieldDescriptionDefault
nameShop or product name“Оплата” (Payment)
logoLogo URL — only http(s):// or data:image/none
primaryAccent colour: hex, a named colour or rgb() / rgba(). Does not apply to the payment method buttons#1a1f36
backgroundWidget background colour#ffffff
textColorText colourPicked automatically from the background colour if the partner has not set textColor

Values are validated on the widget side: an invalid colour is silently replaced with the default, and a logo with a foreign scheme is not accepted. The parameters arrive through the query string of a public widget page — without that check this would be a markup injection.

If you set your own textColor, you are responsible for the contrast between text and background. The QR code keeps a white background whatever the widget styling. The branded payment method buttons are not recoloured with brand.primary.

Before payment starts, the widget shows the amount including VAT, the subscription period and the legal information configured by the platform.

6.4 Events

EventWhen it arrives
successThe first payment, or a binding without a charge when payment is deferred, completed successfully. Does not replace server notifications
failThe initial step ended in a decline
expiredThe waiting time for the initial step has run out. Does not confirm that the payment was cancelled or that nothing was charged
errorWidget failure, including a persistent state-polling failure
resizeThe content height changed; handled by the wrapper automatically

onError receives an Error object. The text is in error.message; some events also pass error.code. The method_unavailable code means the methods filter contains none of the methods in the available server set. The event is not fatal: the widget keeps working and shows the available server methods. If the iframe failed to load or did not answer the SDK during the handshake, onError is called as well.

Do not create a new subscription automatically on every error: the previous payment operation may already have started and its result may not have arrived yet.

6.5 Ready-made SBP button — renderButton()

A layer on top of mount(): the SDK draws the branded SBP button itself, while creating the subscription stays on the partner side. Handy when you have no checkout of your own.

SBP button on the partner page
<div id="pay"></div> <script src="https://subs.digitalspb.com/Cards/Sbp/embed.js"></script> <script> SbpWidget.renderButton('#pay', { createInvoice: async function () { // on the partner backend: CreateSubscription const r = await fetch('/api/create-subscription', { method: 'POST' }); return r.json(); // must return { token: '...' } }, text: 'Subscribe', brand: { name: 'CoinKeeper', primary: '#1756ef' }, onSuccess: function () { location.href = '/thanks'; }, onFail: function () {}, onExpired: function () {}, onError: function (e) {} }); </script>
ParameterRequiredDescription
createInvoiceYesAsync callback: creates the subscription on the partner backend and returns an object with a token field
textNoButton text; defaults to “Pay”
brandNoWidget branding (table above). It does not affect the button itself — that one is branded
apiBase, methods, webview, poll, onSuccess, onFail, onExpired, onErrorNoSame as for mount()

The result has two methods: reset() puts the button back, and destroy() removes both the button and the widget from the page.

The button looks the same for every partner: dark blue #1D1346 background, the text on the left, the official SBP mark on the right without recolouring. Built to the NSPK brand book.

renderButton() creates a ready-made button that opens the widget once the token has been obtained. It is not a route for a separate button of a particular payment method. For the store’s own buttons that open a chosen method, use §6.12. The name or styling of the ready-made button does not limit the set of methods — methods does that, and with an empty intersection the widget shows the server set and calls onError with the method_unavailable code.

6.6 What the payer sees

Payment methods. The available methods are sbp — SBP, alfapay — Alfa Pay, sberpay — SberPay. T-Pay is temporarily disabled: it is not shown in the widget and is not accepted by the own-buttons route.

The set and order of methods are defined on the platform side and also depend on the subscription terms. When the first period is paid at sign-up, the enabled methods are available. When the first payment is deferred, only SBP is available in this version.

If one method is available, there is no separate choice screen. If there are several, the widget shows their branded buttons. Pressing a button starts the initial operation with the chosen method, and the payer then confirms it in the bank.

The partner can narrow the displayed set with the methods parameter. The widget uses the intersection of the filter with the available server methods and keeps the server order. Code case and surrounding spaces do not matter. The parameter does not enable forbidden methods and does not bypass the temporary T-Pay shutdown.

If the intersection is empty, the widget shows the server set and calls onError with the method_unavailable code. To change the enabled methods or their server order, contact your manager.

Changing the method. The method is chosen before the operation starts. Once it has started, the method cannot be changed for the same subscription: the server returns canChangeMethod=false and the widget hides method switching.

When the widget is opened again with the same token and several methods are available, the choice screen may appear again. To continue, choose the same method. Within the validity period the server reuses the operation already started; choosing a different method for it is rejected.

Another method requires a new subscription with a new token. Before creating it, check the result of the previous operation so as not to take out two subscriptions or make two initial payments.

The store’s own buttons. They use the /Cards/Sbp/pay route: SBP opens the payment screen without a method choice, Alfa Pay and SberPay redirect to the bank’s payment page. The format and limits are in §6.12.

The SBP screen is picked by the widget to suit the device:

DeviceWhat is shown
Desktop browserA QR code, a countdown and a “waiting for payment” caption
Phone or a narrow window (touch input or width under 600 pixels)A button that opens the banking app. If the bank supplied no deep link, a link to the SBP payment page is shown instead

After confirming in the banking app the payer returns to the partner page; the widget keeps polling the state and shows the result itself.

6.7 Timings

WhatValue
Waiting time for the initial operation (QR code, payment link, going to the bank); when it runs out, expired arrives20 minutes
State polling intervalevery 5 seconds, the interval is set by the server
Initial step confirmation delayup to a minute and a half

The widget timer running out does not mean the bank payment was cancelled.

When the widget is opened again within the validity period, the operation already started with the same method is reused. If the data is saved, the server returns the previous QR code or link.

For a free SBP binding the server may request a new binding once the period has expired. For a paid first payment already started, no repeated authorization is sent — this applies to SBP, Alfa Pay and SberPay alike. A timeout or a missing link does not prove the payment was not made.

Before creating a new subscription, check the result of the previous operation from server data and notifications. Do not call CreateSubscription automatically after an error or when the timer runs out.

Reloading the partner page, if it calls CreateSubscription again, creates a new subscription with a new token — whatever the payment method. The previous operation is not cancelled: the SBP QR stays valid and a payment already started may complete, and their confirmation belongs to the previous subscription rather than the one on screen. During testing this is the main source of confusion.

6.8 First charge and trial period

When the first payment happens is defined by the terms of the created subscription, taking into account BillingStartTimeUtc and the project settings. BillingStartTimeUtc is passed in CreateSubscription in the yyyy-mm-dd hh:mm format, UTC.

Subscription termsAvailable methodsInitial step
The first period is paid at sign-upThe enabled SBP, Alfa Pay, SberPayThe first payment, saving the binding for later charges
The first payment is deferredOnly SBP in this versionA binding without a charge; the platform later starts the first payment on schedule

A free trial period is the interval before the first scheduled payment, during which only a binding is made at sign-up. Its minimum length may be limited by the project settings.

A deferred payment needs free SBP binding support from the connected payment provider. BillingStartTimeUtc alone does not guarantee that this flow is available for the project. Alfa Pay and SberPay are not offered when the first payment is deferred.

The type of an operation already started does not change when the widget is reopened: opening a subscription with a deferred payment later does not turn the initial free binding into a paid first step.

6.9 Requirements on the partner side

  1. CreateSubscription is called from your own server only: the project password and the signature must not reach the browser. The signature formula is in Request signature.
  2. The addresses the partner calls the API from must be on our allowlist.
  3. The page hosting the widget must be served over https, otherwise the browser will block the embedding.

6.10 Test procedure

First payment at sign-up — SBP, Alfa Pay or SberPay

  1. Create a separate subscription with the first period paid at sign-up.
  2. Open the widget and choose the method under test.
  3. For SBP, check the QR code or the jump to the bank; for Alfa Pay and SberPay, the redirect to the bank’s payment page.
  4. Confirm the payment in the bank.
  5. Check the widget result, the server confirmation of activation, the actual first charge and the saved binding.
  6. Make sure the first period is counted once and the next payment is scheduled correctly.
  7. Separately check a recurring charge and subscription cancellation.

Deferred first payment — SBP

  1. Create a separate subscription with a deferred first payment on a project that supports free binding.
  2. Make sure Alfa Pay and SberPay are not offered.
  3. Confirm the binding via SBP.
  4. Check that nothing was charged at sign-up, the widget result, the activation and the saved binding.
  5. Check the first charge at the scheduled time.
  6. Separately check subscription cancellation.

The store’s own buttons

  1. Create a separate subscription and get its token.
  2. Open the route from §6.12 with a specific method.
  3. Make sure SBP opens the payment screen without a method choice, while Alfa Pay and SberPay redirect to the bank.
  4. Confirm the operation and check its result from server data.
  5. Check reopening with the same method: it must not create a repeated paid first payment.

Use a separate subscription for each method. Before creating a new one, check the result of the previous operation. Opening a QR code or a bank page does not yet confirm the full payment cycle.

6.11 Closing the widget and cancelling the subscription

Closing the modal window with the cross, Escape or SbpWidget.close(), and removing the widget with destroy(), stop showing the interface. They do not cancel the subscription, do not revoke the binding and do not cancel a payment already sent. The onClose callback only reports that the window was closed.

To cancel a subscription use the server-side cancellation API — the CancelSubscription method in Subscription management methods. For YP, while processing the cancellation the platform revokes the saved payment token or binding at the provider before saving the local Cancelled status. If there is no binding, no external revocation is needed. If the revocation is not confirmed because of a provider or connection error, cancellation processing stops before the status changes.

An API request accepted for processing should not be taken as confirmation that the cancellation is complete on its own: check the outcome by the subscription state and server notifications. Cancelling a subscription is not a refund for payments made earlier.

6.12 The store’s own buttons — redirect route

The partner can place their own payment method buttons. The subscription is created on the partner backend with CreateSubscription; the links carry the token from AuthorizeUrl.

GET https://subs.digitalspb.com/Cards/Sbp/pay?token=TOKEN&method=sbp GET https://subs.digitalspb.com/Cards/Sbp/pay?token=TOKEN&method=alfapay GET https://subs.digitalspb.com/Cards/Sbp/pay?token=TOKEN&method=sberpay
ParameterRequiredDescription
tokenYes32 hexadecimal characters from AuthorizeUrl, without hyphens
methodYessbp, alfapay or sberpay; the method must be available for this subscription

The route checks that the method is available and returns HTTP 302:

  • sbp — to the SBP widget screen with the method preselected, without a choice screen;
  • alfapay and sberpay — to the bank’s HTTP(S) payment page.

For a bank method, calling the route starts the initial payment operation. It is not a diagnostic request and not a request for the list of methods. SBP starts the operation when the target screen loads.

Own buttons on the partner page
<a href="https://subs.digitalspb.com/Cards/Sbp/pay?token=TOKEN&amp;method=sbp">Pay with SBP</a> <a href="https://subs.digitalspb.com/Cards/Sbp/pay?token=TOKEN&amp;method=alfapay">Pay with Alfa Pay</a> <a href="https://subs.digitalspb.com/Cards/Sbp/pay?token=TOKEN&amp;method=sberpay">Pay with SberPay</a>

Replace TOKEN in the example with a real token. When building the URL in code, use encodeURIComponent for the parameter values. The token is sensitive data and is present in the URL; its absence from logs is not guaranteed.

Do not open these links automatically, do not prefetch them and do not call them to check availability. The navigation must follow an explicit action by the payer. Get the list of methods with GET /Cards/Sbp/methods and an Authorization: Bearer TOKEN header; that request does not create a payment.

Once the operation has started, changing the method with the same token is forbidden. Reopening a paid operation must not create a new authorization. Another method needs a new token, but first check the outcome of the previous operation.

An invalid token or an unknown or unavailable method results in an HTTP 400 rejection. A payment provider failure may result in HTTP 500. The route is meant for browser navigation and returns an HTML page on rejection, not a JSON contract.

Going to the bank through this route does not create an SDK instance: for Alfa Pay and SberPay do not expect onSuccess / onFail callbacks from SbpWidget. Check the operation result by server notifications and the subscription state. Closing the page does not cancel the payment or the subscription.