Comment ça marche
- Associez un écran : dans Tchin, page API › Écrans clients › Ajouter un écran. Un code à 6 chiffres s’affiche (10 minutes). Sur la tablette, ouvrez
tchin.tech/caisseet tapez ce code. L’écran reste associé, même après un redémarrage. - Votre caisse envoie le montant à cet écran (
terminal_id) par l’API. - L’écran client affiche le montant et ouvre la caméra. Le client montre son visage, sans geste. Tchin le reconnaît, puis lui demande son Tchin PIN.
- Votre caisse est prévenue : webhook signé, ou lecture de l’état. Vous imprimez le ticket.
Si le visage n’est pas reconnu (lumière, photo, solde…), l’écran laisse le client réessayer, jusqu’à 3 fois. Le paiement attend le client 5 minutes, puis expire.
Lister vos écrans
/api/v1/terminals{
"success": true,
"data": [
{ "id": "ecr_y9umcepnvrwbomtjst0f", "name": "Caisse 1", "paired": true, "online": true, "last_seen": "2026-10-20T09:12:40+00:00" }
]
}Envoyer un paiement à un écran
/api/v1/terminal/payments| Champ | Type | Description |
|---|---|---|
terminal_id | texte | l’écran client (ecr_…), obligatoire. |
amount | entier | le montant en FCFA, de 200 à 500 000. |
reference | texte | votre référence (numéro du ticket). |
callback_url | URL | votre webhook : l’issue y arrive. |
env | texte | live (par défaut) ou test : en test, l’écran propose « Simuler : payé / refusé », sans visage ni argent. |
curl -X POST https://tchin.tech/api/v1/terminal/payments \
-H "TCHIN-PUBLIC-KEY: tchin_pk_..." -H "TCHIN-PRIVATE-KEY: tchin_sk_..." \
-H "Content-Type: application/json" \
-d '{"terminal_id": "ecr_y9umcepnvrwbomtjst0f", "amount": 2500, "reference": "Ticket 0042", "callback_url": "https://wintg.group/webhook-tchin"}'$r = $http->post('https://tchin.tech/api/v1/terminal/payments', [
'terminal_id' => 'ecr_y9umcepnvrwbomtjst0f', 'amount' => 2500,
'reference' => 'Ticket 0042', 'callback_url' => 'https://wintg.group/webhook-tchin',
]);
$id = $r['data']['id'];const r = await fetch('https://tchin.tech/api/v1/terminal/payments', {
method: 'POST',
headers: { 'TCHIN-PUBLIC-KEY': pk, 'TCHIN-PRIVATE-KEY': sk, 'Content-Type': 'application/json' },
body: JSON.stringify({ terminal_id: 'ecr_y9umcepnvrwbomtjst0f', amount: 2500, reference: 'Ticket 0042' }),
});
const { data } = await r.json();{
"success": true,
"data": {
"id": "pvc_kqvfoxms4hn1ccvm5uwtdfou",
"reference": "Ticket 0042",
"terminal_id": "ecr_y9umcepnvrwbomtjst0f",
"amount": 2500,
"currency": "XOF",
"status": "pending",
"mode": "live",
"customer": null,
"failure_reason": null,
"created_at": "2026-10-20T09:15:02+00:00",
"completed_at": null
}
}Un écran ne montre qu’un paiement à la fois : un nouveau paiement annule celui qui attendait encore sur le même écran.
Suivre le paiement
/api/v1/terminal/payments/{id}| status | Ce que ça veut dire |
|---|---|
<code>pending</code> | l’écran attend le visage du client. |
<code>processing</code> | reconnaissance du visage ou paiement en cours. |
<code>awaiting_pin</code> | client reconnu : il tape son Tchin PIN. |
<code>completed</code> | payé : l’argent est sur votre solde. customer donne son Tchin ID et son nom. |
<code>failed</code> | refusé (raison dans failure_reason). |
<code>expired</code> | le client ne s’est pas présenté (5 minutes) ou n’a pas tapé son PIN (2 minutes). |
<code>canceled</code> | annulé par votre caisse, ou remplacé par un nouveau paiement. |
Annuler
/api/v1/terminal/payments/{id}/cancelPossible tant que le client n’a pas payé (pending ou awaiting_pin). Sinon : 409.
Le webhook
À chaque issue (completed, failed, expired, canceled), Tchin envoie à votre callback_url les mêmes champs que la réponse, avec kind = "terminal" et event = "terminal_payment.completed" (etc.). La signature se vérifie comme pour les paiements : voir Webhooks et signature.
Et les TPE Tchin ?
Les futurs TPE Tchin seront des écrans clients comme les autres : votre intégration restera la même, il suffira d’associer le TPE avec son code.