Sandbox
Un établissement de démonstration pour rejouer toute l'intégration — identité bancaire, ouverture d'un enfant, carte, virement, autorisation carte, événements — sans système bancaire réel. Données fictives uniquement.
Ce que magot vous remet
Un fichier de kit (JSON), par canal sûr :
| Élément | Sert à |
|---|---|
identityProvider.issuer, audience, privateJwk | fabriquer des jetons « bancaires » de test (ES256), comme le ferait votre IdP |
issuerCredential.keyId, secret | signer les appels banque → magot (autorisation carte, règlement des virements) |
eventSubscription.secret | vérifier les événements que magot pousse vers votre URL de test |
Particularités de la sandbox :
- les virements parent → enfant restent en attente : c'est vous qui les réglez (ou rejetez) avec le webhook de règlement, comme le ferait votre core banking ;
- la carte est émise par le registre interne magot : pour les autorisations,
désignez l'enfant par
child_id(en production :card_ref, la référence de la carte chez l'émetteur) ; - pas de mTLS : la signature HMAC reste obligatoire.
Pas à pas
Outils : curl, jq, openssl, uuidgen, Node 20+ (pour le jeton de test).
API=https://api-staging.magot.ch # API app
BANK=https://api-staging.magot.ch/bank # service d'autorisation (banque → magot)
KIT=./sandbox-kit.json
KEY_ID=$(jq -r .issuerCredential.keyId $KIT)
SECRET=$(jq -r .issuerCredential.secret $KIT)
1. Un jeton de votre banque, échangé contre une session magot
Jeton de test (token.mjs, après npm i jose) :
import { readFileSync } from "node:fs";
import { SignJWT, importJWK } from "jose";
const { issuer, audience, privateJwk } = JSON.parse(readFileSync(process.argv[2], "utf8")).identityProvider;
const jwt = await new SignJWT({ name: process.argv[4] ?? "Parent test" })
.setProtectedHeader({ alg: "ES256", kid: privateJwk.kid })
.setIssuer(issuer).setAudience(audience).setSubject(process.argv[3])
.setIssuedAt().setExpirationTime("15m")
.sign(await importJWK(privateJwk, "ES256"));
console.log(jwt);
BANK_TOKEN=$(node token.mjs $KIT client-001 "Camille")
SESSION=$(curl -s $API/v1/identity/token \
-d grant_type=urn:ietf:params:oauth:grant-type:token-exchange \
-d subject_token_type=urn:ietf:params:oauth:token-type:id_token \
--data-urlencode subject_token=$BANK_TOKEN | jq -r .access_token)
app() { # app <méthode> <chemin> [json]
curl -s -X "$1" "$API$2" -H "Authorization: Bearer $SESSION" \
-H "Idempotency-Key: $(uuidgen)" -H "Content-Type: application/json" ${3:+-d "$3"}
}
app GET /v1/tenant
Premier passage de client-001 : son foyer est créé (provisioned: true).
2. Ouvrir un enfant, émettre sa carte
CHILD=$(app POST /v1/children '{"display_name":"Noa","birth_date":"2016-06-15"}' | jq -r .child_id)
app POST /v1/children/$CHILD/card
3. Recharger : le virement attend votre core banking
TRANSFER=$(app POST /v1/children/$CHILD/topups '{"amount_cents":2000}' | jq -r .transfer_id)
# → "funding_status": "pending" ; le solde n'a pas bougé
Appels banque → magot, signés HMAC :
bank() { # bank <chemin> <json>
local ts sig
ts=$(date +%s)
sig=$(printf '%s.%s' "$ts" "$2" | openssl dgst -sha256 -hmac "$SECRET" | sed 's/^.*= //')
curl -s "$BANK$1" -H "Content-Type: application/json" \
-H "Magot-Key-Id: $KEY_ID" -H "Magot-Timestamp: $ts" -H "Magot-Signature: v1=$sig" -d "$2"
}
bank /webhooks/funding/transfer "{\"transfer_id\":\"$TRANSFER\",\"status\":\"settled\"}"
app GET /v1/state | jq '.children[0].wallet' # balance_cents: 2000
4. Autoriser un paiement carte
bank /webhooks/issuer/authorization \
"{\"idempotency_key\":\"$(uuidgen)\",\"child_id\":\"$CHILD\",\"amount_cents\":450,\"mcc\":\"5411\",\"merchant\":\"Kiosque\"}"
# → {"approved":true,…}
app PUT /v1/children/$CHILD/rules '{"txn_limit_cents":1000}'
bank /webhooks/issuer/authorization \
"{\"idempotency_key\":\"$(uuidgen)\",\"child_id\":\"$CHILD\",\"amount_cents\":1200}"
# → {"approved":false,"reason":"txn_limit_exceeded",…}
5. Recevoir les événements
Si une URL de test a été fournie à la création du kit, elle reçoit, dans
l'ordre, ChildCreated, CardIssued, TopUpPending, TopUpReceived,
MilestoneReached, AuthorizationApproved, RulesChanged,
AuthorizationDeclined — signés avec eventSubscription.secret
(outbound-events.md). Sans URL, le même flux se lit
côté app :
app GET "/v1/events?cursor=0" | jq '[.events[].type]'
Créer un kit (exploitation magot)
tsx scripts/sandbox-setup.ts banque-x --name "Banque X (sandbox)" --events https://… --out sandbox-banque-x.json
kill -HUP <pid node>
Le fichier contient une clé privée et des secrets : il est écrit en mode
600, et se transmet par canal sûr. scripts/sandbox-token.ts fabrique aussi
des jetons de test à partir du kit.