API
Introduction
Welcome to our API documentation! Here, you'll find all the information you need to interact with our payment services. đ
General Response Structure
All our API responses follow a consistent structure to make things easy and uniform. Here's what you can expect:
- status: Indicates success (
true
) or failure (false
) of the request. - message: A human-readable message explaining the response.
- data: Contains the details of the response (if any).
Error Responses
When something goes wrong, we'll let you know with an error response. You'll get a status of false, an error code and the message will describe what went wrong.
- status: Indicates success (
true
) or failure (false
) of the request. - message: A human-readable message explaining the response.
- error_code: An error code in case there is a problem with a payment
- errors: Field validation errors
API Keys
Make sure to replace sk_test_...
with your actual API key. Your API key is your unique identifier, and keep it safe! đ
Parameters
When you see :parameter
, make sure to replace it with the actual value that you want to pass to the API. For example, :checkout_reference
should be replaced with the actual checkout reference.
Example of Invalid API Key Response
If you use an incorrect API key, you'll receive a response like this:
checkouts
What is Checkout?
Checkout is the process by which customers complete their purchase. It involves initializing a payment and redirecting the customer to an authorization URL. đł
Steps to Accept Payment with the Checkout API
- Initialize Checkout: Call the initialization endpoint.
- Redirect the Customer: Redirect the customer to the
authorization_url
returned if the request is successful.
initialize checkout
You can initialize a checkout by making the following request:
curl --location 'https://api.senfenico.com/v1/payment/checkouts/initialize/' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'X-API-KEY: sk_test_...' \
--data '{
"email": "customer@mail.com",
"amount": 600,
"success_url": "https://example.com/sucess",
"cancel_url": "https://example.com/cancel"
}'
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\n \"email\": \"customer@mail.com\",\n \"amount\": 600,\n \"success_url\": \"https://example.com/sucess\",\n \"cancel_url\": \"https://example.com/cancel\"\n}");
Request request = new Request.Builder()
.url("https://api.senfenico.com/v1/payment/checkouts/initialize/")
.method("POST", body)
.addHeader("Content-Type", "application/json")
.addHeader("Accept", "application/json")
.addHeader("X-API-KEY", "sk_test_...")
.build();
Response response = client.newCall(request).execute();
var https = require('follow-redirects').https;
var fs = require('fs');
var options = {
'method': 'POST',
'hostname': 'api.senfenico.com',
'path': '/v1/payment/checkouts/initialize/',
'headers': {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-API-KEY': 'sk_test_...'
},
'maxRedirects': 20
};
var req = https.request(options, function (res) {
var chunks = [];
res.on("data", function (chunk) {
chunks.push(chunk);
});
res.on("end", function (chunk) {
var body = Buffer.concat(chunks);
console.log(body.toString());
});
res.on("error", function (error) {
console.error(error);
});
});
var postData = JSON.stringify({
"email": "customer@mail.com",
"amount": 600,
"success_url": "https://example.com/sucess",
"cancel_url": "https://example.com/cancel"
});
req.write(postData);
req.end();
Parameter | Type | Optional | Description |
---|---|---|---|
string | Yes | Customer's email address | |
amount | int | No | Amount to charge |
success_url | string | No | URL to redirect on success |
cancel_url | string | No | URL to redirect if the customer cancels |
Sample Response
Response Element | Type | Description |
---|---|---|
reference | string | Unique reference for the checkout |
authorization_url | string | URL to redirect the user to proceed with the payment |
Note: Redirect the user to authorization_url
to proceed with the payment.
fetch checkout
Fetch the details of a specific checkout:
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("text/plain");
RequestBody body = RequestBody.create(mediaType, "");
Request request = new Request.Builder()
.url("https://api.senfenico.com/v1/payment/checkouts/{checkout_reference}")
.method("GET", body)
.addHeader("Accept", "application/json")
.addHeader("X-API-KEY", "sk_test_...")
.build();
Response response = client.newCall(request).execute();
var https = require('follow-redirects').https;
var fs = require('fs');
var options = {
'method': 'GET',
'hostname': 'api.senfenico.com',
'path': '/v1/payment/checkouts/{checkout_reference}',
'headers': {
'Accept': 'application/json',
'X-API-KEY': 'sk_test_...'
},
'maxRedirects': 20
};
var req = https.request(options, function (res) {
var chunks = [];
res.on("data", function (chunk) {
chunks.push(chunk);
});
res.on("end", function (chunk) {
var body = Buffer.concat(chunks);
console.log(body.toString());
});
res.on("error", function (error) {
console.error(error);
});
});
req.end();
Sample Response
{
"status": true,
"message": "Checkout details retrieved",
"data": {
"email": "customer@mail.com",
"reference": "8872031d-178e-464c-9261-7984f0a313c0",
"charge_reference": null,
"amount": 600,
"success_url": "https://yourwebsite.com/success",
"cancel_url": "https://yourwebsite.com/cancel",
"phone": "",
"provider": "",
"live_mode": true,
"created_at": "2023-07-25T19:55:48.223895Z",
"updated_at": "2023-07-25T19:55:48.223956Z",
"status": "pending"
}
}
Checkout Status:
pending
: Awaiting paymentsuccess
: Payment successfulfailed
: Payment failed
list checkout
Retrieve a list of checkouts:
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("text/plain");
RequestBody body = RequestBody.create(mediaType, "");
Request request = new Request.Builder()
.url("https://api.senfenico.com/v1/payment/checkouts")
.method("GET", body)
.addHeader("Accept", "application/json")
.addHeader("X-API-KEY", "sk_test_...")
.build();
Response response = client.newCall(request).execute();
var https = require('follow-redirects').https;
var fs = require('fs');
var options = {
'method': 'GET',
'hostname': 'api.senfenico.com',
'path': '/v1/payment/checkouts',
'headers': {
'Accept': 'application/json',
'X-API-KEY': 'sk_test_...'
},
'maxRedirects': 20
};
var req = https.request(options, function (res) {
var chunks = [];
res.on("data", function (chunk) {
chunks.push(chunk);
});
res.on("end", function (chunk) {
var body = Buffer.concat(chunks);
console.log(body.toString());
});
res.on("error", function (error) {
console.error(error);
});
});
req.end();
Sample Response
{
"status": true,
"message": "Checkout list retreived",
"data": [
{
"email": "customer@mail.com",
"reference": "0ecae2d5-4d8d-47d9-98b2-38826c562c37",
"charge_reference": "822039fa-9cbc-447b-bd3a-b374e06faa40",
"amount": 3500,
"success_url": "https://yourwebsite.com/success",
"cancel_url": "https://yourwebsite.com/cancel",
"phone": "77xxxxxx",
"provider": "orange_bf",
"live_mode": false,
"created_at": "2023-07-13T11:36:50.827975Z",
"updated_at": "2023-07-13T11:41:35.663261Z",
"status": "pending"
},
{
"email": "customer@mail.com",
"reference": "e222060a-0f49-4759-a400-c241a88f623c",
"charge_reference": "734c24d4-a379-47a6-8994-1880a3e63af4",
"amount": 200,
"success_url": "https://yourwebsite.com/success",
"cancel_url": "https://yourwebsite.com/cancel",
"phone": "71xxxxxx",
"provider": "moov_bf",
"live_mode": false,
"created_at": "2023-07-11T22:34:44.375268Z",
"updated_at": "2023-07-12T01:34:21.373271Z",
"status": "success"
}
]
}
charges
Charges are all about handling all the payment steps by yourself. It is suitable for mobile or destop apps as it does not require redirection. Hereâs how you can accept a payment with the charges
endpoints:
- Create a Charge: Define the charge details.
- Customer Action: The customer MAY have to enter a USSD code to validate the transaction on their phone.
- Submit OTP
create a charge
Create a new charge for a payment:
curl --location 'https://api.senfenico.com/v1/payment/charges/' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'X-API-KEY: sk_test_...' \
--data '{
"amount": "500",
"currency": "XOF",
"payment_method": "mobile_money",
"payment_method_details": {
"phone": "76xxxxxx",
"provider": "orange_bf"
}
}'
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\n \"amount\": \"500\",\n \"currency\": \"XOF\",\n \"payment_method\": \"mobile_money\",\n \"payment_method_details\": {\n \"phone\": \"76xxxxxx\",\n \"provider\": \"orange_bf\"\n }\n}");
Request request = new Request.Builder()
.url("https://api.senfenico.com/v1/payment/charges/")
.method("POST", body)
.addHeader("Content-Type", "application/json")
.addHeader("Accept", "application/json")
.addHeader("X-API-KEY", "sk_test_...")
.build();
Response response = client.newCall(request).execute();
var https = require('follow-redirects').https;
var fs = require('fs');
var options = {
'method': 'POST',
'hostname': 'api.senfenico.com',
'path': '/v1/payment/charges/',
'headers': {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-API-KEY': 'sk_test_...'
},
'maxRedirects': 20
};
var req = https.request(options, function (res) {
var chunks = [];
res.on("data", function (chunk) {
chunks.push(chunk);
});
res.on("end", function (chunk) {
var body = Buffer.concat(chunks);
console.log(body.toString());
});
res.on("error", function (error) {
console.error(error);
});
});
var postData = JSON.stringify({
"amount": "500",
"currency": "XOF",
"payment_method": "mobile_money",
"payment_method_details": {
"phone": "76xxxxxx",
"provider": "orange_bf"
}
});
req.write(postData);
req.end();
Parameter | Type | Optional | Description |
---|---|---|---|
amount | int | No | Amount to charge |
currency | string | Yes | Currency code (default: "XOF") |
payment_method | string | Yes | Payment method (default: "mobile_money") |
payment_method_details | object | No | Details about the payment method |
phone | string | No | your client phone number |
provider | object | No | the phone number operator. Available options:
|
Note on Provider Constraints:
- For
orange_bf
: Use an Orange BF phone number (8 digits). Valid prefixes: 04, 05, 06, 07, 54, 55, 56, 57, 64, 65, 66, 67, 74, 75, 76, 77. - For
moov_bf
: Use a Moov BF phone number (8 digits). Valid prefixes: 70, 71, 72, 73, 60, 61, 62, 63, 50, 51, 52, 53, 01, 02, 03. - For
coris_bf
: You can use any Burkina-Faso mobile phone number - For
sank_bf
: You can use any Burkina-Faso mobile phone number
Sample Response
{
"status": true,
"message": "Charge attempted",
"data": {
"reference": "ch_1ba1b4d1268b41079ee84c777e999f51",
"status": "send_otp",
"display_text": "Composez *144*4*6*500# pour obtenir un code OTP, puis entrez-le sur notre plateforme pour finaliser la transaction.",
"live_mode": false
}
}
Interpretation of Responses:
-
When the status is
send_otp
, the user is required to enter an OTP code to complete the transaction. This OTP is usually sent via SMS from their mobile money operator without needing further action from the user. However, in some cases, like with Orange Money, the user may need to manually dial a USSD code as shown in thedisplay_text
to receive the OTP. -
When the status is
pay_offline
, the user must finalize the payment directly on their mobile phone, typically by executing a USSD code and following the instructions as indicated in thedisplay_text
. In this case, no OTP code is required from the user. Once the payment is completed, the integrator will receive a notification if they have subscribed to the appropriate events in the webhook menu on the admin interface.
submit OTP
Submit the OTP for a charge:
curl --location 'https://api.senfenico.com/v1/payment/charges/submit' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'X-API-KEY: sk_test_...' \
--data '{
"otp": "839231",
"charge_reference": "ch_39728149f97a46f1806a694dbb620b23"
}'
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\n \"otp\": \"839231\",\n \"charge_reference\": \"ch_39728149f97a46f1806a694dbb620b23\"\n}");
Request request = new Request.Builder()
.url("https://api.senfenico.com/v1/payment/charges/submit")
.method("POST", body)
.addHeader("Content-Type", "application/json")
.addHeader("Accept", "application/json")
.addHeader("X-API-KEY", "sk_test_...")
.build();
Response response = client.newCall(request).execute();
var https = require('follow-redirects').https;
var fs = require('fs');
var options = {
'method': 'POST',
'hostname': 'api.senfenico.com',
'path': '/v1/payment/charges/submit',
'headers': {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-API-KEY': 'sk_test_...'
},
'maxRedirects': 20
};
var req = https.request(options, function (res) {
var chunks = [];
res.on("data", function (chunk) {
chunks.push(chunk);
});
res.on("end", function (chunk) {
var body = Buffer.concat(chunks);
console.log(body.toString());
});
res.on("error", function (error) {
console.error(error);
});
});
var postData = JSON.stringify({
"otp": "839231",
"charge_reference": "ch_39728149f97a46f1806a694dbb620b23"
});
req.write(postData);
req.end();
Parameter | Type | Optional | Description |
---|---|---|---|
otp | string | No | One-Time Password (OTP) |
charge_reference | string | No | Reference of the charge |
Sample Response
{
"status": true,
"message": "charge completed successfully",
"data": {
"reference": "ch_39728149f97a46f1806a694dbb620b23",
"amount": 1000,
"fees": 3.5,
"currency": "XOF",
"transaction_date": "2024-03-18T11:43:47.380565+00:00",
"ip_address": "172.18.0.1",
"status": "success",
"live_mode": false,
"payment_method": "mobile_money",
"provider": "orange_bf",
"cancelled_at": null,
"cancellation_reason": null,
"confirmation_attempts": [
{
"ip_address": "172.18.0.1",
"attempt_date": "2024-03-18T11:44:16.683423+00:00",
"confirmation_status": "success"
}
]
}
}
Response Element | Type | Description |
---|---|---|
reference | string | Reference of the charge |
amount | int | Charged amount |
fees | float | Transaction fees in percent (e.g., 2%) |
currency | string | Currency code ["XOF"] |
status | string | Status of the charge |
live_mode | bool | Indicates if it's a live transaction |
payment_method | string | Payment method ["mobile_money"] |
provider | string | Provider name ["orange_bf", "moov_bf", "coris_bf", "sank_bf"] |
cancelled_at | null | Cancellation time (if any) |
cancellation_reason | null | Reason for cancellation (if any) |
fetch charge
Retrieve the details of a specific charge:
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("text/plain");
RequestBody body = RequestBody.create(mediaType, "");
Request request = new Request.Builder()
.url("https://api.senfenico.com/v1/payment/charges/{charge_reference}")
.method("GET", body)
.addHeader("Accept", "application/json")
.addHeader("X-API-KEY", "sk_test_...")
.build();
Response response = client.newCall(request).execute();
var https = require('follow-redirects').https;
var fs = require('fs');
var options = {
'method': 'GET',
'hostname': 'api.senfenico.com',
'path': '/v1/payment/charges/{charge_reference}',
'headers': {
'Accept': 'application/json',
'X-API-KEY': 'sk_test_...'
},
'maxRedirects': 20
};
var req = https.request(options, function (res) {
var chunks = [];
res.on("data", function (chunk) {
chunks.push(chunk);
});
res.on("end", function (chunk) {
var body = Buffer.concat(chunks);
console.log(body.toString());
});
res.on("error", function (error) {
console.error(error);
});
});
req.end();
Parameter | Type | Optional | Description |
---|---|---|---|
charge_reference | string | No | Reference of the charge |
Sample Response
{
"status": true,
"message": "Charge attempted",
"data": {
"reference": "ch_45a408fd483a4d66b98aeea97aa7c2e2",
"amount": 1000,
"fees": "3.50",
"currency": "XOF",
"transaction_date": "2024-03-18T12:52:33.569084Z",
"ip_address": "172.18.0.1",
"status": "success",
"live_mode": false,
"payment_method": "mobile_money",
"provider": "orange_bf",
"phone": "76000000",
"cancelled_at": null,
"cancellation_reason": null,
"created_at": "2024-03-18T12:52:33.572801Z",
"updated_at": "2024-03-18T12:58:04.376754Z",
"confirmation_attempts": [
{
"ip_address": "172.18.0.1",
"attempt_date": "2024-03-18T12:53:00.220699Z",
"confirmation_status": "failed"
},
{
"ip_address": "172.18.0.1",
"attempt_date": "2024-03-18T12:58:04.911978Z",
"confirmation_status": "success"
}
]
}
}
Response Element | Type | Description |
---|---|---|
reference | string | Reference of the charge |
amount | int | Charged amount |
fees | string | Transaction fees in percent (2 means 2%) |
currency | string | Currency code ["XOF" ] |
transaction_date | string | Transaction date and time |
status | string | Status of the charge [success , failed , send_otp , pay_offline , pending , canceled ] |
live_mode | bool | Indicates if it's a live transaction |
payment_method | string | Payment method (e.g., "mobile_money") |
provider | string | Provider name (e.g., "orange_bf") |
phone | string | Phone number used for transaction |
cancelled_at | null | Cancellation time (if any) |
cancellation_reason | null | Reason for cancellation (if any) |
list charges
Retrieve a list of all the charges:
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("text/plain");
RequestBody body = RequestBody.create(mediaType, "");
Request request = new Request.Builder()
.url("https://api.senfenico.com/v1/payment/charges")
.method("GET", body)
.addHeader("Accept", "application/json")
.addHeader("X-API-KEY", "sk_test_...")
.build();
Response response = client.newCall(request).execute();
var request = require('request');
var options = {
'method': 'GET',
'url': 'https://api.senfenico.com/v1/payment/charges',
'headers': {
'Accept': 'application/json',
'X-API-KEY': 'sk_test_...'
}
};
request(options, function (error, response) {
if (error) throw new Error(error);
console.log(response.body);
});
Sample Response
{
"status": true,
"message": "Charge list retreived",
"data": [
{
"reference": "01b6f627-6123-43e7-89a7-4fcb9079b173",
"amount": 500,
"fees": "2.00",
"currency": "XOF",
"transaction_date": "2023-07-27T10:26:50.563219Z",
"ip_address": "172.23.0.1",
"status": "success",
"live_mode": false,
"payment_method": "mobile_money",
"provider": "orange_bf",
"phone": "77xxxxxx",
"cancelled_at": null,
"cancellation_reason": null,
"created_at": "2023-07-27T10:26:50.571137Z",
"updated_at": "2023-07-27T10:27:19.402034Z",
"confirmation_attempts": [
{
"ip_address": "172.23.0.1",
"attempt_date": "2023-07-27T10:27:19.385100Z",
"confirmation_status": "success"
}
]
},
{
"reference": "285a232b-a3b0-4af4-92f3-9c07d10aa13c",
"amount": 500,
"fees": "2.00",
"currency": "XOF",
"transaction_date": "2023-07-14T12:02:10.115968Z",
"ip_address": "127.0.0.1",
"status": "success",
"live_mode": false,
"payment_method": "mobile_money",
"provider": "orange_bf",
"phone": "77xxxxxx",
"cancelled_at": null,
"cancellation_reason": null,
"created_at": "2023-07-14T12:02:10.128193Z",
"updated_at": "2023-07-14T12:03:59.453015Z",
"confirmation_attempts": [
{
"ip_address": "127.0.0.1",
"attempt_date": "2023-07-14T12:03:59.403481Z",
"confirmation_status": "success"
},
{
"ip_address": "127.0.0.1",
"attempt_date": "2023-07-14T12:04:52.029977Z",
"confirmation_status": "failed"
},
{
"ip_address": "127.0.0.1",
"attempt_date": "2023-07-14T12:12:23.276340Z",
"confirmation_status": "failed"
}
]
},
]
}
(You can refer to the previously explained attributes for this list as they remain the same.)
balance
Retrieve the current balance information:
fetch user balance
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("text/plain");
RequestBody body = RequestBody.create(mediaType, "");
Request request = new Request.Builder()
.url("https://api.senfenico.com/v1/payment/balances")
.method("GET", body)
.addHeader("Accept", "application/json")
.addHeader("X-API-KEY", "sk_test_...")
.build();
Response response = client.newCall(request).execute();
var https = require('follow-redirects').https;
var fs = require('fs');
var options = {
'method': 'GET',
'hostname': 'api.senfenico.com',
'path': '/v1/payment/balances',
'headers': {
'Accept': 'application/json',
'X-API-KEY': 'sk_test_...'
},
'maxRedirects': 20
};
var req = https.request(options, function (res) {
var chunks = [];
res.on("data", function (chunk) {
chunks.push(chunk);
});
res.on("end", function (chunk) {
var body = Buffer.concat(chunks);
console.log(body.toString());
});
res.on("error", function (error) {
console.error(error);
});
});
req.end();
Sample Response
{
"status": true,
"message": "Balance retrieved",
"data": {
"collection_balances": {
"available": 18692.0,
"pending": 5000.0,
"currency": "XOF"
},
"transfer_balances": {
"total_available": 0.0,
"total_pending": 0.0,
"orange_money_bf": {
"available": 0.0,
"pending": 0.0
},
"moov_money_bf": {
"available": 0.0,
"pending": 0.0
},
"sank_money_bf": {
"available": 0.0,
"pending": 0.0
}
}
}
}
Balance vs Usable Balance:
available
: This represents the funds that are immediately accessible for use. These funds are ready for transactions. There are no restrictions on the use of this balance.pending
: This includes funds that are in the process of being transferred or settled. These amounts are not available for use.
transfers
Overview
With Senfenico, you have two distinct types of balances: Collection Balance and Transfer Balance. When you collect or receive payments, the funds are credited to your Collection Balance. Conversely, when you send money, i.e., make a transfer, the amount is deducted from your Transfer Balance. Itâs essential to ensure that your Transfer Balance has sufficient funds to cover the intended transfer.
At present, each wallet type maintains its own separate Transfer Balance:
- Orange Money Transfer Balance
- Moov Money Transfer Balance
- Sank Money Transfer Balance
Transfer Balance Management
In the short term, we aim to provide a unified Transfer Balance across all wallets. In the medium term, our goal is to unify the Collection and Transfer Balances into a single, seamless balance. However, for now, you must individually top up each Transfer Balance to make transfers to the corresponding wallet.
For example, to transfer funds to a Moov Africa Burkina Faso account, you must ensure your Moov BF Transfer Balance has sufficient funds. The same applies to Orange Money and Sank Money.
single transfer
curl --location 'https://api.senfenico.com/v1/payment/transfers/' \
--header 'Content-Type: application/json' \
--header 'X-API-KEY: sk_live_...' \
--data '{
"amount": 100,
"recipient_phone": "63xxxxxx",
"recipient_wallet": "sank_bf",
"ext_id": "98b96ea4-210d-4ea0-9b03-8d7fd96e4064"
}'
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\r\n \"amount\": 100,\r\n \"recipient_phone\": \"63xxxxxx\",\r\n \"recipient_wallet\": \"sank_bf\",\r\n \"ext_id\": \"98b96ea4-210d-4ea0-9b03-8d7fd96e4064\"\r\n}");
Request request = new Request.Builder()
.url("https://api.senfenico.com/v1/payment/transfers/")
.method("POST", body)
.addHeader("Content-Type", "application/json")
.addHeader("X-API-KEY", "sk_live_...")
.build();
Response response = client.newCall(request).execute();
const axios = require('axios');
let data = JSON.stringify({
"amount": 100,
"recipient_phone": "63xxxxxx",
"recipient_wallet": "sank_bf",
"ext_id": "98b96ea4-210d-4ea0-9b03-8d7fd96e4064",
});
let config = {
method: 'post',
maxBodyLength: Infinity,
url: 'https://api.senfenico.com/v1/payment/transfers/',
headers: {
'Content-Type': 'application/json',
'X-API-KEY': 'sk_live_...'
},
data : data
};
axios.request(config)
.then((response) => {
console.log(JSON.stringify(response.data));
})
.catch((error) => {
console.log(error);
});
Parameter | Type | Optional | Description |
---|---|---|---|
amount | int | No | The amount you wish to transfer. (min: 100) |
recipient_phone | string | No | The phone number of the recipient. |
recipient_wallet | string | Yes | (Optional) The wallet to which the transfer is being made. If not provided, Senfenico will determine the wallet based on the recipientâs phone number. For example, a Moov BF number will automatically direct the transfer to a Moov BF wallet, and the same logic applies to Orange Money. Available options : [orange_bf , moov_bf , sank_bf ] |
ext_id | string | Yes | External ID |
Sample Response
{
"status": true,
"message": "Transfer has been queued",
"data": {
"reference": "tr_ef733c5a98b04a51b00773256d8b8f2f",
"amount": 100,
"fees": 1.5,
"recipient_phone": "63000000",
"currency": "XOF",
"ip_address": "172.18.0.1",
"status": "pending",
"live_mode": true,
"recipient_wallet": "sank_bf",
"ext_id": "98b96ea4-210d-4ea0-9b03-8d7fd96e4064",
"created_at": "2024-03-21T21:08:33.336879Z"
}
}
Response Element | Type | Description |
---|---|---|
reference | string | Reference of the transfer |
amount | int | Transfered amount |
fees | string | Transaction fees in percent (2 means 2%) |
recipient_phone | string | Phone number used for transaction |
currency | string | Currency code ["XOF" ] |
status | string | Status of the charge [success , failed , pending ] |
live_mode | bool | Indicates if it's a live transaction |
recipient_wallet | string | recipient wallet [orange_bf , moov_bf , sank_bf ] |
ext_id | string | Unique external identifier provided by the client for tracking and referencing this transfer within their own system. Typically generated using methods like UUID. |
bulk transfer
Senfenico supports bulk transfers, allowing you to process multiple transfers in a single request. However, please note the following constraints:
- Batch Size: Limited to 100 transfers per request. If you require a larger batch size, please contact our support team.
- Rate Limit: You cannot call the bulk transfer endpoint more than 5 times per second.
curl --location 'https://api.senfenico.com/v1/payment/transfers/bulk/' \
--header 'Content-Type: application/json' \
--header 'X-API-KEY: sk_live_...' \
--data '{
"transfers": [
{
"amount": 100,
"recipient_phone": "65xxxxxx",
"recipient_wallet": "orange_bf"
},
{
"amount": 100,
"recipient_phone": "63xxxxxx"
}
]
}'
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\r\n \"transfers\": [\r\n {\r\n \"amount\": 100,\r\n \"recipient_phone\": \"65xxxxxx\",\r\n \"recipient_wallet\": \"orange_bf\"\r\n },\r\n {\r\n \"amount\": 100,\r\n \"recipient_phone\": \"63xxxxxx\"\r\n }\r\n ]\r\n}");
Request request = new Request.Builder()
.url("https://api.senfenico.com/v1/payment/transfers/bulk/")
.method("POST", body)
.addHeader("Content-Type", "application/json")
.addHeader("X-API-KEY", "sk_live_...")
.build();
Response response = client.newCall(request).execute();
const axios = require('axios');
let data = JSON.stringify({
"transfers": [
{
"amount": 100,
"recipient_phone": "65xxxxxx",
"recipient_wallet": "orange_bf"
},
{
"amount": 100,
"recipient_phone": "63xxxxxx"
}
]
});
let config = {
method: 'post',
maxBodyLength: Infinity,
url: 'https://api.senfenico.com/v1/payment/transfers/bulk/',
headers: {
'Content-Type': 'application/json',
'X-API-KEY': 'sk_live_...'
},
data : data
};
axios.request(config)
.then((response) => {
console.log(JSON.stringify(response.data));
})
.catch((error) => {
console.log(error);
});
$senfenico = new \Senfenico\Senfenico('sk_test_...');
$senfenico->transfer->bulkCreate([
'transfers' => [
[
'amount' => 100,
'recipient_phone' => '65xxxxxx',
'recipient_wallet' => 'orange_bf'
],
[
'amount' => 100,
'recipient_phone' => '63xxxxxx',
'ext_id' => '98b96ea4-210d-4ea0-9b03-8d7fd96e4064'
]
]
]);
import senfenico
senfenico.api_key = 'sk_test_...'
senfenico.Transfer.bulk_create(
transfers=[
{"amount": 100, "recipient_phone": "65xxxxxx", "recipient_wallet": "orange_bf"},
{"amount": 100, "recipient_phone": "63xxxxxx", "ext_id": "98b96ea4-210d-4ea0-9b03-8d7fd96e4064"}
]
)
Sample Response
{
"status": true,
"message": "1 transfers queued for processing",
"failed_transfers": [
{
"data": {
"amount": 100,
"recipient_phone": "63xxxxxx",
"recipient_wallet": null,
"ext_id": "98b96ea4-210d-4ea0-9b03-8d7fd96e4064"
},
"errors": {
"non_field_errors": [
"Insufficient balance for Moov BF transfer including fees."
]
}
}
]
}
(You can refer to the previously explained attributes for this list as they remain the same.)
list transfers
This endpoint is used to retrieve a list of all transfers.
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("text/plain");
RequestBody body = RequestBody.create(mediaType, "");
Request request = new Request.Builder()
.url("https://api.senfenico.com/v1/payment/transfers/")
.method("GET", body)
.addHeader("X-API-KEY", "sk_live_...")
.build();
Response response = client.newCall(request).execute();
const axios = require('axios');
let config = {
method: 'get',
maxBodyLength: Infinity,
url: 'https://api.senfenico.com/v1/payment/transfers/',
headers: {
'X-API-KEY': 'sk_live_...'
}
};
axios.request(config)
.then((response) => {
console.log(JSON.stringify(response.data));
})
.catch((error) => {
console.log(error);
});
Sample Response
{
"status": true,
"message": "Transfer list retrieved",
"data": [
{
"reference": "tr_61242504fdf24d1b9f5344deed8657f9",
"amount": 100,
"fees": "1.50",
"currency": "XOF",
"ip_address": "172.18.0.1",
"status": "pending",
"live_mode": true,
"recipient_wallet": "orange_bf",
"recipient_phone": "65xxxxxx",
"ext_id": "98b96ea4-210d-4ea0-9b03-8d7fd96e4064",
"created_at": "2024-03-21T13:34:34.268389Z"
},
{
"reference": "tr_f93b234379994c4498c0014d87936dae",
"amount": 100,
"fees": "1.50",
"currency": "XOF",
"ip_address": "172.18.0.1",
"status": "pending",
"live_mode": true,
"recipient_wallet": "orange_bf",
"recipient_phone": "76xxxxxx",
"ext_id": "73b96ea4-210d-4ea0-9b03-8d7fd96e4052",
"created_at": "2024-03-21T13:22:51.139807Z"
}
]
}
(You can refer to the previously explained attributes for this list as they remain the same.)
fetch single transfer
You can fetch details of a single transfer using this endpoint:
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("text/plain");
RequestBody body = RequestBody.create(mediaType, "");
Request request = new Request.Builder()
.url("https://api.senfenico.com/v1/payment/transfers/tr_61242504fdf24d1b9f5344deed8657f9")
.method("GET", body)
.addHeader("X-API-KEY", "sk_live_...")
.build();
Response response = client.newCall(request).execute();
const axios = require('axios');
let config = {
method: 'get',
maxBodyLength: Infinity,
url: 'https://api.senfenico.com/v1/payment/transfers/tr_61242504fdf24d1b9f5344deed8657f9',
headers: {
'X-API-KEY': 'sk_live_...'
}
};
axios.request(config)
.then((response) => {
console.log(JSON.stringify(response.data));
})
.catch((error) => {
console.log(error);
});
Sample Response
{
"status": true,
"message": "Transfer details retrieved",
"data": {
"reference": "tr_61242504fdf24d1b9f5344deed8657f9",
"amount": 100,
"fees": "1.50",
"currency": "XOF",
"ip_address": "172.18.0.1",
"status": "pending",
"live_mode": true,
"recipient_wallet": "orange_bf",
"recipient_phone": "65xxxxxx",
"created_at": "2024-03-21T13:34:34.268389Z",
"ext_id": "98b96ea4-210d-4ea0-9b03-8d7fd96e4064",
}
}
(You can refer to the previously explained attributes for this list as they remain the same.)
Important Considerations Transfers in Senfenico are processed asynchronously. Therefore, we strongly advise you to implement webhooks to monitor the status of your transfers in real-time. This ensures that your application can respond to transfer updates promptly and accurately.
settlements
Settlements allow you to transfer funds from your account to a designated payment account. Use this section to manage settlements.
create settlement
This endpoint is used to create a settlement to a designated payment account.
Note: If a payment account is not created, the settlement creation request will fail because the system needs to know where to send the money. You can create a payment account from the administration interface in the business setting menu.
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\n \"amount\": \"1000\"\n}");
Request request = new Request.Builder()
.url("https://api.senfenico.com/v1/payment/settlements/")
.method("POST", body)
.addHeader("Content-Type", "application/json")
.addHeader("Accept", "application/json")
.addHeader("X-API-KEY", "sk_test_...")
.build();
Response response = client.newCall(request).execute();
const axios = require('axios');
let data = JSON.stringify({
"amount": "1000"
});
let config = {
method: 'post',
maxBodyLength: Infinity,
url: 'https://api.senfenico.com/v1/payment/settlements/',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-API-KEY': 'sk_test_...',
},
data : data
};
axios.request(config)
.then((response) => {
console.log(JSON.stringify(response.data));
})
.catch((error) => {
console.log(error);
});
Parameter | Type | Optional | Description |
---|---|---|---|
amount | int | No | Amount to settlement |
Sample Response
{
"status": true,
"message": "Payout created",
"data": {
"reference": "d1b19637-3cce-45e7-898b-265ddfbc981c",
"amount": 1000,
"settlement_fees": 2,
"currency": "XOF",
"status": "processing",
"live_mode": false,
"created_at": "2023-07-30T19:23:01.658300Z",
"updated_at": "2023-07-30T19:23:01.659013Z",
"account_type": "bank",
"iban": null,
"rib": "YOUR RIB",
"bank_name": null,
"bank_address": null,
"account_holder_name": null,
"account_holder_address": null,
"swift_bic_code": null,
"provider": null,
"phone": null,
"usdt_wallet_address": null
}
}
Response Element | Type | Possible Values | Description |
---|---|---|---|
reference | string | - | Reference of the settlement |
amount | int | - | Payout amount |
settlement_fees | int | - | Payout fees |
currency | string | "XOF" | Currency code |
status | string | "processing", "in_transit", "success", "cancelled" | Status of the settlement |
live_mode | bool | true, false | Indicates if it's a live transaction |
account_type | string | "bank", "mobile", "usdt" | Account type |
rib | string | - | RIB of the bank |
other banking details | null | - | Other banking information as null if not provided |
fetch settlement
This endpoint is used to retrieve the details of a specific settlement
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("text/plain");
RequestBody body = RequestBody.create(mediaType, "");
Request request = new Request.Builder()
.url("https://api.senfenico.com/v1/payment/settlements/{settlement_reference}")
.method("GET", body)
.addHeader("Accept", "application/json")
.addHeader("X-API-KEY", "sk_test_...")
.build();
Response response = client.newCall(request).execute();
const axios = require('axios');
let config = {
method: 'get',
maxBodyLength: Infinity,
url: 'https://api.senfenico.com/v1/payment/settlements/{settlement_reference}',
headers: {
'Accept': 'application/json',
'X-API-KEY': 'sk_test_...',
}
};
axios.request(config)
.then((response) => {
console.log(JSON.stringify(response.data));
})
.catch((error) => {
console.log(error);
});
Sample Response
{
"status": true,
"message": "Payout retrieved",
"data": {
"reference": "dc5a902d-a4eb-4025-b4f1-3c9b30468b64",
"amount": 1000,
"settlement_fees": 2,
"currency": "XOF",
"status": "processing",
"live_mode": false,
"created_at": "2023-07-30T20:42:31.147100Z",
"updated_at": "2023-07-30T20:42:31.147612Z",
"account_type": "bank",
"iban": null,
"rib": "YOUR RIB",
"bank_name": null,
"bank_address": null,
"account_holder_name": null,
"account_holder_address": null,
"swift_bic_code": null,
"provider": null,
"phone": null,
"usdt_wallet_address": null
}
}
(You can refer to the previously explained attributes for this list as they remain the same.)
list settlements
This endpoint is used to retrieve a list of all settlements.
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("text/plain");
RequestBody body = RequestBody.create(mediaType, "");
Request request = new Request.Builder()
.url("https://api.senfenico.com/v1/payment/settlements")
.method("GET", body)
.addHeader("Accept", "application/json")
.addHeader("X-API-KEY", "sk_test_...")
.build();
Response response = client.newCall(request).execute();
const axios = require('axios');
let config = {
method: 'get',
maxBodyLength: Infinity,
url: 'https://api.senfenico.com/v1/payment/settlements',
headers: {
'Accept': 'application/json',
'X-API-KEY': 'sk_test_...',
}
};
axios.request(config)
.then((response) => {
console.log(JSON.stringify(response.data));
})
.catch((error) => {
console.log(error);
});
Sample Response
{
"status": true,
"message": "settlement list retrieved",
"data": [
{
"reference": "c067a1ce-0112-44d0-bd1f-ed1c004ee98b",
"amount": 1000,
"settlement_fees": 2,
"currency": "XOF",
"status": "in_transit",
"live_mode": false,
"created_at": "2023-07-30T20:59:33.794435Z",
"updated_at": "2023-07-30T21:11:43.494159Z",
"account_type": "bank",
"iban": null,
"rib": "YOUR RIB",
"bank_name": null,
"bank_address": null,
"account_holder_name": null,
"account_holder_address": null,
"swift_bic_code": null,
"provider": null,
"phone": null,
"usdt_wallet_address": null
}
]
}
(You can refer to the previously explained attributes for this list as they remain the same.)
cancel settlement
This endpoint is used to cancel a specific settlement. A settlement can only be canceled if it's in 'processing' status.
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("text/plain");
RequestBody body = RequestBody.create(mediaType, "");
Request request = new Request.Builder()
.url("https://api.senfenico.com/v1/payment/settlements/{settlement_reference}/cancel/")
.method("GET", body)
.addHeader("Accept", "application/json")
.addHeader("X-API-KEY", "sk_test_...")
.build();
Response response = client.newCall(request).execute();
const axios = require('axios');
let config = {
method: 'get',
maxBodyLength: Infinity,
url: 'https://api.senfenico.com/v1/payment/settlements/{settlement_reference}/cancel/',
headers: {
'Accept': 'application/json',
'X-API-KEY': 'sk_test_...',
}
};
axios.request(config)
.then((response) => {
console.log(JSON.stringify(response.data));
})
.catch((error) => {
console.log(error);
});
Sample Response
{
"status": true,
"message": "Payout cancelled",
"data": {
"reference": "dc5a902d-a4eb-4025-b4f1-3c9b30468b64",
"amount": 1000,
"settlement_fees": 2,
"currency": "XOF",
"status": "cancelled",
"live_mode": false,
"created_at": "2023-07-30T20:42:31.147100Z",
"updated_at": "2023-07-30T20:57:18.909852Z",
"account_type": "bank",
"iban": null,
"rib": "YOUR RIB",
"bank_name": null,
"bank_address": null,
"account_holder_name": null,
"account_holder_address": null,
"swift_bic_code": null,
"provider": null,
"phone": null,
"usdt_wallet_address": null
}
}
(You can refer to the previously explained attributes for this cancel response as they remain the same.)
Testing your integration
Before going live with your integration, we recommend thoroughly testing it to ensure everything works as expected. To facilitate this, we've provided test numbers and balances that you can use to simulate transactions.
provider | phone | amount | OTP |
---|---|---|---|
orange_bf | 76000000 | 1000 | 123456 |
orange_bf | 77000000 | 1500 | 123456 |
orange_bf | 65000000 | 2000 | 123456 |
moov_bf | 70000000 | 1000 | 123456 |
moov_bf | 71000000 | 1500 | 123456 |
moov_bf | 72000000 | 2000 | 123456 |
coris_bf | 72000000 | 1000 | 123456 |
coris_bf | 75000000 | 1500 | 123456 |
coris_bf | 68000000 | 2000 | 123456 |
sank_bf | 72000000 | 1000 | 123456 |
sank_bf | 75000000 | 1500 | 123456 |
sank_bf | 68000000 | 2000 | 123456 |
For testing transfers, you can initiate a transfer to any valid Burkina Faso number. We've topped up your test transfer balances with 100,000 FCFA for each wallet, giving you ample room to test the feature.
We recommend using the same numbers listed above for payment testing to ensure consistency across your tests.
Once you've successfully tested your integration and are satisfied with the results, you can proceed to use your live API key for real transactions.
Error Codes
In this section, you will find a comprehensive list of error codes that our API might return. Each error code is accompanied by a brief description of the issue, both in English and French, to help you understand and resolve any issues that may arise during integration or usage.
Error Codes Overview When something goes wrong with an API request, you will receive an error response that includes the following fields:
- status: Always
false
for error responses. - message: A human-readable message explaining the response.
- error_code: A unique code identifying the error.
- description: A detailed description of the error.
- description_fr: La description détaillée de l'erreur en français.
Below is a list of possible error codes along with their descriptions:
Code | Description | Description(fr) |
---|---|---|
BATCH_SIZE_LIMIT_EXCEEDED | The request exceeds the maximum batch size of 100 items. Please reduce the number of items in the batch and try again. | La requĂȘte dĂ©passe la taille maximale autorisĂ©e de 100 Ă©lĂ©ments. Veuillez rĂ©duire le nombre d'Ă©lĂ©ments dans le lot et rĂ©essayer. |
RATE_LIMIT_EXCEEDED | You have exceeded the rate limit for API requests. Please wait before making further requests. | Vous avez dĂ©passĂ© la limite de taux pour les requĂȘtes API. Veuillez patienter avant de faire d'autres requĂȘtes. |
UNSUPPORTED_PROVIDER | UNSUPPORTED_PROVIDER | UNSUPPORTED_PROVIDER |
OTP_INVALID | The OTP is invalid. Please try again. | OTP invalide fournies. Veuillez vérifier et réessayer. |
OTP_EXPIRED | The OTP has expired. Please request a new one. | Le code OTP a expiré. Veuillez en demander un nouveau. |
OTP_STILL_VALID | The current OTP is still valid. Please use the existing OTP or wait until it expires to request a new one. | Le code OTP actuel est toujours valide. Veuillez utiliser le code OTP existant ou attendre qu'il expire pour en demander un nouveau. |
INSUFFICIENT_FUNDS | Account holder does not have enough funds to complete this transaction. | Le titulaire du compte n'a pas suffisamment de fonds pour effectuer cette transaction. |
INVALID_ACCOUNT | The phone number is not valid or is not associated with an account with operator. Please check the phone number and try again. | Le numéro de téléphone n'est pas valide ou n'est pas associé à un compte chez l'opérateur. Veuillez vérifier le numéro de téléphone et réessayer. |
NETWORK_ERROR | A network error occurred. Please try again later. | Une erreur de réseau est survenue. Veuillez réessayer plus tard. |
CHARGE_NOT_FOUND | Charge with the given reference not found. | La transaction avec la référence donnée est introuvable. |
INTERNAL_ERROR | An internal error occurred. Please try again later. | Une erreur interne est survenue. Veuillez réessayer plus tard. |
UNKNOWN_ERROR | An unknown error occurred. Please try again later. | Une erreur inconnue est survenue. Veuillez réessayer plus tard. |
TRANSACTION_NOT_FOUND | Transaction not found. | Transaction introuvable. |
UNAUTHORIZED_CHARGE_PROVIDER | You are not authorized to charge this provider. If you think this is an error, please contact Senfenico team. | Vous n'ĂȘtes pas autorisĂ© Ă collecter les paiement de cet opĂ©rateur. Si vous pensez qu'il s'agit d'une erreur, veuillez contacter l'Ă©quipe de Senfenico. |
EXTERNAL_API_ERROR | The OTP is invalid. Please try again. | An error occurred with an external API. Please try again later or contact support if the issue persists. Veuillez réessayer plus tard ou contacter le support si le problÚme persiste. |
INVALID_DATA | Invalid data provided. Please check the input and try again. | Données invalides fournies. Veuillez vérifier les données saisies et réessayer. |
Once you've successfully tested your integration, you can proceed to use your live API key for real transactions
Conclusion
This API documentation provides an overview of the available endpoints for managing checkouts and charges with Senfenico's payment API. By following the guidelines and examples provided, you can integrate Senfenico into your application and start accepting payments seamlessly.
If you have any questions or need further assistance, please contact Senfenico's support team at contact@senfenico.com
.