API Paiement Direct (Headless) — TROLOOPAY
Collectez des paiements sans redirection ni page de paiement : un seul appel POST initie le paiement directement sur le mobile du payeur (USSD push). Vérifiez le statut par polling ou recevez une notification IPN signée. Aucune intégration frontend requise.
Sommaire
Prérequis & sécurité
- HTTPS obligatoire : toutes les requêtes doivent être faites en HTTPS.
- Rate limiting : 10 requêtes par minute sur l'endpoint d'initiation, 30/min sur le statut.
- JSON uniquement : envoyez Content-Type: application/json.
- Credentials côté serveur : client_secret ne doit jamais transiter dans le code frontend (JS, mobile natif).
- ipn_url : doit être HTTPS et pointer vers un hôte public — les IP privées (10.x, 192.168.x, 127.x) sont bloquées (protection SSRF).
- Signature IPN : vérifiez systématiquement le header X-TrolooPay-Signature avec votre client_secret.
- reference_id unique : chaque transaction doit avoir un identifiant unique pour garantir l'idempotence.
Credentials : client_id & client_secret
Pour appeler l'API Direct, vous avez besoin d'un couple client_id / client_secret associé à votre compte marchand TROLOOPAY.
- Créez un compte marchand sur troloo.com/register et complétez votre profil (informations de l'entreprise, logo, devise).
- Accédez à la page Credentials depuis votre tableau de bord (Dashboard → Marchand → Applications API).
- Créez une application : un couple client_id / client_secret est généré automatiquement.
- Attendez l'approbation par l'administrateur TROLOOPAY (état Approved) avant de commencer à collecter.
- Stockez le client_secret en sécurité — une fois généré, il ne s'affiche plus en clair. Utilisez une variable d'environnement côté serveur.
| Champ | Type | Usage |
|---|---|---|
| client_id | Identifiant public | Identifie votre application marchande. Peut être visible. |
| client_secret | Secret confidentiel | Authentifie chaque requête. Ne jamais exposer côté client. |
1) Initier un paiement direct
Un seul appel POST déclenche le paiement directement sur le téléphone du payeur (USSD push). Aucune redirection ni page de paiement.
| Paramètre | Requis | Description |
|---|---|---|
| client_id | Oui | Identifiant de votre application marchande |
| client_secret | Oui | Secret confidentiel (jamais côté client) |
| amount | Oui | Montant à collecter (numérique, min 1). Entier recommandé pour le mobile money. |
| currency | Oui | Code devise : XOF, USD, EUR… |
| method | Oui | Méthode de paiement : MTNMOMO ou MOOVMONEY |
| payer_phone | Oui | Numéro du payeur au format E.164 (ex : 2290154000000) |
| reference_id | Oui | Votre identifiant unique de transaction (UUID, ID commande…). Garantit l'idempotence. |
| ipn_url | Non | URL HTTPS de callback (recommandé) — reçoit l'IPN signé dès confirmation |
| description | Non | Message affiché au payeur (ex : nom du produit) |
{
"code": 200,
"transaction_uuid": "550e8400-e29b-41d4-a716-446655440000",
"reference_id": "ORDER-12345",
"status": "PENDING",
"method": "MTNMOMO",
"message": "Payment request sent. Waiting for payer confirmation."
}
Conservez transaction_uuid pour interroger le statut ultérieurement. Le paiement est en attente de confirmation USSD par le payeur.
2) Calcul des frais
Les frais dépendent du moyen de paiement (merchant_payment_fee, ex : 1.9% pour MTN Bénin) et du paramètre support_fee de votre compte marchand, qui détermine qui supporte les frais gateway. Pour le mobile money, les montants sont systématiquement arrondis au plafond (ceil).
| support_fee | Montant débité au payeur | Frais portés par le marchand | Crédité au wallet marchand |
|---|---|---|---|
| Yes | amount (prix exact) | ceil(amount × fee%) | amount − ceil(fee) |
| No | amount + ceil(amount × fee%) | 0 (marchand reçoit le montant plein) | amount |
| Shared | amount + ceil(fee / 2) | ceil(fee / 2) | amount − ceil(fee/2) |
| amount | support_fee | gateway_fee | payeur débité | paid_amount (wallet) |
|---|---|---|---|---|
| 100 | Yes | 1.9 → ceil=2 | 100 | 98 |
| 105 | Yes | 1.995 → ceil=2 | 105 | 103 |
| 100 | No | 1.9 → ceil=2 | 102 | 100 |
| 100 | Shared | 1.9 → ½=0.95 → ceil=1 | 101 | 99 |
La plateforme perçoit toujours la totalité du gateway_fee, quelle que soit la valeur de support_fee.
3) Vérifier le statut (polling)
Si votre ipn_url n'est pas disponible ou pour confirmer côté serveur, interrogez le statut par polling avec client_id et client_secret en query string.
{
"code": 200,
"transaction_uuid": "550e8400-e29b-41d4-a716-446655440000",
"reference_id": "ORDER-12345",
"status": "SUCCESSFUL",
"method": "MTNMOMO",
"currency": "XOF",
"amount": 100,
"paid_amount": 98,
"created_at": "2026-05-28T18:23:24.000000Z"
}
IPN / Callback & signature
Si vous fournissez ipn_url, TROLOOPAY envoie un POST JSON signé sur cette URL dès que le paiement est confirmé ou échoué. C'est la méthode recommandée pour les intégrations serveur-à-serveur — plus fiable que le polling.
{
"method": "MTNMOMO",
"status": "SUCCESSFUL",
"transuuid": "550e8400-e29b-41d4-a716-446655440000",
"reference_id": "ORDER-12345",
"gateway_reference": "uuid-interne-gateway",
"currency": "XOF",
"amount": 100,
"paid_amount": 98
}
| Champ | Description |
|---|---|
| method | Méthode utilisée : MTNMOMO, MOOVMONEY |
| status | SUCCESSFUL ou FAILED |
| transuuid | UUID de la transaction (retourné à l'initiation) |
| reference_id | Votre identifiant de transaction transmis à l'initiation |
| amount | Montant de la transaction (tel qu'envoyé lors de l'initiation) |
| paid_amount | Montant net crédité au wallet marchand (après frais). 0 en cas d'échec. |
Chaque IPN est signé avec votre client_secret. La signature est transmise dans le header X-TrolooPay-Signature: sha256=<hash>.
<?php
$clientSecret = getenv('TROLOOPAY_CLIENT_SECRET');
$rawBody = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_TROLOOPAY_SIGNATURE'] ?? '';
// Extraire le hash (format "sha256=<hash>")
$receivedHash = str_starts_with($signature, 'sha256=') ? substr($signature, 7) : $signature;
$expectedHash = hash_hmac('sha256', json_encode(json_decode($rawBody, true)), $clientSecret);
if (!hash_equals($expectedHash, $receivedHash)) {
http_response_code(401);
exit('Invalid signature');
}
// Signature valide — traiter le paiement
$payload = json_decode($rawBody, true);
if ($payload['status'] === 'SUCCESSFUL') {
$uuid = $payload['transuuid'];
$refId = $payload['reference_id'];
$paidAmount = $payload['paid_amount'];
// Mettre à jour votre base de données...
}
http_response_code(200);
echo json_encode(['received' => true]);
Votre ipn_url doit répondre avec un code 2xx pour accuser réception. Ne jamais délivrer une commande sans avoir vérifié la signature.
Idempotence (reference_id)
Le champ reference_id garantit qu'une même transaction n'est jamais initiée deux fois. Si vous soumettez la même valeur avec le même client_id, l'API retourne un 409 Conflict avec l'UUID et le statut de la transaction existante — aucun nouveau débit n'est effectué.
{
"code": 409,
"message": "Conflict: reference_id already used for this application.",
"transaction_uuid": "550e8400-e29b-41d4-a716-446655440000",
"status": "SUCCESS"
}
Méthodes de paiement supportées
L'API Direct supporte les paiements mobile money avec déclenchement USSD push. Le pays est détecté automatiquement à partir du préfixe E.164 du numéro.
| Valeur method | Réseau | Pays détectés (préfixe) | Fee gateway |
|---|---|---|---|
| MTNMOMO | MTN Mobile Money | 229 (Bénin), 233 (Ghana), 256 (Uganda), 225 (CI), 237 (Cameroun), 250 (Rwanda)… | 1.9% |
| MOOVMONEY | Moov Money | 229 (Bénin) | 1.9% |
Erreurs & codes
| HTTP | Code | Message type | Cause fréquente |
|---|---|---|---|
| 400 | 400 | Validation error | Champ requis manquant, amount < 1, devise inactive, méthode inconnue |
| 400 | 400 | SSRF protection | ipn_url non HTTPS ou IP privée (10.x, 192.168.x, 127.x) |
| 401 | 401 | Invalid credentials | client_id ou client_secret incorrect |
| 403 | 403 | Account not approved | Compte marchand en attente, suspendu ou inactif |
| 409 | 409 | Conflict: reference_id | reference_id déjà utilisé pour cette application (idempotence) |
| 422 | 422 | Gateway rejected | Numéro non supporté par MTN/Moov, montant hors limites gateway |
| 429 | 429 | Too Many Requests | Rate limiting : 10 req/min sur /charge, 30 req/min sur /status |
| 502 | 502 | Gateway unavailable | La gateway mobile money est temporairement indisponible |
| 500 | 500 | Internal error | Erreur serveur interne — contacter le support TROLOOPAY |
Exemples d'intégration
Exemples prêts à copier/coller. Choisissez votre langage.
# 1) Initier le paiement
curl -s -X POST https://troloo.com/api/payment/direct \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"client_id": "VOTRE_CLIENT_ID",
"client_secret": "VOTRE_CLIENT_SECRET",
"amount": 5000,
"currency": "XOF",
"method": "MTNMOMO",
"payer_phone": "2290154000000",
"reference_id": "ORDER-12345",
"ipn_url": "https://votre-site.com/webhook/payment",
"description": "Commande #12345"
}'
# Réponse :
# { "code": 200, "transaction_uuid": "550e8400-...", "status": "PENDING", ... }
# 2) Vérifier le statut (polling)
curl -s "https://troloo.com/api/payment/direct/status/550e8400-e29b-41d4-a716-446655440000\
?client_id=VOTRE_CLIENT_ID&client_secret=VOTRE_CLIENT_SECRET"