Docs

Webhooks

Webhooks let SecurePayAPI notify your server in real time as a transaction moves through its lifecycle — from the initial payment, through settlement, and on to any later refunds or disputes — so you can fulfill orders, update your records, or alert a customer without polling.

Set up an endpoint

  1. Build an HTTPS endpoint on your server that accepts POST requests.
  2. Add its URL in the Developer section of your dashboard.

SecurePayAPI will start posting events to that URL right away.

When webhooks fire

A webhook is sent on every status change of a transaction — through initiation and 3-D Secure, payment and settlement, and any later refunds or disputes. You keep receiving events until the transaction reaches a final state. See the full list under Transaction statuses.

Payload

Each webhook is a JSON POST describing the transaction's current state:

{
  "created": 1717000000,
  "uuid": "a1b2c3d4-…",
  "id": "abcdef0123456789abcdef01",
  "famt": 20,
  "currency": "usd",
  "externalid": "order_123",
  "customerid": "cus_8842",
  "status": 16,
  "statustext": "PAYING",
  "statushistory": [
    { "status": 15, "statustext": "AUTH3DSRESULT" },
    { "status": 16, "statustext": "PAYING" }
  ]
}
FieldDescription
createdWhen the transaction was created, in Unix seconds
uuid / idTransaction identifiers
famtPayment amount, in the currency's major units (e.g. 20 = $20.00)
currencyCurrency code
externalidThe identifier you set when creating the payment
customeridYour customer identifier
statusCurrent status code — see Transaction statuses
statustextName of the current status
statushistoryArray of { status, statustext } — status changes bundled since the last webhook
challenge3-D Secure challenge data — present only when a challenge occurred
auth3ds3-D Secure authentication data — present only when available
Note

To keep the number of calls down, several rapid status changes may be delivered in a single webhook — the intermediate states are listed in statushistory.

Transaction statuses

Each webhook carries a numeric status code and its statustext name.

Payment

CodeStatusMeaning
0UNPAIDCreated, not yet paid
10INITPayment initiated
11DDCDevice data collection (3-D Secure)
12AUTH3DS3-D Secure authentication started
13AUTH3DSCHALLENGE3-D Secure challenge required
14AUTH3DSCHALLENGEFORMChallenge form presented
15AUTH3DSRESULTAuthentication result received
16PAYINGPayment is being processed
1PAIDPayment succeeded
3FAILEDPayment failed
5CANCELEDPayment canceled

Settlement

CodeStatusMeaning
19SETTLEMENTPENDINGAwaiting settlement
20SETTLEDFunds settled

Refunds & holds

CodeStatusMeaning
8FROZENFunds held for review
34REFUNDPENDINGRefund in progress
9REFUNDEDPayment refunded

Disputes

Sent throughout the dispute lifecycle:

CodeStatusMeaning
21INFORMATIONREQUESTEDIssuer requested more information
22INFORMATIONRESPONDEDInformation supplied
23INFORMATIONEXPIREDInformation request expired
24CHARGEBACKOPENChargeback opened
25CHARGEBACKREVERSEDChargeback reversed
26CHARGEBACKWONChargeback won
27CHARGEBACKLOSTChargeback lost
28PREARBITRATIONOPENPre-arbitration opened
29PREARBITRATIONWONPre-arbitration won
30PREARBITRATIONLOSTPre-arbitration lost
31SCHEMEARBITRATIONOPENScheme arbitration opened
32SCHEMEARBITRATIONWONScheme arbitration won
33SCHEMEARBITRATIONLOSTScheme arbitration lost

Acknowledge receipt

Return HTTP 204 as soon as you've stored the event. A 204 confirms delivery and stops further redelivery. If your endpoint returns anything else — or times out — SecurePayAPI retries.

Note

Acknowledge first, process later. Return 204 quickly, then run any heavy work (order fulfillment, emails) asynchronously so the delivery doesn't time out.

Retries

If a delivery isn't acknowledged with 204, it's retried on an escalating schedule:

AttemptWait before retry
15 seconds
25 seconds
310 seconds
430 seconds
560 seconds
610 minutes
730 minutes
830 minutes
9+1 hour

Verify the signature

When your account has an API secret set, every request carries two headers so you can confirm it genuinely came from SecurePayAPI:

HeaderDescription
X-TimestampCurrent time in seconds, rounded down
X-SignatureAn HMAC-SHA256 signature of the request

Recompute the signature with your secret key and compare it to X-Signature:

const crypto = require('crypto');

function isValidWebhook(req, apiSecret) {
  const timestamp = req.headers['x-timestamp'];
  const expected = crypto
    .createHmac('sha256', apiSecret)
    .update(timestamp + JSON.stringify(req.body))
    .digest('hex');
  return expected === req.headers['x-signature'];
}

Reject any request whose signature doesn't match.

Caution

Always verify the signature before trusting a webhook. Don't act on an event you can't authenticate.

Allowlist our IP

Webhook requests are sent from 157.230.218.195. If your infrastructure filters inbound traffic, allow this address.

© SecurePayAPI · Built with Markdoc