T
T
TrolooPAY
Se Connecter S'inscrire
Documentation Développeur

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.

Base URL
https://troloo.com
Token
POST /generate-withdrawal-token
Valide 1 heure
Retrait
POST /initiate-withdrawal-api
Traite un retrait

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).
Bonnes pratiques
Ne logguez jamais vos tokens ou secrets en clair. Protégez vos endpoints IPN (validation de signature HMAC).

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.

  1. Créer un couple wapi_key / wapi_secret dans l’espace développeur.
  2. Définir un mot de passe d’opérations (utilisé pour chiffrer le secret côté client).
  3. Attendre l’état approved.
  4. Optionnel : définir une whitelist IP (recommandé en production).

1) Générer un token

Endpoint
POST https://troloo.com/generate-withdrawal-token
Headers
  • Authorization: Basic base64(wapi_key : AES256(wapi_secret))
  • Ehopay-Withdrawal-Type: USER_OPERATION | MERCHANT_OPERATION | REQUEST_TO_PAY
Note : le nom de ce header conserve Ehopay- pour compatibilité.
PHP — génération du header Authorization
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);
Réponse attendue (200)
{
  "status": 200,
  "access_token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "expires_in": 3600,
  "message": "Token Generated Successfully"
}

2) Initier un retrait

Endpoint
POST https://troloo.com/initiate-withdrawal-api
Headers requis
  • 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é)
Body JSON
{
  "user_id": "229xxxxxxxx" ,
  "currency": "XOF",
  "payment_method": "MTN-MOMO-BENIN",
  "payment_method_reference": {
    "momoformattedPhone": "229xxxxxxxx"
  },
  "amount": "100",
  "external_reference": "optional-string"
}
user_id — email ou téléphone du titulaire de la clé API (expéditeur), ou son merchant_uuid.
momoformattedPhone — numéro du bénéficiaire : code pays + numéro local, sans caractère spécial ni espace ni « + ».
Exemple Bénin : 229 + 01964510402290196451040
Réponse attendue (202)
{
  "StatusCode": 202,
  "status": "SUCCESSFUL",
  "transaction_id": 2054,
  "withdrawal_id": 24,
  "gateway_reference": "e23fd847-fb27-4cdb-96f4-ebd228f8708b",
  "message": "transaction successful"
}
Pièges fréquents — à lire avant toute intégration
user_id ≠ numéro bénéficiaire. Le champ user_id est l'email ou téléphone du titulaire de la clé API (le compte qui envoie l'argent). Le numéro du bénéficiaire va dans payment_method_reference.momoformattedPhone.
Reference-Id doit être un UUID v4 valide. Une chaîne arbitraire comme MTN-TEST-123 sera refusée avec une erreur 400. Générez toujours un vrai UUID : crypto.randomUUID() (JS), uuid.uuid4() (Python), Ramsey\Uuid::uuid4() (PHP).
Operation-Type doit correspondre au token. Le type utilisé dans Ehopay-Withdrawal-Type à la génération du token doit être identique au header Operation-Type lors du retrait, sinon → 403.
Le token est à usage unique. Il est invalidé immédiatement après un retrait réussi. Générez un nouveau token pour chaque retrait.
Payment methods supportés
Crypto / Wallets
  • Coinpayments
  • Binance
  • Payeer
  • Perfectmoney
Mobile Money
  • 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.

Signature
Header préféré : X-TrolooPay-Signature = HMAC-SHA256 du body JSON avec votre wapi_secret. Compat : X-Ehopay-Signature.
PHP — vérifier la signature IPN
$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);
Important : la callback URL doit être HTTPS et ne doit pas résoudre vers des IPs privées (ex: 10.x, 192.168.x, 127.x).

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.

Générer un UUID v4 (Linux)
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.

Sécurité — stockez vos credentials dans des variables d'environnement (getenv() / os.environ / process.env) et non en dur dans le code source.
PHP — test-withdrawal.php
<?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 — initier un retrait
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\"}'