Claude Code Payment Integration: Stripe + Sepay From A to Z (2026)
You can use Claude Code to build a real payment flow for a Next.js or Node app: pick a gateway (Stripe for international customers, Sepay/VietQR for Vietnamese customers), then let Claude Code read your repo, generate the checkout route and webhook handler, and finally you test and review. Five core steps: choose a gateway, brief Claude Code with a CLAUDE.md, create checkout, write an idempotent webhook, then test and harden. Because money is involved, a human always reviews. Never let the agent run production commands on its own.
Why use Claude Code to integrate payments?
Payment integration is the kind of work that is repetitive but easy to get wrong: creating a checkout route, handling the redirect, writing a webhook, verifying signatures, saving orders, and covering every test case. Doing it by hand from the docs eats an afternoon, and missing one detail (say, a webhook that is not idempotent) can double-charge a customer.
Claude Code's strength here is that it acts as a builder, not just a code suggester. In a single session it reads your repo structure, figures out whether you are on the App Router or Express, then generates the right API route, produces a webhook handler that matches the gateway's payload, and runs the test commands straight from your terminal. Going from "a few hours of digging through docs" to "a few minutes of reviewing a diff" is a genuine difference, especially when you have to wire up both Stripe and Sepay at once. If you have not let Claude Code write an API before, read our guide on building a backend API with Claude Code first to learn how to brief it well.
But let me say this plainly up front: payments involve real money, so a human still has to review every line. Claude Code can verify the wrong currency unit, skip an edge case, or suggest a deprecated API. Treat it like a fast junior dev - you are still the one who signs off. The principle running through this whole article: AI builds, you verify.
Choosing a gateway: Stripe vs Sepay vs Polar
Pick your gateway before you type a prompt, because each one has its own flow and payload. Three popular options for developers serving Vietnamese and global customers:
| Criteria | Stripe | Sepay | Polar |
|---|---|---|---|
| Target customers | International, credit cards | Vietnam, VND | Global SaaS |
| Methods | Cards, wallets, hosted Checkout | VietQR/NAPAS, bank transfer, cards, 44+ banks | Cards via Merchant of Record |
| Strengths | Large ecosystem, Connect for marketplaces | Smooth domestic settlement, low fees, dynamic QR | Handles global tax/VAT, subscriptions, trials |
| Best when | Selling abroad | Selling to VN customers | Selling software/subscriptions cross-border |
The short recommendation: for Vietnamese customers, use Sepay (VietQR/NAPAS, the customer scans a code to transfer, and the money lands in your bank account); for international customers paying by card, use Stripe; for selling SaaS globally when you would rather not deal with tax, use Polar, since they act as the Merchant of Record and handle VAT for you. Many apps run Stripe and Sepay side by side: overseas customers go through Stripe, domestic customers go through VietQR. This article digs into those two gateways; Polar's flow is similar to Stripe's (it has its own Next.js adapter).
What to prepare before you start
A checklist before you open Claude Code (skim it fast and fill in whatever is missing):
- A Next.js or Node/Express app that already runs locally.
- A Stripe account in test mode, or a Sepay account in sandbox (keys look like
SP-TEST-*). - Secret keys kept in
.envand never committed - add.envto.gitignorefirst. - Claude Code installed, and a
CLAUDE.mdin the repo describing your stack and conventions (we use it below). - A public endpoint for the gateway to call the webhook - locally use the Stripe CLI or a tunnel; in production use real HTTPS.
Handling secret keys is the most fragile part of payment security, so also read up on managing Claude Code permissions safely so you do not accidentally let the agent read or print a secret to the logs.
How to brief Claude Code for payment work
Output quality depends almost entirely on how you brief. For payments, the two things that matter most are context in CLAUDE.md and a specific prompt.
Add a few lines of context like this to CLAUDE.md so Claude Code does not have to guess:
# Payment context
- Stack: Next.js 15 App Router, TypeScript, Prisma + PostgreSQL
- Gateways: Stripe (international) + Sepay/VietQR (VN customers)
- Env: all secrets live in .env, NEVER commit, NEVER print to logs
- Convention: use Stripe Checkout Sessions only, NO legacy Charges/Card Element
- Safety: NEVER run production commands, NEVER refund; test mode/sandbox only
Then the prompt. Compare a vague "integrate Stripe for me" with a specific one:
Read app/ and prisma/schema.prisma. Create a POST route app/api/checkout/route.ts
that creates a Stripe Checkout Session (mode payment) from the priceId in the body
and returns session.url. Use the existing env vars. Do not touch other files.
Then document a Stripe CLI test in the README; do not run any live commands.
A specific prompt keeps Claude Code's changes scoped, uses your actual DB schema, and stops it from "inventing" extra files. You can also point it at the gateway docs via fetch/MCP (Stripe docs, developer.sepay.vn) so it follows the latest API instead of stale memory.
The Stripe flow with Claude Code (international customers)
The modern Stripe flow fits into four steps. Prefer Checkout Sessions (Stripe-hosted) and avoid legacy Charges/Card Element, since they are outdated and increase your PCI burden.
- Route to create a Checkout Session. Brief Claude Code to generate a route that takes a
priceId, creates the session, and returnssession.urlfor the client to redirect to. - Success/cancel redirect. Pass
success_urlandcancel_url; do not mark an order "paid" on the success page - wait for the webhook. - Webhook
checkout.session.completed. Verify the signature withconstructEventbefore handling any business logic. - Test with the Stripe CLI right on your local machine, no deploy needed.
// app/api/checkout/route.ts
import { NextResponse } from "next/server";
import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
export async function POST(req: Request) {
const { priceId } = await req.json();
const session = await stripe.checkout.sessions.create({
mode: "payment",
line_items: [{ price: priceId, quantity: 1 }],
success_url: `${process.env.APP_URL}/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.APP_URL}/cancel`,
});
return NextResponse.json({ url: session.url });
}
// app/api/webhooks/stripe/route.ts
import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
export async function POST(req: Request) {
const body = await req.text(); // raw body is required to verify
const sig = req.headers.get("stripe-signature")!;
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(
body, sig, process.env.STRIPE_WEBHOOK_SECRET!
);
} catch {
return new Response("Invalid signature", { status: 400 });
}
if (event.type === "checkout.session.completed") {
// idempotent: check whether event.id was already processed before writing the order
}
return new Response("ok", { status: 200 });
}
Test locally with two commands, at zero cost:
stripe listen --forward-to localhost:3000/api/webhooks/stripe
stripe trigger checkout.session.completed
For the full go-live flow and the official checklist, see the Stripe documentation (docs.stripe.com, accessed 08/2026) - verify the live API version before publishing, since Stripe updates it frequently.
The Sepay/VietQR flow with Claude Code (VN customers)
Sepay fits Vietnamese customers well: you generate a VietQR code or virtual account, the customer scans and transfers, and Sepay fires a webhook confirming the money arrived. The four-step flow:
- Create the order and generate the VietQR (or virtual account) for that order, with a transfer memo you can reconcile against.
- The customer scans the QR and transfers through their banking app.
- The Sepay webhook reports the transaction with
transferType: "in", along withtransferAmount,content, andreferenceCode. - Verify, dedupe, and return
{"success": true}in under 5 seconds so Sepay does not retry.
// app/api/webhooks/sepay/route.ts
import { db } from "@/lib/db";
export async function POST(req: Request) {
const auth = req.headers.get("authorization");
if (auth !== `Apikey ${process.env.SEPAY_API_KEY}`) {
return new Response("Unauthorized", { status: 401 });
}
const data = await req.json();
// data: { id, transferType, transferAmount, content, referenceCode }
if (data.transferType !== "in") {
return Response.json({ success: true }); // ignore outgoing transactions
}
const existed = await db.transaction.findUnique({
where: { sepayId: data.id },
});
if (existed) return Response.json({ success: true }); // dedupe by id
await db.transaction.create({
data: {
sepayId: data.id,
amount: data.transferAmount,
content: data.content,
ref: data.referenceCode,
},
});
// TODO: reconcile content with the order, then update its status to "paid"
return Response.json({ success: true });
}
Saving transactions and reconciling them against orders should live cleanly in the data layer - see how to store orders and transactions with a database to design tidy orders/transactions tables. Sepay has its own sandbox and a 2 req/s rate limit, so just let Claude Code read the official docs (developer.sepay.vn, accessed 08/2026) to stick to the correct endpoint and payload.
Webhooks and idempotency - the easiest part to get wrong
This is where manual tutorials skip ahead, and also where the damage is worst. Payment gateways do not guarantee a webhook is delivered exactly once: flaky networks, timeouts, and retries all cause the same event to arrive multiple times. If your handler is not idempotent, you write duplicate orders, credit a balance twice, or ship an item twice.
The rule: dedupe by event identifier. For Sepay, that is the transaction id; for Stripe, it is event.id. Store the processed id (a unique index in the DB) and skip it if you have seen it before. For more complex cases, use a composite key (for example orderId plus event type).
Timing and retry constraints to remember:
- Sepay: the handler must return 2xx in under 5 seconds; otherwise Sepay auto-retries up to 7 times over roughly 5 hours on a Fibonacci schedule. That means the same transaction can arrive 7 times - deduping is mandatory.
- Stripe: retries on its own schedule when it does not receive a 2xx; you still need to dedupe by
event.id.
A practical tip: save the event first, process the business logic later. Write the event record (idempotently), then run the heavy logic in a separate step, so that if the logic fails, a retry is still safe. This is a good place to ask Claude Code to write a test for the "webhook arrives twice" case - it usually forgets unless you remind it.
Testing and security before go-live
Before you turn on real money, run through this checklist (it is also what manual tutorials most often miss):
Testing:
- Stripe: use the Stripe CLI (
stripe listen/stripe trigger) and test cards; no real money involved. - Sepay: use the sandbox with
SP-TEST-*keys; simulate an incoming transaction and check the webhook. - Test the failure cases: bad signature, webhook arriving twice, amount that does not match the order.
Security:
- Never expose your secret key - server-side only, never in the client bundle, never printed to logs.
- Verify every webhook: Stripe via the HMAC signature (
constructEvent), Sepay via the API Key headerAuthorization: Apikey .... - Require HTTPS in production; consider IP whitelisting for the webhook endpoint.
- Use Checkout Sessions / PaymentIntents / SetupIntents only; avoid legacy Sources/Tokens/Charges to shrink your PCI scope. If you touch a raw PAN, you fall into the complex PCI-compliance bucket - don't.
Go-live: Stripe has its own go-live checklist (switch to live keys, configure production webhooks). Sepay requires NAPAS QR/card approval, usually 3-7 days - budget for it. Before you publish, run one more pass of a security and audit review with Claude Code to catch key leaks and verification gaps.
Ship it far faster with a prebuilt skill (AgentKit)
If you would rather not hand-write every handler, there is a faster path: the ak-payment-integration skill in the AgentKit Engineer Kit. This skill already packages all three gateways - SePay, Polar, and Stripe - covering checkout, webhook verification (with ready-made scripts), QR, subscriptions, and multi-provider orders, so Claude Code just activates it and builds in minutes instead of digging through each gateway's docs.
One line to avoid confusion: AgentKit here is the kit for Claude Code at agentkit.best (20% off via link) (the ak CLI), not OpenAI's AgentKit. The Engineer Kit is priced at $99 (the site lists no recurring fee). If you want to see what this skill actually solves, read the detailed Engineer Kit review and what AgentKit is before you decide - don't buy for one skill if you only need one flow.
Limits and cautions when you let AI touch money
Payments sit close to YMYL, so this section matters as much as the code. A few hard boundaries when you let Claude Code handle payments:
- Always review money-related code. Read the amount calculations, unit conversions, and the conditions that update an order's status carefully.
- Do not let the agent run production commands or refunds on its own. Keep Claude Code in test mode/sandbox; you press the live commands yourself after reviewing.
- Verify amounts and units. Stripe counts in the smallest unit (cents); VND has no decimal places - this is where AI often slips up on conversions.
- Test webhook edge cases thoroughly (arriving twice, wrong signature, mismatched amount) and keep logs for reconciliation when a dispute comes up.
Bottom line: AI helps you move fast, but the responsibility for money is still yours. Speed is no substitute for one clear-headed review.
Frequently asked questions (FAQ)
Will Claude Code integrate payments completely for me?
It builds most of it: reads the repo, generates the checkout route, writes the webhook handler, and writes tests. But you still have to review the code, register with the gateway yourself, configure production keys, and press the go-live command. Because money is involved, the human is the final approver.
For Vietnamese customers, should I choose Stripe or Sepay?
For VN customers paying in VND, Sepay fits better thanks to VietQR/NAPAS, transfers across 44+ banks, and money landing straight in your bank account. Stripe fits when selling internationally and taking credit cards. Many apps run both in parallel.
What do I do if the webhook never receives a transaction?
Check that the endpoint is public and HTTPS, that the signature/API Key verification is correct, and that the handler returns 2xx in under 5 seconds. Sepay auto-retries up to 7 times over roughly 5 hours, so if your handler is idempotent, a later retry still records it correctly.
How do I test the payment flow without spending real money?
Use test mode: Stripe has the Stripe CLI (stripe listen/stripe trigger) and test cards; Sepay has a sandbox with SP-TEST-* keys. Both let you simulate transactions and webhooks without touching real money.
Can I follow this article if I am not good at coding?
You need basic coding knowledge to read and review the diffs Claude Code produces - because money is on the line, you should not merge code you do not understand. Claude Code cuts the amount of manual typing, but the ability to read and understand the code is still essential.
How is the ak-payment-integration skill different from hand-writing it?
Hand-writing gives you control over every line but costs time digging through three gateways' docs. The ak-payment-integration skill packages checkout, webhook verification, QR, and subscriptions for SePay/Polar/Stripe, which builds faster; in exchange you should still review the output, since every app has its own business logic.
Conclusion and next steps
To recap the five steps: choose a gateway (Stripe for international / Sepay for VN customers), brief Claude Code with a CLAUDE.md, create checkout, write an idempotent webhook, then test and harden before going live. The make-or-break points are idempotent webhooks and one human review, because this is real money. From here, you can move on to building a complete backend API or tightening a security audit before release.
Want to build your payment flow faster? The ak-payment-integration skill in the Engineer Kit packages checkout, webhook verification, and QR for Stripe/Sepay/Polar - handy when you need to wire up several gateways without digging through each one's docs.