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)
  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"
},
"withdrawal": {
"_id": "651e7653db37ee6b74883fe1",
"status": "complete", // possible values: "pending", "complete", "failed"
"withdrawAmount": 2,
"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
};

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
walletAddressaddress of wallet
userPhoneNumberphone number of the client, should include country code
swapProviderprovider of a swap, possible values: carrier, mpesa, mobile_money, bank_transfer
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"
},
"withdrawal": {
"_id": "651e7653db37ee6b74883fe1",
"status": "complete", // possible values: "pending", "complete", "failed"
"withdrawAmount": 2,
"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
},
],
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 for a given amount of mobile money and vice versa.

Parameters

ParameterDescription
networkUSDC/cUSD network name, possible values: POLYGON, ETHEREUM, STELLAR, AVALANCHE, SOLANA, ALGORAND, TRON, CELO, BASE, OPTIMISM, NEAR
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: carrier, mpesa, mobile_money, bank_transfer
carrierIdcarrier id, required if provider is not the bank_transfer

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 = {
"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 funds user will receive before fees
"withdrawAmount": 5, // amount of funds user will receive after fees
"feeAmount": 0.12, // total fee amount (fonbnk fee + partner fee)
"localCurrencyFeeAmount": 120, // total fee amount in local currency (fonbnk fee + partner fee)
"fonbnkFeeAmount": 0.07,
"localCurrencyFonbnkFeeAmount": 70,
"partnerFeeAmount": 0.05,
"localCurrencyPartnerFeeAmount": 50,
"networkFeeAmount": 0,
"localCurrencyNetworkFeeAmount": 0,

// 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)

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",
}

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: "carrier" | "mpesa" | "mobile_money" | "bank_transfer";
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
countrycountry iso code, example: KE for Kenya, NG for Nigeria
providerfunds source provider, possible values: carrier, mpesa, mobile_money, bank_transfer
carrierIdcarrier id, required if provider is not the bank_transfer

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": { // 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?: // 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;
}
}