Intégrez les paiements Mobile Money FrikPay dans votre application : créez votre application marchande, testez en sandbox sans aucune démarche préalable, puis passez en production une fois votre KYC approuvé.
Votre compte marchand doit d'abord être activé par l'équipe FrikPay — sans quoi aucun appel API n'aboutit, ni en sandbox ni en production. Une fois activé, le sandbox fonctionne immédiatement — pas besoin d'attendre la validation KYC. La production exige en revanche que le KYC de votre compte marchand soit approuvé. Le même code fonctionne dans les deux environnements : seule la paire de clés API change.
Cette API est en tout début de vie. Voici précisément ce qu'elle sait faire aujourd'hui — et ce qu'elle ne sait pas encore faire. Merci d'en tenir compte avant d'intégrer.
Seul le Franc CFA (XOF) est accepté. Toute autre devise est refusée par l'API.
Seul MTN Mobile Money (Bénin) est actif. Moov Money et Kkiapay ne sont pas activés sur cette plateforme.
country_code ne peut être que BJ pour le moment.
La livraison est signée et vérifiable, mais tentée une seule fois. Gardez le polling sur GET /api/v1/payments/{reference} comme filet de sécurité. Détails plus bas.
Aucun appel API (sandbox compris) n'aboutit tant que votre compte marchand n'a pas été activé côté FrikPay. Voir Démarrage rapide.
Ces limites reflètent l'état réel du système en production aujourd'hui,
pas une restriction arbitraire de cette documentation. Toute tentative
de paiement avec une devise, un réseau ou un pays non supporté sera
refusée par l'API (le plus souvent avec le message
Aucune route de paiement disponible. ou
Réseau introuvable pour le numéro fourni.).
Rendez-vous sur le portail marchand FrikPay et créez votre compte, puis une application (menu Applications). Une application représente un projet / intégration : vous pouvez en avoir plusieurs.
Cette étape ne nécessite aucun document KYC.
Un compte marchand qui vient d'être créé est au statut
pending, pas active — c'est
indépendant du KYC. Tant qu'un administrateur FrikPay n'a pas
activé votre compte, toute requête vers
/api/v1/payments, sandbox ou production, échoue
avec HTTP 401 :
{
"success": false,
"message": "Marchand inactif ou non autorisé.",
"errors": []
}
Si vous rencontrez ce message alors que vos clés API sont correctes, contactez l'équipe FrikPay pour faire activer votre compte — ce n'est pas une action que vous pouvez déclencher vous-même depuis le portail marchand.
Dans votre application, ouvrez Clés API et générez une paire de clés sandbox :
X-FRIKLABEL-PUBLIC-KEY: pk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
X-FRIKLABEL-SECRET-KEY: sk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
La secret_key ne s'affiche qu'une seule
fois à la génération — stockez-la immédiatement dans un
endroit sûr (gestionnaire de secrets, variables d'environnement).
Ne la commitez jamais dans votre code.
Contrairement à la production, générer et utiliser des clés sandbox ne nécessite pas que votre compte marchand ait terminé sa vérification KYC. Vous pouvez tester votre intégration dès aujourd'hui.
Appelez POST /api/v1/payments avec vos clés sandbox.
En sandbox, aucun opérateur réel n'est appelé :
le paiement est simulé à partir du numéro de téléphone envoyé
dans customer_phone.
curl -X POST https://api.frikpay.digital/api/v1/payments \
-H "Content-Type: application/json" \
-H "X-FRIKLABEL-PUBLIC-KEY: pk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-FRIKLABEL-SECRET-KEY: sk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-d '{
"merchant_reference": "CMD-TEST-0001",
"amount": 1500,
"currency": "XOF",
"customer_phone": "2290197000001"
}'
Le résultat d'un paiement sandbox (succès, échec, etc.) dépend
entièrement du numéro de téléphone envoyé dans
customer_phone — un numéro qui ne figure pas dans
cette liste renvoie l'erreur
Numéro de test sandbox non configuré.
| customer_phone | Résultat simulé |
|---|---|
2290197000001 | Succès |
2290197000002 | Échec (PAYMENT_FAILED) |
2290197000003 | En attente (pending) |
2290197000004 | Annulé par le client (CUSTOMER_CANCELLED) |
2290197000005 | Expiration (PROVIDER_TIMEOUT) |
Ces numéros sont valides pour country_code = BJ,
currency = XOF, payment_method =
mobile_money. Une série équivalente existe pour Moov
Bénin (2290195000001 à 2290195000005,
même correspondance succès/échec/attente/annulation/expiration),
mais Moov n'est pas encore activé au niveau des routes de
paiement — un appel avec un numéro Moov échoue aujourd'hui avec
Aucune route de paiement disponible., ce qui est
attendu tant que Moov n'est pas en production.
Réponse typique (simulation réussie) :
{
"success": true,
"message": "Paiement sandbox simulé.",
"data": {
"success": true,
"simulation_status": "success",
"reference_id": "FRIK-20260821143012-A1B2C3D4",
"merchant_reference": "CMD-TEST-0001",
"status": "success",
"network": "mtn_benin",
"amount": 1500,
"fee_bearer": "beneficiary",
"fee_amount": 22.5,
"customer_debited_amount": 1522.5,
"beneficiary_net_amount": 1500,
"currency": "XOF",
"sandbox": true,
"message": "Paiement sandbox simulé."
}
}
Conservez data.reference_id : c'est l'identifiant à
utiliser à l'étape suivante.
curl https://api.frikpay.digital/api/v1/payments/FRIK-20260821143012-A1B2C3D4 \
-H "X-FRIKLABEL-PUBLIC-KEY: pk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "X-FRIKLABEL-SECRET-KEY: sk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
En production, un paiement passe par
pending (demande envoyée au client) avant d'atteindre
un statut terminal (success ou failed)
une fois que le client a validé — ou non — l'opération sur son
téléphone. Interrogez cet endpoint (toutes les 3 à 5 secondes
pendant 1 à 2 minutes, par exemple) jusqu'à statut terminal ; ne
vous fiez pas au seul appel de création pour connaître l'issue
réelle du paiement.
Une fois le KYC de votre compte marchand approuvé,
générez une paire de clés
production
(pk_live_... / sk_live_...) depuis le
portail marchand, dans la même section Clés API.
Votre intégration n'a rien d'autre à changer :
même URL (https://api.frikpay.digital), même format
de requête/réponse. Seule la paire de clés utilisée détermine
l'environnement réellement exécuté.
Tant que kyc_status de votre compte marchand n'est
pas approved, tout appel authentifié avec une clé
de production échoue avec HTTP 401 et le
message KYC marchand non validé pour la production.
Chaque requête doit inclure deux en-têtes HTTP. Il n'y a pas d'OAuth, pas de jeton porteur (bearer token) : uniquement une paire de clés.
| En-tête | Obligatoire | Description |
|---|---|---|
X-FRIKLABEL-PUBLIC-KEY | Oui | Clé publique de l'application (pk_test_... ou pk_live_...) |
X-FRIKLABEL-SECRET-KEY | Oui | Clé secrète associée (sk_test_... ou sk_live_...) |
X-FRIKLABEL-ENVIRONMENT | Non | sandbox ou production — cas particuliers uniquement ; l'environnement est normalement déjà déterminé par le type de clé utilisée |
Les clés API se génèrent exclusivement depuis le portail marchand (Applications → Clés API) ; il n'existe pas d'endpoint public pour créer des clés par API — cette action nécessite une session authentifiée sur le portail.
L'API répond toujours en JSON avec success: false et un
message en français lisible. Il n'y a
pas de champ code machine-lisible
aujourd'hui — distinguez les cas via le code HTTP, et au besoin le
texte du message. Voici les messages observés dans le code, avec leur
code HTTP réel :
| HTTP | Message | Cause |
|---|---|---|
| 401 | Clés API manquantes. | En-têtes X-FRIKLABEL-PUBLIC-KEY / X-FRIKLABEL-SECRET-KEY absents ou vides |
| 401 | Environnement API invalide. | X-FRIKLABEL-ENVIRONMENT fourni avec une valeur autre que sandbox/production |
| 401 | Clé API invalide. | Clé publique inconnue |
| 401 | Clé API inactive ou révoquée. | Clé désactivée côté portail |
| 401 | Clé API expirée. | Date d'expiration dépassée |
| 401 | Clé API non autorisée pour cet environnement. | X-FRIKLABEL-ENVIRONMENT demandé ne correspond pas à l'environnement réel de la clé |
| 401 | Secret API invalide. | Clé secrète incorrecte pour cette clé publique |
| 401 | Application marchand introuvable. | Application associée à la clé introuvable |
| 401 | Application marchande inactive ou non autorisée. | Application désactivée |
| 401 | Marchand inactif ou non autorisé. | Compte marchand pas encore activé par FrikPay (cas le plus fréquent pour un compte tout juste créé), ou désactivé |
| 401 | KYC marchand non validé pour la production. | Clé de production utilisée alors que kyc_status ≠ approved |
POST /api/v1/payments| HTTP | Message | Cause |
|---|---|---|
| 400 | Payload JSON invalide. | Corps de requête absent ou JSON mal formé |
| 405 | Méthode non autorisée. | Méthode HTTP autre que POST |
| 422 | {champ} est obligatoire. | merchant_reference, amount, currency ou customer_phone manquant/vide |
| 422 | amount doit être supérieur à zéro. | amount ≤ 0 ou non numérique |
| 422 | Wallet marchand bénéficiaire introuvable ou inactif. | Compte marchand mal configuré côté FrikPay (rare — contactez le support) |
| 422 | Numéro de test sandbox non configuré. | Sandbox uniquement : customer_phone ne correspond à aucun scénario de test actif |
| 422 | Réseau introuvable pour le numéro fourni. | Le numéro ne correspond à aucun préfixe MTN Bénin reconnu |
| 422 | Aucune route de paiement disponible. | Combinaison pays/devise/réseau/méthode non supportée (ex. autre chose que XOF + BJ + MTN + mobile_money) |
| 422 | Tarification introuvable pour cette collection. | Aucune règle tarifaire configurée pour ce montant/cette route |
200 (success:false) | Requête déjà traitée. | Rejeu d'une merchant_reference déjà utilisée — la transaction d'origine est renvoyée dans errors, voir la référence API |
| 502 | Tous les prestataires de paiement ont échoué. | Production uniquement : le prestataire (MTN) a rejeté la tentative |
GET /api/v1/payments/{reference}| HTTP | Message | Cause |
|---|---|---|
| 404 | Transaction introuvable. | Référence inexistante, ou appartenant à un autre marchand |
| 405 | Méthode non autorisée. | Méthode HTTP autre que GET |
FrikPay peut notifier votre serveur par HTTP à chaque changement de statut terminal d'un paiement, en plus du polling décrit dans le démarrage rapide. C'est une option par application : tant qu'aucune URL de webhook n'est configurée, aucune requête n'est envoyée.
Renseignez l'URL de réception dans les paramètres de votre application,
depuis le portail marchand.
Cette URL doit être en HTTPS ; une URL non valide ou en
http:// est refusée à l'enregistrement.
| Type | Déclenché quand |
|---|---|
payment.success | Le paiement a été confirmé |
payment.failed | Le paiement a échoué |
payment.cancelled | Le paiement a été annulé par le client |
payment.refunded | Le paiement a été remboursé |
payment.reversed | Le paiement a été contre-passé |
Corps de la requête (Content-Type: application/json) :
{
"id": "evt_3f1a7c2e9b6d4a0f8c1e2b5a9d6f7c3e",
"type": "payment.success",
"created_at": "2026-08-31T12:42:27+00:00",
"data": {
"reference_id": "FRIK-20260831124227-7A8B2588",
"merchant_reference": "CMD-TEST-0001",
"status": "success",
"amount": 1500,
"currency": "XOF",
"payment_method": "mobile_money",
"network": "mtn_benin"
}
}
| En-tête | Description |
|---|---|
X-FrikPay-Event-Id | Identifiant unique de cette notification (evt_...) |
X-FrikPay-Timestamp | Horodatage Unix (secondes) au moment de l'envoi |
X-FrikPay-Signature | Signature au format v1=<hex>, voir ci-dessous |
Chaque webhook est signé en HMAC-SHA256 avec le
webhook_secret de l'application concernée. Ce secret vous
est affiché une seule fois, au même moment que la
secret_key — à la génération ou à la rotation de vos clés
API depuis le portail marchand. Conservez-le comme un secret : lui
seul permet de vérifier qu'une requête vient bien de FrikPay.
La valeur à signer est la concaténation de l'horodatage, d'un point, et du corps brut de la requête (les octets reçus tels quels — ne recalculez pas la signature sur une version reformatée ou ré-encodée du JSON, l'ordre des clés ou les espaces changeraient la valeur) :
$signature_attendue = hash_hmac(
'sha256',
$_SERVER['HTTP_X_FRIKPAY_TIMESTAMP'] . '.' . file_get_contents('php://input'),
$webhook_secret
);
$recue = explode('=', $_SERVER['HTTP_X_FRIKPAY_SIGNATURE'], 2)[1] ?? '';
if (!hash_equals($signature_attendue, $recue)) {
http_response_code(401);
exit;
}
En plus de la vérification de signature, rejetez les requêtes dont
X-FrikPay-Timestamp est trop ancien (5 minutes, par
exemple). Traitez aussi chaque id d'évènement de façon
idempotente : le recevoir deux fois ne doit pas recréditer votre
système deux fois.
Si votre serveur ne répond pas avec un code 2xx (ou ne
répond pas du tout), la notification n'est pas
retentée automatiquement pour l'instant. Continuez à traiter
le polling sur
GET /api/v1/payments/{reference}
comme la source de vérité pour réconcilier vos paiements ; le
webhook est un gain de réactivité, pas un remplacement du polling.