For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to the page URL.
Navigation principale
Theme
Vue d’ensemble Documentation utilisateur de ChatGPT et Codex Cas d’usage

Référence de l’API de paiement

Intégrez le paiement à l’interface facultative de votre plugin.

Vue d’ensemble

Les développeurs de plugins choisissent comment monétiser leur plugin. Aujourd’hui, la solution recommandée et disponible en version générale est le paiement externe. Les utilisateurs finalisent leurs achats sur le domaine du développeur. Pour le moment, seuls les plugins permettant d’acheter des biens physiques peuvent être approuvés. Nous travaillons à la prise en charge d’autres types de transactions.

Nous proposons également le paiement intégré via le formulaire de paiement de ChatGPT en version bêta à certaines places de marché partenaires. Nous prévoyons d’en élargir progressivement l’accès à d’autres places de marché et aux détaillants de biens physiques. En attendant, nous vous recommandons de diriger les utilisateurs vers votre parcours de paiement externe habituel pour finaliser leurs achats.

Méthode de monétisation recommandée

✅ Paiement externe (recommandé)

Avec le paiement externe, vous dirigez les utilisateurs de ChatGPT vers un parcours de paiement que vous hébergez sur votre site ou dans votre application. Vous y gérez les prix, les paiements, l’expédition et le traitement des commandes de biens physiques éligibles.

Nous recommandons cette méthode à la plupart des développeurs de plugins.

Fonctionnement

  1. L’utilisateur interagit avec l’interface de votre plugin dans ChatGPT.
  2. L’interface de votre plugin présente les biens physiques éligibles, par exemple avec un bouton « Acheter maintenant ».
  3. Lorsque l’utilisateur décide d’acheter, l’interface lui propose un lien vers votre parcours de paiement externe ou l’y redirige depuis ChatGPT.
  4. Vous gérez le paiement, la facturation, les taxes, les remboursements et la conformité entièrement sur votre domaine.
  5. Après l’achat, l’utilisateur peut revenir dans ChatGPT avec la confirmation de commande ou les informations de suivi.

Paiement avec un moyen de paiement enregistré

Vous pouvez intégrer à l’interface facultative de votre plugin un parcours de paiement qui permet aux clients d’utiliser les moyens de paiement qu’ils ont déjà enregistrés auprès de vous. Ce parcours peut uniquement afficher les moyens de paiement enregistrés. Il ne peut pas recueillir les données d’un nouveau moyen de paiement auprès du client.

Le client peut ainsi finaliser son achat dans ChatGPT sans être redirigé vers une interface externe.

Fonctionnement

  1. L’utilisateur interagit avec l’interface de votre plugin dans ChatGPT.
  2. L’interface présente les biens physiques éligibles et les totaux correspondants.
  3. Elle affiche les moyens de paiement éligibles que le client a déjà enregistrés auprès de vous.
  4. Le client choisit un moyen de paiement enregistré et confirme son achat dans ChatGPT.
  5. Votre serveur traite l’achat avec le moyen de paiement enregistré et renvoie une confirmation au plugin.

Paiement via le formulaire de paiement de ChatGPT (bêta privée)

Le paiement via le formulaire de paiement de ChatGPT est actuellement réservé à certaines places de marché. Seuls certains utilisateurs y ont accès.

Pour recueillir les données d’un nouveau moyen de paiement pendant l’achat, vous devez utiliser le formulaire de paiement de ChatGPT. Appelez requestCheckout avec les données de la session de paiement (lignes de commande, totaux et moyens de paiement enregistrés) pour ouvrir le formulaire. Lorsque l’utilisateur confirme l’achat, ChatGPT envoie à votre serveur MCP un jeton représentant le moyen de paiement choisi en appelant l’outil complete_checkout. Utilisez ce jeton pour encaisser le paiement via votre intégration avec votre prestataire de services de paiement (PSP), puis renvoyez les détails de la commande finalisée depuis complete_checkout.

Déroulement du paiement

  1. Le serveur prépare la session. Un outil MCP renvoie les données de la session de paiement (identifiant de session, lignes de commande, totaux et prestataire de paiement) dans structuredContent.
  2. Le widget affiche le panier. Il présente les lignes de commande et les totaux pour que l’utilisateur puisse les vérifier et les confirmer.
  3. Le widget appelle requestCheckout. Il exécute requestCheckout(session_data). ChatGPT ouvre le formulaire de paiement et affiche le montant à débiter ainsi que les différents moyens de paiement.
  4. Le serveur finalise la commande. Lorsque l’utilisateur clique sur le bouton de paiement, le widget appelle l’outil complete_checkout de votre serveur MCP. Cet outil renvoie la commande finalisée, que le widget reçoit en réponse à requestCheckout.

Session de paiement

Vous devez préparer les données de la session de paiement que l’hôte affichera. Les valeurs de certains champs, comme id et payment_provider, dépendent de votre prestataire de services de paiement et de votre système de gestion des ventes. En pratique, votre outil MCP doit renvoyer les éléments suivants :

  • Les lignes de commande et les quantités achetées par l’utilisateur.
  • Les totaux (sous-total, taxes, remises, frais et total), qui doivent correspondre aux montants calculés par votre serveur.
  • Les métadonnées du prestataire nécessaires à l’intégration avec votre PSP.
  • Les liens vers les documents juridiques et les politiques applicables (conditions générales, politique de remboursement, etc.).

Appel de requestCheckout depuis le widget

L’hôte fournit window.openai.requestCheckout. Utilisez cette méthode pour ouvrir le formulaire de paiement de ChatGPT lorsque l’utilisateur lance un achat.

Exemple :

async function handleCheckout(sessionJson: string) {
  const session = JSON.parse(sessionJson);

  if (!window.openai?.requestCheckout) {
    throw new Error("requestCheckout is not available in this host");
  }

  // Host opens the ChatGPT payment sheet.
  const order = await window.openai.requestCheckout({
    ...session,
    id: String(session.id), // Use a unique ID for every checkout session.
  });

  return order; // Host returns the order payload.
}

Dans votre composant, vous pouvez déclencher cet appel lorsque l’utilisateur clique sur un bouton :

import { useState } from "react";

function CheckoutButton({
  checkoutSessionJson,
}: {
  checkoutSessionJson: string;
}) {
  const [isLoading, setIsLoading] = useState(false);
  const [order, setOrder] = useState<Record<string, unknown> | null>(null);
  const checkoutButton = (
    <button
      onClick={async () => {
        setIsLoading(true);
        try {
          const orderResponse = await handleCheckout(checkoutSessionJson);
          setOrder(orderResponse);
        } catch (error) {
          console.error(error);
        } finally {
          setIsLoading(false);
        }
      }}
    >
      {isLoading ? "Loading..." : "Checkout"}
    </button>
  );
  return (
    <>
      {checkoutButton}
      <pre>{JSON.stringify(order, null, 2)}</pre>
    </>
  );
}

Voici un exemple complet de session de paiement que votre widget peut transmettre à l’hôte. Votre plugin fournit les champs de session ci-dessous. ChatGPT ajoute les champs gérés par l’hôte, comme merchant, logo_url, conversation_id, connector_id et ecosystem_app_uri. Renseignez le champ merchant_id avec la valeur indiquée par votre PSP.

const checkoutRequest = {
  id: "checkout_session_123",
  payment_provider: {
    provider: "stripe",
    merchant_id: "merchant_123",
    supported_payment_methods: [
      {
        type: "card",
        allowed_card_brands: ["visa", "mastercard"],
      },
      { type: "apple_pay" },
      { type: "google_pay" },
    ],
    managed_payment_methods: [
      {
        type: "card",
        id: "pm_123",
        display_name: "Visa ending in 4242",
        display_last4: "4242",
        display_brand: "visa",
      },
    ],
  },
  payment_mode: "live",
  status: "ready_for_payment",
  currency: "USD",
  metadata: {
    cart_id: "cart_123",
    merchant_order_reference: "order_ref_123",
  },
  line_items: [
    {
      id: "line_item_123",
      item: {
        id: "item_123",
        quantity: 1,
      },
      name: "Canvas backpack",
      description: "A weather-resistant everyday backpack.",
      images: ["https://merchant.example.com/images/canvas-backpack.png"],
      base_amount: 3000,
      discount: 0,
      subtotal: 3000,
      tax: 300,
      total: 3300,
    },
  ],
  totals: [
    {
      type: "items_base_amount",
      display_text: "Items subtotal",
      amount: 3000,
    },
    {
      type: "subtotal",
      display_text: "Subtotal",
      amount: 3000,
    },
    {
      type: "fulfillment",
      display_text: "Shipping",
      amount: 550,
    },
    {
      type: "tax",
      display_text: "Tax",
      amount: 300,
    },
    {
      type: "total",
      display_text: "Total",
      amount: 3850,
    },
  ],
  fulfillment_options: [
    {
      id: "standard_shipping",
      type: "shipping",
      title: "Standard shipping",
      subtitle: "Arrives in 3-5 business days",
      carrier: "USPS",
      earliest_delivery_time: "2027-01-15T15:00:00Z",
      latest_delivery_time: "2027-01-19T18:00:00Z",
      subtotal: 500,
      tax: 50,
      total: 550,
    },
  ],
  fulfillment_option_id: "standard_shipping",
  fulfillment_address: {
    name: "Jane Customer",
    line_one: "123 Main St",
    line_two: "Apt 4B",
    city: "San Francisco",
    state: "CA",
    country: "US",
    postal_code: "94107",
    phone_number: "+14155550123",
  },
  messages: [
    {
      type: "info",
      param: "fulfillment_address",
      content_type: "plain",
      content: "Free returns within 30 days.",
    },
  ],
  links: [
    { type: "terms_of_use", url: "https://merchant.example.com/terms" },
    { type: "privacy_policy", url: "https://merchant.example.com/privacy" },
    { type: "support_url", url: "https://merchant.example.com/support" },
  ],
};

const response = await window.openai.requestCheckout(checkoutRequest);

À retenir :

  • window.openai.requestCheckout(session) ouvre l’interface de paiement de l’hôte.
  • La promesse se résout avec le résultat de la commande. Elle est rejetée en cas d’erreur ou d’annulation.
  • Affichez les données JSON de la session pour que l’utilisateur puisse vérifier ce qu’il s’apprête à payer.
  • Exprimez tous les montants en nombres entiers dans la plus petite unité de la devise.
  • Utilisez payment_provider.managed_payment_methods pour les moyens de paiement que le client a déjà enregistrés auprès de vous.
  • Conservez les valeurs de metadata sous forme de chaînes de caractères.
  • Pour provider, utilisez le slug du PSP requis par votre intégration. Demandez à votre PSP quelle valeur utiliser pour merchant_id.

Mise à disposition de l’outil complete_checkout sur le serveur MCP

Vous pouvez reprendre cet exemple et y intégrer votre propre logique.

Installez fastmcp et pydantic pour exécuter cet exemple Python. L’attribut output_schema de l’outil déclare le schéma MCP outputSchema. L’attribut ToolResult.structured_content fournit structuredContent et meta fournit _meta. L’outil de test local accepte example-session et example-payment-token et renvoie un exemple de réponse sans débiter de moyen de paiement ni créer de commande.

from typing import Any, Literal

from fastmcp import FastMCP
from fastmcp.tools.tool import ToolResult
from pydantic import BaseModel

server = FastMCP("Checkout response fixture")
META_SESSION_ID = "openai/checkoutSessionId"


class Buyer(BaseModel):
    email: str


class PaymentData(BaseModel):
    # Deliberately accepts only a local fixture token, never real payment credentials.
    token: Literal["example-payment-token"]


class CompleteCheckoutOutput(BaseModel):
    id: str
    status: str
    currency: str
    line_items: list[dict[str, Any]]
    fulfillment_address: dict[str, Any]
    fulfillment_options: list[dict[str, Any]]
    fulfillment_option_id: str
    totals: list[dict[str, Any]]
    order: dict[str, Any]


@server.tool(
    description="Return the local checkout response fixture; does not charge a payment method or create a real order.",
    output_schema=CompleteCheckoutOutput.model_json_schema(),
)
async def complete_checkout(
    checkout_session_id: str,
    buyer: Buyer,
    payment_data: PaymentData,
) -> ToolResult:
    if checkout_session_id != "example-session":
        raise ValueError("This response fixture only accepts example-session")
    return ToolResult(
        content=[],
        structured_content={
            "id": checkout_session_id,
            "status": "completed",
            "currency": "USD",
            "line_items": [
                {
                    "id": "line_item_1",
                    "item": {
                        "id": "item_1",
                        "quantity": 1,
                    },
                    "base_amount": 3000,
                    "discount": 0,
                    "subtotal": 3000,
                    "tax": 300,
                    "total": 3300,
                },
            ],
            "fulfillment_address": {
                "name": "Jane Customer",
                "line_one": "123 Main St",
                "line_two": "Apt 4B",
                "city": "San Francisco",
                "state": "CA",
                "country": "US",
                "postal_code": "94107",
                "phone_number": "+1 (555) 555-5555",
            },
            "fulfillment_options": [
                {
                    "id": "fulfillment_option_1",
                    "type": "shipping",
                    "title": "Standard shipping",
                    "subtitle": "3-5 business days",
                    "carrier": "USPS",
                    "earliest_delivery_time": "2026-02-24T15:00:00Z",
                    "latest_delivery_time": "2026-02-28T18:00:00Z",
                    "subtotal": 0,
                    "tax": 0,
                    "total": 0,
                },
            ],
            "fulfillment_option_id": "fulfillment_option_1",
            "totals": [
                {
                    "type": "items_base_amount",
                    "display_text": "Items subtotal",
                    "amount": 3000,
                },
                {
                    "type": "subtotal",
                    "display_text": "Subtotal",
                    "amount": 3000,
                },
                {
                    "type": "tax",
                    "display_text": "Tax",
                    "amount": 300,
                },
                {
                    "type": "total",
                    "display_text": "Total",
                    "amount": 3300,
                },
            ],
            "order": {
                "id": "order_id_123",
                "checkout_session_id": checkout_session_id,
                "permalink_url": "",
            },
        },
        meta={META_SESSION_ID: "checkout-flow"},
        is_error=False,
    )

Adaptez cet exemple à votre système :

  • Utilisez l’intégration avec votre prestataire de services de paiement pour débiter le moyen de paiement indiqué dans payment_data.
  • Enregistrez la commande dans votre système.
  • Renvoyez les données de référence de la commande ou du reçu.
  • Incluez _meta.ui.resourceUri si vous souhaitez afficher un widget de confirmation (ChatGPT accepte également _meta["openai/outputTemplate"] comme alias de compatibilité facultatif).

Les prestataires de services de paiement suivants peuvent traiter les paiements effectués via le formulaire de paiement de ChatGPT :

Réception des données brutes des moyens de paiement (facultatif)

Si vous êtes un marchand certifié PCI DSS de niveau 1, vous pouvez recevoir directement les données brutes des moyens de paiement en implémentant le point de terminaison Delegate Payment du protocole Agentic Commerce Protocol. La requête de paiement délégué contient toutes les données du moyen de paiement nécessaires à votre parcours de paiement, notamment le numéro de carte complet, la date d’expiration, le CVC, l’adresse de facturation, les limites de l’autorisation de paiement, les signaux de risque et les métadonnées.

Voici un exemple de requête contenant les données brutes d’une carte de paiement :

{
  "payment_method": {
    "type": "card",
    "card_number_type": "fpan",
    "number": "4242424242424242",
    "exp_month": "11",
    "exp_year": "2026",
    "name": "Jane Doe",
    "cvc": "223",
    "checks_performed": ["avs", "cvv"],
    "iin": "424242",
    "display_card_funding_type": "credit",
    "display_brand": "visa",
    "display_last4": "4242",
    "metadata": {}
  },
  "allowance": {
    "reason": "one_time",
    "max_amount": 5000,
    "currency": "usd",
    "checkout_session_id": "cs_01HV3P3ABC123",
    "merchant_id": "acme_corp",
    "expires_at": "2026-02-13T12:00:00Z"
  },
  "billing_address": {
    "name": "Jane Doe",
    "line_one": "185 Berry Street",
    "line_two": "Suite 550",
    "city": "San Francisco",
    "state": "CA",
    "country": "US",
    "postal_code": "94107"
  },
  "risk_signals": [
    {
      "type": "card_testing",
      "score": 5,
      "action": "authorized"
    }
  ],
  "metadata": {
    "session_id": "sess_abc123",
    "user_agent": "ChatGPT/2.0"
  }
}

La réponse doit contenir un identifiant représentant le moyen de paiement. Cet identifiant sera transmis à complete_checkout dans payment_data.

{
  "id": "vt_01J8Z3WXYZ9ABC123",
  "created": "2026-02-12T14:30:00Z",
  "metadata": {
    "source": "agent_checkout",
    "merchant_id": "acme_corp",
    "idempotency_key": "idem_xyz789"
  }
}

Gestion des erreurs

L’appel à l’outil complete_checkout peut renvoyer des messages de type error. ChatGPT affiche dans son formulaire de paiement les messages d’erreur dont le champ code vaut payment_declined ou requires_3ds. Le widget reçoit tous les autres messages d’erreur en réponse à requestCheckout et peut les afficher comme vous le souhaitez.

Mode de paiement de test

Vous pouvez définir le champ payment_mode sur test lors de l’appel à requestCheckout. ChatGPT ouvre alors un formulaire de paiement qui accepte les cartes de test, comme la carte de test 4242. Vous pouvez traiter le token obtenu, transmis dans payment_data à l’outil complete_checkout, dans l’environnement de préproduction de votre PSP. Vous pouvez ainsi tester le parcours de bout en bout sans transférer de fonds réels.

En mode de paiement de test, vous devrez peut-être utiliser une valeur différente pour merchant_id. Consultez le guide de monétisation de votre prestataire de paiement pour en savoir plus.

Liste de vérification de l’implémentation

  1. Définissez votre modèle de session de paiement. Incluez les identifiants, l’objet décrivant le prestataire de paiement, les lignes de commande, les totaux et les liens vers les documents juridiques.
  2. Renvoyez la session depuis votre outil MCP dans structuredContent, avec le modèle de votre widget.
  3. Affichez la session dans le widget pour que l’utilisateur puisse vérifier les articles, les totaux et les conditions.
  4. Appelez requestCheckout(session_data) lorsque l’utilisateur lance le paiement. Traitez la commande renvoyée ou l’erreur éventuelle.
  5. Encaissez le paiement de l’utilisateur en implémentant l’outil MCP complete_checkout. Cet outil doit renvoyer une réponse conforme à la spécification de paiement.
  6. Testez le parcours de bout en bout avec des montants, des taxes et des remises réalistes pour vérifier que l’hôte affiche les totaux attendus.