Lien générique (webhook) : envoyer les ventes à Stokyo
Avec le webhook générique, toute caisse, boutique en ligne ou intégrateur envoie ses ventes à Stokyo, qui les déduit automatiquement du stock. Cette page s’adresse à la personne qui construit le lien.
- 1. Adresse et clé secrète
- 2. Envoyer une vente
- 3. Champs
- 4. Signer (HMAC-SHA256)
- 5. Retours et annulations
- 6. Réponses
- 7. Bon à savoir
1. Adresse et clé secrète
L’exploitant les trouve dans Stokyo sous Plus → Connexions → Lien générique → Afficher l’adresse et la clé. Chaque commerce reçoit sa propre adresse (se termine par /pos-webhook/ep_…) et sa propre clé secrète (commence par whsec_). Gardez la clé uniquement sur le serveur, jamais dans une appli ou une page web.
2. Envoyer une vente
Envoyez un POST avec un corps JSON. Un seul événement (sans liste events) ou jusqu’à 100 à la fois.
POST https://tcphaxqfzdfgvkxixbzk.supabase.co/functions/v1/pos-webhook/ep_…
Content-Type: application/json
Stokyo-Signature: t=1793400000,v1=5f2b…c91e
{
"events": [
{
"ticketId": "1287",
"type": "sale",
"occurredAt": "2026-10-30T21:14:00+01:00",
"lines": [
{ "itemId": "PILS25", "name": "Pils 25 cl", "qty": 2, "unitPrice": 3.20, "barcode": "5410228142218" },
{ "itemId": "MOJ", "name": "Mojito", "qty": 1, "unitPrice": 9.50 }
]
}
]
}3. Champs
| Champ | Obligatoire | Explication |
|---|---|---|
ticketId | oui | Identifiant unique du ticket dans la caisse. Renvoyer le même ticket n’encode rien en double. |
type | non | sale (par défaut), refund (retour) ou void (annulation d’un ticket entier). |
occurredAt | non | Moment de la vente au format ISO 8601, avec fuseau horaire. Vide = maintenant. |
refundOf | non | Pour refund ou void : le ticketId d’origine. |
lines[].itemId | un des trois | Identifiant de l’article dans la caisse (PLU, SKU). Stokyo retient à quel produit il est lié. |
lines[].barcode | un des trois | Code EAN. S’il correspond à un produit Stokyo, le lien est fait automatiquement. |
lines[].name | un des trois | Nom tel que sur le ticket. Sert aux suggestions lors de la liaison. |
lines[].qty | oui | Quantité, supérieure à 0. Positive aussi pour un retour. |
lines[].unitPrice | non | Prix de vente unitaire, à titre d’information. |
4. Signer (HMAC-SHA256)
Chaque requête contient l’en-tête Stokyo-Signature: t=<secondes unix>,v1=<hex>. v1 est le HMAC-SHA256 de <t>.<corps brut> avec la clé secrète. Signez exactement les octets envoyés. L’horodatage peut s’écarter de 5 minutes au maximum, pour qu’une requête interceptée ne puisse pas être rejouée.
Node.js
import crypto from "node:crypto";
const body = JSON.stringify(payload); // send exactly this text
const t = Math.floor(Date.now() / 1000);
const v1 = crypto.createHmac("sha256", SECRET).update(`${t}.${body}`).digest("hex");
await fetch(URL, {
method: "POST",
headers: { "Content-Type": "application/json", "Stokyo-Signature": `t=${t},v1=${v1}` },
body,
});Python
import hmac, hashlib, json, time, requests
body = json.dumps(payload, separators=(",", ":"))
t = int(time.time())
v1 = hmac.new(SECRET.encode(), f"{t}.{body}".encode(), hashlib.sha256).hexdigest()
requests.post(URL, data=body, headers={
"Content-Type": "application/json",
"Stokyo-Signature": f"t={t},v1={v1}",
})Shell (curl)
BODY='{"events":[{"ticketId":"T-1","lines":[{"itemId":"PILS25","qty":1}]}]}'
T=$(date +%s)
SIG=$(printf '%s' "$T.$BODY" | openssl dgst -sha256 -hmac "$SECRET" | sed 's/^.* //')
curl -X POST "$URL" -H "Content-Type: application/json" \
-H "Stokyo-Signature: t=$T,v1=$SIG" --data "$BODY"5. Retours et annulations
Un refund remet les quantités indiquées en stock. Un void annule tout ce qui a été encodé pour le ticket d’origine. Si une annulation arrive avant le ticket lui-même, Stokyo attend et la traite dès que le ticket est là.
{ "events": [
{ "ticketId": "1288", "type": "refund", "refundOf": "1287",
"lines": [ { "itemId": "PILS25", "qty": 1 } ] },
{ "ticketId": "1287", "type": "void" }
] }6. Réponses
200 signifie : enregistré en toute sécurité. La réponse indique ce qui s’est passé :
{ "ok": true, "accepted": 1, "duplicates": 0, "booked": 1, "needsMapping": 0, "waiting": 0, "queuedForRetry": 0 }duplicates: tickets déjà reçus (rien n’est encodé deux fois).needsMapping: tickets avec des articles inconnus de Stokyo. Ils apparaissent dans l’appli sous À lier et sont encodés dès que l’exploitant les lie.queuedForRetry: pas traité pour l’instant ; Stokyo réessaie tout seul.
| Code | Signification | Que faire |
|---|---|---|
| 401 | Signature absente, incorrecte ou trop ancienne | Vérifier la clé et l’horloge. Ne pas renvoyer sans correction. |
| 404 | Adresse inconnue | Recopier l’adresse depuis Stokyo. |
| 400 / 422 | JSON invalide ou champs incorrects (details indique lesquels) | Corriger le corps. |
| 413 | Trop grand (plus de 100 événements ou 1 Mo) | Découper. |
| 503 | Temporairement indisponible | Réessayer, p. ex. après 1, 5 et 30 minutes. Un double envoi ne pose aucun problème. |
7. Bon à savoir
- Les produits composés (un cocktail ou un menu) se configurent dans Stokyo : un article de caisse peut ainsi diminuer plusieurs produits du stock.
- Créer une nouvelle clé dans Stokyo rend l’ancienne invalide tout de suite.
- Questions ou proposition de lien direct : contact@stokyo.app.