API Withdrawal / Disbursement — TROLOOPAY
Cette page explique comment intégrer l’API de retrait (disbursement) dans une application externe : génération de token, création de retrait, headers requis, formats, références, erreurs et exemples de code.
Sommaire
Prérequis & sécurité
- HTTPS obligatoire : toutes les requêtes doivent être faites en HTTPS.
- Rate limiting : la plateforme limite les appels pour éviter l’abus.
- Token 1h : un token expiré renvoie une erreur (renouveler via génération token).
- Operation-Type : doit correspondre au type autorisé par le token.
- Callback URL : doit être HTTPS et ne doit pas pointer vers des IPs privées (protection SSRF).
Création des clés API
Les clés API sont générées depuis votre espace TROLOOPAY (page credentials). Elles sont ensuite approuvées par l’administrateur avant utilisation.
- Créer un couple wapi_key / wapi_secret dans l’espace développeur.
- Définir un mot de passe d’opérations (utilisé pour chiffrer le secret côté client).
- Attendre l’état approved.
- Optionnel : définir une whitelist IP (recommandé en production).
1) Générer un token
- Authorization: Basic base64(wapi_key : AES256(wapi_secret))
- Ehopay-Withdrawal-Type: USER_OPERATION | MERCHANT_OPERATION | REQUEST_TO_PAY
function encryptAes256($data, $key) {
$ivLength = openssl_cipher_iv_length('aes-256-cbc');
$iv = openssl_random_pseudo_bytes($ivLength);
$encrypted = openssl_encrypt($data, 'aes-256-cbc', $key, 0, $iv);
return base64_encode($encrypted . '::' . $iv);
}
function createAuthHeader($api_key, $api_secret, $password) {
$encrypted_api_secret = encryptAes256($api_secret, $password);
$authString = $api_key . ':' . $encrypted_api_secret;
return 'Basic ' . base64_encode($authString);
}
$api_key = 'VOTRE_WAPI_KEY';
$api_secret = 'VOTRE_WAPI_SECRET';
$password = 'VOTRE_MOT_DE_PASSE_OPERATIONS';
$authHeader = createAuthHeader($api_key, $api_secret, $password);
{
"status": 200,
"access_token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"expires_in": 3600,
"message": "Token Generated Successfully"
}
2) Initier un retrait
- Authorization: Bearer <access_token>
- Operation-Type: USER_OPERATION | MERCHANT_OPERATION | REQUEST_TO_PAY
- Reference-Id: UUID v4 (unique)
- Custom-Callback-url: https://... (optionnel mais recommandé)
{
"user_id": "229xxxxxxxx" ,
"currency": "XOF",
"payment_method": "MTN-MOMO-BENIN",
"payment_method_reference": {
"momoformattedPhone": "229xxxxxxxx"
},
"amount": "100",
"external_reference": "optional-string"
}
Exemple Bénin : 229 + 0196451040 → 2290196451040
{
"StatusCode": 202,
"status": "SUCCESSFUL",
"transaction_id": 2054,
"withdrawal_id": 24,
"gateway_reference": "e23fd847-fb27-4cdb-96f4-ebd228f8708b",
"message": "transaction successful"
}
- Coinpayments
- Binance
- Payeer
- Perfectmoney
- MOOV-BENIN
- Orangesenegal
- MTN-MOMO-BENIN
- MTN-MOMO-UGANDA
- MTN-MOMO-COTE-DIVOIRE
- MTN-MOMO-ZAMBIE
- MTN-MOMO-CAMEROUN
- MTN-MOMO-CONGO
- MTN-MOMO-SWAZILAND
- MTN-MOMO-GUINEE-CONAKRY
- MTN-MOMO-SOUTH-AFRICA
- MTN-MOMO-LIBERIA
- MTN-MOMO-RWANDA
IPN / Callback & signature
Si vous fournissez le header Custom-Callback-url, la plateforme enverra une notification après un résultat SUCCESSFUL ou FAILED.
$secret = 'VOTRE_WAPI_SECRET';
$rawBody = file_get_contents('php://input');
// Header nouveau (préféré) + ancien (compat)
$signature = $_SERVER['HTTP_X_TROLOOPAY_SIGNATURE'] ?? ($_SERVER['HTTP_X_EHOPAY_SIGNATURE'] ?? '');
$computed = hash_hmac('sha256', $rawBody, $secret);
if (!hash_equals($computed, $signature)) {
http_response_code(401);
exit('Invalid signature');
}
// OK: traiter la notification
$payload = json_decode($rawBody, true);
Erreurs & codes
| HTTP | StatusCode | Message | Cause fréquente |
|---|---|---|---|
| 400 | 400 | Invalid / missing fields | Body JSON incomplet, Reference-Id invalide, callback invalide |
| 401 | 401 | Invalid token / Token expired | Token invalide ou expiré |
| 403 | 403 | IP mismatch / Operation mismatch | Whitelist IP active ou Operation-Type différent du token |
| 409 | 409 | Conflict: Reference-Id transactions | Reference-Id déjà utilisé (idempotence) |
| 429 | 429 | Too Many Requests | Rate limiting (réessayer après quelques secondes) |
| 500 | 500 | Internal server error | Erreur interne (contacter le support) |
Références : UUID & idempotence
Le header Reference-Id doit être un UUID v4 unique par requête. Si vous renvoyez le même Reference-Id pour une transaction déjà existante, vous recevrez une erreur 409.
cat /proc/sys/kernel/random/uuid
Script de test complet
Script clé-en-main qui enchaîne les deux étapes (génération du token + initiation du retrait) avec gestion d'erreurs et affichage détaillé. Remplacez les constantes en haut du fichier par vos propres credentials — ne commitez jamais de vraies clés dans un dépôt.
<?php
// ── Credentials (à remplacer — NE PAS commiter en clair) ──────────
$WAPI_KEY = getenv('TROLOOPAY_WAPI_KEY') ?: 'VOTRE_WAPI_KEY';
$WAPI_SECRET = getenv('TROLOOPAY_WAPI_SECRET') ?: 'VOTRE_WAPI_SECRET';
$WAPI_PASSWORD = getenv('TROLOOPAY_PASSWORD') ?: 'VOTRE_MOT_DE_PASSE';
// ── Paramètres du retrait ─────────────────────────────────────────
$SENDER_USER_ID = 'email_ou_phone_du_titulaire_de_la_cle'; // ex: malickenagnon@gmail.com ou 229xxxxxxxx
$RECIPIENT_PHONE = '229xxxxxxxx'; // numéro mobile money bénéficiaire
$AMOUNT = '1000'; // montant en XOF
$PAYMENT_METHOD = 'MTN-MOMO-BENIN';
$OPERATION_TYPE = 'USER_OPERATION';
$CALLBACK_URL = ''; // optionnel : https://votre-domaine.com/ipn/withdrawal
// ── Fonctions ─────────────────────────────────────────────────────
function encryptAes256(string $data, string $key): string {
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
return base64_encode(openssl_encrypt($data, 'aes-256-cbc', $key, 0, $iv) . '::' . $iv);
}
function uuidV4(): string {
return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
mt_rand(0,0xffff), mt_rand(0,0xffff), mt_rand(0,0xffff),
mt_rand(0,0x0fff)|0x4000, mt_rand(0,0x3fff)|0x8000,
mt_rand(0,0xffff), mt_rand(0,0xffff), mt_rand(0,0xffff));
}
function apiCall(string $url, array $headers, ?string $body = null): array {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $body !== null ? 'POST' : 'GET',
CURLOPT_HTTPHEADER => $headers,
CURLOPT_POSTFIELDS => $body,
CURLOPT_TIMEOUT => 20,
]);
$resp = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return [$code, json_decode($resp, true)];
}
// ── Étape 1 : Générer le token ────────────────────────────────────
$authHeader = 'Basic ' . base64_encode(
$WAPI_KEY . ':' . encryptAes256($WAPI_SECRET, $WAPI_PASSWORD)
);
[$tokenCode, $tokenData] = apiCall(
'https://troloo.com/generate-withdrawal-token',
['Authorization: ' . $authHeader,
'Ehopay-Withdrawal-Type: ' . $OPERATION_TYPE,
'Accept: application/json'],
''
);
if ($tokenCode !== 200 || empty($tokenData['access_token'])) {
echo "ERREUR token ($tokenCode): " . json_encode($tokenData) . PHP_EOL;
exit(1);
}
$token = $tokenData['access_token'];
echo "✓ Token généré (expire dans {$tokenData['expires_in']}s)" . PHP_EOL;
// ── Étape 2 : Initier le retrait ──────────────────────────────────
$referenceId = uuidV4();
$headers = [
'Authorization: Bearer ' . $token,
'Operation-Type: ' . $OPERATION_TYPE,
'Reference-Id: ' . $referenceId,
'Content-Type: application/json',
'Accept: application/json',
];
if ($CALLBACK_URL) $headers[] = 'Custom-Callback-url: ' . $CALLBACK_URL;
$payload = json_encode([
'user_id' => $SENDER_USER_ID,
'currency' => 'XOF',
'payment_method' => $PAYMENT_METHOD,
'payment_method_reference' => ['momoformattedPhone' => $RECIPIENT_PHONE],
'amount' => $AMOUNT,
'external_reference' => 'retrait-' . date('YmdHis'),
]);
[$code, $data] = apiCall('https://troloo.com/initiate-withdrawal-api', $headers, $payload);
echo ($code === 202 ? '✓' : '✗') . " HTTP $code — " . ($data['message'] ?? 'N/A') . PHP_EOL;
if (!empty($data['transaction_id'])) {
echo " transaction_id : {$data['transaction_id']}" . PHP_EOL;
echo " withdrawal_id : {$data['withdrawal_id']}" . PHP_EOL;
echo " gateway_ref : {$data['gateway_reference']}" . PHP_EOL;
}
Exemples d’intégration
Exemples prêts à copier/coller pour initier un retrait. Choisissez votre langage dans les onglets.
curl -X POST https://troloo.com/initiate-withdrawal-api \
-H \"Authorization: Bearer <access_token>\" \
-H \"Operation-Type: USER_OPERATION\" \
-H \"Reference-Id: <uuid-v4>\" \
-H \"Custom-Callback-url: https://votre-domaine.com/ipn/withdrawal\" \
-H \"Content-Type: application/json\" \
-d '{\"user_id\":\"229xxxxxxxx\",\"currency\":\"XOF\",\"payment_method\":\"MTN-MOMO-BENIN\",\"payment_method_reference\":{\"momoformattedPhone\":\"229xxxxxxxx\"},\"amount\":\"100\",\"external_reference\":\"order-123\"}'