Skip to main content

Merchant API

Overview

Via merchant API you can fetch individual or a list of off-ramp orders associated with your account. Also, you can perform a full flow of the off-ramp without using our web client. Additionally, this API enables developers to offer airtime services directly to their end users through our prepaid model.

API servers

EnvironmentServer URL [SERVER_URL]
Sandboxhttps://sandbox-api.fonbnk.com
Productionhttps://aten.fonbnk-services.com

Request Authentication

All requests should be signed using a HMAC256 algorithm and provided clientId and clientSecret.

How to get the signature of the request?

  1. Generate a timestamp (Epoch Unix Timestamp) in milliseconds
  2. Concatenate the timestamp and the endpoint that is called {timestamp}:{endpoint}
  3. Decode the base64 encoded clientSecret
  4. Compute the SHA256 hash of the concatenated string. Use decoded clientSecret as a key. Convert the result to base64
  5. Add the clientId, signature, and timestamp to HTTP headers

The following pseudocode example demonstrates and explains how to sign a request

timestamp = CurrentTimestamp();
stringToSign = timestamp + ":" + endpoint;
signature = Base64 ( HMAC-SHA256 ( Base64-Decode ( clientSecret ), UTF8 ( concatenatedString ) ) );

Typescript example

import crypto from "crypto";

const generateSignature = ({
clientSecret,
timestamp,
endpoint,
}: {
clientSecret: string;
timestamp: string;
endpoint: string;
}) => {
let hmac = crypto.createHmac("sha256", Buffer.from(clientSecret, "base64"));
let stringToSign = `${timestamp}:${endpoint}`;
hmac.update(stringToSign);
return hmac.digest("base64");
};

Each request should include the following headers

HeaderDescriptionExample
x-client-idclientId which can be found at the merchant dashboard settings page645a20cbaf1d31dbd52c0fda
x-timestampA UNIX timestamp you generate before sending a request to us. Please generate this timestamp right before sending a request to us and re-generate it for every request.1663240633
x-signatureComputed signature using clientSecret provided to you.Y90dweZduRFNEF8MsmEUExBg8b8ha=

API Methods

Get a single order

Returns a single order by its ID

Request Example

Request URL
[GET] [SERVER_URL]/api/offramp/order/:id

Endpoint for signature
/api/offramp/order/:id

Request Headers
x-client-id: 645a20cbaf1d31dbd52c0fda
x-timestamp: 1663240633
x-signature: Y90dweZduRFNEF8MsmEUExBg8b8ha5SLYHz5uoYO8wA=

Response

A successful request will return the following JSON encoded response for crypto offramp orders

const response = {
"_id": "6603f8fc1c3f3f94d6b30818",
"offerId": "65b8c6b8b188250081f30165",
"paymentType":"CRYPTO_WALLET",
"network": "CELO",
"asset": "CUSD",
"exchangeRate": 1268.6275,
"cashout": {
"localCurrencyAmount": 1294,
"usdAmount": 1.02,
"feeAmountUsd": 0.02,
"feeAmountUsdFonbnk": 0.02,
"feeAmountUsdPartner": 0,
"feeAmountLocalCurrency": 26,
"feeAmountLocalCurrencyFonbnk": 26,
"feeAmountLocalCurrencyPartner": 0
},
"fromAddress": "0x5b7ae3c6c87F4A3F94b35c77233b13191eBFAD20",
"toAddress": "0xe9bBDDCF0E8fcD41Bd1345aa5688B565931B0184",
"status": "offramp_success",
"createdAt": "2024-03-27T10:46:20.513Z",
"expiresAt": "2024-03-27T11:46:20.509Z",
"hash": "0xbda5dd11b17d65249238137f5d542320b66141ac96be3b750804354a6eadfb04",
"statusHistory": [
{
"status": "initiated",
"changedAt": "2024-03-27T10:46:20.509Z"
},
{
"status": "initiated",
"changedAt": "2024-03-27T10:46:20.522Z"
},
{
"status": "awaiting_transaction_confirmation",
"changedAt": "2024-03-27T10:46:31.007Z"
},
{
"status": "transaction_confirmed",
"changedAt": "2024-03-27T10:46:43.357Z"
},
{
"status": "offramp_pending",
"changedAt": "2024-03-27T10:46:44.568Z"
},
{
"status": "offramp_success",
"changedAt": "2024-03-27T10:50:02.649Z"
}
],
"requiredFields": {
"bankCode": "011:02",
"buyerBankAccountNumber": "3159586782",
"phoneNumber": "2347000000007"
},
"countryIsoCode": "NG",
"currencyIsoCode": "NGN",
"offerRequiredFields": [
{
"label": "Phone number",
"type": "phone",
"value": "2347000000007"
},
{
"label": "Bank name",
"type": "enum",
"value": "First Bank of Nigeria"
},
{
"label": "Bank account Number",
"type": "string",
"value": "3179586682"
}
],
"orderParams": "43"
}
;
warning

Please note that orders using our prepaid model will not have the network, asset, fromAddress, toAddress and hash fields in the response.

Get a list of orders

Returns a paginated list of orders. Filters can be applied to the list by providing query parameters.

Parameters

StrategyDescription
cursorthis parameter should be provided in order to get a next page from the pagination, it should be taken from "nextCursor" response value
limit(required) number from 1 to 100, describes how many records should be in each pagination page
paymentTypepayment type of orders, possible values: CRYPTO_WALLET, VIRTUAL_WALLET
networkwallet type of orders, possible values: AVALANCHE, POLYGON, CELO
assetasset type of orders, possible values: USDC, CUSD
fromAddressaddress of a user wallet
userPhoneNumberphone number of the client, should include country code
hashhash of the user transaction
countryIsoCodecountry code
offrampTypetype of the offramp. Possible values: bank, airtime, mobile_money, paybill
orderParamsvalue of the orderParams query param during order creation
statusstatus of the order. Possible values: initiated, awaiting_transaction_confirmation, transaction_confirmed , offramp_success, transaction_failed, offramp_pending, offramp_failed, refunding, refunded, refund_failed, expired

Request

Request URL
[GET] [SERVER_URL]/api/offramp/orders?limit=1&network=CELO

Endpoint for signature
/api/offramp/orders?limit=1&network=CELO

Response

A successful request will return the following JSON encoded response

const response = {
list: [
{
_id: '6603f8fc1c3f3f94d6b30818',
offerId: '65b8c6b8b188250081f30165',
network: 'CELO',
asset: 'CUSD',
paymentType:'CRYPTO_WALLET',
exchangeRate: 1268.6275,
cashout: {
localCurrencyAmount: 1294,
usdAmount: 1.02,
feeAmountUsd: 0.02,
feeAmountUsdFonbnk: 0.02,
feeAmountUsdPartner: 0,
feeAmountLocalCurrency: 26,
feeAmountLocalCurrencyFonbnk: 26,
feeAmountLocalCurrencyPartner: 0,
},
fromAddress: '0x5b7ae3c6c87F4A3F94b35c77233b13191eBFAD20',
toAddress: '0xe9bBDDCF0E8fcD41Bd1345aa5688B565931B0184',
status: 'offramp_success',
createdAt: '2024-03-27T10:46:20.513Z',
expiresAt: '2024-03-27T11:46:20.509Z',
hash: '0xbda5dd11b17d65249238137f5d542320b66141ac96be3b750804354a6eadfb04',
statusHistory: [
{
status: 'initiated',
changedAt: '2024-03-27T10:46:20.509Z',
},
{
status: 'initiated',
changedAt: '2024-03-27T10:46:20.522Z',
},
{
status: 'awaiting_transaction_confirmation',
changedAt: '2024-03-27T10:46:31.007Z',
},
{
status: 'transaction_confirmed',
changedAt: '2024-03-27T10:46:43.357Z',
},
{
status: 'offramp_pending',
changedAt: '2024-03-27T10:46:44.568Z',
},
{
status: 'offramp_success',
changedAt: '2024-03-27T10:50:02.649Z',
},
],
requiredFields: {
bankCode: '011:02',
buyerBankAccountNumber: '3159586782',
phoneNumber: '2347000000007',
},
countryIsoCode: 'NG',
currencyIsoCode: 'NGN',
offerRequiredFields: [
{
label: 'Phone number',
type: 'phone',
value: '2347000000007',
},
{
label: 'Bank name',
type: 'enum',
value: 'First Bank of Nigeria',
},
{
label: 'Bank account Number',
type: 'string',
value: '3179586682',
},
],
orderParams: '43',
},
],
nextCursor: '646c7e3ce2597a00921e2c53', // cursor for the next page, if there is no next page, this value will be null
};
warning

Please note that orders using our prepaid model will not have the network, asset, fromAddress, toAddress and hash fields in the response.

Get the best offer

Returns the best offer for the provided country, amount and off-ramp type

Parameters

ParameterDescription
amountAmount of usd user wants to pay or amount of local currency user wants to receive depending on the currency param value
currencyCurrency of the amount param. Possible values: usd, local
countrycountry iso code, example: KE for Kenya, NG for Nigeria
typeofframp type. Possible values: bank, airtime, mobile_money, paybill

Request Example

Request URL
[GET] [SERVER_URL]/api/offramp/best-offer?currency=usd&amount=5&country=NG&type=bank

Endpoint for signature
/api/offramp/best-offer?currency=usd&amount=5&country=NG&type=bank

Request Headers
x-client-id: 645a20cbaf1d31dbd52c0fda
x-timestamp: 1663240633
x-signature: Y90dweZduRFNEF8MsmEUExBg8b8ha5SLYHz5uoYO8wA=

Response

A successful request will return the following JSON encoded response

const response = {
offer: {
_id: '65b8c6b8b188250081f30165',
countryIsoCode: 'NG',
currencyIsoCode: 'NGN',
exchangeRate: 1260.2,
requiredFields: { // fields that need to be provided by the user
phoneNumber: {
type: 'phone', // possible values: 'number', 'string', 'date', 'boolean' , 'email' , 'phone'
label: 'Phone number',
required: true,
},
bankCode: {
required: true,
type: 'enum',
label: 'Bank name',
options: [
{
value: '120001:02',
label: '9Payment Service Bank',
},
{
value: '801:02',
label: 'Abbey Mortgage Bank',
},
],
},
buyerBankAccountNumber: {
type: 'string',
label: 'Bank account Number',
required: true,
},
},
type: 'bank',
},
cashout: {
localCurrencyAmount: 6301,
usdAmount: 5,
feeAmountUsd: 0.12,
feeAmountUsdFonbnk: 0.12,
feeAmountUsdPartner: 0,
feeAmountLocalCurrency: 155,
feeAmountLocalCurrencyFonbnk: 155,
feeAmountLocalCurrencyPartner: 0,
},
};

Order limits

Returns minimum and maximum amount of order in USD and local currency with applied fees

Parameters

ParameterDescription
typeofframp type. Possible values: bank, airtime, mobile_money, paybill
countrycountry ISO code

Request Example

Request URL
[GET] [SERVER_URL]/api/offramp/limits?type=bank&country=NG

Endpoint for signature
/api/offramp/limits?type=bank&country=NG

Request Headers
x-client-id: 645a20cbaf1d31dbd52c0fda
x-timestamp: 1663240633
x-signature: Y90dweZduRFNEF8MsmEUExBg8b8ha5SLYHz5uoYO8wA=

Response

A successful request will return the following JSON encoded response

const response = {
"minUsd": 1.02,
"maxUsd": 100,
"minLocalCurrency": 1292,
"maxLocalCurrency": 125921
}

Get supported countries

Returns a list of supported countries and their offramp types

Request Example

Request URL
[GET] [SERVER_URL]/api/offramp/countries

Endpoint for signature
/api/offramp/countries

Request Headers
x-client-id: 645a20cbaf1d31dbd52c0fda
x-timestamp: 1663240633
x-signature: Y90dweZduRFNEF8MsmEUExBg8b8ha5SLYHz5uoYO8wA=

Response

A successful request will return the following JSON encoded response

const response = [
{
"countryIsoCode": "NG",
"currencyIsoCode": "NGN",
"name": "Nigeria",
"offrampTypes": [
{
"type": "bank",
"name": "Bank"
}
]
}
]

Get supported wallet types

Returns a list of supported wallet networks and their assets for crypto wallet orders.

Request Example

Request URL
[GET] [SERVER_URL]/api/offramp/wallets

Endpoint for signature
/api/offramp/wallets

Request Headers
x-client-id: 645a20cbaf1d31dbd52c0fda
x-timestamp: 1663240633
x-signature: Y90dweZduRFNEF8MsmEUExBg8b8ha5SLYHz5uoYO8wA=

Response

A successful request will return the following JSON encoded response

const response = [
{
"network": "POLYGON",
"asset": "USDC"
},
{
"network": "AVALANCHE",
"asset": "USDC"
},
{
"network": "CELO",
"asset": "CUSD"
}
]
warning

Please note that orders using our prepaid model do not need to call this endpoint.

Validate user required fields

The get best offer endpoint returns the required fields that need to be provided by a user. This endpoint allows you to validate the fields provided by a user. Endpoint might return a list of user information that can help a user to verify the correctness of the provided information.

Request body

ParameterDescription
offerIdID of the offer returned from get best offer endpoint
requiredFieldsobject with a user required fields

Request Example

Request URL
[POST] [SERVER_URL]/api/offramp/validate-fields

Endpoint for signature
/api/offramp/validate-fields

Request Body
{
"offerId": "65b8c6b8b188250081f30163",
"requiredFields": {
"bankCode": "011:02",
"buyerBankAccountNumber": "3139586782"
}
}

Request Headers
x-client-id: 645a20cbaf1d31dbd52c0fda
x-timestamp: 1663240633
x-signature: Y90dweZduRFNEF8MsmEUExBg8b8ha5SLYHz5uoYO8wA=

Response

A successful request will return the following JSON encoded response

const response = {
"details": [
{
"label": "Bank account holder name",
"value": "JOHN SMITH"
}
]
}

Create order

Creates an order for a provided user details.

warning

This endpoint can't be used right away in the production. You need to contact us to enable this feature for your account. For sandbox, you can use it right away.

Request body

ParameterDescription
offerIdID of the offer returned from get best offer endpoint
requiredFieldsobject with a user required fields
paymentTypepayment type of the order. Possible values: CRYPTO_WALLET for crypto offramps, VIRTUAL_WALLET for airtime topups.
networknetwork of the wallet from which funds will be sent. Possible values: AVALANCHE, POLYGON, CELO
assetasset of the wallet from which funds will be sent. Possible values: USDC, CUSD
addressaddress of the wallet from which funds will be sent
addressaddress of the wallet from which funds will be sent
currencyCurrency of the amount param. Possible values: usd, local
amountAmount of usd user wants to pay or amount of local currency user wants to receive depending on the currency param value
ipIP address of a user
orderParamsorderParams that need to be associated with an order. This data will be sent in the webhooks

Request Example

Request URL
[POST] [SERVER_URL]/api/offramp/create-order

Endpoint for signature
/api/offramp/create-order

Request Body
{
"offerId": "65b8c4b8b188250081f30165",
"network": "CELO",
"asset": "CUSD",
"amount": "1.02",
"address": "0x5b7ae3c3c87F4A3F94b35c77233b13191eBFAD20",
"requiredFields": {
"bankCode": "011:02",
"buyerBankAccountNumber": "3134586782"
},
"currency": "usd"
}

Request Headers
x-client-id: 645a20cbaf1d31dbd52c0fda
x-timestamp: 1663240633
x-signature: Y90dweZduRFNEF8MsmEUExBg8b8ha5SLYHz5uoYO8wA=

Response

A successful crypto order request will return the following JSON encoded response

const response = {
"_id": "66052657c861bd858fa94921",
"offerId": "65b8c4b8b188250081f30165",
"paymentType":"CRYPTO_WALLET",
"network": "CELO",
"asset": "CUSD",
"exchangeRate": 1251.9608,
"cashout": {
"localCurrencyAmount": 1277, // amount of local currency user will receive
"usdAmount": 1.02, // amount of funds user needs to send
"feeAmountUsd": 0.02,
"feeAmountUsdFonbnk": 0.02,
"feeAmountUsdPartner": 0,
"feeAmountLocalCurrency": 26,
"feeAmountLocalCurrencyFonbnk": 26,
"feeAmountLocalCurrencyPartner": 0
},
"fromAddress": "0x5b4ae3c6c87F4A3F94b35c77233b13191eBFAD20", // user wallet address
"toAddress": "0x5785687C385De9e9298A342F4FE1de485cfB21b0", // wallet address where a user should send funds
"status": "initiated",
"createdAt": "2024-03-28T08:12:07.578Z",
"expiresAt": "2024-03-28T09:12:07.563Z",
"statusHistory": [
{
"status": "initiated",
"changedAt": "2024-03-28T08:12:07.563Z"
}
],
"requiredFields": {
"bankCode": "011:02",
"buyerBankAccountNumber": "3159586782",
"phoneNumber": "3139586382"
},
"countryIsoCode": "NG",
"currencyIsoCode": "NGN",
"offerRequiredFields": [
{
"label": "Phone number",
"type": "phone",
"value": "2347000000007"
},
{
"label": "Bank name",
"type": "enum",
"value": "First Bank of Nigeria"
},
{
"label": "Bank account Number",
"type": "string",
"value": "3139586382"
}
]
}
warning

Please note that orders using our prepaid model will not have the network, asset, fromAddress, toAddress and hash fields in the response.

Confirm order

Confirms an order by providing a transaction hash for crypto orders and order ID returned form the create order endpoint.

Request body

ParameterDescription
orderIdID of the order returned from create order endpoint.
hashtransaction hash for crypto orders

Request Example

Request URL
[POST] [SERVER_URL]/api/offramp/confirm-order

Endpoint for signature
/api/offramp/confirm-order

Request Body
{
"orderId": "660528663163c4652145aaba",
"hash": "0xf4c04687dca90a9060861493d7baa8ce761081c44949ec3507c5413304b13ae3"
}

Request Headers
x-client-id: 645a20cbaf1d31dbd52c0fda
x-timestamp: 1663240633
x-signature: Y90dweZduRFNEF8MsmEUExBg8b8ha5SLYHz5uoYO8wA=
warning

Please note that orders using our prepaid model do not need the hash in the request body.

Response

A successful request will return the following JSON encoded response

const response = {
"_id": "660528663163c4652145aaba",
"offerId": "65b8c6b8b188250081f30165",
"paymentType":"CRYPTO_WALLET",
"network": "CELO",
"asset": "CUSD",
"exchangeRate": 1251.9608,
"cashout": {
"localCurrencyAmount": 1277,
"usdAmount": 1.02,
"feeAmountUsd": 0.02,
"feeAmountUsdFonbnk": 0.02,
"feeAmountUsdPartner": 0,
"feeAmountLocalCurrency": 26,
"feeAmountLocalCurrencyFonbnk": 26,
"feeAmountLocalCurrencyPartner": 0
},
"fromAddress": "0x5b3ae3c6c87F4A3F94b35c77233b13191eBFAD20",
"toAddress": "0x5785487C385De9e9298A342F4FE1de485cfB21b0",
"status": "awaiting_transaction_confirmation",
"createdAt": "2024-03-28T08:20:54.134Z",
"expiresAt": "2024-03-28T09:20:54.128Z",
"hash": "0xf4c04387dca90a9060861493d7baa8ce761081c44949ec3507c5413304b13ae3",
"statusHistory": [
{
"status": "initiated",
"changedAt": "2024-03-28T08:20:54.128Z"
},
{
"status": "awaiting_transaction_confirmation",
"changedAt": "2024-03-28T08:21:06.338Z"
}
],
"requiredFields": {
"bankCode": "011:02",
"buyerBankAccountNumber": "3139486582",
"phoneNumber": "2347000000007"
},
"countryIsoCode": "NG",
"currencyIsoCode": "NGN",
"offerRequiredFields": [
{
"label": "Phone number",
"type": "phone",
"value": "2347000000007"
},
{
"label": "Bank name",
"type": "enum",
"value": "First Bank of Nigeria"
},
{
"label": "Bank account Number",
"type": "string",
"value": "3139486582"
}
]
}
warning

Please note that airtime orders using our prepaid model will not have the network, asset, fromAddress, toAddress and hash fields in the response.

Integration guide

Crypto Offramp

In order to perform a full crypto off-ramp flow using the merchant API, you need to follow the steps below:

  1. Get list of supported wallet types and assets using the get supported wallet types endpoint and check if the wallet type and asset you want to use are supported.
  2. Get the list of countries and off-ramp types using the get supported countries endpoint and check if the country and off-ramp type you want to use are supported.
  3. Fetch the order limits using the order limits endpoint. Use this data to validate an order amount provided by a user.
  4. Get the best offer for the provided country, amount and off-ramp type using the get best offer endpoint.
  5. Ask a user to provide required fields returned from the get best offer endpoint.
  6. Validate the user required fields using the validate user required fields endpoint.
  7. Create an off-ramp order using the create order endpoint.
  8. Send funds to the wallet address returned from the create order endpoint.
  9. Confirm the order using the confirm order endpoint by providing a transaction hash and order ID.
  10. Check the order status using the get a single off-ramp order endpoint.

Airtime Topup

In order to perform a full airtime topup flow using the merchant API, you need to follow the steps below:

  1. Get the list of countries using the get supported countries endpoint.
  2. Fetch the order limits using the order limits endpoint. Use this data to validate an order amount provided by a user.
  3. Get the best offer for the provided country and amount using the get best offer endpoint.
  4. Ask a user to provide required fields returned from the get best offer endpoint.
  5. Validate the user required fields using the validate user required fields endpoint.
  6. Create a virtual wallet order using the create order endpoint.
  7. Confirm the order using the confirm order endpoint by providing an order ID.
  8. Check the order status using the get a single off-ramp order endpoint.

Statuses

StatusDescription
initiatedorder was created
awaiting_transaction_confirmationuser has sent us a transaction hash, waiting for confirmation
transaction_confirmeduser transaction was confirmed
offramp_successuser has received the funds
transaction_faileduser transaction failed
offramp_pendingofframp in progress
offramp_failedofframp failed
refundingofframp failed, refund in progress
refundedofframp failed, refund was successful
refund_failedofframp failed, refund failed
expireduser did not send us a transaction hash in time