Skip to Content
🚀Agentic Commerce Protocol is now live! Instant Checkout is available in ChatGPT. Learn more →
DocumentationStep-by-Step TutorialsYour First Integration

Your First Integration

This tutorial walks you through creating a minimal ACP implementation. By the end, you’ll have a working integration that AI agents can use to browse products and complete purchases.

What We’re Building

A simple store with:

  • 3 products in a product feed
  • Basic checkout session management
  • Stripe payment processing

Time required: ~30 minutes

Prerequisites

  • Node.js 18+
  • npm or pnpm
  • A Stripe account (we’ll use test mode)
  • Basic JavaScript knowledge

Step 1: Project Setup

Create the project

mkdir my-acp-store cd my-acp-store npm init -y

Install dependencies

npm install express stripe dotenv uuid

Create project structure

    • index.js
    • products.js
    • checkout.js
    • .env
    • package.json

Set up environment variables

Create a .env file:

STRIPE_SECRET_KEY=sk_test_your_test_key_here PORT=3000
🔑

Get your Stripe test key from the Stripe Dashboard . Never commit real keys!

Step 2: Define Your Products

Create products.js:

// products.js const products = [ { id: 'prod_001', title: 'Classic T-Shirt', description: 'Comfortable cotton t-shirt, perfect for everyday wear.', price: 2999, // cents currency: 'USD', availability: 'in_stock', category: 'Clothing > T-Shirts', images: ['https://example.com/tshirt.jpg'], variants: [ { id: 'var_001_s', size: 'S', price: 2999 }, { id: 'var_001_m', size: 'M', price: 2999 }, { id: 'var_001_l', size: 'L', price: 2999 }, ] }, { id: 'prod_002', title: 'Wireless Earbuds', description: 'High-quality wireless earbuds with 24-hour battery life.', price: 7999, currency: 'USD', availability: 'in_stock', category: 'Electronics > Audio', images: ['https://example.com/earbuds.jpg'] }, { id: 'prod_003', title: 'Coffee Mug', description: 'Large ceramic mug, microwave and dishwasher safe.', price: 1499, currency: 'USD', availability: 'in_stock', category: 'Home > Kitchen', images: ['https://example.com/mug.jpg'] } ]; module.exports = { products };

Step 3: Create the Product Feed Endpoint

Create index.js:

// index.js require('dotenv').config(); const express = require('express'); const { products } = require('./products'); const { createCheckoutRoutes } = require('./checkout'); const app = express(); app.use(express.json()); // Product Feed Endpoint app.get('/acp/v1/products', (req, res) => { // Support optional filtering const { category, min_price, max_price, search } = req.query; let filtered = [...products]; if (category) { filtered = filtered.filter(p => p.category.toLowerCase().includes(category.toLowerCase()) ); } if (min_price) { filtered = filtered.filter(p => p.price >= parseInt(min_price)); } if (max_price) { filtered = filtered.filter(p => p.price <= parseInt(max_price)); } if (search) { const searchLower = search.toLowerCase(); filtered = filtered.filter(p => p.title.toLowerCase().includes(searchLower) || p.description.toLowerCase().includes(searchLower) ); } res.json({ products: filtered, total: filtered.length, currency: 'USD' }); }); // Single product endpoint app.get('/acp/v1/products/:id', (req, res) => { const product = products.find(p => p.id === req.params.id); if (!product) { return res.status(404).json({ error: 'Product not found' }); } res.json(product); }); // Mount checkout routes const checkoutRoutes = createCheckoutRoutes(products); app.use('/acp/v1', checkoutRoutes); const PORT = process.env.PORT || 3000; app.listen(PORT, () => { console.log(`ACP Store running on http://localhost:${PORT}`); console.log(`Product feed: http://localhost:${PORT}/acp/v1/products`); });

Step 4: Implement Checkout Sessions

Create checkout.js:

// checkout.js const express = require('express'); const { v4: uuidv4 } = require('uuid'); const Stripe = require('stripe'); const stripe = new Stripe(process.env.STRIPE_SECRET_KEY); // In-memory session storage (use a database in production!) const sessions = new Map(); function createCheckoutRoutes(products) { const router = express.Router(); // Create checkout session router.post('/checkout_sessions', async (req, res) => { try { const { cart, buyer_context } = req.body; // Validate cart items const validatedItems = []; let subtotal = 0; for (const item of cart.items) { const product = products.find(p => p.id === item.product_id); if (!product) { return res.status(400).json({ error: `Product not found: ${item.product_id}` }); } const lineTotal = product.price * item.quantity; subtotal += lineTotal; validatedItems.push({ product_id: product.id, title: product.title, quantity: item.quantity, unit_price: product.price, line_total: lineTotal }); } // Calculate totals const shipping = subtotal >= 5000 ? 0 : 500; // Free shipping over $50 const taxRate = 0.0825; // 8.25% const tax = Math.round(subtotal * taxRate); const total = subtotal + shipping + tax; // Create session const sessionId = `cs_${uuidv4().replace(/-/g, '')}`; const session = { checkout_session_id: sessionId, state: buyer_context?.shipping_address ? 'ready_for_payment' : 'not_ready_for_payment', cart: { items: validatedItems }, buyer_context: buyer_context || {}, totals: { subtotal, shipping, tax, total, currency: 'USD' }, created_at: new Date().toISOString(), expires_at: new Date(Date.now() + 30 * 60 * 1000).toISOString() // 30 min }; sessions.set(sessionId, session); res.status(201).json(session); } catch (error) { console.error('Create session error:', error); res.status(500).json({ error: 'Failed to create session' }); } }); // Get checkout session router.get('/checkout_sessions/:id', (req, res) => { const session = sessions.get(req.params.id); if (!session) { return res.status(404).json({ error: 'Session not found' }); } // Check expiration if (new Date(session.expires_at) < new Date()) { session.state = 'expired'; } res.json(session); }); // Update checkout session router.patch('/checkout_sessions/:id', (req, res) => { const session = sessions.get(req.params.id); if (!session) { return res.status(404).json({ error: 'Session not found' }); } if (session.state === 'completed' || session.state === 'expired') { return res.status(400).json({ error: 'Cannot update completed/expired session' }); } const { buyer_context, cart } = req.body; // Update buyer context (shipping address, email, etc.) if (buyer_context) { session.buyer_context = { ...session.buyer_context, ...buyer_context }; } // Recalculate if cart updated if (cart) { // ... recalculation logic (similar to create) } // Update state if we now have shipping address if (session.buyer_context?.shipping_address) { session.state = 'ready_for_payment'; } res.json(session); }); // Complete checkout session router.post('/checkout_sessions/:id/complete', async (req, res) => { const session = sessions.get(req.params.id); if (!session) { return res.status(404).json({ error: 'Session not found' }); } if (session.state !== 'ready_for_payment') { return res.status(400).json({ error: 'Session not ready for payment', current_state: session.state }); } const { payment_token } = req.body; if (!payment_token) { return res.status(400).json({ error: 'Payment token required' }); } try { // In a real implementation, you would: // 1. Verify the SPT with Stripe // 2. Capture the payment // For this demo, we'll simulate success const paymentIntent = await stripe.paymentIntents.create({ amount: session.totals.total, currency: 'usd', payment_method: payment_token, confirm: true, automatic_payment_methods: { enabled: true, allow_redirects: 'never' } }); // Update session session.state = 'completed'; session.order = { order_id: `ord_${uuidv4().replace(/-/g, '').slice(0, 16)}`, payment_intent_id: paymentIntent.id, created_at: new Date().toISOString() }; res.json({ success: true, order_id: session.order.order_id, session }); } catch (error) { console.error('Payment error:', error); res.status(400).json({ error: 'Payment failed', message: error.message }); } }); return router; } module.exports = { createCheckoutRoutes };

Step 5: Test Your Integration

Start the server

node index.js

Test the product feed

curl http://localhost:3000/acp/v1/products

Expected output:

{ "products": [ { "id": "prod_001", "title": "Classic T-Shirt", "price": 2999, ... }, ... ], "total": 3, "currency": "USD" }

Test search functionality

curl "http://localhost:3000/acp/v1/products?search=earbuds"

Create a checkout session

curl -X POST http://localhost:3000/acp/v1/checkout_sessions \ -H "Content-Type: application/json" \ -d '{ "cart": { "items": [ { "product_id": "prod_001", "quantity": 2 }, { "product_id": "prod_003", "quantity": 1 } ] }, "buyer_context": { "email": "customer@example.com", "shipping_address": { "line1": "123 Main St", "city": "San Francisco", "state": "CA", "postal_code": "94102", "country": "US" } } }'

Check session status

curl http://localhost:3000/acp/v1/checkout_sessions/YOUR_SESSION_ID

What You Built

Congratulations! You now have:

âś… Product Feed API - AI agents can discover your products âś… Checkout Sessions - Manage purchase state âś… Payment Processing - Ready for Stripe payments

Next Steps

This minimal implementation demonstrates core concepts. For production, you’ll need:

  • Database storage for sessions
  • Proper error handling
  • Request validation
  • Rate limiting
  • Authentication
  • Webhooks for order fulfillment

Continue with the next tutorials:

🎉

You’ve completed your first ACP integration! You now understand the core concepts and are ready to build production-ready implementations.

Specification maintained by OpenAI and Stripe

AboutPrivacyTermsRSS

Apache 2.0 · Open Source