Encaisser un paiement
L'encaissement (checkout hébergé) est la façon la plus simple de faire payer vos clients par Mobile Money. Vous n'avez ni page de paiement à construire, ni opérateur à intégrer un par un : Tchin héberge une page de paiement sécurisée sur laquelle le client choisit son pays, son opérateur (Orange Money, MTN, Moov, Wave, Yas…), saisit son numéro et valide. Vous recevez ensuite la confirmation par webhook.
Le parcours en 4 temps
- Créer le paiement — votre serveur appelle
POST /paymentset reçoit unepayment_urlainsi qu'untoken. - Rediriger le navigateur du client vers cette
payment_url. - Le client paie sur la page hébergée Tchin (choix pays + opérateur, saisie du numéro, OTP).
- Confirmation — le client est renvoyé sur votre
return_url, et Tchin notifie votrecallback_url(le webhook est la source de vérité pour valider la commande).
💡 Toute la création de paiement se fait côté serveur. Ne placez jamais votre clé secrète (tchin_sk_…) dans une page web ou une application mobile.
Créer un paiement
POST /api/v1/payments
Cette requête crée un paiement en attente et renvoie l'URL de la page de paiement. Elle ne déplace aucun argent : le débit du client survient seulement lorsqu'il paie sur la page hébergée.
Paramètres du corps (JSON)
| Champ | Type | Requis | Description |
|---|---|---|---|
amount | entier | Oui | Montant en FCFA (XOF ; XAF au Cameroun), sans décimales. Entre 100 et 100 000 000. |
description | texte | Non | Libellé affiché au client sur la page de paiement (max 255 caractères). Ex. « Commande #1024 ». |
env | texte | Non | test (défaut) ou live. En test, aucun argent réel n'est déplacé. |
return_url | URL | Non | Page de votre site où renvoyer le client après un paiement réussi (max 500 caractères). |
cancel_url | URL | Non | Page de retour en cas d'annulation. Par défaut : la return_url. |
callback_url | URL | Non | URL de votre webhook pour CE paiement. Remplace le webhook par défaut de l'application (max 500 caractères). |
fees_on_customer | booléen | Non | Si true, la commission Tchin est ajoutée au montant payé par le client. Par défaut : le réglage de votre application. |
Exemple de requête
curl -X POST https://tchin.tech/api/v1/payments \
-H "TCHIN-PUBLIC-KEY: tchin_pk_xxxxxxxx" \
-H "TCHIN-PRIVATE-KEY: tchin_sk_xxxxxxxx" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"amount": 5000,
"description": "Commande #1024",
"env": "live",
"return_url": "https://monsite.com/merci",
"cancel_url": "https://monsite.com/panier",
"callback_url": "https://monsite.com/tchin/webhook",
"fees_on_customer": false
}'
<?php
// Création d'un paiement — À EXÉCUTER CÔTÉ SERVEUR uniquement.
// Ne placez JAMAIS la clé secrète dans une page web ou une app mobile.
$body = [
'amount' => 5000, // FCFA (XOF), entier, min 100
'description' => 'Commande #1024',
'env' => 'live', // "test" ou "live"
'return_url' => 'https://monsite.com/merci',
'cancel_url' => 'https://monsite.com/panier',
'callback_url' => 'https://monsite.com/tchin/webhook',
'fees_on_customer'=> false,
];
$ch = curl_init('https://tchin.tech/api/v1/payments');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'TCHIN-PUBLIC-KEY: ' . getenv('TCHIN_PUBLIC_KEY'),
'TCHIN-PRIVATE-KEY: ' . getenv('TCHIN_SECRET_KEY'),
'Content-Type: application/json',
'Accept: application/json',
],
CURLOPT_POSTFIELDS => json_encode($body),
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$data = json_decode($response, true);
if ($httpCode === 200 && ($data['success'] ?? false)) {
// 1) Mémorisez $data['token'] avec votre commande (statut « en attente »).
// 2) Redirigez le client vers la page de paiement hébergée.
header('Location: ' . $data['payment_url']);
exit;
}
// Sinon : afficher/journaliser $data['message'].
http_response_code(400);
echo $data['message'] ?? 'Erreur lors de la création du paiement.';
// Node.js (18+) / Express — création d'un paiement côté serveur.
import express from 'express';
const app = express();
app.post('/payer', async (req, res) => {
try {
const r = await fetch('https://tchin.tech/api/v1/payments', {
method: 'POST',
headers: {
'TCHIN-PUBLIC-KEY': process.env.TCHIN_PUBLIC_KEY,
'TCHIN-PRIVATE-KEY': process.env.TCHIN_SECRET_KEY,
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({
amount: 5000, // FCFA (XOF), entier, min 100
description: 'Commande #1024',
env: 'live', // "test" ou "live"
return_url: 'https://monsite.com/merci',
cancel_url: 'https://monsite.com/panier',
callback_url: 'https://monsite.com/tchin/webhook',
fees_on_customer: false,
}),
});
const data = await r.json();
if (!r.ok || !data.success) {
return res.status(400).send(data.message || 'Paiement impossible.');
}
// Mémorisez data.token avec la commande, puis redirigez le client.
res.redirect(data.payment_url);
} catch (e) {
res.status(500).send('Erreur serveur.');
}
});
app.listen(3000);
# Python (requests) — création d'un paiement côté serveur.
import os
import requests
resp = requests.post(
'https://tchin.tech/api/v1/payments',
headers={
'TCHIN-PUBLIC-KEY': os.environ['TCHIN_PUBLIC_KEY'],
'TCHIN-PRIVATE-KEY': os.environ['TCHIN_SECRET_KEY'],
'Accept': 'application/json',
},
json={
'amount': 5000, # FCFA (XOF), entier, min 100
'description': 'Commande #1024',
'env': 'live', # "test" ou "live"
'return_url': 'https://monsite.com/merci',
'cancel_url': 'https://monsite.com/panier',
'callback_url': 'https://monsite.com/tchin/webhook',
'fees_on_customer': False,
},
timeout=30,
)
data = resp.json()
if resp.status_code == 200 and data.get('success'):
token = data['token'] # à mémoriser avec la commande
payment_url = data['payment_url'] # rediriger le client vers cette URL
# return redirect(payment_url)
else:
raise Exception(data.get('message', 'Paiement impossible.'))
Exemple de réponse
{
"success": true,
"token": "abc123def456ghi789",
"payment_url": "https://tchin.tech/pay/abc123def456ghi789",
"env": "live"
}
| Champ | Description |
|---|---|
success | Booléen. Vérifiez-le toujours en plus du code HTTP 200. |
token | Référence unique du paiement. Mémorisez-la avec votre commande : elle vous servira à suivre le statut et à recouper les webhooks. |
payment_url | URL de la page de paiement hébergée vers laquelle rediriger le client. |
env | Environnement effectif du paiement (test ou live). |
Rediriger le client vers payment_url
Une fois la payment_url reçue, il ne vous reste qu'à y envoyer le client. Deux options :
- Redirection HTTP 302 depuis votre serveur (le plus courant) — voir
header('Location: …')etres.redirect(…)dans les exemples ci-dessus. - Lien ou bouton : affichez simplement un bouton « Payer » pointant sur la
payment_url.
Sur cette page, le client réalise l'intégralité du paiement (choix du pays, de l'opérateur, saisie du numéro, validation OTP). Vous n'avez rien d'autre à coder côté paiement. Pour connaître les opérateurs disponibles par pays, consultez Moyens de paiement.
⚠️ Une payment_url correspond à un seul paiement, à usage unique. Pour chaque commande, créez un nouveau paiement.
Frais : absorbés ou répercutés (fees_on_customer)
Tchin prélève une commission à l'encaissement (dès 3,5 %, variable selon le pays et votre volume). Le paramètre fees_on_customer détermine qui la supporte :
false(frais absorbés) — le client paie exactement leamountdemandé ; la commission est déduite de votre part. Vous êtes crédité du net.true(frais répercutés) — le montant est majoré de la commission sur la page de paiement ; le client paie plus, et vous recevez l'intégralité duamount.
Si vous ne fournissez pas ce champ, c'est le réglage par défaut de votre application (tableau de bord) qui s'applique.
// fees_on_customer = false (frais absorbés par le marchand)
// Le client paie exactement 5 000 FCFA.
// Commission Tchin (ex. 3,5 % = 175) déduite de votre part.
// Vous êtes crédité de : net = 5000 - 175 = 4825 FCFA.
// fees_on_customer = true (frais répercutés au client)
// Le montant est majoré de la commission sur la page de paiement.
// Le client paie 5 175 FCFA.
// Vous êtes crédité de la totalité : 5000 FCFA.
Retour du client : return_url et cancel_url
Ces deux URL contrôlent où le navigateur du client atterrit à la fin du parcours :
return_url— page affichée après un paiement réussi (ou, à défaut decancel_url, après une annulation).cancel_url— page affichée si le client annule. Par défaut, elle vaut lareturn_url.
Toutes deux sont optionnelles : sans elles, Tchin renvoie le client sur la page d'origine. Tchin ajoute deux paramètres à l'URL de retour :
| Paramètre | Valeur |
|---|---|
status | success (payé) ou cancel (annulé). |
token | La référence du paiement (identique à celle reçue à la création). |
Exemple d'URL de retour : https://monsite.com/merci?status=success&token=abc123def456ghi789
<?php
// Votre return_url — page où le client atterrit après le paiement.
// Tchin y ajoute deux paramètres : status et token.
$status = $_GET['status'] ?? null; // "success" ou "cancel"
$token = $_GET['token'] ?? null;
if ($status === 'success') {
// Afficher un écran « Merci ». NE livrez PAS encore : attendez le webhook.
echo 'Merci ! Votre paiement est en cours de confirmation.';
} elseif ($status === 'cancel') {
echo 'Paiement annulé. Vous pouvez réessayer.';
}
// La commande n'est validée/livrée QUE lorsque le webhook confirme "completed".
⚠️ Le retour navigateur sert uniquement à l'affichage. Le client peut fermer son onglet avant de revenir : ne validez et ne livrez jamais une commande sur ce seul retour. La source de vérité est le webhook.
Suivre l'état d'un paiement
GET /api/v1/payments/{token}/status
À tout moment, interrogez l'état d'un paiement grâce à son token. Utile en complément du webhook — par exemple depuis votre page de retour, ou pour rattraper un webhook manqué.
curl https://tchin.tech/api/v1/payments/abc123def456ghi789/status \
-H "TCHIN-PUBLIC-KEY: tchin_pk_xxxxxxxx" \
-H "TCHIN-PRIVATE-KEY: tchin_sk_xxxxxxxx" \
-H "Accept: application/json"
<?php
// Vérifier l'état d'un paiement à partir de son token.
$token = 'abc123def456ghi789';
$ch = curl_init('https://tchin.tech/api/v1/payments/' . $token . '/status');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'TCHIN-PUBLIC-KEY: ' . getenv('TCHIN_PUBLIC_KEY'),
'TCHIN-PRIVATE-KEY: ' . getenv('TCHIN_SECRET_KEY'),
'Accept: application/json',
],
]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
// $data['status'] : pending | completed | failed | cancelled
if (($data['status'] ?? null) === 'completed') {
// Paiement confirmé.
}
// Node.js (18+) — état d'un paiement.
const token = 'abc123def456ghi789';
const r = await fetch(`https://tchin.tech/api/v1/payments/${token}/status`, {
headers: {
'TCHIN-PUBLIC-KEY': process.env.TCHIN_PUBLIC_KEY,
'TCHIN-PRIVATE-KEY': process.env.TCHIN_SECRET_KEY,
'Accept': 'application/json',
},
});
const data = await r.json();
// data.status : pending | completed | failed | cancelled
console.log(data.status, data.amount, data.currency);
# Python (requests) — état d'un paiement.
import os
import requests
token = 'abc123def456ghi789'
r = requests.get(
f'https://tchin.tech/api/v1/payments/{token}/status',
headers={
'TCHIN-PUBLIC-KEY': os.environ['TCHIN_PUBLIC_KEY'],
'TCHIN-PRIVATE-KEY': os.environ['TCHIN_SECRET_KEY'],
'Accept': 'application/json',
},
timeout=30,
)
data = r.json()
# data['status'] : pending | completed | failed | cancelled
print(data['status'], data['amount'], data['currency'])
Exemple de réponse
{
"success": true,
"token": "abc123def456ghi789",
"status": "completed",
"amount": 5000,
"currency": "XOF",
"env": "live",
"customer": {
"name": "Awa Diop",
"email": "awa@example.com",
"phone": "+221771234567"
}
}
| Champ | Description |
|---|---|
status | pending (en attente), completed (payé), failed (échoué) ou cancelled (annulé). |
amount | Montant du paiement, en entier FCFA. |
currency | Devise, généralement XOF. |
env | Environnement du paiement (test ou live). |
customer | Coordonnées du payeur (nom, email, téléphone), lorsqu'elles sont connues. |
Un token inconnu renvoie une erreur 404.
💡 Ne « bouclez » pas sur cet endpoint en attendant qu'un paiement passe à completed. Laissez le webhook vous prévenir, et servez-vous du statut comme d'un simple contrôle ponctuel.
Tester votre intégration (mode test)
Avant la production, validez tout le parcours en passant "env": "test" à la création du paiement. Le paiement est alors entièrement simulé : aucun argent réel n'est déplacé et votre solde n'est pas crédité.
- Utilisez le compte de test fictif de votre tableau de bord (menu API → « Compte test ») : email, numéro et mot de passe factices à saisir sur la page de paiement.
- Tchin envoie tout de même un webhook de test (champ
mode: test) : vous validez ainsi la réception et la vérification duhashsans risque. - Le retour navigateur (
return_url/cancel_url) fonctionne exactement comme en production.
Quand tout est vert en test, passez simplement à "env": "live" pour encaisser réellement.
Exemple complet, de bout en bout
Voici l'enchaînement typique pour une commande de 5 000 FCFA, de la création du paiement jusqu'au point où le webhook prend le relais :
<?php
// ---------------------------------------------------------------
// EXEMPLE DE BOUT EN BOUT (PHP) : payer une commande de 5 000 FCFA.
// ---------------------------------------------------------------
// 1) L'ACHETEUR clique « Payer » -> votre serveur crée le paiement.
$ch = curl_init('https://tchin.tech/api/v1/payments');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'TCHIN-PUBLIC-KEY: ' . getenv('TCHIN_PUBLIC_KEY'),
'TCHIN-PRIVATE-KEY: ' . getenv('TCHIN_SECRET_KEY'),
'Content-Type: application/json',
'Accept: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'amount' => 5000,
'description' => 'Commande #1024',
'env' => 'live',
'return_url' => 'https://monsite.com/merci',
'callback_url' => 'https://monsite.com/tchin/webhook',
]),
]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
// 2) On enregistre le token AVEC la commande (statut = "en attente").
$orders[$data['token']] = ['ref' => 'CMD-1024', 'status' => 'pending'];
// 3) On redirige le client vers la page de paiement hébergée Tchin.
header('Location: ' . $data['payment_url']); // https://tchin.tech/pay/...
exit;
// 4) Le client choisit SN + Wave, saisit 771234567, valide l'OTP -> il paie.
// 5) Tchin appelle votre callback_url (webhook) : c'est LÀ qu'on livre.
// (voir la page Webhooks pour vérifier data[hash] puis marquer "payé".)
Pour aller plus loin
- Webhooks — recevoir la confirmation, vérifier le
hashet livrer la commande (source de vérité). - Moyens de paiement — la liste des pays et opérateurs, avec leur disponibilité du moment.
- Erreurs & statuts — comprendre les codes 400/401/403/404/422 et les messages métier.