Substantive changes to the API, webhooks, dashboard and docs. Subscribe via RSS at /changelog/rss.xml (coming soon) or get the same feed via webhook on maxfi.changelog events.
Channels
Channels — multiple payment channels under one merchant account (Adyen-style)
›Every merchant can now configure N independent payment Channels — one per traffic source (bot, web, mobile app, POS, marketplace). Each channel has its own credentials at the acquirer, its own branding (logo, colors, return_url), its own webhook URL + secret, and its own currency mix. Settlement and KYC stay merchant-wide.
›New optional field on POST /v2/payments and POST /v2/payments/checkout: channel_id. Accepts a UUID, a stable public id (ch_xxx) or the merchant-defined slug. Omit it and the request runs against the merchant default channel — the existing behaviour is preserved for everyone.
›New API key scope: channel-scoped and restricted keys. POST /internal/merchants/{id}/api-keys accepts scope=channel|restricted with a channel_id, plus an optional permissions array for restricted keys (e.g. ["payments:write","refunds:read"]). Restricted keys ship with the rk_live_ / rk_test_ prefix; channel-scoped sk_ keys are pinned to a channel and a request that targets a different channel is rejected with 403.
›New endpoints under /v1/channels — list / create / update / pause / resume / promote-default / soft-delete / restore / hard-delete. Hard-delete is blocked when MCAs / API keys / transactions still reference the channel; the response carries a 409 with a human-readable reason list. The default channel cannot be deleted.
›Webhooks now resolve target URL + secret in 4 tiers: per-session override → channel webhook → channel-scoped settings row → merchant-wide settings row. Merchants who only ever had one webhook URL keep working unchanged.
›Receipts and the hosted checkout page resolve branding per-channel: channel-level fields (logo_url, primary_color, return_url, custom_title) override merchant defaults via COALESCE. Customers always see one consistent brand per channel.
›Backfill is automatic. Migration 043 creates one default channel for every existing merchant and binds every existing MCA / API key / transaction / checkout session / webhook delivery row to it. Existing integrations need no code changes.
›Webhook payloads now include channel_id (and channel_code) on payment.* events so the receiving service can route bot / web traffic separately. Integrations that ignore unknown fields stay compatible.
Payment methods
SBP bank picker — NSPK whitelist validation + 4 new public endpoints
›POST /checkout/{sessionId}/submit-method for method_code: "sbp" now validates extra.memberId against the live NSPK participant whitelist. Unknown or stale member ids are rejected sub-millisecond with a localised error, before any acquirer round-trip — no more cryptic 500–800 ms failures from the bank.
›New public endpoint GET /sbp/members returns the full whitelist (member_id, localised name in en/ru/az, NSPK-hosted logo_url, ios/android deeplinks, is_popular flag, display_order). No auth required.
›New public endpoint GET /sbp/members/popular?limit=12 — the curated subset to render on a bank-picker grid. Same schema, stable ordering across refreshes.
›New session-scoped endpoint GET /checkout/{sessionId}/sbp-members?limit=12 — returns the whitelist plus a currency_supported flag (false for non-RUB sessions, so the UI can hide the picker without a second round-trip).
›New widget endpoint GET /widget/v1/sbp-members — merchant-authed, Cache-Control: public, max-age=300, for embedding the picker in custom widgets.
›All four endpoints fail-open: any upstream error returns an empty array so the hosted checkout degrades to an "Any bank" placeholder rather than breaking the session.
›Hosted MAXFI checkout (the "SBP" tab) now renders a visual bank grid with logos, deeplinks, and a single-click pre-selection. No-op for integrators — the existing flow keeps working without changes.
›Backwards compatible: legacy extra.bank_id is still accepted as an alias for extra.memberId. Empty/omitted values are not validated (whitelist check only runs on non-empty input).
Payment methods
ATB Bank — full Apple Pay & Google Pay support (card + both wallets now live)
›ATB connector now advertises three canonical methods in the catalog: card, apple_pay, google_pay. The effective catalog on the hosted checkout page, the Widget SDK, and direct-API integrations (GET /checkout/{id}/methods, GET /widget/v1/methods) returns all three for ATB merchants once activation is complete.
›Apple Pay domain association — MAXFI now serves /.well-known/apple-developer-merchantid-domain-association per-merchant. Once your Merchant Connector Account holds the domain verification blob, the endpoint is automatically available on every domain you register. No extra deploy on your side.
›Merchant Portal → Payment methods gains wallet-specific activation guidance. Apple Pay / Google Pay show an inline "Awaiting verification" banner with a step-by-step checklist (Apple Merchant ID, Google Pay Merchant ID, acquirer agreement, gateway config) and a "Contact MAXFI support" CTA while the wallets are being provisioned.
›No changes to the POST /checkout/{id}/submit-method contract — wallets submit with method_code: "apple_pay" or "google_pay" and the response carries the usual next_action.
›New public docs: /docs/payment-methods now contains an "Apple Pay / Google Pay activation" section with both checklists.
Checkout
Multi-method checkout — SBP, SberPay, TPay, YooMoney, Apple Pay, Google Pay, recurrent
›MAXFI hosted checkout now presents a tabbed method picker. The list is the intersection of what the merchant connector account (MCA) allows, the connector capability matrix (CMC), and per-merchant overrides (MCS).
›New public endpoint GET /checkout/{sessionId}/methods returns the effective catalog for a given session. Fail-open to card-only on any lookup error — customers always see at least one method.
›New public endpoint POST /checkout/{sessionId}/submit-method accepts any non-card method (sbp, sberpay, tpay, yoomoney, recurrent, apple_pay, google_pay). Response carries next_action (show_qr / redirect / three_ds_challenge / poll) instructing the browser what to do.
›Card flow is unchanged — PCI SAQ A-EP card form + 3DS2 on POST /checkout/{sessionId}/pay.
›CheckoutSession now exposes selected_method_code and next_action on GET /status for live polling.
›New optional initial_method on POST /v2/payments/checkout pre-selects a tab on the hosted page.
Widget
Widget SDK v2.0.0 — embedded method picker
›/widget/v1/maxfi.js now renders a method picker (SBP/SberPay/TPay/YooMoney/Apple Pay/Google Pay) in the modal before opening the iframe. Card flow works as before, but all methods share the same UX.
›New endpoint GET /widget/v1/methods (merchant-authed) returns the effective catalog for the widget to render. Fail-open to card-only.
›POST /widget/v1/session accepts an optional initial_method to pre-select a tab on the embedded checkout.
›BREAKING (inline-only integrations): the legacy inline card form inside the widget modal is removed. Card entry now always happens in the iframe (PCI-correct, avoids re-typing card data). If you relied on the previous shape, host the checkout page directly — the Widget SDK is unchanged for the standard button integration.
API
Bearer auth for API keys · status=open · success_url on root
›Authorization: Bearer sk_live_… / sk_test_… now works everywhere (was routing to JWT verifier and 401-ing).
›X-API-Key remains a legacy alias — pick one per request.
›success_url / cancel_url canonical location is the top level of the body (Stripe-compatible). Legacy settings.* path kept for back-compat; top-level wins when both are set.
›Checkout session initial status reported as `open` (was internal `pending`). Matches the documented enum open → processing → requires_action → succeeded/failed/expired.
›Internal `connector` field removed from the POST /v2/payments/checkout public response (brand rule: merchants see only MAXFI).