Documentation Index Fetch the complete documentation index at: https://docs.agentaos.ai/llms.txt
Use this file to discover all available pages before exploring further.
A real e-commerce backend showing how to accept regulated stablecoin payments with AgentaOS.
Flow
What It Does
Customer clicks “Buy” → your server creates a checkout
Customer is redirected to the AgentaOS checkout page
Customer pays with their crypto wallet
AgentaOS sends a webhook → you fulfill the order
Customer is redirected back to your success page
Prerequisites
AGENTAOS_API_KEY=sk_live_... # Developer → API Keys
AGENTAOS_WEBHOOK_SECRET=whsec_... # Developer → Webhooks → Reveal signing secret
Full Code
import { AgentaOS , WebhookVerificationError } from '@agentaos/pay' ;
import express from 'express' ;
const app = express ();
const agentaos = new AgentaOS ( process . env . AGENTAOS_API_KEY ! );
// Simple in-memory order store (use a real database in production)
const orders = new Map ();
const sessionToOrder = new Map ();
// Parse JSON for all routes except webhooks
app . use (( req , res , next ) => {
if ( req . path === '/webhooks' ) return next ();
express . json ()( req , res , next );
});
// ─── Create Checkout ───────────────────────────────────────────
app . post ( '/checkout' , async ( req , res ) => {
const { product , amount , email } = req . body ;
// 1. Create checkout via SDK
const checkout = await agentaos . checkouts . create ({
amount: Number ( amount ),
currency: 'EUR' ,
description: product ,
buyerEmail: email ,
successUrl: 'https://myshop.com/success' ,
cancelUrl: 'https://myshop.com/cart' ,
webhookUrl: 'https://myshop.com/webhooks' ,
});
// 2. Store order locally
const orderId = crypto . randomUUID ();
orders . set ( orderId , {
id: orderId ,
product ,
amount ,
sessionId: checkout . sessionId ,
status: 'pending' ,
});
sessionToOrder . set ( checkout . sessionId , orderId );
// 3. Redirect customer to checkout
res . json ({ checkoutUrl: checkout . checkoutUrl });
});
// ─── Webhook Handler ───────────────────────────────────────────
// IMPORTANT: express.raw() preserves body for signature verification
app . post ( '/webhooks' , express . raw ({ type: 'application/json' }), ( req , res ) => {
let event ;
try {
event = agentaos . webhooks . verify (
req . body ,
req . headers [ 'x-agentaos-signature' ],
process . env . AGENTAOS_WEBHOOK_SECRET ! ,
);
} catch ( err ) {
if ( err instanceof WebhookVerificationError ) {
return res . status ( 400 ). send ( 'Invalid signature' );
}
throw err ;
}
if ( event . type === 'checkout.session.completed' ) {
const orderId = sessionToOrder . get ( event . data . sessionId );
const order = orders . get ( orderId );
if ( order && order . status === 'pending' ) {
order . status = 'paid' ;
order . txHash = event . data . txHash ;
console . log ( `Order ${ orderId } PAID. tx: ${ event . data . txHash } ` );
// YOUR BUSINESS LOGIC HERE:
// - Send confirmation email
// - Grant access to product
// - Trigger shipping
}
}
res . sendStatus ( 200 );
});
// ─── Order Status (polling fallback) ───────────────────────────
app . get ( '/order/:id' , async ( req , res ) => {
const order = orders . get ( req . params . id );
if ( ! order ) return res . status ( 404 ). json ({ error: 'Not found' });
// If still pending, check AgentaOS
if ( order . status === 'pending' ) {
const checkout = await agentaos . checkouts . retrieve ( order . sessionId );
if ( checkout . status === 'completed' ) {
order . status = 'paid' ;
}
}
res . json ({ status: order . status , txHash: order . txHash });
});
app . listen ( 3001 , () => console . log ( 'Shop running at http://localhost:3001' ));
Key Points
Don't fulfill on the success page
The successUrl redirect is best-effort. The customer might close their browser before reaching it. Always fulfill orders via the webhook, that’s server-to-server and reliable.
Use express.raw() for webhooks
Webhook signature verification needs the raw request body. If you parse JSON first (express.json()), the signature won’t match because JSON.stringify doesn’t preserve formatting.
If your webhook endpoint is down, you can poll checkouts.retrieve() as a backup. Check the session status periodically until it’s completed or expired.
Idempotent webhook handling
Webhooks may be delivered more than once. Use event.data.sessionId to check if you’ve already processed the payment before fulfilling again.
Source Code
Full source with HTML shop page and error handling:
github.com/AgentaOS/agentaos/tree/main/examples/pay-express-shop