MAXFIMAXFIdeveloper hub
Changelog

Changelog

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).
API

Refunds v2: idempotency, partial chaining, audit log, payment.refunded webhook

  • POST /v2/payments/{id}/refund accepts idempotency_key (24h Redis cache).
  • refunded_amount + refund_count tracked atomically; over-refund returns 400 with remaining balance.
  • Each refund call enqueues a signed payment.refunded webhook to the merchant.
  • Audit stages refund_request and refund_response with duration_ms persisted.
API

GET /v2/payments and /v2/payments/{id} return full PCI-safe payment fields

  • Added card_last4, card_brand, source, customer_email, error_code, error_message, checkout_session_id, connector_transaction_id, metadata.
  • Lookup accepts payment_id, order_id, checkout session id (cs_*), or upstream reference.
  • Stable payment_id == checkout sessionId. No more duplicate rows when the upstream id is assigned later.
Webhooks

Outbound webhook delivery worker shipped (8 retries, exp backoff to 24h)

  • merchant_webhook_deliveries outbox table with FOR UPDATE SKIP LOCKED.
  • X-MAXFI-Signature: sha256=<hex>, X-MAXFI-Event, X-MAXFI-Event-Id, X-MAXFI-Attempt headers on every delivery.
  • Per-session webhook_url override on POST /v2/payments/checkout.
  • New event payment.requires_action — fires the moment a 3DS challenge URL is available.
Checkout

Checkout reliability fixes

  • HTTP timeout to upstream raised to 90s (slow synchronous flows used to time out at 30s).
  • Auto-confirm step after the initial create call when the upstream signals confirm.
  • Real status polling — getTransactionStatus now hits the live upstream endpoint, falls back to DB.
  • Both shapes of upstream redirect (next_action object and customer_interaction.redirect.url) are parsed.
Docs

dev.exezine.az launched — public Developer Hub

  • Animated landing, full /docs, /api, /webhooks, multi-language code samples (cURL, Node, Python, PHP, Go).
  • Search ⌘K, Ask AI panel, EN/RU/AZ language switch, /apply form (replaces direct portal link), /contact.
  • OpenAPI spec at /api/openapi.json · Markdown bundle at /api/openapi.md · printable PDF at /api/openapi.pdf.