Skip to main content

Merchant API

Overview

Via merchant API you can fetch individual or a list of pay widget orders associated with your account.

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 pay widget order

Returns a single pay widget order by its ID or orderParams query parameter.

Parameters

ParameterDescription
orderIdid of the order which you could receive via a webhook or iframe events
orderParamsValue which you provided in the orderParams parameter of the pay widget URL

Request Example

Request URL
[GET] [SERVER_URL]/api/pay-widget-merchant/order?orderId=6495a7427c8d2a730d97dc6b

Endpoint for signature
/api/pay-widget-merchant/order?orderId=6495a7427c8d2a730d97dc6b

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 = {
"_id": "651e764399a4b360c7eb0178",
"walletType": "SOLANA",
"walletAddress": "53L9ahwHcNnD4F11TqNHQgVUa75WgaCL2WGeiKiK2gCc",
"feePercent": 2.5, // total fee percent (fonbnk fee + partner fee)
"fonbnkFeePercent": 1.5,
"partnerFeePercent": 1,
"gasUsdAmount": 0,
"merchantId": "645a20cbaf1d31dbd52c0fda",
"createdAt": "2023-10-05T08:39:31.691Z",
"buySwap": {
"_id": "651e764399a4b360c7eb016d",
"buyerUserPhone": "380962669394",
"sellerUserPhone": "380960000000",
"amount": 210,
"airtimeAmount": 2100,
"status": "seller_confirmed", // possible values: "initiated", "expired", "buyer_confirmed", "buyer_rejected", "seller_confirmation_pending", "seller_confirmation_failed", "seller_confirmed", "seller_rejected"
"provider": "bank_transfer",
"expiresAt": "2023-10-05T09:39:31.492Z",
"createdAt": "2023-10-05T08:39:31.500Z",
"buyerConfirmedAt": "2023-10-05T08:39:37.027Z",
"countryIsoCode": "NG"
},
"withdrawal": {
"_id": "651e7653db37ee6b74883fe1",
"status": "complete", // possible values: "pending", "complete", "failed"
"withdrawAmount": 2, // amount in USD
"cryptoTotalAmount": 2, //amount in crypto
"transactionHash": "F19TfyGxBgebZt4q8h5bgf9Luabpypm3zSSzWR5ZUp8CKGNvL2r58zqGvWXvKftppiWKszV11jNQtsMUoFppmyb"
},
"feeAmount": 0.05, // total fee amount (fonbnk fee + partner fee)
"localCurrencyFeeAmount": 50, // total fee amount in local currency (fonbnk fee + partner fee)
"fonbnkFeeAmount": 0.03,
"localCurrencyFonbnkFeeAmount": 30,
"partnerFeeAmount": 0.02,
"localCurrencyPartnerFeeAmount": 20,
"networkFeeAmount": 0,
"localCurrencyNetworkFeeAmount": 0,
"resumeUrl": "https://pay.fonbnk.com/swap-status?orderId=651e764399a4b360c7eb0178", // URL where user can resume his order, it point either to the transfer instructions page or to the status page
};

Get a list of pay widget orders

Returns a paginated list of pay widget 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
walletTypewallet type of orders, possible values: POLYGON, ETHEREUM, STELLAR, AVALANCHE, SOLANA, ALGORAND, TRON, CELO, BASE, OPTIMISM, NEAR, LISK
walletAddressaddress of wallet
userPhoneNumberphone number of the client, should include country code
swapProviderprovider of a swap, possible values: bank_transfer, mpesa, mobile_money, carrier
buySwapStatusstatus of a swap, possible values: initiated, expired, buyer_confirmed, buyer_rejected, seller_confirmation_pending, seller_confirmation_failed, seller_confirmed, seller_rejected
withdrawalStatusstatus of the USDC/cUSD transfer, possible values: pending, failed, complete

Request

Request URL
[GET] [SERVER_URL]/api/pay-widget-merchant/orders?limit=1&walletType=POLYGON

Endpoint for signature
/api/pay-widget-merchant/orders?limit=1&walletType=POLYGON

Response

A successful request will return the following JSON encoded response

const response = {
list: [
{
"_id": "651e764399a4b360c7eb0178",
"walletType": "SOLANA",
"walletAddress": "53L9ahwHcNnD4F11TqNHQgVUa75WgaCL2WGeiKiK2gCc",
"feePercent": 2.5, // total fee percent (fonbnk fee + partner fee)
"fonbnkFeePercent": 1.5,
"partnerFeePercent": 1,
"merchantId": "645a20cbaf1d31dbd52c0fda",
"createdAt": "2023-10-05T08:39:31.691Z",
"buySwap": {
"_id": "651e764399a4b360c7eb016d",
"buyerUserPhone": "380962669391",
"sellerUserPhone": "380960000000",
"amount": 210,
"airtimeAmount": 2100,
"status": "seller_confirmed", // possible values: "initiated", "expired", "buyer_confirmed", "buyer_rejected", "seller_confirmation_pending", "seller_confirmation_failed", "seller_confirmed", "seller_rejected"
"provider": "bank_transfer",
"expiresAt": "2023-10-05T09:39:31.492Z",
"createdAt": "2023-10-05T08:39:31.500Z",
"buyerConfirmedAt": "2023-10-05T08:39:37.027Z",
"countryIsoCode": "NG"
},
"withdrawal": {
"_id": "651e7653db37ee6b74883fe1",
"status": "complete", // possible values: "pending", "complete", "failed"
"withdrawAmount": 2, // amount in USD
"withdrawCryptoAmount": 2, // amount in crypto
"transactionHash": "F19TfyGxBgebZt4q8h5bgf9Luabpypm3zSSzWR5ZUp8CKGNvL2r58zqGvWXvKftppiWKszV11jNQtsMUoFppmyb"
},
"feeAmount": 0.05, // total fee amount (fonbnk fee + partner fee) in USD
"localCurrencyFeeAmount": 50, // total fee amount in local currency (fonbnk fee + partner fee)
"fonbnkFeeAmount": 0.03, // fonbnk fee amount in USD
"localCurrencyFonbnkFeeAmount": 30,
"partnerFeeAmount": 0.02, // partner fee amount in USD
"localCurrencyPartnerFeeAmount": 20,
"networkFeeAmount": 0, // network fee amount in USD
"localCurrencyNetworkFeeAmount": 0,
"resumeUrl": "https://pay.fonbnk.com/swap-status?orderId=651e764399a4b360c7eb0178", // URL where user can resume his order, it point either to the transfer instructions page or to the status page
},
],
nextCursor: "646c7e3ce2597a00921e2c53", // cursor for the next page, if there is no next page, this value will be null
};

Get expected price

Returns expected price in USDC, cUSD etc. for a given amount of mobile money and vice versa.

Parameters

ParameterDescription
networknetwork name, possible values: POLYGON, ETHEREUM, STELLAR, AVALANCHE, SOLANA, ALGORAND, TRON, CELO, BASE, OPTIMISM, NEAR, LISK
assetUSDC, CUSD, USDT, USDC_E. Default is USDC for every network except CELO, for CELO it's CUSD.
currencypossible values: usdc, local. If usdc is povided, amount of mobile money required to get this USDC/cUSD amount will be calculated. If local is provided, amount of USDC for provided mobile money will be calculated
amountamount of mobile money or USDC/cUSD
countrycountry iso code, example: KE for Kenya, NG for Nigeria
providerfunds source provider, possible values: bank_transfer, mpesa, mobile_money, carrier
carrierId(optional) carrier id, provide if need to check for a specific carrier

Request Example

Request URL
[GET] [SERVER_URL]/api/pay-widget-merchant/price?network=SOLANA&currency=usdc&amount=5&country=NG&provider=bank_transfer

Endpoint for signature
/api/pay-widget-merchant/price?network=SOLANA&currency=usdc&amount=5&country=NG&provider=bank_transfer

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 = {
"quoteId": "651e764399a4b360c7eb0178", // unique quote id
"localCurrencyAmount": 5120, // amount of localc currency user should pay
"feePercent": 2.5, // total fee percent (fonbnk fee + partner fee)
"fonbnkFeePercent": 1.5,
"partnerFeePercent": 1,
"totalAmount": 5.12, // amount of USD user will receive before fees
"withdrawAmount": 5, // amount of USD user will receive after fees
"feeAmount": 0.12, // total fee amount in USD (fonbnk fee + partner fee)
"localCurrencyFeeAmount": 120, // total fee amount in local currency (fonbnk fee + partner fee)
"fonbnkFeeAmount": 0.07, // fonbnk fee amount in USD
"localCurrencyFonbnkFeeAmount": 70,
"partnerFeeAmount": 0.05, // partner fee amount in USD
"localCurrencyPartnerFeeAmount": 50,
"networkFeeAmount": 0, // network fee amount in USD
"localCurrencyNetworkFeeAmount": 0,
"cryptoTotalAmount": 5.12, // amount of crypto user will receive before fees
"cryptoWithdrawAmount": 5, // amount of crypto user will receive after fees
"cryptoFeeAmount": 0.12, // total fee amount in crypto (fonbnk fee + partner fee)
"cryptoGasAmount": 0, // network fee in crypto

// deprecated fields below, use fields above instead
"usdcTotalAmount": 5.12, // amount of USDC user will receive before fees
"usdcWithdrawAmount": 5, // amount of USDC user will receive after fees
"usdcFeeAmount": 0.12, // fonbnk service fee
"usdcGasAmount": 0, // network fee
}

Get providers list

Returns a list of countries and their supported providers, each provider has a list of supported mobile carriers(if provider requires it)

Parameters

ParameterDescription
includeLimitsShould limitations data be included in the response. Defaults to true. If limitations are not included the request will be much faster

Request Example

Request URL
[GET] [SERVER_URL]/api/pay-widget-merchant/providers

Endpoint for signature
/api/pay-widget-merchant/providers

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

Response

A successful request will return the following JSON encoded response

enum WalletType {
POLYGON = "POLYGON",
ETHEREUM = "ETHEREUM",
STELLAR = "STELLAR",
AVALANCHE = "AVALANCHE",
SOLANA = "SOLANA",
ALGORAND = "ALGORAND",
TRON = "TRON",
CELO = "CELO",
LISK = "LISK"
}

type ProviderLimitations = {
[key in WalletType]: {
cryptoLimits: {
// usdc limits
min: number;
max: number;
};
fees: {
feePercent: number; // total fee percent (fonbnk fee + partner fee)
fonbnkFeePercent: number,
partnerFeePercent: number,
gasAmount: number; // gas price of selected network
minFee: number; // minimum fee
};
localCurrency?:
| {
type: "open_range";
max: number;
min: number;
step: number; // step size of currency, for example, if step is 5, then amount can be 5, 10, 15 etc.
withCents?: boolean; // can local currency include cents
}
| {
type: "fixed_list";
values: number[]; // list of supported amounts
withCents?: boolean;
};
};
};

type ProvidersResponse = {
countryIsoCode: "NG";
currencyIsoCode: "NGN";
providers: {
name: "bank_transfer" | "mpesa" | "mobile_money" | "carrier";
description: string;
requiresCarrier: boolean;
limits?: ProviderLimitations; //provider limits if carrier is not required
carriers: {
name: string;
id: string;
limits: ProviderLimitations;
}[];
}[];
}[];

Get order limitations

Returns minimum and maximum amount of order in USDC/cUSD and local currency and applied fees

Parameters

ParameterDescription
networkUSDC/cUSD network name, possible values: POLYGON, ETHEREUM, STELLAR, AVALANCHE, SOLANA, ALGORAND, TRON, CELO, BASE, OPTIMISM, NEAR, LISK
assetUSDC, CUSD, USDT, USDC_E. Default is USDC for every network except CELO, for CELO it's CUSD.
countrycountry iso code, example: KE for Kenya, NG for Nigeria
providerfunds source provider, possible values: bank_transfer, mpesa, mobile_money, carrier
carrierId(optional) carrier id, provide if need to check for a specific carrier

Request Example

Request URL
[GET] [SERVER_URL]/api/pay-widget-merchant/limits?network=SOLANA&country=NG&provider=bank_transfer

Endpoint for signature
/api/pay-widget-merchant/limits?network=SOLANA&country=NG&provider=bank_transfer

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

Response

A successful request will return the following JSON encoded response

type Response = {
"cryptoLimits": { // crypto limits
"min": number,
"max": number
},
"fees": {
"feePercent": number, // total fee percent (fonbnk fee + partner fee)
"fonbnkFeePercent": number,
"partnerFeePercent": number,
"gasAmount": number, // gas price of selected network
"minFee": number // minimum fee
},
localCurrency?: // local currency limits
| {
type: 'open_range';
max: number;
min: number;
step: number; // step size of currency, for example, if step is 5, then amount can be 5, 10, 15 etc.
withCents?: boolean; // can local currency include cents
}
| {
type: 'fixed_list';
values: number[]; // list of supported amounts
withCents?: boolean;
}
}

Endpoints data

Order status

Data from get order and get orders endpoints contains buySwap.status and withdrawal.status fields which represent the status of the order. buySwap.status represents the status of the P2P interaction between the cliend and the agent. withdrawal.status represents the status of the cryptocurrency transfer from the fonbnk to the client.

buySwap.status can have the following values:

  • initiated - the order was created and the client has not yet confirmed the payment
  • expired - the order has expired
  • buyer_confirmed - the client has confirmed the payment
  • buyer_rejected - the client has rejected the payment
  • seller_confirmation_pending - the agent is confirming the payment
  • seller_confirmation_failed - error occurred during the agent's confirmation
  • seller_confirmed - the agent has confirmed the payment
  • seller_rejected - the agent has rejected the payment

withdrawal.status can have the following values:

  • pending - the transfer is pending
  • complete - the transfer is complete
  • failed - the transfer has failed