Webhook d'autorisation émetteur
À chaque autorisation carte d'un enfant, le processeur de la banque demande à magot s'il faut approuver. magot répond en quelques millisecondes à partir des règles parentales en mémoire (gel, plafonds, catégories, solde).
Point d'accès
POST https://<hôte-autorisation>/webhooks/issuer/authorization
Content-Type: application/json
En production, l'hôte n'est joignable qu'en mTLS : le processeur présente
un certificat client émis par l'autorité convenue avec magot. Sans TLS
configuré, le service n'écoute qu'en local (127.0.0.1).
Authentification
Chaque banque reçoit un credential, lié à un seul établissement :
| Élément | Exemple | Remise |
|---|---|---|
| Identifiant de clé | mgt_ik_… | en clair |
| Secret HMAC | mgt_is_… | une seule fois, par canal sûr |
| Empreinte du certificat client | SHA-256 | fournie par la banque |
Trois en-têtes sur chaque requête :
| En-tête | Valeur |
|---|---|
Magot-Key-Id | l'identifiant de clé |
Magot-Timestamp | secondes Unix de l'envoi |
Magot-Signature | v1= + HMAC-SHA256 hex de <timestamp>.<corps brut>, clé = secret (octets UTF-8) |
Règles côté magot :
- signature vérifiée sur le corps brut, avant tout parsing ; toute modification du corps l'invalide ;
- horodatage accepté à ±300 s (anti-rejeu) ;
- l'établissement est celui du credential, jamais une valeur du corps ;
- si le credential porte une empreinte de certificat, la connexion doit présenter ce certificat précis, validé par l'autorité convenue ;
- tout échec :
401, sans détail.
Exemple de signature (Node.js) :
import { createHmac } from "node:crypto";
const body = JSON.stringify(authorization); // envoyé tel quel
const ts = Math.floor(Date.now() / 1000);
const signature = "v1=" + createHmac("sha256", secret).update(`${ts}.${body}`).digest("hex");
// en-têtes : Magot-Key-Id, Magot-Timestamp: ts, Magot-Signature: signature
Corps
| Champ | Type | |
|---|---|---|
idempotency_key | chaîne ASCII, 1–128 | obligatoire ; identifiant unique de l'autorisation chez le processeur |
card_ref | chaîne, 1–128 | référence de la carte chez l'émetteur (voie normale) |
child_id | UUID | alternative sandbox à card_ref |
amount_cents | entier, 1–10 000 000 | obligatoire ; montant en centimes, jamais une chaîne |
mcc | 4 chiffres | code catégorie commerçant |
merchant | chaîne ≤ 200 | libellé commerçant |
category | chaîne ≤ 64 | catégorie magot si le processeur la connaît |
day | YYYY-MM-DD | jour local de la transaction (défaut : aujourd'hui) |
tenant_id | UUID | facultatif ; s'il est présent, il doit être celui du credential (sinon 403) |
card_ref ou child_id est obligatoire. Corps invalide : 400.
Réponse
200 dans tous les cas où une décision est prise — un refus est un résultat,
pas une erreur :
{ "approved": false, "reason": "daily_limit_exceeded",
"values": { "dailyLimitCents": 2000, "spentTodayCents": 1800, "amountCents": 300 } }
reason | Sens |
|---|---|
card_frozen | carte gelée par le parent ou l'enfant |
daily_limit_exceeded | plafond du jour atteint |
txn_limit_exceeded | plafond par transaction dépassé |
category_blocked | catégorie bloquée par le parent |
insufficient_funds | solde insuffisant |
unknown_card | carte inconnue de cet établissement |
unknown_child | enfant inconnu de cet établissement (sandbox) |
Idempotence
Rejouer la même idempotency_key rend la même décision sans débiter une
seconde fois. Le processeur peut donc réessayer sans risque après un timeout.
Indisponibilité de magot
Si magot ne répond pas dans le budget convenu, le processeur applique sa politique de repli (stand-in). Les limites et le gel sont répliqués sur la carte chez l'émetteur pour que ce repli reste conforme aux règles parentales : magot appelle l'émetteur à chaque gel et dégel. Geler ne dépend jamais de l'émetteur (la carte est gelée chez magot immédiatement, la resynchronisation suit) ; dégeler exige son accord.
Mise en service (exploitation magot)
MAGOT_SECRETS_KEY(32 octets en base64) dans l'environnement du service : les secrets sont scellés en base (AES-256-GCM), un dump seul ne suffit pas à forger une autorisation.tsx scripts/mint-issuer-credential.ts <tenant> <libellé> <empreinte-sha256>→ identifiant + secret, affichés une fois.- mTLS :
AUTHZ_TLS_KEY,AUTHZ_TLS_CERT(certificat serveur) etAUTHZ_TLS_CA(autorité des certificats clients) ; le service écoute alors sur toutes les interfaces (AUTHZ_HOSTpour forcer). - Redémarrer le service (ou
kill -HUPsur le process node) pour charger le nouveau credential. Révocation :revoked_atpuis rechargement.