const session = await afriex.checkout.createSession({
amount: 500000,
currency: "NGN",
merchantReference: "order-2026-05-12-001",
redirectUrl: "https://yourapp.com/checkout/return",
customer: {
name: "John Doe",
email: "john@example.com",
phone: "+2348192837465",
countryCode: "NG",
},
// Send the same list everywhere; unsupported ones are dropped
channels: ["VIRTUAL_BANK_ACCOUNT", "MOBILE_MONEY", "CARD"],
metadata: { orderId: "order-456", cartId: "cart-123" },
});
// Keep session.checkoutId to match the CHECKOUT_SESSION.CREATED webhook
console.log(session.checkoutId);
// Redirect customer to session.checkoutUrl
console.log(session.checkoutUrl);
// What the payer will be shown, e.g. ["VIRTUAL_BANK_ACCOUNT", "CARD"]
console.log(session.channels);
curl --request POST \
--url https://sandbox.api.afriex.com/api/v1/checkout-session \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"amount": 500000,
"currency": "NGN",
"merchantReference": "order-2026-05-12-001",
"redirectUrl": "https://merchant.example.com/checkout/return",
"customer": {
"name": "John Doe",
"email": "john.doe@example.com",
"phone": "+2348192837465",
"countryCode": "NG"
},
"channels": [
"VIRTUAL_BANK_ACCOUNT",
"MOBILE_MONEY",
"CARD"
]
}
'import requests
url = "https://sandbox.api.afriex.com/api/v1/checkout-session"
payload = {
"amount": 500000,
"currency": "NGN",
"merchantReference": "order-2026-05-12-001",
"redirectUrl": "https://merchant.example.com/checkout/return",
"customer": {
"name": "John Doe",
"email": "john.doe@example.com",
"phone": "+2348192837465",
"countryCode": "NG"
},
"channels": ["VIRTUAL_BANK_ACCOUNT", "MOBILE_MONEY", "CARD"]
}
headers = {
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
amount: 500000,
currency: 'NGN',
merchantReference: 'order-2026-05-12-001',
redirectUrl: 'https://merchant.example.com/checkout/return',
customer: {
name: 'John Doe',
email: 'john.doe@example.com',
phone: '+2348192837465',
countryCode: 'NG'
},
channels: ['VIRTUAL_BANK_ACCOUNT', 'MOBILE_MONEY', 'CARD']
})
};
fetch('https://sandbox.api.afriex.com/api/v1/checkout-session', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://sandbox.api.afriex.com/api/v1/checkout-session",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'amount' => 500000,
'currency' => 'NGN',
'merchantReference' => 'order-2026-05-12-001',
'redirectUrl' => 'https://merchant.example.com/checkout/return',
'customer' => [
'name' => 'John Doe',
'email' => 'john.doe@example.com',
'phone' => '+2348192837465',
'countryCode' => 'NG'
],
'channels' => [
'VIRTUAL_BANK_ACCOUNT',
'MOBILE_MONEY',
'CARD'
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://sandbox.api.afriex.com/api/v1/checkout-session"
payload := strings.NewReader("{\n \"amount\": 500000,\n \"currency\": \"NGN\",\n \"merchantReference\": \"order-2026-05-12-001\",\n \"redirectUrl\": \"https://merchant.example.com/checkout/return\",\n \"customer\": {\n \"name\": \"John Doe\",\n \"email\": \"john.doe@example.com\",\n \"phone\": \"+2348192837465\",\n \"countryCode\": \"NG\"\n },\n \"channels\": [\n \"VIRTUAL_BANK_ACCOUNT\",\n \"MOBILE_MONEY\",\n \"CARD\"\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://sandbox.api.afriex.com/api/v1/checkout-session")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"amount\": 500000,\n \"currency\": \"NGN\",\n \"merchantReference\": \"order-2026-05-12-001\",\n \"redirectUrl\": \"https://merchant.example.com/checkout/return\",\n \"customer\": {\n \"name\": \"John Doe\",\n \"email\": \"john.doe@example.com\",\n \"phone\": \"+2348192837465\",\n \"countryCode\": \"NG\"\n },\n \"channels\": [\n \"VIRTUAL_BANK_ACCOUNT\",\n \"MOBILE_MONEY\",\n \"CARD\"\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://sandbox.api.afriex.com/api/v1/checkout-session")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"amount\": 500000,\n \"currency\": \"NGN\",\n \"merchantReference\": \"order-2026-05-12-001\",\n \"redirectUrl\": \"https://merchant.example.com/checkout/return\",\n \"customer\": {\n \"name\": \"John Doe\",\n \"email\": \"john.doe@example.com\",\n \"phone\": \"+2348192837465\",\n \"countryCode\": \"NG\"\n },\n \"channels\": [\n \"VIRTUAL_BANK_ACCOUNT\",\n \"MOBILE_MONEY\",\n \"CARD\"\n ]\n}"
response = http.request(request)
puts response.read_bodyCreate Checkout Session
Creates a hosted checkout session for a customer and returns a checkoutUrl that the customer should be redirected to in order to complete payment. The session captures the merchant intent (amount, currency, merchant reference, customer details, and allowed payment channels) and is identified end-to-end by the merchant-supplied merchantReference.
Available in sandbox and production. Create a session with a sandbox API key to try the full flow in test mode, and with a production API key to take real payments.
const session = await afriex.checkout.createSession({
amount: 500000,
currency: "NGN",
merchantReference: "order-2026-05-12-001",
redirectUrl: "https://yourapp.com/checkout/return",
customer: {
name: "John Doe",
email: "john@example.com",
phone: "+2348192837465",
countryCode: "NG",
},
// Send the same list everywhere; unsupported ones are dropped
channels: ["VIRTUAL_BANK_ACCOUNT", "MOBILE_MONEY", "CARD"],
metadata: { orderId: "order-456", cartId: "cart-123" },
});
// Keep session.checkoutId to match the CHECKOUT_SESSION.CREATED webhook
console.log(session.checkoutId);
// Redirect customer to session.checkoutUrl
console.log(session.checkoutUrl);
// What the payer will be shown, e.g. ["VIRTUAL_BANK_ACCOUNT", "CARD"]
console.log(session.channels);
curl --request POST \
--url https://sandbox.api.afriex.com/api/v1/checkout-session \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"amount": 500000,
"currency": "NGN",
"merchantReference": "order-2026-05-12-001",
"redirectUrl": "https://merchant.example.com/checkout/return",
"customer": {
"name": "John Doe",
"email": "john.doe@example.com",
"phone": "+2348192837465",
"countryCode": "NG"
},
"channels": [
"VIRTUAL_BANK_ACCOUNT",
"MOBILE_MONEY",
"CARD"
]
}
'import requests
url = "https://sandbox.api.afriex.com/api/v1/checkout-session"
payload = {
"amount": 500000,
"currency": "NGN",
"merchantReference": "order-2026-05-12-001",
"redirectUrl": "https://merchant.example.com/checkout/return",
"customer": {
"name": "John Doe",
"email": "john.doe@example.com",
"phone": "+2348192837465",
"countryCode": "NG"
},
"channels": ["VIRTUAL_BANK_ACCOUNT", "MOBILE_MONEY", "CARD"]
}
headers = {
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
amount: 500000,
currency: 'NGN',
merchantReference: 'order-2026-05-12-001',
redirectUrl: 'https://merchant.example.com/checkout/return',
customer: {
name: 'John Doe',
email: 'john.doe@example.com',
phone: '+2348192837465',
countryCode: 'NG'
},
channels: ['VIRTUAL_BANK_ACCOUNT', 'MOBILE_MONEY', 'CARD']
})
};
fetch('https://sandbox.api.afriex.com/api/v1/checkout-session', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://sandbox.api.afriex.com/api/v1/checkout-session",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'amount' => 500000,
'currency' => 'NGN',
'merchantReference' => 'order-2026-05-12-001',
'redirectUrl' => 'https://merchant.example.com/checkout/return',
'customer' => [
'name' => 'John Doe',
'email' => 'john.doe@example.com',
'phone' => '+2348192837465',
'countryCode' => 'NG'
],
'channels' => [
'VIRTUAL_BANK_ACCOUNT',
'MOBILE_MONEY',
'CARD'
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://sandbox.api.afriex.com/api/v1/checkout-session"
payload := strings.NewReader("{\n \"amount\": 500000,\n \"currency\": \"NGN\",\n \"merchantReference\": \"order-2026-05-12-001\",\n \"redirectUrl\": \"https://merchant.example.com/checkout/return\",\n \"customer\": {\n \"name\": \"John Doe\",\n \"email\": \"john.doe@example.com\",\n \"phone\": \"+2348192837465\",\n \"countryCode\": \"NG\"\n },\n \"channels\": [\n \"VIRTUAL_BANK_ACCOUNT\",\n \"MOBILE_MONEY\",\n \"CARD\"\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://sandbox.api.afriex.com/api/v1/checkout-session")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"amount\": 500000,\n \"currency\": \"NGN\",\n \"merchantReference\": \"order-2026-05-12-001\",\n \"redirectUrl\": \"https://merchant.example.com/checkout/return\",\n \"customer\": {\n \"name\": \"John Doe\",\n \"email\": \"john.doe@example.com\",\n \"phone\": \"+2348192837465\",\n \"countryCode\": \"NG\"\n },\n \"channels\": [\n \"VIRTUAL_BANK_ACCOUNT\",\n \"MOBILE_MONEY\",\n \"CARD\"\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://sandbox.api.afriex.com/api/v1/checkout-session")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"amount\": 500000,\n \"currency\": \"NGN\",\n \"merchantReference\": \"order-2026-05-12-001\",\n \"redirectUrl\": \"https://merchant.example.com/checkout/return\",\n \"customer\": {\n \"name\": \"John Doe\",\n \"email\": \"john.doe@example.com\",\n \"phone\": \"+2348192837465\",\n \"countryCode\": \"NG\"\n },\n \"channels\": [\n \"VIRTUAL_BANK_ACCOUNT\",\n \"MOBILE_MONEY\",\n \"CARD\"\n ]\n}"
response = http.request(request)
puts response.read_body0001 fails, 0002 triggers the mobile-money OTP page, 0003 stays pending until you call /simulate); add SIMULATE_INSTANT anywhere in merchantReference to settle in about 30 seconds. Sandbox sessions also enable in-page test-mode helpers (test-number hints, OTP hint, simulate-transfer button, pre-filled test card) so payers can drive outcomes without leaving the page. Full reference: Testing in Sandbox.How it works
- Call this endpoint with the amount you want to collect, the customer’s details, your
merchantReference, and aredirectUrlthat starts withhttps://. - Afriex returns a
checkoutUrland acheckoutId. - Redirect the customer to that URL.
- When the payment succeeds or fails, Afriex redirects the customer back to your
redirectUrlwithstatus(successorfailed),session_id, andmerchant_referenceadded as query parameters. - The payment itself is reported by Transaction Events on the deposit the session creates.
CHECKOUT_SESSION.CREATED, when you create the session. It says nothing about payment. Don’t treat the redirect as proof of payment either: confirm it from the transaction.
Identifying the session
ThemerchantReference you supply is the end-to-end identifier, and it must be unique. You can’t reuse it while an earlier checkout with that reference is still open, or after a transaction has used it. Both return 409. The checkoutId in the response is the last part of the checkoutUrl, and it is the sessionId in the CHECKOUT_SESSION.CREATED webhook, so use it to match that webhook to the checkout you created. There is no endpoint to fetch a session. To find the deposit a session created, call List Transactions with ?reference= set to your merchantReference.
Amounts are in minor units
amount is denominated in the smallest unit of the currency. For example, kobo for NGN or cents for USD. To charge ₦5,000.00, pass 500000. Minimum value is 100 (one major unit).
Choosing payment channels
channels is required and lists the deposit rails you are willing to offer the customer. Supported values are VIRTUAL_BANK_ACCOUNT, MOBILE_MONEY, and CARD; pass at least one.
CARD is available here but not on Create Payment Method; to accept card payments, always route them through a checkout session.currency cannot collect on are dropped then, so you can send the same list on every corridor and let Afriex offer the right subset. For example, ["VIRTUAL_BANK_ACCOUNT", "MOBILE_MONEY", "CARD"] becomes mobile money for a KES checkout and cards for a USD one, all from one integration.
The response returns channels as the list you sent. Creating a checkout does not narrow it.
channels suit the currency. Test each currency you support in sandbox.Authorizations
Static business API key issued from the dashboard. A business can provision multiple API keys, each scoped to a configurable set of permissions (e.g. read transactions, create deposits, etc). Permissions are chosen per key at creation time in the dashboard and may be revoked by deleting the key. Manage your keys and their permissions under Developer → API keys in the dashboard.
A request whose key does not carry the permission the target
endpoint requires is rejected with 401 Unauthorized, the same
response an unrecognised, malformed, revoked or disabled key
returns. The API does not distinguish the two cases on the wire.
See the Permissions reference for the permission each endpoint requires.
Headers
API version in ISO 8601 format. The only supported version is 2026-05-18, which is also the default when the header is omitted. Any other value is rejected with a 400 Bad Request.
Body
The transaction amount in minor currency units (e.g. kobo for NGN, cents for USD). Minimum 100 (equivalent to 1 major currency unit).
100 <= x <= 9007199254740991500000
3-letter ISO 4217 currency code (e.g. NGN, GHS). Must be one of the currencies enabled for checkout sessions: NGN, GHS, KES, UGX, TZS, ETB, XAF, XOF, SLE, USD.
"NGN"
Unique merchant-supplied reference for this session. Used end-to-end to look up the session and any resulting transaction.
1"order-2026-05-12-001"
HTTPS URL the customer is redirected to after the hosted checkout flow completes.
^https://"https://merchant.example.com/checkout/return"
Show child attributes
Show child attributes
The payment channels you are willing to offer the customer, at least one. This is a cap, not an exact list: you do not need to vary it per country. The channels the customer is shown are decided when they open the checkout page, once their currency is known: channels it cannot collect on are dropped then, so the same list works for every country (send ["VIRTUAL_BANK_ACCOUNT", "MOBILE_MONEY", "CARD"] and a KES checkout offers mobile money while a USD one offers cards).
1VIRTUAL_BANK_ACCOUNT, MOBILE_MONEY, CARD Optional flat key/value metadata to attach to the session. Both keys and values must be strings. At most 50 entries; keys up to 128 characters, values up to 1024 characters.
Show child attributes
Show child attributes
Response
Checkout session created successfully.
Show child attributes
Show child attributes
