Signing requests
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 ) ) );
Request examples
The following examples send HTTP request to get price on-ramp API endpoint:
import crypto from 'crypto';
const BASE_URL = 'https://aten.fonbnk-services.com';
const ENDPOINT = '/api/pay-widget-merchant/price';
const CLIENT_ID = '';
const CLIENT_SECRET = '';
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');
};
const main = async () => {
const timestamp = new Date().getTime();
const queryParams = new URLSearchParams({
country: 'NG',
amount: '10',
currency: 'usdc',
network: 'CELO',
asset: 'CUSD',
provider: 'bank_transfer',
});
const endpoint = `${ENDPOINT}?${queryParams.toString()}`;
const signature = generateSignature({
clientSecret: CLIENT_SECRET,
timestamp: timestamp.toString(),
endpoint,
});
const headers = {
'Content-Type': 'application/json',
'x-client-id': CLIENT_ID,
'x-timestamp': timestamp.toString(),
'x-signature': signature,
};
const response = await fetch(`${BASE_URL}${endpoint}`, {
method: 'GET',
headers,
});
const data = await response.json();
console.log(JSON.stringify(data, null, 2));
};
main().catch(console.error);
import hmac
import base64
import time
import requests
from urllib.parse import urlencode
BASE_URL = 'https://aten.fonbnk-services.com'
ENDPOINT = '/api/pay-widget-merchant/price'
CLIENT_ID = ''
CLIENT_SECRET = ''
def pad_base64(base64_string):
return base64_string + '=' * (-len(base64_string) % 4)
def generate_signature(client_secret, timestamp, endpoint):
client_secret_padded = pad_base64(client_secret)
hmac_obj = hmac.new(base64.b64decode(client_secret_padded), f'{timestamp}:{endpoint}'.encode('utf-8'), 'sha256')
return base64.b64encode(hmac_obj.digest()).decode('utf-8')
def main():
timestamp = str(int(time.time() * 1000))
query_params = {
'country': 'NG',
'amount': '10',
'currency': 'usdc',
'network': 'CELO',
'asset': 'CUSD',
'provider': 'bank_transfer',
}
endpoint = f"{ENDPOINT}?{urlencode(query_params)}"
signature = generate_signature(CLIENT_SECRET, timestamp, endpoint)
headers = {
'Content-Type': 'application/json',
'x-client-id': CLIENT_ID,
'x-timestamp': timestamp,
'x-signature': signature,
}
response = requests.get(f"{BASE_URL}{endpoint}", headers=headers)
data = response.json()
print(data)
if __name__ == "__main__":
main()
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strings"
"time"
)
const (
BASE_URL = "https://aten.fonbnk-services.com"
ENDPOINT = "/api/pay-widget-merchant/price"
CLIENT_ID = ""
CLIENT_SECRET = ""
)
func padBase64(base64String string) string {
return base64String + strings.Repeat("=", (4-len(base64String)%4)%4)
}
func generateSignature(clientSecret, timestamp, endpoint string) (string, error) {
clientSecretPadded := padBase64(clientSecret)
decodedSecret, err := base64.StdEncoding.DecodeString(clientSecretPadded)
if err != nil {
return "", err
}
message := fmt.Sprintf("%s:%s", timestamp, endpoint)
h := hmac.New(sha256.New, decodedSecret)
h.Write([]byte(message))
signature := base64.StdEncoding.EncodeToString(h.Sum(nil))
return signature, nil
}
func main() {
timestamp := fmt.Sprintf("%d", time.Now().UnixNano()/int64(time.Millisecond))
queryParams := url.Values{
"country": {"NG"},
"amount": {"10"},
"currency": {"usdc"},
"network": {"CELO"},
"asset": {"CUSD"},
"provider": {"bank_transfer"},
}
endpoint := fmt.Sprintf("%s?%s", ENDPOINT, queryParams.Encode())
signature, err := generateSignature(CLIENT_SECRET, timestamp, endpoint)
if err != nil {
fmt.Println("Error generating signature:", err)
return
}
client := &http.Client{}
req, err := http.NewRequest("GET", BASE_URL+endpoint, nil)
if err != nil {
fmt.Println("Error creating request:", err)
return
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("x-client-id", CLIENT_ID)
req.Header.Set("x-timestamp", timestamp)
req.Header.Set("x-signature", signature)
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error making request:", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error reading response body:", err)
return
}
var data map[string]interface{}
if err := json.Unmarshal(body, &data); err != nil {
fmt.Println("Error unmarshalling response:", err)
return
}
fmt.Println(data)
}
<?php
define('BASE_URL', 'https://aten.fonbnk-services.com');
define('ENDPOINT', '/api/pay-widget-merchant/price');
define('CLIENT_ID', '');
define('CLIENT_SECRET', '');
function pad_base64($base64_string) {
return $base64_string . str_repeat('=', (4 - strlen($base64_string) % 4) % 4);
}
function generate_signature($client_secret, $timestamp, $endpoint) {
$client_secret_padded = pad_base64($client_secret);
$hmac = hash_hmac('sha256', "$timestamp:$endpoint", base64_decode($client_secret_padded), true);
return base64_encode($hmac);
}
function main() {
$timestamp = (string) round(microtime(true) * 1000);
$query_params = [
'country' => 'NG',
'amount' => '10',
'currency' => 'usdc',
'network' => 'CELO',
'asset' => 'CUSD',
'provider' => 'bank_transfer',
];
$endpoint = ENDPOINT . '?' . http_build_query($query_params);
$signature = generate_signature(CLIENT_SECRET, $timestamp, $endpoint);
$headers = [
'Content-Type: application/json',
'x-client-id: ' . CLIENT_ID,
'x-timestamp: ' . $timestamp,
'x-signature: ' . $signature,
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, BASE_URL . $endpoint);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
print_r($data);
}
main();
?>
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class Main {
private static final String BASE_URL = "https://aten.fonbnk-services.com";
private static final String ENDPOINT = "/api/pay-widget-merchant/price";
private static final String CLIENT_ID = "";
private static final String CLIENT_SECRET = "";
public static void main(String[] args) throws Exception {
long timestamp = System.currentTimeMillis();
Map<String, String> queryParams = new HashMap<>();
queryParams.put("country", "NG");
queryParams.put("amount", "10");
queryParams.put("currency", "usdc");
queryParams.put("network", "CELO");
queryParams.put("asset", "CUSD");
queryParams.put("provider", "bank_transfer");
String endpoint = ENDPOINT + "?" + getQuery(queryParams);
String signature = generateSignature(CLIENT_SECRET, String.valueOf(timestamp), endpoint);
URL url = new URL(BASE_URL + endpoint);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("x-client-id", CLIENT_ID);
connection.setRequestProperty("x-timestamp", String.valueOf(timestamp));
connection.setRequestProperty("x-signature", signature);
Scanner scanner = new Scanner(connection.getInputStream());
String response = scanner.useDelimiter("\\A").next();
System.out.println(response);
scanner.close();
}
private static String padBase64(String base64String) {
return base64String + "=".repeat((4 - base64String.length() % 4) % 4);
}
private static String generateSignature(String clientSecret, String timestamp, String endpoint) throws Exception {
String clientSecretPadded = padBase64(clientSecret);
SecretKeySpec secretKeySpec = new SecretKeySpec(Base64.getDecoder().decode(clientSecretPadded), "HmacSHA256");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(secretKeySpec);
String data = timestamp + ":" + endpoint;
byte[] hmacBytes = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(hmacBytes);
}
private static String getQuery(Map<String, String> params) throws Exception {
StringBuilder result = new StringBuilder();
for (Map.Entry<String, String> entry : params.entrySet()) {
if (result.length() > 0) {
result.append("&");
}
result.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
result.append("=");
result.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
}
return result.toString();
}
}
import 'dart:convert';
import 'package:crypto/crypto.dart';
import 'package:http/http.dart' as http;
void main() async {
const String BASE_URL = "https://aten.fonbnk-services.com";
const String ENDPOINT = "/api/pay-widget-merchant/price";
const String CLIENT_ID = "";
const String CLIENT_SECRET = "";
// Get the current timestamp in milliseconds
int timestamp = DateTime.now().millisecondsSinceEpoch;
// Create query parameters
Map<String, String> queryParams = {
"country": "NG",
"amount": "10",
"currency": "usdc",
"network": "CELO",
"asset": "CUSD",
"provider": "bank_transfer",
};
// Generate the query string
String queryString = getQuery(queryParams);
// Create the endpoint with query parameters
String endpoint = ENDPOINT + "?" + queryString;
// Generate the signature
String signature = generateSignature(CLIENT_SECRET, timestamp.toString(), endpoint);
// Build the URL
String url = BASE_URL + endpoint;
// Set up the HTTP GET request
var headers = {
"Content-Type": "application/json",
"x-client-id": CLIENT_ID,
"x-timestamp": timestamp.toString(),
"x-signature": signature,
};
// Send the GET request
var response = await http.get(Uri.parse(url), headers: headers);
// Print the response body
print(response.body);
}
String getQuery(Map<String, String> params) {
return params.entries
.map((entry) =>
Uri.encodeQueryComponent(entry.key) + "=" + Uri.encodeQueryComponent(entry.value))
.join("&");
}
String generateSignature(String clientSecret, String timestamp, String endpoint) {
// Use the custom lenient Base64 decoder
List<int> secretKey = lenientBase64Decode(clientSecret);
Hmac hmac = Hmac(sha256, secretKey);
String data = '$timestamp:$endpoint';
Digest digest = hmac.convert(utf8.encode(data));
// Encode the signature using Base64
String signature = base64Encode(digest.bytes);
return signature;
}
List<int> lenientBase64Decode(String input) {
// Base64 index table
const String base64Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
// Remove all characters that are not in the Base64 alphabet
String sanitizedInput = input.replaceAll(RegExp(r'[^A-Za-z0-9+/]'), '');
// Map each character to its Base64 index
List<int> buffer = [];
int bits = 0;
int bitsCount = 0;
for (int i = 0; i < sanitizedInput.length; i++) {
int val = base64Chars.indexOf(sanitizedInput[i]);
if (val < 0) {
// Skip invalid characters
continue;
}
bits = (bits << 6) | val;
bitsCount += 6;
if (bitsCount >= 8) {
bitsCount -= 8;
int byte = (bits >> bitsCount) & 0xFF;
buffer.add(byte);
}
}
return buffer;
}
Mix.install([
{:httpoison, "~> 1.8"},
{:jason, "~> 1.4"}
])
defmodule FonbnkClient do
@moduledoc """
A client for interacting with the Fonbnk API.
"""
@base_url "https://aten.fonbnk-services.com"
@endpoint "/api/pay-widget-merchant/price"
@client_id ""
@client_secret ""
def pad_base64(base64_string) do
pad_length = Integer.mod(-String.length(base64_string), 4)
base64_string <> String.duplicate("=", pad_length)
end
def generate_signature(client_secret, timestamp, endpoint) do
client_secret_padded = pad_base64(client_secret)
{:ok, client_secret_decoded} = Base.decode64(client_secret_padded)
message = "#{timestamp}:#{endpoint}"
hmac = :crypto.mac(:hmac, :sha256, client_secret_decoded, message)
Base.encode64(hmac)
end
def main do
timestamp = :os.system_time(:millisecond) |> Integer.to_string()
query_params = %{
"country" => "NG",
"amount" => "10",
"currency" => "usdc",
"network" => "CELO",
"asset" => "CUSD",
"provider" => "bank_transfer"
}
encoded_query = URI.encode_query(query_params)
endpoint = @endpoint <> "?" <> encoded_query
signature = generate_signature(@client_secret, timestamp, endpoint)
headers = [
{"Content-Type", "application/json"},
{"x-client-id", @client_id},
{"x-timestamp", timestamp},
{"x-signature", signature}
]
url = @base_url <> endpoint
case HTTPoison.get(url, headers) do
{:ok, %HTTPoison.Response{body: body, status_code: code}} when code in 200..299 ->
data = Jason.decode!(body)
IO.inspect(data)
{:ok, %HTTPoison.Response{body: body, status_code: code}} ->
IO.puts("HTTP Error #{code}: #{body}")
{:error, %HTTPoison.Error{reason: reason}} ->
IO.puts("Request Error: #{inspect(reason)}")
end
end
end
FonbnkClient.main()
Last updated