Skip to main content

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:

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 USDC/cUSD user received
"network": // network user received USDC/cUSD on
| "POLYGON"
| "ETHEREUM"
| "STELLAR"
| "AVALANCHE"
| "SOLANA"
| "ALGORAND"
| "TRON"
| "CELO"
"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
},
"hash": string, // SHA256 encrypted request.data string to validate a webhook request
};

We send a hash field in our webhook request body 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:

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');