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

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

Last updated