Changes to the MAXFI API, webhooks, merchant portal and this documentation, newest first.
Payment page
Payments from shops on InSales, Ecwid and Tilda without a module
›MAXFI accepts the payment forms of InSales (external payment method) and Tilda (universal payment system): the merchant connects the shop in the merchant portal and copies the payment URL and credentials into the platform admin. The customer pays on the MAXFI payment page; MAXFI tells the platform the order is paid. No API key is given to the platform.
›The amount is taken exactly as the platform sends it: extra decimal places are refused, not rounded. "Paid" is reported only from the MAXFI payment status and only when the amount and currency match the order; failed notifications are retried up to 20 times.
Payment methods
Apple Pay and Google Pay: one path for the payment page and the API
›A wallet payment from the MAXFI payment page now reaches the processing bank the same way as a wallet payment through POST /v2/payments. If the customer’s bank asks for 3-D Secure, the page takes the customer there and back.
›POST /v2/payments: capture_method: manual now works for apple_pay and google_pay — the payment is authorised (requires_capture) instead of being captured at once.
›payment_token: send the Apple Pay token (event.payment.token, whole or its paymentData) or the Google Pay tokenizationData.token as a JSON string; Base64 of either is accepted too. If the wallet is not available for your account, the response is 400 payment_method_not_supported.
Widget SDK
Widget SDK 3.3: the decline code in onError
›After a decline, onError and maxfi.on("error", fn) get error_code — the decline code from the MAXFI list (for example insufficient_funds or user_cancelled), the same as in GET /v2/payments/checkout/{id} and the payment.failed webhook. The error text is still not passed.
›The Widget SDK guide now explains a window closed without a result: only maxfi.on("close", fn) fires, so check the payment status on your server.
API
One list of decline codes for every payment
›A declined payment now always gets a code from the MAXFI list (see Decline codes in the docs): in error.code of the POST /v2/payments response, in error_code of GET /v2/payments/{id} and GET /v2/payments/checkout/{id}, and in the merchant portal. Before, a live payment could return the processing bank’s own code or text.
›The payment.failed webhook now carries data.error_code; data.error_message is the fixed English text of that code. A code you do not know should be treated as processing_error.
›error.message and error_message are now the fixed text of the code, not a processor message. Branch on the code.
Webhooks
A payment created with the API gets a webhook when the result comes right away
›When POST /v2/payments returns the final status in its response (succeeded or failed), your webhook URL now also receives payment.succeeded or payment.failed. This works in test mode (cards 4242… and the decline cards) and in live mode. Before, such a payment got no webhook in test mode, and in live mode only if the bank sent a separate notification.
›A successful POST /v2/payments/{id}/capture sends payment.succeeded; data.amount is the captured amount. Before, a payment with manual capture got no webhook.
›When POST /v2/payments answers requires_action (3-D Secure, a wallet, an SBP QR code), your webhook URL receives payment.requires_action. Before, a payment created with the API never got this event.
›GET /v2/payments/{id} for a payment declined right away now returns error_code and error_message — the same as in the POST /v2/payments response. Before, both were empty.
›Each payment gets payment.succeeded or payment.failed once: if the bank later sends its own notification, no second event follows.
Widget SDK
Widget SDK 3.2: onSuccess and onError get the payment result
›The payment page in the widget window now reports the payment result to your page: onSuccess or onError is called with { maxfi: true, status: 'succeeded' | 'failed', session_id }, and the same object comes to maxfi.on("success" | "error", fn). It is the result MAXFI has confirmed on the server. Before, onSuccess was never called. After a success the window closes in 2 seconds, after a decline in 4 seconds, so that the customer can read the reason.
›Fulfil orders by the signed webhook payment.succeeded or GET /v2/payments/checkout/{id}, as before: the result does not reach your page if the customer closes the window first.
›The result goes only to a site listed in Form Builder → Sites allowed to embed, and only to the exact address of the window that embedded the page. If you embed the payment page in your own iframe without the SDK, listen for the message with type 'maxfi.checkout.result' from https://maxfi-api.exezine.az.
›After a 3-D Secure check on the bank’s page, the widget window now shows the payment result screen. Before, a paid session opened your return_url inside the window.
Webhooks
Test mode: a payment created with the API gets its result and webhook after the test page
›A test payment created with POST /v2/payments that needs an extra step — the 3-D Secure check, confirmation in SberPay, T-Pay or YooMoney, payment by SBP QR code — now gets its final status as soon as you press confirm or decline on the MAXFI test page, and your webhook URL receives payment.succeeded or payment.failed with "livemode": false, as a live payment does after the bank’s answer. data.payment_id is the ID of the payment (pay_…), data.payment_method is the method used. Each event is sent once, even if you press the button again. Before, the result reached the payment only when you requested GET /v2/payments/{id}, and no webhook was sent.
›If such a payment has no return_url, the test page shows the result itself. Before, it led to a page that said "Payment Successful!" even after you declined.
›GET /v2/payments/{id} for a payment that is waiting for its result now returns error_code and error_message as soon as the result is known. The webhook is sent once, whichever comes first: your status request or the result itself.
Widget SDK
Widget SDK 3.1: the customer’s language and the payment currency
›The widget window is now in the customer’s language: the locale option if you pass it (new Maxfi('pk_…', { locale }), openCheckout({ locale }) or data-locale), otherwise the customer’s browser language, otherwise the lang attribute of your <html>, otherwise English; en, ru or az. The header, the pay button, the footer and the error message are translated too. Before, the SDK used only the lang attribute of your <html>, and only the method names and hints were translated.
›The payment page in the widget window now picks its language itself, as when checkout_url is opened directly: the session language if you passed it, then the customer’s browser language, then your default language from Form Builder. Before, the SDK always passed the language of your page, which overrode the language fixed in the session. To set the language of both the window and the page, pass locale.
›The SDK passes the payment currency to GET /widget/v1/methods — from the currency option, data-currency or the session response — so the window offers only the methods available in that currency: SBP, SberPay, T-Pay and YooMoney only for RUB. If the list is empty or cannot be loaded, the window shows no method tabs and the customer picks the method on the payment page; before, the window showed a card tab in that case.
›The amount in the window header and on the pay button is shown as on the payment page, the currency sign before the amount (₼5.00, ₽5.00, $5.00); without a currency it is a plain number (before, US dollars were assumed). Existing integrations keep working without changes: the new options are optional.
API
Connect a website: modules for online stores get their keys without copying
›New endpoints without an API key: POST /v2/connect/requests, GET /v2/connect/requests/{id} and POST /v2/connect/requests/{id}/claim. A module asks for a connection, the merchant approves it in the merchant portal, and the module receives its keys once after a PKCE check (S256).
›Each connected website gets its own payment channel, restricted keys rk_test_/rk_live_ (payments:write, refunds:write, integrations:write) and its own webhook secret. The merchant can disconnect a website in the merchant portal; its keys stop working immediately.
›New endpoint POST /v2/integrations/heartbeat: the module reports its version, the platform version and whether MAXFI events reach the website; the merchant sees it in the list of connected websites.
Payouts
Card payouts in the common API (/v2/payouts)
›New endpoints: POST /v2/payouts, GET /v2/payouts, GET /v2/payouts/{id} and POST /v2/payouts/{id}/cancel. They use the same secret keys as payments; restricted keys need payouts:read or payouts:write. Both POST endpoints accept the Idempotency-Key header.
›The method field selects the payout method; today it is card. You create a payout and send its payout_url to the recipient, who enters the card number on a MAXFI page. The API does not accept card numbers; you see only the first 6 and last 4 digits of the card.
›Card payouts are made in AZN. MAXFI enables live card payouts for each account (until then a live request gets 403 card_payouts_not_enabled); test mode works for every account. In live mode the amount is held on your available balance when the payout is created: it is charged when the payout succeeds and released when it fails, is canceled or expires.
›New webhook events: payout.processing, payout.succeeded, payout.failed, payout.canceled, payout.expired and payout.reversed. They go to the same webhook URL and are signed like payment events. Select them in the merchant portal under Developer Hub → Webhooks.
›Do not confuse these endpoints with the payouts description removed from this documentation earlier the same day: that one was never released, while the endpoints above work as described on the Card payouts page.
API
Confirmation steps in test mode, methods by currency, channel currencies, error and time format
›In test mode, steps that a bank or wallet would show — the 3-D Secure check, confirmation in SberPay, T-Pay or YooMoney, payment by SBP QR code — open a MAXFI test page (maxfi-api.exezine.az/checkout/sandbox/confirm/…) with two buttons: confirm or decline. Then the customer returns to the payment page, or to the return_url of a payment created with POST /v2/payments. With card 4000 0000 0000 3220 you choose the result of the 3-D Secure check; 4000 0000 0000 3063 fails it whichever button you press (3ds_failed). Phone +79000000001 opens the test page; +79000000002 to +79000000006 are declined without it. Before, these steps led to pages that did not open, and such test payments could not be completed.
›The payment page of a test session now offers the methods of the test environment, not only the card form. Apple Pay and Google Pay are not shown on the page in test mode, because their buttons need your registration with Apple and Google: test them through POST /v2/payments with any payment_token.
›The saved card (recurrent) is no longer offered on the payment page or in the widget window, in test or live mode: charge a saved card with POST /v2/payments (payment_method_data.recurrent). settings.payment_methods with only recurrent returns 400 PAYMENT_METHODS_UNAVAILABLE. The MAXFI test page now also opens inside the widget window.
›SBP, SberPay, T-Pay and YooMoney are offered only for payments in RUB, on the payment page and in method lists. GET /checkout/{id}/methods, GET /widget/v1/methods and GET /v1/payment-methods no longer return flow_description, and GET /v1/payment-methods no longer returns connector_id. GET /v1/payment-methods/catalog is retired and returns 410 GONE. If the list cannot be loaded, both methods endpoints answer with data: [] and unavailable: true; before, the widget endpoint returned a list with the card only. GET /widget/v1/methods accepts an optional currency parameter; before, the list was always built for RUB.
›Every JSON error response now carries error.request_id, including errors such as SESSION_NOT_FOUND, IDEMPOTENCY_IN_PROGRESS and fraud_blocked that did not have it. An Idempotency-Key longer than 255 characters returns 400 IDEMPOTENCY_KEY_TOO_LONG in the usual error format.
›created_at and updated_at in GET /v2/payments and GET /v2/payments/{id} are ISO 8601 in UTC, for example 2026-11-13T07:30:00.123Z. Before, they were returned with a +04 offset in another format.
›GET /v1/balance/transactions returns only test payments for a test key and only live ones for a live key; total is the number of all matching payments, not the length of the page; a limit or offset that is not a positive number means 20 or 0. Before, the list mixed both modes.
›POST /v2/payments rejects webhook_url with 400 UNSUPPORTED_PARAMETER; before, it was accepted and ignored. Events of such payments go to the webhook URL set in the merchant portal; a per-session webhook_url is available in POST /v2/payments/checkout.
›The payment currency must be enabled for the payment channel: POST /v2/payments, POST /v2/payments/checkout and POST /widget/v1/session return 400 CURRENCY_NOT_ALLOWED for a currency the channel does not accept. The channel’s currencies are set in the merchant portal, on the Channels page. Before, they were not checked.
›GET /v2/payments/checkout/{id} returns 404 SESSION_NOT_FOUND for a session of another merchant, the same as for an unknown session; before, it returned 403 FORBIDDEN.
›POST /v2/payments/{id}/void accepts Idempotency-Key. A repeat by the idempotency_key body field now returns the status code of the first response (202 for requires_action and refund_pending) with idempotent-replay: true; before, it always returned 200 without that header.
›A live key on an account where live payments are not set up gets 409 LIVE_PAYMENTS_NOT_CONFIGURED in POST /v2/payments/checkout and POST /widget/v1/session, and 400 payment_method_not_supported in POST /v2/payments; before, these requests could end with 500.
›data.payment_id in webhook events is always the MAXFI payment ID (cs_… or pay_…). Before, events sent after a status update from the payment processor carried the processor’s operation number instead.
API
Test sessions on the payment page, page language, payment method selection, idempotency
›POST /v2/payments/checkout with a test key (sk_test_) now creates a real session on the MAXFI payment page: a cs_… id, checkout_url on maxfi-api.exezine.az/checkout/…, amount and currency in the response, and livemode: false. Pay it with the test cards from the Quickstart; declining test cards such as 4000 0000 0000 0002 now decline on the page too. Before, a test key received a link to a page that did not open.
›language is optional. If you pass it, the page opens in that language and the customer can still switch it; if you omit it, the page uses the customer’s browser language, then your default language from merchant portal → Form Builder. Before, the page opened in English unless you passed another language, and the browser language was not used.
›initial_method opens the tab of that payment method first. settings.payment_methods limits the session to the listed methods, and a payment by another method is refused; if none of them is available, the API returns 400 PAYMENT_METHODS_UNAVAILABLE. Before, both fields were ignored.
›Fields that never worked are now rejected instead of being ignored: settings.button_text, settings.auto_return and settings.customer_fields return 400 UNSUPPORTED_PARAMETER. test_mode cannot switch the mode, which is set by the key: a value that contradicts the key returns 400 TEST_MODE_MISMATCH in POST /v2/payments/checkout and POST /v2/payments.
›customer.ip in POST /v2/payments/checkout is used for risk checks; without it the payment page uses the address of the customer’s browser. A live key on an account where live payments are not set up gets 409 LIVE_PAYMENTS_NOT_CONFIGURED.
›Idempotency-Key now also works on POST /v2/payments/checkout (a repeat returns the same session) and on refunds, which keep the idempotency_key body field. A key is bound to its request: a repeat of the same request within 24 hours returns the stored response with idempotent-replay: true, and the same key with a different request returns 409 IDEMPOTENCY_KEY_REUSED. The idempotency_key body field is bound to its request in the same way. Before, a reused key silently returned the first response.
›GET /v2/payments/checkout/{id}: a test key sees only test sessions and a live key only live ones; a session of the other mode returns 404 SESSION_NOT_FOUND.
›GET /v2/payments and GET /v2/payments/{id} no longer return the connector field, and GET /checkout/{id}/methods and GET /widget/v1/methods no longer return connector_id. payment_method in payments and in webhook events is now the method the customer used (sbp, google_pay, …); before, payments made on the payment page always showed card. Test payments arrive in webhook events with livemode: false.
›POST /widget/v1/session: currency is required (before, RUB was used when it was missing); language and initial_method work as in POST /v2/payments/checkout, and the payment page shows your merchant name and branding. Errors always come as { success: false, error: { code, message } }; new codes: 400 CHANNEL_NOT_CONFIGURED and 409 LIVE_PAYMENTS_NOT_CONFIGURED.
›The event simulator (merchant portal → Developer Hub → Sandbox) now sends test events to the webhook URL from your settings, signed with your webhook secret as X-MAXFI-Signature: sha256=<hex>, like real deliveries, in the envelope { id, type, livemode: false, created_at, data }. The t=…,v1=… signature format is gone. Before, the simulator did not deliver events.
›The /v1/wallet/* endpoints (session, confirm, available, google-pay/config) are retired and return 410 GONE. Accept Apple Pay and Google Pay on the MAXFI payment page: create a session with POST /v2/payments/checkout.
Docs
Documentation accuracy update
›We compared this documentation with the platform and removed what was documented but never released: payouts (POST /v2/payouts), the /v1/channels management endpoints, the public /sbp/members endpoints, BNPL, crypto and IBAN payments, a KYC API and server SDK packages.
›Webhooks: the documentation listed eight event types that are not sent — payment.created, payment.processing, payment.cancelled, refund.created, refund.failed, payout.completed, payout.failed and chargeback.created. MAXFI sends payment.requires_action, payment.succeeded, payment.failed, payment.refunded and payment.expired.
›An authorised payment is cancelled with POST /v2/payments/{id}/void, as in the API; the documentation called this endpoint /cancel.
›The error code tables were corrected to list the codes the API returns.
›Entries in this changelog that announced features that never shipped were corrected.
›The Developer Hub is now available in Russian and Azerbaijani. The language follows your choice in the header or your browser settings. If a translation differs from the English text, the English text prevails.
›Restricted keys (rk_) now act only with the permissions given when the key was created (payments, refunds, customers, balance, webhooks — each read or write; write includes read). Any other endpoint returns 403.
›A key bound to a channel sees only payments of that channel: the lists GET /v1/payments and GET /v2/payments are filtered, and a payment of another channel returns 404. Lists also follow the key mode: a test key sees only test payments.
›If the acquiring bank does not answer a refund request in time, POST /v2/payments/{id}/refund returns 202 with status refund_pending and keeps the amount reserved. Do not repeat the refund: the money may already be on its way back. Check the payment later with GET /v2/payments/{id}.
›New webhook event payment.expired: the acquiring bank gave no final answer within 72 hours (error_code reconcile_timeout). Check the payment before you fulfil the order or refund it. An event that happens once per payment, such as payment.succeeded, is now queued once even if several parts of the platform report it, and livemode reflects the mode of the payment itself.
›metadata.merchant_id and metadata.channel_id are reserved and ignored: the merchant is always taken from your key, and the channel from the key or the channel_id field.
Security
Widget SDK v3 — publishable key in the browser, session created by your server
›The widget now accepts only your publishable key (pk_live_ / pk_test_). A secret key in a web page can be read by any visitor from the page source and used against your account, for example to make refunds or read your payments. The SDK (/widget/v1/maxfi.js) refuses secret keys (sk_, rk_, prd_, snd_): it logs an error to the console and sends nothing.
›Payment sessions are created by your server with your secret key: POST /widget/v1/session (or POST /v2/payments/checkout). The response no longer carries CORS headers, and a publishable key is rejected with 401 PUBLISHABLE_KEY_NOT_ALLOWED. Pass the session id to your page and open it with maxfi.openCheckout({ sessionId }), or use fetchSession / sessionUrl so that the SDK asks your server for it. createCheckout and createButton keep their names; data-maxfi-checkout elements need data-session-id or data-session-url.
›GET /widget/v1/methods takes the publishable key in the key query parameter and is the only endpoint that accepts a publishable key. Secret keys get 403 SECRET_KEY_NOT_ALLOWED; a live publishable key works only after your account is switched to live mode. GET /widget/v1/sbp-members is for your server only (secret key); in a browser, use GET /checkout/{sessionId}/sbp-members for an active session.
›Action required if your page contains a secret key: replace that key in the merchant portal (Developer Hub → API Keys), add a server endpoint that creates the session, and switch the page to your publishable key. Until then the old snippet does not open the payment window. Guide: /docs/widget.
Security
Security headers on hosted stores and API root responses
›Hosted store pages (/store/{slug}, store subdomains and custom store domains) now send a Content-Security-Policy with a nonce that is new for every response. Stores keep working as before: logos, banners and product images from any https address still load. Custom scripts were never supported on store pages, and none are allowed now.
›GET / on maxfi-api.exezine.az now answers from the MAXFI API with its standard security headers (404 in JSON) instead of a bare HTML 404 page. No endpoint under /v1, /v2, /checkout or /widget changes.
Security
Payment page — framing limited to sites you list
›The hosted payment page could be framed by any HTTPS site. It now opens in a frame only on sites listed in the merchant portal under Form Builder → Sites allowed to embed.
›If you use the Widget SDK, check that your site is in the list. For live merchants with a website on file, the site and its www address were added automatically.
›Integrations that redirect the customer to the payment page are not affected: no code changes are needed.
Balance
GET /v1/balance — 503 instead of zero balances when the ledger is unavailable
›Until now, if our ledger was temporarily unreachable, GET /v1/balance answered 200 with all balances equal to zero — indistinguishable from an empty account.
›It now answers 503 with error code BALANCE_UNAVAILABLE in the standard error format. Retry with increasing pauses; never treat this response as a zero balance. A 200 response is unchanged.
›The endpoint is described in the API reference (amounts in minor units; available, pending and reserve per currency).
Channels
Channels — several payment channels under one merchant account
›A merchant can have several payment channels, one per traffic source (website, bot, mobile app, POS, marketplace). Each channel has its own acquiring credentials, its own branding (logo, colours, return URL), its own webhook URL and secret, and its own currencies. Settlement and merchant verification stay at the account level.
›Channels are created and managed in the merchant portal (Channels). There is no public API for managing channels.
›New optional field channel_id on POST /v2/payments and POST /v2/payments/checkout. It accepts the channel UUID, its public id (ch_…) or its code. Without it the request runs in your default channel, so existing integrations keep working.
›API keys can be bound to a channel. Restricted keys (rk_live_ / rk_test_) are bound to a channel and carry a fixed list of permissions. A channel-bound key that targets another channel gets 403.
›The webhook URL and secret are chosen in this order: the webhook_url passed for the payment session, the channel webhook, then your account-wide webhook settings. Merchants with a single webhook URL need no changes.
›The payment page uses the channel branding (logo, colours, title) where it is set, and your account defaults otherwise.
›Every existing account received a default channel, and existing keys and payments were assigned to it. Existing integrations need no code changes.
Payment methods
SBP bank picker and participant bank check
›POST /checkout/{sessionId}/submit-method with method_code "sbp" now checks extra.memberId against the list of SBP participant banks. An unknown bank is rejected with a clear error before the request reaches the bank.
›GET /checkout/{sessionId}/sbp-members?limit=12 returns popular participant banks (name, logo, links to bank apps) for an active session, with a currency_supported flag that is false for sessions not in RUB, so the page can hide the picker.
›GET /widget/v1/sbp-members returns the same list for your server and requires your API key.
›If the bank list cannot be loaded, both endpoints return an empty list, and the payment page offers the “Any bank” option instead of failing.
›The SBP tab of the payment page shows a grid of banks with logos; the customer picks a bank in one click.
›extra.bank_id is still accepted as an alias for extra.memberId. Empty values are not checked.
Payment methods
Apple Pay and Google Pay alongside cards
›Card, Apple Pay and Google Pay can be offered together on the payment page, in the Widget SDK and in direct API integrations (GET /checkout/{id}/methods, GET /widget/v1/methods) once the wallets are activated for your account.
›Once the Apple Pay domain association file is saved for your account, MAXFI serves it at /.well-known/apple-developer-merchantid-domain-association on the domains you registered.
›The merchant portal (Payment methods) shows activation steps for Apple Pay and Google Pay; while the wallets are being set up, their status is “Awaiting verification”.
›The POST /checkout/{id}/submit-method contract is unchanged: wallets use method_code "apple_pay" or "google_pay", and the response carries the usual next_action.
›/docs/payment-methods describes how Apple Pay and Google Pay are activated.
Payment page
Payment page with several methods — SBP, SberPay, T-Pay, YooMoney, Apple Pay, Google Pay, recurring payments
›The payment page shows a method picker. It lists the methods that are enabled for your account and available for the payment.
›GET /checkout/{sessionId}/methods returns the methods available for a session. If the list cannot be loaded, the page shows the card form, so the customer always has a way to pay.
›POST /checkout/{sessionId}/submit-method accepts the methods other than cards (sbp, sberpay, tpay, yoomoney, recurrent, apple_pay, google_pay). The response carries next_action (redirect, show_qr, deeplink or poll), which tells the browser what to do next.
›Cards are paid as before: the card form with 3-D Secure, POST /checkout/{sessionId}/pay.
›GET /checkout/{sessionId}/status now also returns selected_method_code and next_action.
Widget SDK
Widget SDK v2.0.0 — method picker in the widget
›/widget/v1/maxfi.js shows a method picker (SBP, SberPay, T-Pay, YooMoney, Apple Pay, Google Pay) in its window before it opens the payment page in a frame. Card payments work as before.
›New endpoint GET /widget/v1/methods returns the methods for the widget to show; if the list cannot be loaded, only the card is offered. Since Widget SDK v3 it takes your publishable key.
›POST /widget/v1/session accepts an optional initial_method that pre-selects a method on the payment page.
›Breaking for inline-only integrations: the card form inside the widget window was removed. Card details are now always entered on the MAXFI payment page inside the frame. If you relied on the old form, send customers to the payment page directly; the standard button integration is unchanged.
API
Bearer authentication for API keys, status open, success_url at the top level
›Authorization: Bearer sk_live_… / sk_test_… works on every endpoint that takes a secret key; some used to return 401.
›X-API-Key is still accepted as an alternative; use one of the two headers in a request.
›success_url and cancel_url belong at the top level of the request body. settings.success_url and settings.cancel_url are still read for older integrations; the top-level value wins when both are set.
›A new checkout session is reported with status open (previously the internal pending), matching the documented order open → processing → requires_action → succeeded / failed / expired.
›The internal connector field was removed from the POST /v2/payments/checkout response.
API
Refunds: idempotency, partial refunds and the payment.refunded webhook
›POST /v2/payments/{id}/refund accepts idempotency_key: a repeated request with the same idempotency_key within 24 hours returns the first result instead of making a second refund.
›Several partial refunds can be made up to the payment amount. A refund above the remaining amount returns 400 with the amount still available.
›Each successful refund sends a signed payment.refunded webhook.
API
GET /v2/payments and GET /v2/payments/{id} return more payment fields
›Added card_last4, card_brand, source, customer_email, error_code, error_message, checkout_session_id, connector_transaction_id and metadata. The full card number is never returned.
›A payment can be looked up by payment_id, order_id, the checkout session id (cs_…) or the acquiring bank’s reference.
›For payments made on the payment page, payment_id is the checkout session id (cs_…), so the payment is no longer recorded twice when the acquiring bank’s reference arrives later.
Webhooks
Webhook delivery with retries (8 attempts, intervals up to 24 hours)
›Webhooks are delivered from a queue with up to 8 attempts; the interval between attempts grows up to 24 hours.
›Every delivery carries the headers X-MAXFI-Signature: sha256=<hex>, X-MAXFI-Event, X-MAXFI-Event-Id and X-MAXFI-Attempt.
›POST /v2/payments/checkout accepts webhook_url to send the events of one session to a different address.
›New event payment.requires_action — sent as soon as a 3-D Secure challenge URL is available.
Payment page
Payment page reliability fixes
›Requests to the acquiring bank now wait up to 90 seconds instead of 30, so fewer slow payments fail with a timeout.
›The payment status is now checked with the acquiring bank, not only taken from our records.
›Redirects to 3-D Secure are recognised in both response formats the acquiring bank uses.
Docs
dev.exezine.az launched — public Developer Hub
›Documentation (/docs), API reference (/api), webhooks (/webhooks) and code samples in cURL, Node.js, Python, PHP and Go.
›Search (⌘K / Ctrl+K), a form for questions to the MAXFI team, the /apply application form and /contact.
›OpenAPI specification at /api/openapi.json, a Markdown version at /api/openapi.md and a printable version at /api/openapi.pdf.