1. Pourquoi ACP est nécessaire et pourquoi ce n’est pas « juste une autre API REST »
Si l’on regarde de manière cynique, ACP ressemble à un ensemble d’endpoints HTTP ordinaires et de structures JSON : un certain /checkout_sessions, des webhooks, des jetons. Facile de se dire : « OK, c’est encore une API personnalisée d’une plateforme de plus. » Mais l’idée d’ACP va plus loin.
ACP est conçu comme un protocole ouvert d’interaction entre trois acteurs : la plateforme d’IA (par exemple, ChatGPT), votre backend commerce et le prestataire de paiement (PSP). Son objectif est de standardiser la description des produits et des prix, la manière dont l’IA signale l’intention d’achat de l’utilisateur, la création d’une session de checkout, l’exécution du paiement et la façon dont tous les acteurs apprennent le statut final.
L’idée clé : un même backend marchand, implémentant ACP, peut potentiellement fonctionner non seulement avec ChatGPT, mais aussi avec d’autres plateformes LLM qui adopteront ce standard. Autrement dit, vous n’écrivez pas « une API spéciale pour ChatGPT », vous implémentez un protocole d’intégration commerce de nouvelle génération.
Instant Checkout dans ChatGPT — c’est la première implémentation majeure du standard ACP. ChatGPT respecte ce protocole en appelant vos endpoints ACP et en affichant à l’utilisateur une belle UI, mais les règles du jeu sont décrites dans les spécifications ACP, et non dissimulées par une quelconque « magie GPT » dans une boîte noire.
2. Les trois piliers d’ACP : Product Feed, Agentic Checkout, Delegated Payment
ACP comporte trois spécifications principales, que nous évoquerons en permanence :
| Spécification | Rôle | Où cela apparaît dans GiftGenius |
|---|---|---|
| Product Feed Spec | Format et champs du flux produits (SKU, prix, disponibilité, liens, indicateurs). | Flux JSON/CSV de cadeaux indexé par OpenAI. |
| Agentic Checkout | Contrat REST pour checkout_session : création, mise à jour, finalisation. | Notre backend ACP : endpoints /checkout_sessions et webhooks. |
| Delegated Payment | Comment les données de paiement sont transmises au marchand sous forme de jeton délégué. | Utilisation de Stripe Shared Payment Token lors de la finalisation du paiement. |
Nous avons déjà abordé Product Feed dans les leçons précédentes. Nous nous intéressons maintenant aux deux derniers blocs : Agentic Checkout et Delegated Payment.
Il est important de distinguer trois niveaux :
- Standard (SPEC). Des documents officiels décrivent quels champs et endpoints doivent exister, quels statuts sont valides et quelles garanties vous vous engagez à fournir.
- Patron d’architecture (ARCH). Par exemple, décider de stocker les SKU et les commandes dans des tables séparées, créer un service wrapper autour d’ACP ou utiliser une file pour les webhooks. Ce sont de bonnes pratiques, mais pas une partie du standard.
- Implémentation concrète (exemple GiftGenius). C’est notre projet pédagogique : la structure de nos tables, les noms exacts des types en TypeScript, comment nous journalisons les commandes, etc. Tout cela est un exemple, pas un document normatif.
Nous soulignerons constamment où s’arrête la SPEC et où commence votre architecture — pour éviter « j’ai vu dans la leçon le champ persona_tags et j’ai cru que cela faisait partie de la spec officielle ».
3. Checkout session de l’intérieur : structure et statuts
L’objet central de l’Agentic Checkout Spec est la checkout_session sur votre backend. Logiquement, c’est l’état d’un achat : quels produits, pour quel montant, avec quelles options d’exécution/livraison et dans quel statut se trouve la tentative de paiement.
La spec décrit les champs obligatoires de checkout_session à peu près ainsi (formulations simplifiées et partiellement abrégées par rapport à l’original) :
- id — identifiant de session sous forme de chaîne, que vous générez et retournez. ChatGPT l’utilisera dans tous les appels suivants.
- buyer — informations sur l’acheteur : nom, e‑mail, téléphone, parfois adresse. Dans la spec réelle, cet objet est structuré pour que le PSP et vos systèmes puissent l’utiliser de manière fiable.
- status — enum sous forme de chaîne, reflétant l’état actuel de l’achat. Statuts de base :
- not_ready_for_payment — il est encore impossible de payer (par exemple, option de livraison non choisie ou taxes non recalculées).
- ready_for_payment — tout est prêt, on peut demander le jeton de paiement et débiter.
- completed — paiement réussi, commande créée.
- canceled — achat annulé (à l’initiative de l’utilisateur ou suite à une erreur).
- currency — code devise au format ISO 4217 en minuscules ("usd", "eur", etc.).
- line_items — liste des lignes de panier, chacune avec son SKU, sa quantité et son coût calculé.
- fulfillment_address — adresse d’exécution/livraison (si pertinent).
- fulfillment_options et fulfillment_option_id — options possibles d’exécution/livraison et l’option actuellement choisie.
- totals — montants agrégés : coût des biens, taxes, livraison, montant total.
- order — objet décrivant la commande qui sera créée après la session réussie.
- messages — liste de messages utilisateur que ChatGPT peut afficher à l’acheteur : avertissements, erreurs, etc.
- links — liste de liens, par exemple vers la politique de retour, la politique de confidentialité et les conditions d’utilisation.
Nous n’avons pas besoin d’implémenter tous les champs dans la démo, mais l’idée est essentielle : une checkout_session est « l’historique et l’état courant d’une tentative d’achat », et ChatGPT s’attend à y trouver tout ce qui est nécessaire pour un UX correct.
Pour simplifier, introduisons dans notre code pédagogique un type simplifié :
// Modèle simplifié de checkout_session pour GiftGenius (pas la SPEC complète)
type GGCheckoutStatus = 'not_ready_for_payment' | 'ready_for_payment' | 'completed' | 'canceled';
type GGLineItem = { skuId: string; quantity: number; total: number };
type GGCheckoutSession = {
id: string;
status: GGCheckoutStatus;
currency: 'usd';
lineItems: GGLineItem[];
grandTotal: number;
};
Ce modèle est volontairement plus simple que l’officiel, mais il est parfait pour la pratique : apprendre à garder en tête les statuts et les transitions sans se noyer dans une centaine de champs.
4. Cycle de vie d’une checkout_session
La spécification Agentic Checkout décrit plusieurs opérations sur une checkout_session. Sous forme simplifiée, le cycle de vie ressemble à ceci :
- Création de la session : POST /checkout_sessions.
- Mise à jour de la session : POST /checkout_sessions/{id}.
- Finalisation de la session (complete) : POST /checkout_sessions/{id}/complete.
- (Parfois) Annulation : endpoint cancel séparé ou passage à canceled via une mise à jour.
Vu du point de vue des états, on peut dessiner le diagramme suivant :
stateDiagram-v2
[*] --> not_ready_for_payment
not_ready_for_payment --> ready_for_payment: calcul de la livraison/taxes
choix des options
ready_for_payment --> completed: POST /complete réussi
ready_for_payment --> canceled: annulation par l’utilisateur ou erreur
not_ready_for_payment --> canceled: erreur, données incompatibles
Création d’une checkout_session commence généralement à l’état not_ready_for_payment ou directement ready_for_payment, si tout ce qui est nécessaire au paiement est déjà connu (par exemple, produit numérique sans livraison ni taxes). Les mises à jour servent à ajouter des données (adresse, coupons, option d’exécution) et à recalculer les montants. La finalisation — c’est le moment où intervient le Delegated Payment et où l’argent est réellement débité.
Il est important de bien comprendre le partage des rôles :
- ChatGPT initie la création, les mises à jour et la finalisation de la session, en se basant sur le dialogue avec l’utilisateur.
- Votre backend (marchand) est responsable de la logique métier correcte : vérification des SKU, disponibilités, calcul des prix et des taxes, changements de statuts, création des commandes.
- Le PSP (Stripe, etc.) effectue le paiement réel et délivre un Shared Payment Token que le marchand utilise pour débiter les fonds.
Un peu plus loin, nous superposerons à ce diagramme d’états des requêtes HTTP concrètes et de petits exemples de code.
5. Création d’une checkout_session : ce que ChatGPT attend précisément de nous
Quand ChatGPT (ou un agent) décide que l’utilisateur veut réellement acheter quelque chose, il forme les line items à partir du Product Feed : liste des SKU, quantités, devise envisagée et, éventuellement, préférences de livraison. Il appelle ensuite votre endpoint POST /checkout_sessions.
Côté marchand, il faut alors :
- Valider les données entrantes : s’assurer que tous les SKU existent, sont vendables et ne violent pas la politique (par exemple, pas d’alcool pour un mineur).
- Calculer les prix et les taxes selon vos propres règles.
- Préparer les options d’exécution/livraison (fulfillment options), si le produit est physique.
- Retourner une checkout_session correcte avec statut et montants.
Un gestionnaire minimal sur Express pour GiftGenius peut ressembler à ceci :
// Pseudocode : création d’une checkout_session simplifiée
app.post('/checkout_sessions', async (req, res) => {
const items = req.body.lineItems as GGLineItem[]; // skuId + quantity
const pricedItems = await priceItems(items); // calculer total pour chaque SKU
const grandTotal = sum(pricedItems.map(i => i.total));
const session: GGCheckoutSession = {
id: generateId(),
status: 'ready_for_payment', // pour des cadeaux numériques, on peut être prêt à payer immédiatement
currency: 'usd',
lineItems: pricedItems,
grandTotal,
};
res.status(201).json(session);
});
Ici, nous faisons plusieurs choses :
- Nous ne faisons pas confiance aux prix entrants du client (ChatGPT) et les recalculons selon nos données — c’est critique pour la sécurité du commerce.
- Nous générons notre propre id de session (par exemple, préfixe gg_chk_...).
- Nous retournons le statut ready_for_payment s’il n’y a pas d’étapes supplémentaires (pas de livraison, taxes automatiques, modèle simple).
Dans un backend compatible ACP réel, vous retournerez en plus messages, links et l’objet composite totals, et vous remplirez order (au moins en brouillon), comme décrit dans la spécification.
6. Mise à jour de la checkout_session et idempotence
Après la création de la session, ChatGPT peut demander des détails supplémentaires à l’utilisateur : adresse de livraison, application d’un coupon, changement d’option d’exécution. Une fois ces données disponibles, la plateforme appelle POST /checkout_sessions/{id} pour que vous mettiez à jour les calculs.
Du point de vue du code, c’est très similaire à la création, mais au lieu de générer une nouvelle session, vous :
- retrouvez l’existante via son id ;
- appliquez les changements (par exemple, modifier fulfillment_option_id ou ajouter une remise) ;
- recalculez les montants ;
- retournez la checkout_session mise à jour.
Il est important que la spécification autorise des appels répétés (à cause d’erreurs réseau ou de répétitions côté ChatGPT). Par conséquent, comme dans les modules antérieurs où nous avons parlé de l’idempotence des outils et des webhooks, il est recommandé d’utiliser Idempotency-Key dans les en‑têtes de requête et de gérer proprement les répétitions.
Un gestionnaire de mise à jour pourrait ressembler à ceci :
app.post('/checkout_sessions/:id', async (req, res) => {
const id = req.params.id;
const key = req.header('Idempotency-Key'); // même key => même effet
const existing = await loadSessionWithIdempotency(id, key, req.body);
// applyUpdates peut recalculer les prix, la livraison, etc.
const updated = await applyUpdates(existing, req.body);
await saveSession(updated, key);
res.json(updated);
});
Ici, nous ne suivons pas strictement la structure exacte de la SPEC, mais illustrons l’idée : en entrée — des changements et une clé idempotente ; en sortie — un état cohérent de checkout_session. Si une requête identique arrive avec la même clé, vous devez renvoyer le même résultat, sans créer de commandes en trop ni de doublons dans les logs.
7. Finalisation de la checkout_session et Delegated Payment : fonctionnement du Shared Payment Token
Le moment le plus intéressant et stressant — la finalisation de la checkout_session, quand l’argent est effectivement débité. C’est ici qu’entre en jeu la deuxième spécification : Delegated Payment.
Idée de Delegated Payment
L’utilisateur saisit ou choisit ses moyens de paiement dans l’interface de ChatGPT (carte, wallet, moyen de paiement enregistré). La plateforme ne vous envoie pas ces données directement — elle demande plutôt au PSP (par exemple Stripe) un jeton spécial, le Shared Payment Token (SPT), qui :
- est lié de façon univoque au marchand et à la session spécifique ;
- est limité en montant et en durée de vie ;
- ne vous révèle pas le vrai numéro de carte.
Au final, on obtient la situation suivante :
| Acteur | Voit les données de carte | Voit le Shared Payment Token | Voit les détails de commande (SKU, montants) |
|---|---|---|---|
| Utilisateur | Oui (il les saisit dans l’UI) | Non (inutile) | Partiellement (ce qu’il achète et pour quel prix) |
| ChatGPT/OpenAI | Oui (dans le processus de paiement) | Oui | Oui |
| PSP (Stripe) | Oui | Oui | Dans le cadre du paiement |
| Marchand | Non | Oui | Oui |
Cette conception permet au marchand d’éviter de stocker des données de paiement et de se concentrer sur la logique métier de la commande, en laissant la conformité au PSP et à la plateforme.
Insight
Le sens du Shared Payment Token est de cacher à votre backend les données de carte, tout en vous laissant exécuter le paiement. Mais on peut aussi le voir un peu différemment.
Vous avez sans doute déjà vu des situations où un magasin ou un hôtel effectue d’abord une pré‑autorisation (hold) sur votre carte, puis débite plus tard. Considérez le Shared Payment Token comme un jeton de pré‑autorisation. ChatGPT a bloqué les fonds sur le compte de l’utilisateur, mais ne les a pas débités. Il vous a transmis ce jeton de pré‑autorisation et vous pouvez maintenant l’envoyer à Stripe pour débiter.
Il y a deux nuances importantes :
- les montants de la pré‑autorisation et du débit ne doivent pas trop diverger, idéalement ils doivent coïncider ;
- vous pouvez vendre via ChatGPT le premier mois d’abonnement à 1 $, puis prélever 49,99 $ chaque mois.
Requête POST /checkout_sessions/{id}/complete
Quand l’utilisateur appuie sur le bouton de confirmation de paiement dans Instant Checkout, ChatGPT :
- Demande un SPT au PSP (par exemple via l’API ACP de Stripe).
- Envoie ce jeton à votre backend via POST /checkout_sessions/{id}/complete avec les données de l’acheteur.
La spec décrit le corps de la requête à peu près ainsi (exemple adapté et abrégé de la documentation officielle) :
POST /checkout_sessions/checkout_session_123/complete
{
"buyer": {
"first_name": "John",
"last_name": "Smith",
"email": "johnsmith@mail.com"
},
"payment_data": {
"token": "spt_123",
"provider": "stripe"
}
}
Votre backend doit alors :
- Trouver la checkout_session avec l’id checkout_session_123.
- Vérifier que le statut permet une finalisation (généralement ready_for_payment).
- Créer un paiement chez le PSP en utilisant le jeton spt_123 (la méthode dépend du PSP, dans le cas de Stripe — un endpoint et un type de moyen de paiement spécifiques).
- Attendre la confirmation de l’opération de paiement.
- Mettre à jour la checkout_session en completed, créer et enregistrer la commande, renseigner le champ order dans la structure de session.
- Retourner la checkout_session à jour dans la réponse.
En TypeScript très simplifié, cela pourrait ressembler à :
app.post('/checkout_sessions/:id/complete', async (req, res) => {
const { id } = req.params;
const { buyer, payment_data } = req.body;
const session = await loadSession(id);
await chargeWithSharedToken(payment_data.token, session.grandTotal);
const completed = await markSessionCompleted(session, buyer);
res.json(completed);
});
Dans le monde réel, entre ces lignes se cachent la gestion des erreurs, les nouvelles tentatives, le logging et l’intégration avec votre modèle de commandes.
Si quelque chose se passe mal (par exemple, paiement refusé), vous devez retourner une checkout_session avec le statut not_ready_for_payment ou canceled et remplir messages de sorte que ChatGPT puisse expliquer correctement à l’utilisateur ce qui s’est passé.
8. Instant Checkout dans ChatGPT : comment tout s’assemble en un seul flux
Assemblons maintenant ces pièces en un scénario cohérent « de l’intention au paiement » dans ChatGPT. Vous pouvez voir cette leçon comme le « décodage » de ce qui se cache derrière le bouton « Acheter » du widget.
Scénario simplifié :
- L’utilisateur écrit : « Propose un cadeau numérique pour un ami jusqu’à 50 $ et finalise l’achat tout de suite. »
- L’agent (ou l’app ChatGPT elle‑même) utilise le Product Feed pour trouver des SKU adaptés au budget.
- ChatGPT affiche dans le chat plusieurs cartes de cadeaux (via votre widget GiftGenius) et propose d’en choisir un.
- Après le choix, ChatGPT forme les line items et appelle POST /checkout_sessions sur votre backend ACP, obtenant une checkout_session avec montants et statut.
- Dans l’UI Instant Checkout, l’utilisateur voit le montant final, le nom du produit, la politique de retour et le bouton de confirmation.
- À la confirmation, ChatGPT obtient un Shared Payment Token auprès du PSP et appelle POST /checkout_sessions/{id}/complete, comme évoqué plus haut.
- Votre backend exécute le paiement, crée la commande, retourne une checkout_session au statut completed.
- ChatGPT affiche une confirmation à l’utilisateur, et votre backend (via les webhooks de l’Agentic Checkout Spec) peut envoyer un événement à OpenAI pour que la plateforme connaisse le sort de la commande.
Sous forme de diagramme de séquence, cela ressemble à :
sequenceDiagram
actor U as Utilisateur
participant GPT as ChatGPT
participant GG as Backend ACP GiftGenius
participant PSP as Stripe (PSP)
U->>GPT: Je veux un cadeau jusqu’à 50 $ et l’acheter ici même
GPT->>GG: POST /checkout_sessions (line_items)
GG-->>GPT: checkout_session (ready_for_payment)
GPT->>U: Affiche Instant Checkout (produit, prix, CGU)
U->>GPT: Appuie sur « Confirmer le paiement »
GPT->>PSP: Demande un SPT pour le montant et le marchand
PSP-->>GPT: Shared Payment Token (spt_xxx)
GPT->>GG: POST /checkout_sessions/{id}/complete (token + buyer)
GG->>PSP: Paiement avec SPT
PSP-->>GG: Paiement réussi
GG-->>GPT: checkout_session (completed + order)
GPT-->>U: Affiche la confirmation d’achat
Dans ce scénario, il n’y a nulle part un appel « arbitraire » à votre base de données ou à des endpoints internes étranges. Tout s’inscrit dans un contrat ACP strictement décrit, où chaque acteur connaît son rôle.
9. Mini-pratique : un backend ACP simplifié pour GiftGenius
Pour que cette leçon ne reste pas purement théorique, il est important de « faire défiler » mentalement l’implémentation de la couche ACP pour notre projet pédagogique.
Imaginez que GiftGenius dispose déjà de :
- Une base de SKU et de prix à partir de laquelle nous formons le Product Feed (nous l’avons modélisée dans les leçons précédentes).
- Un modèle de commande simple : table orders avec champs id, userId, skuId, amount, currency, status, createdAt.
- L’interface de l’app ChatGPT et une couche MCP capable de recommander des cadeaux (construite dans les modules précédents du cours).
Votre tâche maintenant — ajouter par‑dessus un petit service gg-acp :
- Endpoint POST /checkout_sessions :
- Prend la liste des SKU et des quantités.
- Recalcule les montants sur la base de votre BD.
- Crée une commande brouillon (par exemple, statut pending) et une checkout_session au statut ready_for_payment.
- Retourne la checkout_session.
- Endpoint POST /checkout_sessions/{id} :
- Retrouve la session et la commande.
- Applique les changements (par exemple, support d’un code promo réduisant le total).
- Retourne la checkout_session mise à jour.
- Endpoint POST /checkout_sessions/{id}/complete :
- Reçoit le SPT, le montant et les données de l’acheteur.
- Dans la version démo, peut simplement marquer la commande comme « payée » sans véritable appel d’intégration au PSP (ou vous pouvez simuler Stripe).
- Met à jour la checkout_session au statut completed et lui associe order_id.
Tout ce service peut être implémenté dans une petite application Node/Express ou via des endpoints Next.js App Router. L’essentiel — respecter le contrat de format et de statuts, même si vous émulez le paiement.
Un modèle de commande en TypeScript pourrait ressembler à :
// Modèle de commande simplifié pour GiftGenius
type GGOrderStatus = 'pending' | 'paid' | 'canceled';
type GGOrder = {
id: string;
userId: string;
skuId: string;
amount: number;
currency: 'usd';
status: GGOrderStatus;
};
En production, vous ajouterez par‑dessus des liens avec votre Auth/Identity (pour savoir quel utilisateur est dans le chat), des webhooks vers OpenAI et des scénarios de retours plus complexes. Mais comme étape d’apprentissage dans le cadre de cette leçon, il suffit de savoir boucler avec assurance : créer une session → mettre à jour → finaliser, sans perdre ni l’argent ni le bon sens.
10. Erreurs typiques lors de la conception ACP / Instant Checkout
Erreur n° 1 : mélange des rôles (« ChatGPT — c’est ma boutique »).
Parfois, les développeurs érigent mentalement ChatGPT en « système central de tenue des comptes » et tentent de stocker l’état métier de la commande côté plateforme : « il y a bien une checkout_session, je lirai donc l’historique des commandes depuis OpenAI ». C’est une impasse. Une checkout_session est un objet de protocole, pas une source de vérité sur les commandes. La source de vérité — c’est votre backend commerce : c’est là que doivent vivre les commandes, statuts, retours et rapports. ChatGPT dans ce schéma n’est qu’un « frontend de confiance dans le chat ».
Erreur n° 2 : faire confiance aux prix entrants de ChatGPT.
On peut penser : « l’agent a déjà choisi les SKU et même calculé le montant, prenons simplement ce montant et débitons ». À ne pas faire. L’entrée venant de ChatGPT (line items, prix envisagés) doit être perçue comme une proposition, pas comme un ordre. Votre backend doit vérifier lui‑même les SKU, les prix, la disponibilité, l’applicabilité des remises, etc., en les comparant avec le Product Feed et votre BD. Sinon, vous rencontrerez une classe amusante de bugs « l’utilisateur a acheté le produit pour 0,01 $ parce que le modèle a arrondi ».
Erreur n° 3 : ignorer les statuts et la machine d’états.
Dans les premiers prototypes, on voit souvent une implémentation « trouée » : le statut de la session est toujours completed, ou simplement ok, et tout écart avec l’état réel du paiement est masqué. Au final, ChatGPT ne peut pas montrer correctement à l’utilisateur ce qui se passe : paiement en cours, déjà terminé ou annulé. Il est bien plus fiable d’implémenter honnêtement la machine d’états not_ready_for_payment → ready_for_payment → completed/canceled et de renvoyer le statut réel depuis le backend, plutôt que d’inventer des champs ad hoc.
Erreur n° 4 : utiliser le Shared Payment Token comme une « carte réutilisable ».
Par conception, le SPT est un jeton à usage unique ou strictement limité : il est lié à une opération, un montant et un marchand précis. Tenter de le mettre en cache « au cas où » ou de le réutiliser pour un autre achat — mauvaise idée. Au mieux, le PSP refusera la seconde tentative ; au pire, vous embrouillerez la comptabilité des paiements et des commandes. Chaque checkout_session.complete doit avoir son jeton frais ; si le paiement échoue — il faut en demander un nouveau.
Erreur n° 5 : absence d’idempotence dans /checkout_sessions et les webhooks.
Dans un réseau réel, les requêtes peuvent être dupliquées : ChatGPT peut répéter un POST /checkout_sessions après un timeout, le PSP peut renvoyer un webhook après une erreur temporaire. Si votre implémentation crée à chaque fois une nouvelle commande et une nouvelle entrée en base, vous obtiendrez vite le chaos : doubles débits, doublons de commandes et divergences étranges entre systèmes. L’utilisation de Idempotency-Key, la détection des répétitions et la conservation des résultats des appels précédents — ce n’est pas « une optimisation optionnelle », mais un élément indispensable d’une intégration ACP fiable.
Erreur n° 6 : oublier le lien avec le Product Feed.
Parfois, la couche ACP est conçue « dans le vide » : les SKU et les prix viennent de tables internes qui ne correspondent pas à ce qui alimente le Product Feed. Résultat : ChatGPT montre une chose (selon le feed), et au checkout via ACP il passe tout autre chose. Pour éviter ces surprises, il est essentiel que votre modèle de SKU et de prix soit unifié : le feed, le backend ACP et la base interne doivent s’appuyer sur la même source de vérité, même si des projections et caches différents existent au‑dessus.
GO TO FULL VERSION