Webhook
We can notify a merchant about the statuses of off-ramp 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: {
orderId: string,
offrampType: "bank",
status: OfframpStatus,
date: string,
cashout: {
localCurrencyAmount: number, // how much the user will receive in local currency
usdAmount: number, // how much user must send in USD
feeAmountUsd: number, // total fee amount in USD
feeAmountUsdFonbnk: number, // fee amount in USD for Fonbnk
feeAmountUsdPartner: number, // fee amount in USD for partner
feeAmountLocalCurrency: number, // total fee amount in local currency
feeAmountLocalCurrencyFonbnk: number, // fee amount in local currency for Fonbnk
feeAmountLocalCurrencyPartner: number, // fee amount in local currency for partner
},
exchangeRate: number,
network: "AVALANCHE" | "POLYGON" | "CELO",
asset: "USDC" | "CUSD",
fromAddress: string,
toAddress: string,
userPhoneNumber: string,
requiredFields: { label: string, type: 'number' | 'string' | 'date' | 'boolean' | 'email' | 'phone', value: string }[],// user account data
orderParams?: string, // contents of orderParams query parameter during order creation
countryIsoCode: string,
currencyIsoCode: string,
},
hash: string,
};
enum OfframpStatus {
INITIATED = 'initiated', // order was created
AWAITING_TRANSACTION_CONFIRMATION = 'awaiting_transaction_confirmation', // user has sent us a transaction hash, waiting for confirmation
TRANSACTION_CONFIRMED = 'transaction_confirmed', // user transaction was confirmed
OFFRAMP_SUCCESS = 'offramp_success', // user has received the funds
TRANSACTION_FAILED = 'transaction_failed', // user transaction failed
OFFRAMP_PENDING = 'offramp_pending', // offramp in progress
OFFRAMP_FAILED = 'offramp_failed', // offramp failed
REFUNDING = 'refunding', // offramp failed, refund in progress
REFUNDED = 'refunded', // offramp failed, refund was successful
REFUND_FAILED = 'refund_failed', // offramp failed, refund failed
EXPIRED = 'expired', // user did not send us a transaction hash in time
}
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');