Documentation développeur
API REST et SDK JavaScript pour initier des paiements, déclencher des payouts et recevoir des webhooks signés HMAC-SHA256. Environnements sandbox et production séparés.
https://axiapayhub.comDémarrage rapide
API REST server-to-server, sans redirection. Votre back-end appellePOST /api/public/v1/payins, puis /confirm avec le numéro et l'opérateur saisis dans votre formulaire. Le client reste sur votre site.
curl -X POST https://axiapayhub.com/api/public/v1/payins \
-H "Authorization: Bearer axp_sk_test_..." \
-H "Content-Type: application/json" \
-d '{"amount": 15000, "currency": "XOF", "country": "SN", "reference": "cmd-4821"}'Authentification
Deux paires de clés — une par environnement — disponibles depuis /business → Clés API. La clé publique identifie votre compte côté client. La clé secrète autorise les appels serveur viaAuthorization: Bearer <clé secrète> et ne doit jamais être exposée dans un bundle front-end.
axp_pk_test_…/axp_sk_test_…— sandbox, sans flux réel.axp_pk_live_…/axp_sk_live_…— production, activée après validation KYB.
L'API est REST : utilisez curl, PHP, Python, Node ou tout client HTTP. Le SDK JavaScript est optionnel.
# Lister les opérateurs mobile money d'un pays (endpoint public, sans clé)
curl "https://axiapayhub.com/api/public/v1/operators?country=SN&service=payin"
# Réponse
# {
# "country": "SN",
# "service": "payin",
# "operators": [
# { "name": "Orange Money", "slug": "orange", "otp_required": true, "ussd_code": "#144#391*CODE#" },
# { "name": "Wave", "slug": "wave", "otp_required": false, "ussd_code": null }
# ]
# }Payin (server-to-server, sans redirection)
Disponible pour tous les comptes (particulier KYC ou entreprise KYB) une fois le pays activé. Vous hébergez votre propre formulaire de paiement sur votre domaine — aucune redirection vers AxiaPayHub. Flux en deux étapes :
POST /api/public/v1/payins puis /confirmpayin.paye signé HMAC-SHA256 sur votre endpointpayin. Consultez GET /operators?country=SN.- Créer une session —
POST /api/public/v1/payinsrenvoiesessionId, la grille des frais, les totaux (clientPays,youReceive,afterFees) et la liste des opérateurs disponibles pour le pays. - Confirmer —
POST /api/public/v1/payins/{sessionId}/confirmavecphone,operatoret (sirequiresOtp)otp.
fees.amount — frais AxiaPayHub calculés au taux du pays (fees.percent, 6% à 17% selon le pays)totals.clientPays — Total à payer par le client (montant + frais)totals.youReceive — Montant brut de la commandetotals.afterFees — Montant net crédité sur votre wallet AxiaPayHub/confirm.Statut final par webhook payin.paye / payin.echoue. Vous pouvez aussi relire le statut via GET /api/public/v1/payins/{id} ou GET /api/public/v1/payins/{id}/status avec sessionId, votre reference ou le transactionId SebPay.
# 1) Créer la session
curl -X POST https://axiapayhub.com/api/public/v1/payins \
-H "Authorization: Bearer axp_sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"amount": 15000, "currency": "XOF", "country": "SN",
"reference": "cmd-4821",
"description": "Formation TikTok Ads",
"metadata": { "userId": "user-123" },
"customer": { "email": "client@example.com", "phone": "+221701234567" },
"ttlSeconds": 3600
}'
# 2) Confirmer avec les infos saisies dans VOTRE formulaire
curl -X POST https://axiapayhub.com/api/public/v1/payins/payin_xxx/confirm \
-H "Authorization: Bearer axp_sk_live_..." \
-H "Content-Type: application/json" \
-d '{ "phone": "+221701234567", "operator": "orange" }'
# Étape OTP si status === "otp_required"
curl -X POST https://axiapayhub.com/api/public/v1/payins/payin_xxx/confirm \
-H "Authorization: Bearer axp_sk_live_..." \
-H "Content-Type: application/json" \
-d '{ "phone": "+221701234567", "operator": "orange", "otp": "482913" }'Payout instantané
Réservé aux comptes entreprise KYB validés. Le nom et prénom du bénéficiaire sont obligatoires (conformité). Retourne 202 Accepted — le statut final arrive par webhook.
const payout = await axia.payouts.send({
amount: 50000, currency: "XOF", country: "CI",
operator: "orange",
beneficiary: {
firstName: "Awa",
lastName: "Diallo",
phone: "+2250701234567",
},
description: "Commission vendeur",
});{
"id": "b0d1…",
"externalReference": "axp_out_9c1a…",
"transactionId": "sebpay_tx_xxx",
"status": "processing",
"amount": 50000, "currency": "XOF", "country": "CI",
"env": "production"
}GET status
Utilisez ces endpoints pour synchroniser votre back-end si un webhook est en retard ou si votre client revient sur une page de confirmation. La clé secrète limite la réponse à vos propres transactions et à son environnement (_test_ ou _live_).
# Payin : recherche par sessionId, reference marchand ou transactionId SebPay
curl https://axiapayhub.com/api/public/v1/payins/cmd-4821/status -H "Authorization: Bearer axp_sk_live_..."
# Payout : recherche par id, externalReference ou transactionId SebPay
curl https://axiapayhub.com/api/public/v1/payouts/axp_out_9c1a.../status -H "Authorization: Bearer axp_sk_live_..."{
"sessionId": "payin_9c1a...",
"status": "paye",
"amount": 15000,
"currency": "XOF",
"country": "SN",
"reference": "cmd-4821",
"operator": "orange",
"payer": { "phone": "221701234567", "msisdn": "221701234567", "operator": "orange", "country": "SN" },
"customer": { "email": "client@example.com", "phone": "+221701234567" },
"metadata": { "userId": "user-123" },
"transactionId": "sebpay_tx_xxx",
"fees": 900,
"netCredited": 14100,
"env": "production",
"paidAt": "2026-07-16T10:12:34.000Z"
}{
"id": "b0d1...",
"status": "reussi",
"amount": 50000,
"currency": "XOF",
"country": "CI",
"operator": "orange",
"fees": 3000,
"totalDebit": 53000,
"beneficiary": { "firstName": "Awa", "lastName": "Diallo", "phone": "+2250701234567" },
"externalReference": "axp_out_9c1a...",
"transactionId": "sebpay_tx_yyy",
"env": "production"
}Opérateurs par pays & OTP
Utilisez le slug exact ci-dessous dans le champ operator des appels/payins et /payouts. La liste est chargée en direct depuisGET /api/public/v1/operators?country=XX&service=payin|payout. Un opérateur marqué OTP requis demande un code de validation à saisir par le payeur.
| Opérateur | Slug | OTP | USSD |
|---|---|---|---|
| Aucun opérateur disponible pour ce pays / service. | |||
Lorsque otp = requis, l'appel POST /api/public/v1/payins/{sessionId}/confirmrenvoie status: "otp_required" et un ussdCode à afficher au client ; renvoyez ensuite la même requête avec le champ otp saisi par le payeur.
Webhooks HMAC
Chaque endpoint enregistré depuis /business → Webhooksreçoit son propre secret HMAC-SHA256. L'en-tête X-AxiaPay-Signature est calculé sur le corps brut de la requête. Vérifiez-le avant tout traitement.
{
"id": "evt_abc123",
"type": "payin.paye",
"created_at": "2026-07-16T10:12:34.000Z",
"env": "production",
"data": {
"sessionId": "payin_9c1a…",
"reference": "cmd-4821",
"amount": 15000, "currency": "XOF", "country": "SN",
"fees": 900, "netCredited": 14100,
"operator": "orange",
"transactionId": "sebpay_tx_xxx",
"payer": {
"phone": "221701234567",
"msisdn": "221701234567",
"operator": "orange",
"country": "SN"
},
"customer": {
"email": "client@example.com",
"phone": "+221701234567"
},
"metadata": { "userId": "user-123" }
}
}{
"id": "evt_abc124",
"type": "payin.echoue",
"created_at": "2026-07-16T10:12:34.000Z",
"env": "production",
"data": {
"sessionId": "payin_9c1a…",
"reference": "cmd-4821",
"reason": "rejected_by_provider",
"payer": { "phone": "221701234567", "operator": "orange", "country": "SN" },
"customer": { "email": "client@example.com", "phone": "+221701234567" },
"metadata": { "userId": "user-123" }
}
}{
"id": "evt_abc125",
"type": "payout.reussi",
"created_at": "2026-07-16T10:12:34.000Z",
"env": "production",
"data": {
"id": "b0d1…",
"externalReference": "axp_out_9c1a…",
"amount": 50000, "currency": "XOF", "country": "CI",
"operator": "orange",
"transactionId": "sebpay_tx_yyy"
}
}{
"id": "evt_abc126",
"type": "payout.echoue",
"created_at": "2026-07-16T10:12:34.000Z",
"env": "production",
"data": {
"id": "b0d1…",
"externalReference": "axp_out_9c1a…",
"reason": "rejected_by_provider"
}
}import crypto from "node:crypto";
import express from "express";
const app = express();
app.use(express.raw({ type: "application/json" }));
app.post("/webhooks/axia", (req, res) => {
const raw = req.body.toString("utf8");
const signature = req.header("x-axia-signature") ?? "";
const expected = crypto
.createHmac("sha256", process.env.AXIA_WEBHOOK_SECRET)
.update(raw)
.digest("hex");
const ok = crypto.timingSafeEqual(
Buffer.from(signature.replace(/^sha256=/, "")),
Buffer.from(expected),
);
if (!ok) return res.status(401).send("invalid signature");
const event = JSON.parse(raw);
switch (event.type) {
case "payin.paye": /* créditer la commande event.data.reference */ break;
case "payin.echoue": /* marquer la commande en échec */ break;
case "payout.reussi": /* marquer le payout comme envoyé */ break;
case "payout.echoue": /* replanifier ou notifier */ break;
}
res.status(200).send("ok");
});Spec OpenAPI 3.0
Spec générée depuis le code qui sert les routes — elle ne peut pas se désynchroniser. Importable dans Postman, Insomnia ou Swagger UI.
Endpoint : GET https://axiapayhub.com/api/public/v1/openapi.json
Sandbox et production
- Les clés
_test_et_live_pointent sur des flux séparés. - Les webhooks portent le champ
env(sandboxouproduction). - Les journaux /business → Logs filtrent par environnement.
Format d'erreur
Toutes les erreurs sont JSON :
{
"error": "Unknown API key",
"code": "invalid_api_key"
}Codes courants : missing_api_key, invalid_api_key, server_error,insufficient_balance (422 — solde du wallet insuffisant pour un payout),provider_validation (422 — opérateur / numéro / OTP rejeté par SebPay),provider_error (502 — erreur remontée par SebPay), plus les erreurs de validation Zod avec le détail du champ.