Payfrica Partner API
The Payfrica Partner API allows 3rd party integrators to leverage Payfrica's Onramp and Offramp capabilities.
Base URL
Public API
https://backend.payfrica.xyz/api/v1/partnersAuthentication
Public API Authentication (API Keys)
Used for server-to-server integration.
CAUTION
Never expose your x-partner-api-secret in frontend code. Public API requests must be made from your secure backend server.
1. Public API Endpoints
Base URL: /api/v1/partners
/ratesGet real-time exchange rates for Onramp or Offramp.
Query Parameters
Response
{
"rate": "1650.50",
"fiatAmount": "5000",
"suiAmount": "3.03"
}/offramp/instantInitiate a crypto-to-fiat transaction with immediate payout. This is currently only supported for NGN. The flow is two-step: first execute the on-chain smart contract call to lock funds and get a transaction digest, then submit that digest to this endpoint to trigger the fiat payout.
Contract IDs
0x4378b5bf4c21d313150634d529218ea11166ca113a2869a31c88deb4bb8c2417Payfrica Move package ID on Sui
0x50eee353064aa82f120bfc212486acf9bd9acf5b3a3b5aadea8284ce30a20f84Shared config object passed to the off_ramp Move function
0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDCFully-qualified coin type for USDC (6 decimals)
0x2::sui::SUIFully-qualified coin type for SUI (9 decimals)
Step 1 — Smart Contract Call (Sui)
Before calling this endpoint, execute the on-chain transaction to lock the funds. The returned digest is passed to the API in Step 2.
amount + fee as the coin value to the smart contract. The fee is automatically deducted on-chain — the contract will take the fee from the coin you provide, so ensure the coin covers the full amount plus the 0.5% fee.import { Transaction } from "@mysten/sui/transactions";
const PAYFRICA_ID = "0x4378b5bf4c21d313150634d529218ea11166ca113a2869a31c88deb4bb8c2417";
const PAYFRICA_CONFIG_ID = "0x50eee353064aa82f120bfc212486acf9bd9acf5b3a3b5aadea8284ce30a20f84";
const USDC_COIN_TYPE = "0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC";
const SUI_COIN_TYPE = "0x2::sui::SUI";
// Fee: 0.5% flat for amounts 1–500 USDC or 1–500 SUI
const fee = (amount * 5n) / 1000n; // 0.5%
const tx = new Transaction();
const coinType = USDC_COIN_TYPE; // or SUI_COIN_TYPE
// Split the coin to cover amount + fee, then pass to off_ramp
// const [splitCoin] = tx.splitCoins(tx.gas, [amount + fee]); for SUI
const coins = await client.getCoins({ owner, coinType });
const coinObjects = coins.data;
// if (!coinObjects.length) throw new Error(`No ${coinType} coins found for address ${owner}`);
// Sort by balance to identify the largest coin as primary
const sortedCoins = [...coinObjects].sort((a: any, b: any) => Number(a.balance) - Number(b.balance));
const primary = sortedCoins[sortedCoins.length - 1].coinObjectId;
const others = sortedCoins.slice(0, sortedCoins.length - 1);
if (coinObjects.length > 1) {
tx.mergeCoins(
tx.object(primary),
others.map((c: any) => tx.object(c.coinObjectId))
);
}
const [splitCoin] = tx.splitCoins(tx.object(primary), [tx.pure.u64(amount + fee)]);
tx.moveCall({
target: `${PAYFRICA_ID}::payfrica_lite_v2::off_ramp`,
arguments: [
tx.object(PAYFRICA_CONFIG_ID), // Shared config object
splitCoin, // Coin covering amount + fee
tx.pure.string("NGN"), // Target fiat currency
tx.object.clock(), // Sui clock (0x6)
],
typeArguments: [coinType],
});
const result = await signAndExecuteTransaction({ transaction: tx });
const digest = result.digest; // Pass this to the API endpoint belowStep 2 — Request Body
{
"digest": "GxQ7...Sui_tx_digest",
"cryptoAsset": "USDC",
"recipient": {
"institution": "058",
"accountIdentifier": "0123456789",
"accountName": "John Doe",
"currency": "NGN",
"memo": "Instant payout"
}
}Response
{
"message": "Offramp order created",
"data": {
"rampOrderId": "uuid",
"status": "COMPLETED"
}
}/banksGet a list of supported financial institutions for payouts.
Query Parameters
Response
{
"success": true,
"data": [
{
"name": "Guaranty Trust Bank",
"bankCode": "058",
"routingKey": "058",
"logoImage": "..."
},
{
"name": "Zenith Bank",
"bankCode": "057",
"routingKey": "057",
"logoImage": "..."
},
{
"name": "Access Bank",
"bankCode": "044",
"routingKey": "044",
"logoImage": "..."
}
],
"message": "Supported institutions retrieved successfully"
}/verify-accountVerify a bank account number and retrieve the account holder's name. This endpoint uses Paystack's account resolution service.
Query Parameters
Response
{
"success": true,
"data": {
"accountNumber": "0123456789",
"accountName": "JOHN DOE"
},
"message": "Bank account verified successfully"
}/orders/:idGet the status of an order.
Query Parameters
Response
{
"id": "uuid",
"status": "COMPLETED",
"cryptoAmountReceived": "100",
"nairaPayoutCalculated": "150000"
}/verify-ninVerify the identity of a user using their National Identification Number (NIN) or Phone Number. This is required for KYC compliance.
Request Body
{
"firstname": "John",
"lastname": "Doe",
"suiAddress": "0x123...",
"nin": "12345678901",
"phone": "08012345678",
"dob": "1990-01-01",
"gender": "m"
}Response
{
"status": {
"status": "verified",
"state": "MATCH_FOUND"
},
"nin": {
"firstname": "John",
"lastname": "Doe",
"nin": "12345678901",
"phone": "08012345678",
"dob": "1990-01-01",
"gender": "m"
}
}2. Virtual Sub-Bank Accounts
Programmatically manage virtual Naira bank accounts for your users. Base URL: /api/v1/partners
/usersGet a paginated list of users associated with your partner account.
Query Parameters
Response
{
"data": [
{
"id": "uuid",
"suiAddress": "0x123...",
"kycStatus": "VERIFIED",
"accountNumber": "1234567890"
}
],
"total": 1,
"page": 1,
"limit": 10
}/users/:suiAddress/sub-bank-account/onrampInitiate a dynamic virtual account for onramp. This creates a short-lived bank account that the user can transfer NGN to. Once received, Payfrica automatically converts the NGN to crypto and sends it to the user's wallet. Note: The amountExpected in the response includes a processing fee (0.5% for amounts < 10k, or flat ₦50 for amounts >= 10k).
Query Parameters
Request Body
{
"amount": 5000,
"cryptoAsset": "USDC"
}Response
{
"accountNumber": "1234567890",
"accountName": "PAYFRICA-ONRAMP-XYZ",
"bankName": "Safehaven Microfinance Bank",
"amountExpected": 5025,
"currency": "NGN",
"expiresAt": "2024-03-27T13:00:00Z",
"orderId": "uuid",
"transactionId": "uuid"
}/users/:suiAddress/transactionsGet the transaction history (conversions, withdrawals, deposits) for a specific user.
Query Parameters
Response
{
"data": [
{
"id": "uuid",
"type": "WITHDRAWAL",
"status": "COMPLETED",
"amount": "5000.00",
"currency": "NGN",
"reference": "WITH-123",
"narration": "Bank withdrawal",
"createdAt": "2024-03-27T12:00:00Z"
}
],
"total": 1,
"page": 1,
"limit": 10
}Webhooks
Payfrica can send real-time notifications to your system via Webhooks. All webhook requests are sent as POST requests with a JSON body and an HMAC-SHA256 signature.
Signature Verification
Each webhook request includes an x-payfrica-signature header. You should verify this signature using your Public API Key as the secret.
const crypto = require('crypto');
function verifySignature(payload, signature, secret) {
const hmac = crypto.createHmac('sha256', secret);
const calculated = hmac.update(JSON.stringify(payload)).digest('hex');
return calculated === signature;
}Events and Payloads
virtual_account.onramp.completed
Sent when a dynamic virtual account payment is received and crypto fulfillment is completed.
{
"event": "virtual_account.onramp.completed",
"data": {
"id": "order-uuid",
"dynamicAccountId": "acc-uuid",
"type": "ONRAMP_DYNAMIC",
"amount": 5000,
"currency": "NGN",
"reference": "REF123456",
"suiAddress": "0x...",
"targetAsset": "USDC",
"cryptoAmount": "3.0303",
"txHash": "0x...",
"createdAt": "2024-03-27T12:00:00Z"
},
"timestamp": 1711545600000
}subaccount.created
Sent when a user's sub-bank account is successfully provisioned.
{
"event": "subaccount.created",
"data": {
"suiAddress": "0x...",
"accountNumber": "1234567890",
"accountName": "John Doe",
"kycStatus": "VERIFIED"
},
"timestamp": 1711545600000
}subaccount.transaction.completed
Sent when a conversion (NGN to Crypto) or withdrawal (NGN to Bank) is successful.
{
"event": "subaccount.transaction.completed",
"data": {
"id": "tx-uuid",
"type": "WITHDRAWAL",
"amount": "5000.00",
"currency": "NGN",
"reference": "WITH-123",
"suiAddress": "0x...",
"createdAt": "2024-03-27T12:00:00Z"
},
"timestamp": 1711545600000
}subaccount.transaction.failed
Sent when a transaction fails due to banking errors or payout failures.
{
"event": "subaccount.transaction.failed",
"data": {
"id": "tx-uuid",
"type": "CONVERSION",
"amount": 1000,
"currency": "NGN",
"suiAddress": "0x...",
"targetAsset": "USDC",
"error": "Payout failed",
"createdAt": "2024-03-27T12:00:00Z"
},
"timestamp": 1711545600000
}Error Responses
401 Unauthorized - Paused Partner
{
"statusCode": 401,
"message": "Partner operations are currently paused. Please contact support."
}401 Unauthorized - Unverified Partner
{
"statusCode": 401,
"message": "Partner account is not verified. Please contact support."
}403 Forbidden - Key Rotation
{
"statusCode": 403,
"message": "Partner account must be verified before API keys can be generated or rotated"
}