Webhooks
Quand l'état d'un paiement change, Tchin envoie une requête POST à votre callback_url (ou au webhook de votre application). C'est la source de vérité pour valider une commande — ne vous reposez jamais sur le seul retour navigateur, qui peut être interrompu (réseau, fermeture d'onglet…).
Format
L'envoi est de type application/x-www-form-urlencoded et les informations sont regroupées sous la clé data. Vous récupérez donc data avant de lire un champ (ex. data['status']).
POST https://monsite.com/tchin/webhook
Content-Type: application/x-www-form-urlencoded
data[response_code]=00
data[response_text]=Transaction completed
data[status]=completed
data[hash]=7f3b9e…(SHA-512)
data[reference]=a1b2c3d4e5f6 # = token du paiement
data[token]=…
data[amount]=5000
data[fee]=250
data[net]=4750
data[currency]=XOF
data[country]=SN
data[method]=wave-senegal
data[mode]=live
data[fail_reason]=
data[customer][name]=Awa Diop
data[customer][email]=awa@exemple.com
data[customer][phone]=771111111
Champ (dans data) | Description |
|---|---|
status | completed, failed, cancelled ou pending. |
hash | SHA-512 de votre clé privée — sert à authentifier l'origine (voir ci-dessous). |
reference | Le token du paiement (à rapprocher de votre commande). |
amount / fee / net | Montant payé / commission Tchin (5%) / montant net crédité. |
currency | Devise (toujours XOF). |
country / method | Pays et moyen de paiement utilisés. |
mode | live ou test (webhook de test en sandbox). |
fail_reason | Motif, renseigné pour les statuts failed / cancelled. |
customer | Nom, email, téléphone du payeur. |
Vérifier l'origine (hash)
Chaque webhook contient un hash = SHA-512 de votre clé privée (la même que l'en-tête TCHIN-PRIVATE-KEY). Recalculez-le de votre côté et comparez : s'ils diffèrent, ignorez la requête. Cela garantit que la notification provient bien de Tchin.
Réception (PHP)
<?php
// Fichier appelé par Tchin sur votre callback_url.
// Les données arrivent en form-urlencoded, sous la clé "data".
$data = $_POST['data'] ?? [];
// 1) Authentifiez l'origine : hash = SHA-512 de VOTRE clé privée (TCHIN-PRIVATE-KEY)
$expected = hash('sha512', 'votre_cle_privee');
if (! hash_equals($expected, $data['hash'] ?? '')) {
http_response_code(401);
exit('Signature invalide');
}
// 2) Agissez selon le statut
if (($data['status'] ?? null) === 'completed') {
$reference = $data['reference']; // = le token du paiement
$net = (int) $data['net']; // ce que vous recevez (après 5%)
// → Retrouvez la commande liée à $reference
// → Si pas déjà payée : marquez-la PAYÉE et livrez (idempotence)
}
http_response_code(200); // accusez réception
echo 'OK';
Réception (Node.js)
// Node.js / Express
import express from 'express';
import crypto from 'crypto';
const app = express();
app.use(express.urlencoded({ extended: true })); // form-urlencoded
app.post('/tchin/webhook', (req, res) => {
const d = req.body.data || {};
const expected = crypto.createHash('sha512').update('votre_cle_privee').digest('hex');
if (d.hash !== expected) return res.sendStatus(401);
if (d.status === 'completed') {
// retrouver la commande d.reference, la marquer payée (une seule fois)
}
res.sendStatus(200);
});
Idempotence
Un même paiement peut, dans de rares cas, déclencher plusieurs appels (latence, réessais). Identifiez chaque webhook par sa reference et n'appliquez l'effet (marquer payé, livrer) qu'une seule fois.
Bonnes pratiques
- Vérifiez systématiquement le
hashavant d'agir. - Répondez HTTP 200 rapidement ; traitez le reste en tâche de fond si nécessaire.
- Votre
callback_urldoit être en HTTPS et accessible publiquement. - Le montant
net=amount − fee(commission 5%). - En cas d'erreur (≠ 200), gardez l'opération idempotente (Tchin pourra réémettre).