# Accept a payment Accept payments with your MCP app. # Redirect Collect payments outside your app with a prebuilt Stripe-hosted Checkout page. This guide shows how to: - Define Model Context Protocol (MCP) tools to display products and let customers select items to buy - Collect payment details with [a prebuilt Stripe-hosted Checkout page](https://docs.stripe.com/payments/checkout.md) - Monitor webhooks after a successful payment ## Set up Stripe Add the Stripe API library to your back end. #### Ruby ```bash # Available as a gem sudo gem install stripe ``` ```ruby # If you use bundler, you can add this line to your Gemfile gem 'stripe' ``` ## Create products and prices This example displays a group of products in the MCP app. [Create products and prices in the Dashboard or with the Stripe CLI](https://docs.stripe.com/products-prices/manage-prices.md). ## Register a Checkout MCP tool Register an MCP tool that creates a [Checkout Session](https://docs.stripe.com/api/checkout/sessions.md) for a set of *Prices* (Prices define how much and how often to charge for products. This includes how much the product costs, what currency to use, and the interval if the price is for subscriptions). You call this tool from the MCP app in a later step. ```javascript import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { registerAppTool, registerAppResource, RESOURCE_MIME_TYPE, } from "@modelcontextprotocol/ext-apps/server"; import { readFileSync } from "node:fs"; import Stripe from "stripe"; import { z } from "zod"; // Follow https://docs.stripe.com/keys-best-practices to protect your Stripe API keys. const stripe = new Stripe(process.env.STRIPE_API_KEY); const server = new McpServer({ name: "my-mcp-server", version: "1.0.0" }); const resourceUri = "ui://list-products.html"; async function createCheckoutSession(priceIds) { const lineItems = priceIds.map((price) => ({ price, quantity: 1 })); const session = await stripe.checkout.sessions.create({ mode: "payment", line_items: lineItems, success_url: "https://example.com/checkout/success", }); return session; } // Register the tool that creates a checkout session server.registerTool( "buy-products", { title: "Buy products", description: "Create a checkout page link for purchasing the selected products", inputSchema: { priceIds: z.array(z.string()) }, }, async ({ priceIds }) => {const session = await createCheckoutSession(priceIds); return { content: [ { type: "text", text: `[Complete your purchase here](${session.url})`, }, ], structuredContent: { checkoutSessionId: session.id, checkoutSessionUrl: session.url, }, }; } ); ``` ## Register a UI tool and resource Set up the UI for your MCP app by registering an MCP tool and resource. This UI: 1. Displays a list of products 1. Lets the customer select products to buy 1. Redirects to Stripe Checkout to complete payment ### Register a product list MCP tool Create a product list MCP tool. Its callback returns the price IDs for the products to display in the UI. ```javascript registerAppTool( server, "list-products", { title: "List products", description: "List the products available for purchase", _meta: { ui: { resourceUri } }, }, async () => { const suggestedProducts = [ // The price IDs from the earlier step { priceId: "{{PRICE_ID}}", name: "Test product 1" }, { priceId: "{{PRICE_ID}}", name: "Test product 2" }, ]; return { structuredContent: { products: suggestedProducts }, content: [], }; } ); ``` ### Register a product list UI resource Create an MCP resource for the product list widget. It defines the UI code that displays the products. ```javascript // Register the resource that serves the bundled HTML registerAppResource( server, "list-products-widget", resourceUri, { mimeType: RESOURCE_MIME_TYPE }, async () => { const html = readFileSync("dist/ui/list-products.html", "utf8"); return { contents: [ { uri: resourceUri, mimeType: RESOURCE_MIME_TYPE, text: html, }, ], }; } ); ``` This example uses minimal markup. In a production app, use a framework such as React. See the [MCP Apps documentation](https://modelcontextprotocol.github.io/ext-apps/) for additional examples. ```html
``` ```js import { App } from "@modelcontextprotocol/ext-apps"; const app = new App({ name: "ProductList", version: "1.0.0" }); // Establish communication with the host await app.connect(); /** * UI markup and event handlers */ const renderProduct = (product) => { return ` `; }; const handleSubmit = async (event) => { // We'll fill this in next } const renderApp = (products) => { const root = document.querySelector("#root"); root.innerHTML = `

Select products to purchase

${products.map(renderProduct).join("")}
`; document .querySelector("#product-form") ?.addEventListener("submit", handleSubmit); }; /** * Render the list of products from the tool's structuredContent */ app.ontoolresult = (params) => { const { products } = params.structuredContent ?? {}; if (products) { renderApp(products); } }; ``` ## Redirect to Checkout When the customer clicks **Buy** in the MCP app: 1. Call your “buy products” tool to create a Checkout Session URL. 1. Open the Checkout Session URL. Update the `handleSubmit` function: ```javascript const handleSubmit = async (event) => { event.preventDefault(); const formData = new FormData(event.target); const priceIds = Array.from(formData.values()); // Call the buy-products tool to create a Checkout Session const { structuredContent } = await app.callServerTool({ name: "buy-products", arguments: { priceIds }, }); if (typeof structuredContent?.checkoutSessionUrl === "string") { await app.openLink({ url: structuredContent.checkoutSessionUrl }); } }; ``` ## Handle successful orders Handle successful orders by listening to `checkout.session.completed` (and `checkout.session.async_payment_succeeded` for delayed methods) and calling an idempotent fulfillment function that retrieves the Checkout Session (expand `line_items`), verifies payment_status, and fulfills the items. Use a `success_url` landing page to trigger fulfillment immediately after redirecting, but rely on webhooks to make sure that every payment is fulfilled. Test locally with the Stripe CLI, then deploy your webhook endpoint. For step-by-step instructions, see [Fulfill payments received with the Checkout Sessions API](https://docs.stripe.com/checkout/fulfillment.md?payment-ui=stripe-hosted). ## See also - [Collect taxes](https://docs.stripe.com/payments/checkout/taxes.md) - [Collect shipping and other customer info](https://docs.stripe.com/payments/checkout/collect-additional-info.md) - [Customize your branding](https://docs.stripe.com/payments/checkout/customization.md) - [Customize your success page](https://docs.stripe.com/payments/checkout/custom-success-page.md)