Handling The Callback
Verify and process APM webhook callbacks
After the customer completes (or fails) the payment, Exirom sends a server-to-server POST to your callbackUrl with the final transaction status.
#Processing Steps
- Verify the
X-Checksumheader — recompute the unchanged APM-payment HMAC-SHA256 usingaccountId | orderAmount | orderCurrency | transactionIdand compare. Reject if it doesn't match. See Checksum Authentication. - Look up the transaction by
requestIdortransactionId - Update your system based on
transactionStatus - Return
200 OKpromptly — Exirom retries on non-2xx responses
Checksum amount: Use the exact
orderAmountrepresentation received in the callback when verifying its checksum — do not reuse the original request amount.
#Havale Pay-ins: Credit the Processed Amount
This rule applies only when your approved pay-in route uses Havale, including Havale routed through SMART_BANK_TRANSFER. Before enabling Havale through Smart Bank Transfer or a direct API integration, confirm availability and integration requirements with your Account Manager.
When the callback has transactionStatus: "SUCCEED", credit the customer with processedAmount in processedCurrency. Never credit the requested amount or callback orderAmount when they differ from the processed values.
For example, this request asks the customer for 85,000 TRY:
{
"amount": "85000.00",
"currency": "TRY",
"requestId": "merchant-request-123",
"apmPayload": {
"paymentMethod": "SMART_BANK_TRANSFER",
"paymentType": "BANK_TRANSFER",
"customFields": {}
}
}The complete successful callback body can contain a processed amount of 1,100 TRY. Values are redacted, but the payload shape, field names, and null values match the callback:
{
"order": {
"date": null,
"name": null,
"title": null,
"siteId": null,
"orderId": null,
"domainName": null
},
"accountId": "merchant-account-id",
"createdAt": "2026-08-17T12:00:00.000000",
"requestId": "merchant-request-123",
"callbackUrl": "https://merchant.example.com/payments/callback",
"declineCode": null,
"orderAmount": "85000.0",
"orderCurrency": "TRY",
"transactionId": "transaction-456",
"billingDetails": {
"ssn": null,
"city": null,
"email": "customer@example.com",
"phone": null,
"state": null,
"country": "TR",
"address1": null,
"lastName": "REDACTED",
"firstName": "REDACTED",
"postalCode": null,
"dateOfBirth": null,
"identityCode": null,
"externalUserId": null
},
"conversionRate": "1.0",
"apmResponseData": {
"bic": null,
"iban": null,
"qrData": null,
"country": null,
"bankCity": null,
"bankCode": null,
"bankName": null,
"sortCode": null,
"iframeUrl": null,
"swiftCode": null,
"actionType": "REDIRECTION",
"branchCode": null,
"qrDeepLink": null,
"paymentType": "BANK_TRANSFER",
"redirectUrl": "https://redacted.example/redirect",
"bankMetadata": null,
"accountNumber": null,
"paymentMethod": "SMART_BANK_TRANSFER",
"routingNumber": null,
"transitNumber": null,
"securityAnswer": null,
"bankAccountType": null,
"securityQuestion": null,
"accountHolderName": null,
"institutionNumber": null,
"maskedAccountNumber": null,
"bankCleaningSystemId": null,
"providerTransactionId": "provider-transaction-456"
},
"processedAmount": "1100.0",
"declineSubReason": null,
"apmRequestPayload": {
"bic": null,
"iban": null,
"bankCity": null,
"bankCode": null,
"bankName": null,
"sortCode": null,
"swiftCode": null,
"branchCode": null,
"personalId": null,
"paymentType": "BANK_TRANSFER",
"customFields": {},
"accountNumber": null,
"paymentMethod": "SMART_BANK_TRANSFER",
"routingNumber": null,
"transitNumber": null,
"bankAccountType": null,
"accountHolderName": null,
"institutionNumber": null,
"bankCleaningSystemId": null,
"accountHolderPhoneNumber": null
},
"processedCurrency": "TRY",
"transactionStatus": "SUCCEED"
}In this example, credit 1,100 TRY from processedAmount and processedCurrency — not 85,000 TRY from the request or orderAmount.
#Callback Routing
Exirom appends query parameters to your callbackUrl so you can route before reading the body:
?paymentMethod=apm&apmType={METHOD}— APM callbacks?paymentMethod=card— card callbacks
Use apmType to select the correct parser — apmResponseData structure varies by APM.
For the full callback payload schema, see API Reference: GET /api/v1/payments/apm/info/{id}.
#Related
- Checksum Authentication — HMAC-SHA256 signing algorithm
- Webhook Best Practices — Retry handling, idempotency, security
- Webhook Callback & Transaction Lifecycle — Full webhook routing details