Simulateur GTE ⇄ Frontalien

chargement…
Refuser le prochain créneau du back GTE (409) Refuser la prochaine capture (200 + card_declined) Swagger

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

La réponse (order_id + payment_url) apparaît dans le journal ci-dessous.

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

Aucune requête émise par le simulateur GTE.

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

Aucune commande — déclenche un payment-link (vrai back) ou utilise le volet GTE.

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

Aucun webhook émis.

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.

Aucun appel.

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