Documentation de l'API de paiement FrikPay

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é.

🚀 L'essentiel en une phrase

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.

!Limites actuelles

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.

💱 Devise : XOF uniquement

Seul le Franc CFA (XOF) est accepté. Toute autre devise est refusée par l'API.

📡 Réseau : MTN Bénin uniquement

Seul MTN Mobile Money (Bénin) est actif. Moov Money et Kkiapay ne sont pas activés sur cette plateforme.

🌍 Pays : Bénin (BJ) uniquement

country_code ne peut être que BJ pour le moment.

🔔 Webhooks : pas de nouvelle tentative automatique

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.

🔒 Compte à activer par FrikPay

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.).

→Démarrage rapide

  1. Créez votre compte marchand et une application

    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.

    🔒 Votre compte doit être activé avant tout appel API

    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.

  2. Générez vos clés API sandbox

    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.

    ✅ Aucun KYC requis pour le sandbox

    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.

  3. Effectuez votre premier paiement de test

    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"
          }'

    ⚠️ Numéros de test sandbox

    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_phoneRésultat simulé
    2290197000001Succès
    2290197000002Échec (PAYMENT_FAILED)
    2290197000003En attente (pending)
    2290197000004Annulé par le client (CUSTOMER_CANCELLED)
    2290197000005Expiration (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.

  4. Vérifiez le statut du paiement

    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.

  5. Passez en production

    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é.

    🔒 KYC non approuvé = production refusée

    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.

🔑Authentification

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êteObligatoireDescription
X-FRIKLABEL-PUBLIC-KEYOuiClé publique de l'application (pk_test_... ou pk_live_...)
X-FRIKLABEL-SECRET-KEYOuiClé secrète associée (sk_test_... ou sk_live_...)
X-FRIKLABEL-ENVIRONMENTNonsandbox 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.

✕Erreurs

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 :

Authentification (les deux endpoints)

HTTPMessageCause
401Clés API manquantes.En-têtes X-FRIKLABEL-PUBLIC-KEY / X-FRIKLABEL-SECRET-KEY absents ou vides
401Environnement API invalide.X-FRIKLABEL-ENVIRONMENT fourni avec une valeur autre que sandbox/production
401Clé API invalide.Clé publique inconnue
401Clé API inactive ou révoquée.Clé désactivée côté portail
401Clé API expirée.Date d'expiration dépassée
401Clé API non autorisée pour cet environnement.X-FRIKLABEL-ENVIRONMENT demandé ne correspond pas à l'environnement réel de la clé
401Secret API invalide.Clé secrète incorrecte pour cette clé publique
401Application marchand introuvable.Application associée à la clé introuvable
401Application marchande inactive ou non autorisée.Application désactivée
401Marchand 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é
401KYC marchand non validé pour la production.Clé de production utilisée alors que kyc_status ≠ approved

POST /api/v1/payments

HTTPMessageCause
400Payload JSON invalide.Corps de requête absent ou JSON mal formé
405Méthode non autorisée.Méthode HTTP autre que POST
422{champ} est obligatoire.merchant_reference, amount, currency ou customer_phone manquant/vide
422amount doit être supérieur à zéro.amount ≤ 0 ou non numérique
422Wallet marchand bénéficiaire introuvable ou inactif.Compte marchand mal configuré côté FrikPay (rare — contactez le support)
422Numéro de test sandbox non configuré.Sandbox uniquement : customer_phone ne correspond à aucun scénario de test actif
422Réseau introuvable pour le numéro fourni.Le numéro ne correspond à aucun préfixe MTN Bénin reconnu
422Aucune route de paiement disponible.Combinaison pays/devise/réseau/méthode non supportée (ex. autre chose que XOF + BJ + MTN + mobile_money)
422Tarification 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
502Tous les prestataires de paiement ont échoué.Production uniquement : le prestataire (MTN) a rejeté la tentative

GET /api/v1/payments/{reference}

HTTPMessageCause
404Transaction introuvable.Référence inexistante, ou appartenant à un autre marchand
405Méthode non autorisée.Méthode HTTP autre que GET

🔔Webhooks

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.

Configuration

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.

Évènements envoyés

TypeDéclenché quand
payment.successLe paiement a été confirmé
payment.failedLe paiement a échoué
payment.cancelledLe paiement a été annulé par le client
payment.refundedLe paiement a été remboursé
payment.reversedLe 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êtes de la requête

En-têteDescription
X-FrikPay-Event-IdIdentifiant unique de cette notification (evt_...)
X-FrikPay-TimestampHorodatage Unix (secondes) au moment de l'envoi
X-FrikPay-SignatureSignature au format v1=<hex>, voir ci-dessous

Vérifier la signature

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;
}

🛡️ Se protéger du rejeu

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.

⚠️ Une seule tentative de livraison aujourd'hui

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.