Simulateur GTE ──▶ Frontalien
Joue le rôle du back GTE : émet les requêtes partenaire sans le vrai back. Cible par défaut = l'API du simulateur (boucle locale) ; renseigner une URL pour viser une vraie sandbox Frontalien.
Accompagnement envoyé depuis le GTE
Scénarios en un clic
Utilise la cible et la clé ci-dessus — pensé pour tester une API Frontalien sans le back GTE. Nominal : commande → page d'enregistrement de carte (onglet ouvert) → capture automatique dès que le setup aboutit (poll 3 s, timeout 5 min). Abandon : commande → release.
Requêtes unitaires
La capture envoie {amount: {value, currency: "CHF"}} avec le montant du champ
ci-dessus et Idempotency-Key: capture-{order_id} (comme le fera le back GTE).
Journal des requêtes émises
Simulateur Frontalien ──▶ GTE
Joue le rôle du prestataire : API partenaire qui reçoit les commandes (du vrai back GTE ou du volet ci-dessus), page Saferpay simulée (enregistrement de carte), capture synchrone, et webhooks signés vers le GTE.
Commandes reçues
Webhook Frontalien → GTE (déclenchement manuel)
En v2, les payment.* ne servent qu'à piloter le back GTE actuel
(webhook-driven) en attendant son adaptation à la capture ; les livrables
(invoice.ready…) sont acquittés sans logique métier. « Id de l'accompagnement » = order UUID
ou booking id GTE (résolu depuis les commandes connues) ; une valeur inconnue part telle quelle.
Journal des webhooks envoyés
Journal complet — requêtes GTE → Frontalien (payload · code HTTP · réponse)
Tous les appels vers l'API partenaire : du vrai back GTE back GTE ou du simulateur GTE simulateur GTE.
Référence du contrat v2 — Swagger complet
POST /partner/orders — requête envoyée par le GTE (setup, aucun débit)
{
"external_ref": "<booking_id GTE>",
"member": { "first_name": "…", "last_name": "…", "email": "…", "phone": "…",
"address": { "line": "…", "postal_code": "…", "city": "…", "country": "FR" } },
"slot_start": "2026-11-06T09:00:00+01:00",
"amount": { "value": 12000, "currency": "CHF" },
"line_items": [ { "label": "Base", "amount": 10000 }, { "label": "Option", "amount": 2000 } ],
"return_url": "https://<front>/accompagnements/<id>/confirmation?payment=success&booking_id=…"
}
// en-têtes : Authorization: Bearer <clé> · Idempotency-Key: <booking_id>
POST /partner/orders — réponses attendues (201 / 409)
// 201 — créneau bloqué 15 min, page Saferpay d'ENREGISTREMENT DE CARTE
{ "order_id": "<uuid>", "status": "setup_pending", "setup_status": "pending",
"slot_start": "…", "slot_end": "…",
"hold_expires_at": "<création + 15 min>",
"payment_url": "https://…/pay/<uuid>" }
// 409 — créneau pris entre l'affichage et l'envoi
{ "error": "slot_unavailable" }
GET /partner/orders/{id} — état consolidé (avant capture)
{ "order_id": "<uuid>", "external_ref": "<booking_id>",
"setup_status": "pending | succeeded | failed", // enregistrement de carte
"payment_status": "uncaptured | paid | failed", // uncaptured = carte OK, pas débitée
"appointment_status": "slot_held | confirmed | expired",
"slot_start": "…", "slot_end": "…",
"invoice": {…}, "requirements": {…}, "declaration": {…} }
POST /partner/orders/{id}/capture — encaissement synchrone (miroir du PaymentIntent Stripe)
// requête — en-têtes : Authorization · Idempotency-Key: capture-<order_id>
{ "amount": { "value": 12000, "currency": "CHF" } }
// 200 — succès
{ "order_id": "<uuid>", "payment_status": "paid", "capture_id": "cap_…" }
// 200 — refus bancaire (résultat métier, PAS une erreur de transport)
{ "order_id": "<uuid>", "payment_status": "failed", "failure_reason": "card_declined" }
// 409 { "error": "setup_not_succeeded" } — setup incomplet/abandonné, hold expiré, ou release
// 409 { "error": "already_captured" } — déjà capturée avec une autre clé d'idempotence
// 422 { "error": "amount_mismatch" } — montant ≠ montant de la commande
POST /partner/orders/{id}/release — libérer créneau + invalider le setup non capturé
// 204 sans corps — 404 { "error": "not_found" } toléré par le GTE
// appelé sur : abandon utilisateur, setup échoué, capture refusée, reschedule
Enveloppe webhook (POST signé vers le GTE — payment.* en transition uniquement)
// en-tête : X-Taxflowpro-Signature: t=<unix>,v1=<hmac_sha256(secret, "{t}.{corps}")>
{ "id": "evt_…", "type": "payment.succeeded", "occurred_at": "…",
"order_id": "<uuid>", "external_ref": "<booking_id>",
"data": { "amount": 12000, "currency": "CHF", "slot_start": "…" } }
// le GTE : déduplique sur id · rejette t > 5 min · vérifie amount ≠ devis → pas de confirmation