Accept a payment
Securely accept payments with your ChatGPT app.
Private Vorschau
- Accepting payments in ChatGPT apps is available to OpenAI-approved businesses in the United States.
- Payment methods supported on ChatGPT instant checkout are Cards, Apple Pay, Google Pay, and Link.
This guide demonstrates how to accept a payment within your ChatGPT app using Instant Checkout and Stripe. To learn more about this framework, see the ChatGPT Instant Checkout documentation.
Transaction diagram
Create a Stripe profile and connect your ChatGPT app
Create a Stripe profile and authorize OpenAI to connect to your Stripe account. This lets ChatGPT securely provide a shared payment token (SPT) that represents the customer’s payment details.
- Create your Stripe profile in the Stripe Dashboard.
- Accept Stripe’s agentic seller terms and click Authorize to enable OpenAI to connect to your profile.
- Copy your Network ID. You’ll need this when building your checkout requests in your ChatGPT app.
Set up Stripe
First, add the Stripe API library to your back end:
Build a buy widget with Stripe
Set up the UI for your ChatGPT app by creating a buy product MCP tool and UI resource. This flow:
- Takes a product from chat context.
- Shows product information and collects the shipping address from the customer.
- Calls
window.when the customer is ready to proceed.openai. requestCheckout
Create products and prices
In this example, you can display a checkout flow for a product in the ChatGPT app. Learn how to create products and prices in the Dashboard or with the Stripe CLI.
Hinweis
If you have your own bespoke product logic, you don’t have to create Stripe Products. Instead, replace the Stripe Product API calls in the following sections with your own product logic.
Register a buy product resource and tool in your MCP Server
Configure ChatGPT to render your checkout widget when customers prompt chat to buy a specific product.
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { readFileSync } from "node:fs"; import Stripe from "stripe"; import { z } from "zod"; const server = new McpServer({ name: "my-mcp-server", version: "1.0.0" }); // Set your secret key. Remember to switch to your live secret key in production. // See your keys here: https://dashboard.stripe.com/apikeys const stripe = require('stripe')(); const showBuyProductHTML = readFileSync("public/buy-product.html", "utf8"); const buyProductTemplateUri = "ui://widget/buy-product-template.html"; server.registerResource( "buy-product-widget", buyProductTemplateUri, { title: "Buy Product", description: "Buy Product widget", mimeType: "text/html+skybridge", }, async (uri) => ({ contents: [ { uri: uri.href, mimeType: "text/html+skybridge", text: `<html>${showBuyProductHTML}</html>`, }, ], }) ); server.registerTool( "show-buy-product-widget", { title: "Buy Product", description: "Kickstart a checkout flow for a specific product.", // Add inputs here to help you find the product id inputSchema: { product_name: z.string() }, _meta: { "openai/outputTemplate": buyProductTemplateUri }, annotations: { readOnlyHint: true } }, async () => { // Add logic here to get product id from input schema const product = await stripe.products.retrieve('prod_123456'); const amount = (await stripe.prices.retrieve(product.default_price as string)).unit_amount; return { content: [], structuredContent: { productName: product.name, amount: amount, priceID: product.default_price, }, _meta: { "openai/outputTemplate": showBuyProductHTML, }, }; } );sk_test_BQokikJOvBiI2HlWgH4olfQ2
Create the buy product UI with the Apps SDK
This UI appears when the tool from the previous step runs. The following example uses minimal markup. In a production app, you can use a framework such as React. See the ChatGPT Apps SDK documentation for more examples.
<div id="root"></div> <script> if (!window.openai?.requestCheckout) { throw new Error("requestCheckout is not available in this host"); } const root = document.getElementById("root"); let product = { name: "", amount: 0, priceID: "" }; const render = () => { root.innerHTML = ` <h1>${product.name || "Loading..."}</h1> <p>$${(product.amount / 100).toFixed(2)}</p> <form onsubmit="handleSubmit(event)"> <button type="submit">Proceed</button> </form> `; }; const handleSetGlobal = (e) => { const { productName, amount, priceID } = e.detail.globals.toolOutput ?? {}; product = { name: productName ?? "", amount: amount ?? 0, priceID: priceID ?? "" }; render(); // Re-render after updating product }; render(); window.addEventListener("openai:set_globals", handleSetGlobal, { passive: true }); </script>
Collect the shipping address and tax with Stripe
You can use the Stripe Tax API to calculate taxes used in the next step. For more information, see Collect tax.
Open the ChatGPT Instant Checkout modal
This prompts customers to select a payment method. Add logic to create a checkout session that maps to the price ID from the previous step. The following code snippet appends a UUID to the price ID to create a Checkout Session ID.
const getTax = (priceID) => { // Add your tax integration }; const createCheckoutSession = (priceID) => { const uuid = crypto.randomUUID(); return `${priceID}::${uuid}`; }; const handleSubmit = (e) => { e.preventDefault(); const { name, amount, priceID } = product; const tax = getTax(priceID); window.openai.requestCheckout({ // This is priceID passed in from the MCP buy product tool id: createCheckoutSession(priceID), payment_provider: { provider: "stripe", // Insert your Network ID from the Stripe dashboard merchant_id: networkID, supported_payment_methods: ["card"], }, status: "ready_for_payment", currency: "USD", line_items: [ { id: "line_items_123", item: { id: priceID, quantity: 1, }, base_amount: product.amount, subtotal: product.amount, tax: tax, total: product.amount + tax, }, ], totals: [ { type: "items_base_amount", display_text: product.name, amount: product.amount, }, { type: "subtotal", display_text: "Subtotal", amount: product.amount, }, { type: "tax", display_text: "Tax", amount: tax, }, { type: "total", display_text: "Total", amount: product.amount + tax, }, ], fulfillment_options: [], fulfillment_address: null, messages: [], links: [ { type: "terms_of_service", url: "https://example.com/terms", }, ], }); }
Register MCP tool to complete checkout
When the customer selects a payment method in the ChatGPT payment UI and selects Pay, ChatGPT calls your complete_ tool and returns the SPT that you use to create a PaymentIntent.
Register a complete_ MCP tool that takes a Shared Payment Granted Token as input and passes it to the Payment Intents API for processing.
const retrievePriceID = (checkout_session_id: string) => { const [priceID, uuid] = checkout_session_id.split('::'); return priceID; }; server.registerTool( "complete_checkout", { description: "Complete the checkout and process the payment", inputSchema: { checkout_session_id: z.string(), buyer: z .object({ name: z.string().nullable(), email: z.string().nullable(), phone_number: z.string().nullable(), }) .nullable(), payment_data: z.object({ token: z.string(), provider: z.string(), billing_address: z .object({ name: z.string(), line_one: z.string(), line_two: z.string().nullable(), city: z.string(), state: z.string(), country: z.string(), postal_code: z.string(), phone_number: z.string().nullable(), }) .nullable(), }), }, }, async ({checkout_session_id, buyer, payment_method}) => { const price = (await stripe.prices.retrieve(retrievePriceID(checkout_session_id as string))) // Add your tax logic const tax = getTax() // confirms the SPT stripe.paymentIntents.create({ amount: price.unit_amount + tax, currency: price.currency, shared_payment_granted_token: payment_method.token_id, confirm: true, }); return { content: [], structuredContent: { id: checkout_session_id, status: "completed", currency: price.currency, buyer, line_items: [], order: { id: "123", checkout_session_id, permalink_url: "", }, }, }; } );
Testing
Use ChatGPT’s payments test mode with a Stripe testing environment to test your app without moving real money.
- Enter a sandbox or test mode in the Stripe Dashboard.
- Create a test Stripe profile and connect to ChatGPT within the test environment, and copy your test Network ID.
- Update your ChatGPT app settings to use payments test mode so it expects test cards and generates test SPTs.
- When you request a checkout, provide your test profile ID and set
payment_tomode testso ChatGPT expects test cards and generates test SPTs.server.jswindow.openai.requestCheckout({ id: priceID, payment_mode: "test", payment_provider: { provider: "stripe", merchant_id: "profile_test", supported_payment_methods: ["card"], }, - Use your test Stripe API key in your MCP tool implementation to handle test SPTs from ChatGPT.
- Set up identical webhook configurations in your live and test environments, and make sure test webhook handlers can’t affect your production systems. For example, if your live webhook triggers shipping, the test endpoint should only log that it would have shipped in live mode.
After you complete these steps, evaluate the payments flow in your app without moving real money.
Publish your app to live mode
When you’re ready to promote your app to live mode:
- Update your MCP tool to use your Stripe live secret key (
sk_).live_ . . . - Update your app’s checkout request with your live profile ID and remove the test
payment_option.mode
Your app is then ready to handle live payments, which is required before submitting for ChatGPT app review.
Vorsicht
After you submit your ChatGPT app, don’t use test payment mode because it’s exposed to live customers.