Skip to main content

Callbacks

Callback notification (webhook)

Checkout sends an HTTP POST to your notification_url whenever a transaction state changes. The callback is the source of truth, not the customer return URL.

Configuring your notification URL

For the Checkout protocol, notification_url is not available per request. A system admin configures it once in the admin panel for your merchant. Pass your sandbox and production URLs to your account manager to set them up.

warning

A callback with status=success does not always mean the payment is final. You must check both status AND type. See Final vs intermediary callbacks.

Each order processing generates a sequence of events. The type field identifies the event. The order_status field reflects the current payment or operation status.

Sequence of status changes: alt text

Transport

Value
HTTP methodPOST
Default Content-Typeapplication/x-www-form-urlencoded; charset=utf-8
BodyFlat key=value&... form. Spaces are encoded as +.
Expected response200 OK (any 2xx). Body content is ignored.
Retry on timeout/non-2xxYes; see Delivery & retries.

Event types

A callback fires for any of these type values:

typeMeaning
saleStandard purchase outcome. In DMS mode the auth result is also reported as type=sale with order_status=pending (held but not captured).
captureCapture of a previously authorized payment.
refundRefund outcome.
voidVoid of an authorization.
recurringRecurring (MIT) charge outcome.
debitDebit operation outcome.
creditCredit (payout to customer) outcome.
transferTransfer (P2P) outcome.
3ds3DS authentication step (intermediary).
redirectRedirect step for APMs / HPPs (intermediary).
initCrypto deposit initialization (intermediary; sent before the customer transfers funds, contains the wallet address). Crypto flow only.
chargebackDispute opened by the cardholder (system-generated).
reversalAuto-reversal of a held authorization (system-generated).

Status values

A status of success means the event identified by type completed successfully. It does not mean the overall payment is final. To determine whether a payment is complete, inspect type, status, and order_status together.

status reports the outcome of the event identified by type:

statusMeaning
successThe event completed successfully.
failThe event failed; transaction did not progress.
waitingThe PSP/bank has not returned a final result yet. Poll status or wait for the next callback.
undefinedUncertain due to upstream issues. Do not fulfill. Log + alert + reconcile manually.

Final vs intermediary callbacks

important

Payment completion is determined by order_status (settled, void, and so on). type + status describe only an individual transaction, not the payment.

::: Example: for a 3DS payment you receive two success callbacks:

  1. type=3ds, status=success: the customer was redirected to 3DS page. Payment is not yet complete.
  2. type=sale, status=success: funds captured. Now you can fulfill. :::
warning

Fulfilling on the first callback will result in shipping goods that were never paid for.

typeorder_statusFinal?Action
sale, capture, recurring, debit, transfer, creditsettled✅ yesFulfill the order.
sale (representing AUTH in DMS)pending⚠️ partiallyFunds are held but not captured. Trigger capture (auto / manual / API) or void. See DMS mode.
anyprepare❌ noPayment is being prepared; wait for the next callback.
refundrefund✅ yesMark refunded.
voidvoid✅ yesMark voided.
3ds, redirect, init3ds / redirect / pending❌ noWait for the next callback.
chargebackchargeback✅ yesOpen dispute.
reversalreversal✅ yesUnblock retry / notify customer.

Delivery & retries

note

Each callback is delivered exactly once. There are no automatic retries. If your endpoint is unreachable, returns a non-2xx, or times out, that callback is lost from your perspective. Build your handler accordingly.

What the platform does instead:

  • URL-level circuit breaker. If your notification_url accumulates 5 timeouts within 5 minutes, the URL is blocked for 15 minutes, during which all merchants associated with that URL stop receiving callbacks. The block clears automatically, or can be removed manually in admin panel → Configuration → Merchants → Edit Merchant. The timeout counter resets after a single successful (2xx) response.
  • No per-callback retry queue. A failed callback is not re-sent automatically.

What you should do as integrator:

  1. Make your endpoint highly available (≥ 2 instances, behind a load balancer, with a tight DB-write path). A single 500 from your side = a missed callback.
  2. Acknowledge the callback fast: return 200 OK first, do business work asynchronously. If your handler exceeds the platform timeout, the URL-block mechanism trips.
  3. Reconcile via /api/v1/payment/status as a safety net. Schedule a poll for every payment that is older than N minutes and still in a non-terminal state in your DB.
  4. Keep your handler idempotent: the same callback may still arrive multiple times in edge cases (e.g., manual replays from operations).
tip

Always make your handler idempotent. A callback may arrive multiple times. Deduplicate on (id, type, status) and never apply business effects twice.

Cascading

When cascading is enabled and triggered for an order, only the callback of the last payment attempt (the one that determines the final order status) is sent. Intermediate decline callbacks are suppressed. The exception: the first attempt's callback is sent if it requires customer redirection.

Callback parameters

ParameterTypeRequiredDescription
idstring (UUID)yesPublic payment ID generated by our system, unique value per attempt.
order_numberstring (≤255)yesEchoed order.number from your auth request.
order_amountfloat (XX.XX)yes
order_currencystring (3–6)yes
order_descriptionstring (≤1024)yes
order_statusstringyesOne of prepare, settled, pending, 3ds, redirect, decline, credit, refund, reversal, void, chargeback.
typestring (≤36)yesSee Event types.
statusstring (≤20)yesSee Status values.
reasonstring (≤1024)conditionalDecline reason. Present when status=fail.
cardstring (XXXXXX******XXXX)optionalMasked card number.
card_expiration_datestring (MM/YYYY)optional
card_tokenstringoptionalCard token for purchase and debit operations. Returned when req_token=true was set; also returned in the get-status response.
customer_name / customer_email / customer_country / customer_state / customer_city / customer_addressstringoptionalEchoed customer details.
customer_ipstringyes
payee_name / payee_email / payee_country / payee_state / payee_city / payee_addressstringoptionalReturned for transfer only.
payee_cardstringoptionalReturned for transfer only.
crypto_network / crypto_addressstringoptionalCrypto transactions only.
recurring_init_trans_idstring (UUID)optionalFirst transaction in the recurring chain. Returned when recurring was initialized.
recurring_tokenstringoptionalToken to use for /payment/recurring.
schedule_idstringoptionalRecurring schedule.
vat_amountfloatoptionalReturned when vat_calc=true.
digital_walletstringoptionalgooglepay or applepay if used.
pan_typestringoptionaldpan or fpan for digital wallets.
exchange_rate, exchange_rate_base, exchange_currency, exchange_amountmixedoptionalReturned when currency exchange was applied.
connector_name*stringoptionalInternal connector name.
rrn*stringoptionalRetrieval Reference Number from acquirer.
arn*stringoptionalAcquirer Reference Number.
approval_code*stringoptionalAcquirer approval code.
brand*stringoptionalE.g., visa.
payment_method*stringoptional
gateway_id*stringoptionalIdentifiers from the acquirer`s gateway.
extra_gateway_id*stringoptionalIdentifiers from the acquirer`s gateway.
merchant_name*stringoptionalGiven in the admin panel
mid_name*stringoptionalGiven in the admin panel
issuer_bank*stringoptionalBIN-derived data.
issuer_country*stringoptionalBIN-derived data.
extended_data*stringoptionalCustom key-value data
custom_dataobjectoptionalEchoed custom_data from your auth request (JSON callback mode) or flattened form fields.
merchant_keystringoptionalMerchant key (CLIENT_KEY). Selectable in the callback parameter list under Protocol Mapping — returned in the callback when selected, and in the get-status response when get status response is enabled there.
checkout_payment_method*stringoptionalPayment method name in Checkout terminology (for example card, applepay). Also appended to success_url / cancel_url as payment_method when enabled.
initiator*stringoptionalCard-On-File: initiator of the payment (Customer or Merchant).
sequence*stringoptionalCard-On-File: One-off, Initial, or Subsequent.
source*stringoptionalCard-On-File: Card, Network Token, Card On File, Internal Token, or Recurring Token.
datestring (YYYY-MM-DD HH:MM:SS)optionalTransaction time.
hashstringyesVerify against Hash signature.

* marks fields returned only when explicitly enabled in Protocol Mappings (Add Extended Data to Callback / Add Data to Callback). Fields without * that are marked optional are returned automatically in the scenarios described in their row (for example, recurring, digital-wallet, VAT, or currency-exchange fields), not through this Protocol Mapping toggle.

The set of parameters returned in the get-status response is configurable via Protocol Mapping (admin panel → Configuration → Protocol Mappings). The Card-On-File fields (initiator / sequence / source) are not sent by default; enable them per merchant in Protocol Mapping.

Card-On-File generation

When the MID has Card-On-File support (card_on_file_support) enabled, a Purchase or Debit generates a Card-On-File: Payment Platform requests COF generation from the connector and stores the resulting card_token with the transaction. The merchant reuses that card_token in subsequent Purchase and Debit payments (including recurring); the initiator / sequence / source attributes above describe the Card-On-File context of each such payment.

3DS authentication attributes

When enabled under Protocol Mapping → Add Data to Callback, the callback carries the 3DS authentication result. These are populated when the payment went through an External MPI (stored as-is) or the connector-service / Checkout collector.

FieldDescription
threeDSServerTransIDTransaction ID in the 3DS system.
dsTransIDDirectory server transaction ID.
acsTransIDACS (issuing bank) transaction ID.
xidMain transaction ID in 3DS v1.0.
transStatusAuthentication status (Y success, N failure, C challenge, and so on).
transStatusReasonStatus reason code.
eciElectronic Commerce Indicator.
protocolVersion3DS protocol version.
authenticationFlowFrictionless or Challenge.

Browser metadata attributes

Also configurable under Protocol Mapping → Add Data to Callback, carrying the payer's browser data.

FieldExampleDescription
browser_color_depth24Screen color depth in bits.
browser_screen_height1080Screen height in pixels.
browser_screen_width1920Screen width in pixels.
browser_java_enabledFALSEJava support flag.
browser_javascript_enabledTRUEJavaScript availability flag.
browser_languageuk-UALanguage set in the browser.
browser_timezone_offset-180Minutes between UTC and local time.
browser_user_agentMozilla/5.0 ...User-Agent string.
browser_accept_headerstext/html, ...HTTP Accept header.
browser_platformWin32Payer's operating system.

Reference handler (Node.js)

app.post("/checkout/callback",
express.urlencoded({ extended: false }), // application/x-www-form-urlencoded
async (req, res) => {
const cb = req.body;

// 1. Verify signature (constant-time)
if (!verifyCallback(cb, process.env.CHECKOUT_PASSWORD)) {
return res.status(401).end();
}

// 2. Idempotency
const eventKey = `${cb.id}:${cb.type}:${cb.status}`;
if (await cache.exists(eventKey)) return res.status(200).end();

// 3. Find the transaction in your DB
const tx = await db.tx.findById(cb.id);
if (!tx) return res.status(404).end();

// 4. Decide based on (type, status, order_status); see §6.4
const settlingTypes = ["sale", "capture", "recurring", "debit", "transfer", "credit"];

if (cb.status === "fail") {
tx.state = "decline";
tx.reason = cb.reason;
await db.tx.save(tx);
await notifyCustomerFailure(tx);

} else if (cb.status === "success" &&
settlingTypes.includes(cb.type) &&
cb.order_status === "settled") {
// Final success: fulfill the order
tx.state = "settled";
await db.tx.save(tx);
await fulfillOrder(tx);

} else if (cb.status === "success" &&
cb.type === "sale" &&
cb.order_status === "pending") {
// DMS authorization: funds held, awaiting capture
tx.state = "authorized";
await db.tx.save(tx);
// Trigger capture later or via API; do NOT fulfill yet.

} else if (cb.type === "refund" && cb.status === "success") {
tx.state = "refund";
await db.tx.save(tx);

} else if (cb.type === "void" && cb.status === "success") {
tx.state = "void";
await db.tx.save(tx);

} else if (cb.type === "chargeback") {
await openDispute(tx, cb);

} else if (cb.type === "reversal") {
tx.state = "reversal";
await db.tx.save(tx);
}
// 3ds / redirect / init / waiting / undefined: do not finalize, wait for next callback

await cache.set(eventKey, 1, "EX", 7 * 24 * 3600);
return res.status(200).end();
});

Examples

Successful sale
id=f0a51dfa-fc43-11ec-8128-0242ac120004&
order_number=order-1234&
order_amount=3.01&
order_currency=USD&
order_description=bloodline&
order_status=settled&
type=sale&
status=success&
card=411111****1111&
card_expiration_date=12/2022&
arn=74046106207016278609311&
rrn=789012345678&
approval_code=123456&
date=2022-07-05 09:22:09&
hash=6d8d440e25bdfc5288616ce567496948d2562852
Failed sale
id=1f34f446-fc45-11ec-a50f-0242ac120004&
order_number=order-1234&
order_amount=3.01&
order_currency=USD&
order_description=bloodline&
order_status=decline&
type=sale&
status=fail&
reason=Declined by processing.&
card=411111****1111&
card_expiration_date=12/2022&
date=2022-07-05 09:30:35&
hash=7f15d178e9b2c8507dea57f8ed1efddb9573fa6b
Successful refund
id=f0a51dfa-fc43-11ec-8128-0242ac120004&
order_number=order-1234&
order_amount=3.01&
order_currency=USD&
order_description=bloodline&
order_status=settled&
type=refund&
status=success&
date=2022-07-05 09:28:01&
hash=6d8d440e25bdfc5288616ce567496948d2562852
Crypto initialization (intermediary)
id=ac60a7e6-9abf-11ef-a549-0242ac120002&
order_number=order-1234&
order_amount=0.00000001&
order_currency=BTC&
order_description=Important+gift&
order_status=pending&
type=init&
status=success&
crypto_network=ERC20&
crypto_address=bc1ABIJKL1234abfjkmoPQRTVXY5678prsx&
date=2024-11-04+15%3A15%3A49&
hash=871a243619e4fd04db377cfbabe9fe07505febf5

Note: type=init is the first callback for crypto deposits, sent before the customer transfers funds. It carries the destination crypto_address and crypto_network. A subsequent callback (e.g., type=sale, status=success or type=sale, status=fail) delivers the final result once the transfer is observed on-chain.