payment.succeeded

Sent when a payment completes successfully. This is the event most integrations care about — fulfil the order when you receive it (after verifying the signature).

How webhooks are delivered

SwiftPayIQ sends an HTTP POST to the endpoint URL you configured in the dashboard, with a JSON body of the shape:

{ "event": "payment.succeeded", "data": { /* … */ } }

Headers on every delivery:

HeaderValue
Content-Typeapplication/json
X-SwiftPay-Signaturet=<unix-seconds>,v1=<hex-hmac>

Your endpoint should respond with any 2xx status as quickly as possible. A non-2xx response (or a timeout — the request is aborted after 10 seconds) is treated as a failure and retried.

Verifying the signature

Every delivery is signed with the signing secret of your webhook (shown when you create or rotate it in the dashboard; it starts with whsec_). Verify it before trusting the payload:

  1. Read the X-SwiftPay-Signature header and parse the t (timestamp) and v1 (signature) values.
  2. Reject the request if t is more than 5 minutes away from your current time (replay protection).
  3. Build the signed string: "{t}.{rawRequestBody}" — use the raw, unparsed request body bytes exactly as received.
  4. Compute HMAC-SHA256(signingSecret, signedString) and hex-encode it.
  5. Compare it to v1 using a constant-time comparison. If they match, the request is authentic.
// Node.js (Express) example
const crypto = require('crypto');

function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(',').map(p => p.split('=')));
  const t = parts.t, v1 = parts.v1;
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; // 5 min
  const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
  return crypto.timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(v1, 'hex'));
}

Capture the raw request body for signature verification — re-serializing parsed JSON will change the bytes and the signature will not match.

Retries

If your endpoint doesn't return 2xx, delivery is retried up to 6 attempts total (5 retries) with increasing back-off: 1 min → 5 min → 30 min → 2 hours → 8 hours (about 10.5 hours end-to-end). A 4xx response other than 408/429 is treated as a permanent failure and is not retried. Make your handler idempotent — the same event may arrive more than once. You can inspect delivery history and re-send from the dashboard.

postWebhookpayment.succeeded

Payload

event'payment.succeeded' | 'payment.failed' | 'payment.refunded' | 'invoice.paid' | 'payment_link.paid' required

The event type.

Example payload

{
  "data": {
    "amount": "50000",
    "provider": "ALQASEH"
  }
}

Response

Return any 2xx to acknowledge receipt.

Changes

Webhooks have no change history: a webhook is keyed by its event name rather than a path and method, so no recorded change can name it.