Refund Status Update Webhook

Configure this webhook against a specific Integration in your Business Portal.

HMAC signature verification

Every webhook request includes a super-signature header so you can verify it came from Super and hasn't been tampered with.

Header format

The header contains a timestamp and a signature, separated by a comma:

super-signature: t:1669219987926,v1:vCcZMqom...base64...

  • t: — Unix timestamp in milliseconds, generated when the request was signed
  • v1: — HMAC-SHA256 signature, base64-encoded

Verifying a request

  1. Parse the header. Split on "," to get the timestamp and signature parts, then split each on ":" to extract the values.
  2. Build the signed message. Concatenate the timestamp and the raw request body, with no separator: "message = timestamp + raw_body"
  3. Use the raw bytes of the request body — do not re-serialize parsed JSON, as whitespace or key ordering changes will invalidate the signature.
  4. Compute the expected signature. Generate an HMAC-SHA256 of message using your webhook secret as the key, and base64-encode the result.
  5. Compare signatures. Use a constant-time comparison (e.g. crypto.timingSafeEqual in Node, hmac.compare_digest in Python) to avoid timing attacks. Reject the request if they don't match.
  6. Check the timestamp. Reject requests whose timestamp is more than 5 minutes old to prevent replay attacks.

Example (Node.js)

const crypto = require('crypto');

function verifySuperSignature(rawBody, header, secret) {
  const parts = Object.fromEntries(
    header.split(',').map((p) => { return p.split(':'); })
  );
  const timestamp = parts.t;
  const signature = parts.v1;

  if (Math.abs(Date.now() - Number(timestamp)) > 5 * 60 * 1000) {
    return false;
  }

  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${timestamp}${rawBody}`)
    .digest('base64');

  const expectedBuf = Buffer.from(expected, 'base64');
  const actualBuf = Buffer.from(signature, 'base64');

  if (expectedBuf.length !== actualBuf.length) {
    return false;
  }

  return crypto.timingSafeEqual(expectedBuf, actualBuf);
}
Payload

Payment status notification data

const
enum
required

This will be set to: RefundStatus

Allowed:
uuid
required

A unique ID for the refund transaction.

string
required

An 18 character refund transaction reference allocated by us, used/visible on the associated bank transaction.

string
enum
required

The status of the refund transaction. The refund status will be one of:

  • RefundSuccess: The refund transaction was successful, money has moved from your holding account to the customer's bank account.
  • RefundFailed: The refund transaction failed.
  • RefundAbandoned: The refund transaction timed out.
Allowed:
string

Your unique reference for the refund transaction, e.g. your refundId

Response
200

Return a 200 status to indicate that the data was received successfully, any other response will be deemed a failure and retried

LoadingLoading…