Webhook

We can notify a registered pay widget merchant about the statuses of orders associated with him.

We will make a POST request to a provided webhook URL with the next application/json contents:

Webhook V1:

type WebhookRequest = {
  "data": {
    "status":
      | "swap_initiated" // user has created an order
      | "swap_expired" // an order has expired
      | "swap_buyer_rejected"  // user has rejected an order
      | "swap_buyer_confirmed" // user has confirmed an order
      | "swap_seller_rejected" // agent has rejected an order, happens when agent don't receive a payment
      | "swap_seller_confirmed" // agent has confirmed an order
      | "pending" // USDC/cUSD transaction is pending
      | "complete" // USDC/cUSD transaction is complete
      | "failed", // USDC/cUSD transaction has failed
    "date": string, // date when event has happened
    "orderId": string, // order id in our system
    "phoneNumber": string, // customer phone number
    "localCurrencyAmount": number, // amount of local currency user paid
    "localCurrencyIsoCode": string, // ISO code of local currency user paid, e.g. KES, NGN etc.
    "countryIsoCode": string, // ISO code of country user paid from, e.g. KE, NG etc.
    "provider": // payment provider user paid with
      | "carrier"
      | "mpesa"
      | "mobile_money"
      | "bank_transfer"
    "amount": number, // amount of USD user received
    "amountCrypto": number, // amount of crypto user received
    "network": // network user received USDC/cUSD on
      | "POLYGON"
      | "ETHEREUM"
      | "STELLAR"
      | "AVALANCHE"
      | "SOLANA"
      | "BASE"
      | "CELO"
      | "LISK",
    "asset": "USDC" | "CUSD" | "USDT" | "USDC_E", // asset user received
    "address": string, // address user received USDC/cUSD on
    "orderParams"?: string // Content of a orderParams query parameter provided to a pay widget URL. It might be useful for matching a merchant system user to an order user.
    "hash"?: string, // transaction hash
    "resumeUrl": string, // URL where user can resume his order, it point either to the transfer instructions page or to the status page
  },
  "hash": string, // SHA256 encrypted request.data string to validate a webhook request
};

Webhook V2:

Instead of sending hash inside - WebhookRequest, we will send it as a request x-signature header

Request headers:
x-signature: hash (string)

Webhook verification:

We send a hash field in our webhook to protect merchants from fraudulent requests. Each request should be verified by a secret provided in the dashboard.

Here is how it should be checked in pseudocode:

request.body.hash === SHA256(stringify(request.body.data), secret)

Here is how it should be checked in Node.js:

For Webhook V1 version:

import { createHash } from 'crypto';

request.body.hash === createHash('sha256')
   .update(JSON.stringify(request.body.data))
   .update(createHash('sha256').update(__SECRET__, 'utf8').digest('hex'))
   .digest('hex');

For Webhook V2 version:

import { createHash } from 'crypto';

request 'x-signature' header === createHash('sha256')
   .update(JSON.stringify(request.body))
   .update(createHash('sha256').update(__SECRET__, 'utf8').digest('hex'))
   .digest('hex');

You can see how to make a signature in multiple programming languages HERE

Last updated