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
Environment | Server URL [SERVER_URL] |
---|---|
Sandbox | https://sandbox-api.fonbnk.com |
Production | https://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?
- Generate a timestamp (Epoch Unix Timestamp) in milliseconds
- Concatenate the timestamp and the endpoint that is called
{timestamp}:{endpoint}
- Decode the base64 encoded clientSecret
- Compute the SHA256 hash of the concatenated string. Use decoded clientSecret as a key. Convert the result to base64
- 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
Header | Description | Example |
---|---|---|
x-client-id | clientId which can be found at the merchant dashboard settings page | 645a20cbaf1d31dbd52c0fda |
x-timestamp | A 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-signature | Computed 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"
}
;
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
Strategy | Description |
---|---|
cursor | this 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 |
paymentType | payment type of orders, possible values: CRYPTO_WALLET, VIRTUAL_WALLET |
network | wallet type of orders, possible values: AVALANCHE, POLYGON, CELO |
asset | asset type of orders, possible values: USDC, CUSD |
fromAddress | address of a user wallet |
userPhoneNumber | phone number of the client, should include country code |
hash | hash of the user transaction |
countryIsoCode | country code |
offrampType | type of the offramp. Possible values: bank, airtime, mobile_money, paybill |
orderParams | value of the orderParams query param during order creation |
status | status 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
};
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
Parameter | Description |
---|---|
amount | Amount of usd user wants to pay or amount of local currency user wants to receive depending on the currency param value |
currency | Currency of the amount param. Possible values: usd, local |
country | country iso code, example: KE for Kenya, NG for Nigeria |
type | offramp 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
Parameter | Description |
---|---|
type | offramp type. Possible values: bank, airtime, mobile_money, paybill |
country | country 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"
}
]
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
Parameter | Description |
---|---|
offerId | ID of the offer returned from get best offer endpoint |
requiredFields | object 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.
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
Parameter | Description |
---|---|
offerId | ID of the offer returned from get best offer endpoint |
requiredFields | object with a user required fields |
paymentType | payment type of the order. Possible values: CRYPTO_WALLET for crypto offramps, VIRTUAL_WALLET for airtime topups. |
network | network of the wallet from which funds will be sent. Possible values: AVALANCHE, POLYGON, CELO |
asset | asset of the wallet from which funds will be sent. Possible values: USDC, CUSD |
address | address of the wallet from which funds will be sent |
address | address of the wallet from which funds will be sent |
currency | Currency of the amount param. Possible values: usd, local |
amount | Amount of usd user wants to pay or amount of local currency user wants to receive depending on the currency param value |
ip | IP address of a user |
orderParams | orderParams 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"
}
]
}
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
Parameter | Description |
---|---|
orderId | ID of the order returned from create order endpoint. |
hash | transaction 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=
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"
}
]
}
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:
- 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.
- 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.
- Fetch the order limits using the order limits endpoint. Use this data to validate an order amount provided by a user.
- Get the best offer for the provided country, amount and off-ramp type using the get best offer endpoint.
- Ask a user to provide required fields returned from the get best offer endpoint.
- Validate the user required fields using the validate user required fields endpoint.
- Create an off-ramp order using the create order endpoint.
- Send funds to the wallet address returned from the create order endpoint.
- Confirm the order using the confirm order endpoint by providing a transaction hash and order ID.
- 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:
- Get the list of countries using the get supported countries endpoint.
- Fetch the order limits using the order limits endpoint. Use this data to validate an order amount provided by a user.
- Get the best offer for the provided country and amount using the get best offer endpoint.
- Ask a user to provide required fields returned from the get best offer endpoint.
- Validate the user required fields using the validate user required fields endpoint.
- Create a virtual wallet order using the create order endpoint.
- Confirm the order using the confirm order endpoint by providing an order ID.
- Check the order status using the get a single off-ramp order endpoint.
Statuses
Status | Description |
---|---|
initiated | order was created |
awaiting_transaction_confirmation | user has sent us a transaction hash, waiting for confirmation |
transaction_confirmed | user transaction was confirmed |
offramp_success | user has received the funds |
transaction_failed | user transaction failed |
offramp_pending | offramp in progress |
offramp_failed | offramp failed |
refunding | offramp failed, refund in progress |
refunded | offramp failed, refund was successful |
refund_failed | offramp failed, refund failed |
expired | user did not send us a transaction hash in time |