To build an ecommerce site with AI, you have a coding agent write the product catalogue, cart and checkout step by step; a typical setup is Next.js, a database and a payment provider such as Stripe. The agent writes code fast, but you have to set the rules: prices are calculated on the server, payments are confirmed by webhook, and card data is never stored. Deciding first whether a hosted platform like Shopify already covers your needs is part of the job.
This guide starts with that decision, then walks through building a custom store with an agent in order: data model, cart, checkout, webhooks, stock, emails, legal pages and deployment. Each step has prompts you can copy. At the end there’s a list of the mistakes AI makes most often in ecommerce and a security checklist.
First decision: hosted platform or custom build?
The honest answer is often “hosted platform”. Shopify, WooCommerce and similar platforms handle payments, inventory, shipping integrations, taxes and security updates for you.
| Hosted platform | Custom build (with a coding agent) | |
|---|---|---|
| Time to first sale | Days | Weeks |
| Payments, security, updates | The platform’s job | Your job |
| Design and flow freedom | As far as themes and apps go | Unlimited |
| Cost structure | Monthly fee, sometimes transaction fees | Hosting + payment provider fees + your time |
| Portability | Tied to the platform | You own the code |
A custom build makes sense when your sales flow isn’t supported (say, subscriptions plus a made-to-order configurator), when you’re adding a store inside an existing app, or when you want to learn and keep full control. That’s the path this guide covers. If you’d rather start with a plain marketing site, read how to build a website with AI first.
1. Stack and setup
For a vibe coding project I suggest a stack agents know well and that’s heavily documented:
- Next.js (App Router): pages, server code and API routes in one project.
- PostgreSQL: a managed service (Supabase, Neon and the like) or Docker locally.
- An ORM (Prisma or Drizzle): schema and migrations live in files, so you and the agent read from the same source.
- A payment provider: Stripe is the common choice; in some countries local providers are standard (in Turkey, for example, iyzico or PayTR). What they share is that the card is entered in the provider’s form and the result reaches your server by notification.
- A transactional email service: for order confirmations and shipping notices.
npx create-next-app@latest store
cd store
git status # create-next-app usually initialises git; if not: git init
claude
2. The first prompt: plan before code
Telling an agent “build me a store” gets you everything in one pass, with the critical parts glossed over. Ask for a plan first:
We're building a small ecommerce site with Next.js (App Router), PostgreSQL and Prisma.
Payments will use Stripe Checkout (card data must never reach our server).
Don't write code yet. First produce a plan covering:
1. Data model: product, product variant (size/colour), order, order item, stock.
2. Pages: product list, product detail, cart, checkout return pages, a simple admin.
3. Payment flow: from cart to creating a checkout session to marking the order paid via webhook.
4. Which operations must happen on the server, and why.
Rules:
- Store prices as integers in the smallest currency unit (cents), never floats.
- Never trust a price, total or discount amount sent from the client.
- Secrets live only in .env; no secret ever reaches client code.
Write the plan and wait for my approval.
Read the plan, fix what you don’t like, then implement it piece by piece: data model, then catalogue, then cart, payments last. Commit after each piece.
3. The product database
A few things I watch for in the data model:
- Money is an integer. $14.90 is stored as
1490. Stripe and most providers expect amounts in the smallest currency unit anyway. - Order items copy the price. The order keeps the product’s name and price at the time of purchase, so a price change tomorrow doesn’t rewrite past orders.
- Stock lives on the variant. “Blue T-shirt, size M” is its own stock row.
- Order status is an explicit list:
pending,paid,shipped,cancelled,refundedand so on.
When the agent writes a migration, read the file. If it drops a table or a column that already holds data, stop and ask.
4. The cart
The cart, in the browser (or in the database for signed-in users), should hold only product/variant IDs and quantities. Prices and totals are displayed in the cart but always read from the server.
Implement the cart. Store only a list of {variantId, quantity} in localStorage.
The cart page fetches current prices and the total from the server.
Out-of-stock or deleted products show in the cart with a warning.
Limit quantity to 1–10 and enforce that on the server too.
5. Checkout: prices are always computed on the server
Rule number one of ecommerce: never trust a price from the client. Every value in the browser can be changed; anyone can open dev tools and set the total to $1. The right flow:
- The client sends the server only the list of
variantIdandquantity. - The server reads each item’s price and stock from the database and computes the total itself.
- The server creates an order with status
pending. - The server creates a payment session with the provider (a Stripe Checkout Session, or the local provider’s payment form) and attaches the order ID to it.
- The customer is redirected to the provider’s page or iframe and enters their card there.
A trimmed-down idea of the server code:
// app/api/checkout/route.ts (sketch)
const items = await db.variant.findMany({ where: { id: { in: cart.map(c => c.variantId) } } })
const lineItems = cart.map(c => {
const v = items.find(i => i.id === c.variantId)
if (!v || v.stock < c.quantity) throw new Error('Out of stock')
return { price: v.priceCents, quantity: c.quantity } // price from the DB, not the client
})
Discount codes follow the same rule: the code comes from the client, but whether it’s valid and how much it takes off is decided on the server.
6. Webhooks: paid or not?
A customer landing on your “payment successful” page does not prove the payment went through; that page can be opened by hand, and the customer may have closed the tab. The source of truth is the notification your provider sends to your server (a webhook in Stripe, often called a callback with other providers).
Spell this out to the agent for the webhook endpoint:
Write the Stripe webhook endpoint (/api/webhooks/stripe):
- Verify the signature with STRIPE_WEBHOOK_SECRET; reject unsigned or invalid requests.
- Use the raw request body for signature verification.
- On checkout.session.completed, read the order ID from metadata, and inside one
database transaction mark the order "paid" and decrement stock.
- Make it idempotent: if the same event arrives twice, the second time changes nothing.
- Compare the amount paid with the order total; if they differ, flag the order and log it.
To test locally, the Stripe CLI can forward events to your machine:
stripe listen --forward-to localhost:3000/api/webhooks/stripe
In Stripe’s test mode, test cards such as 4242 4242 4242 4242 let you run the whole flow without real money. Other providers have sandboxes too; have the agent read their docs on how to verify the notification signature or hash.
7. Stock
Stock is where two people can buy the last item at the same moment. Agents usually write “read, then write”, which leaves a race condition in between. Do the check and the decrement in one atomic query instead:
UPDATE variants SET stock = stock - 2
WHERE id = 'var_123' AND stock >= 2;
-- if zero rows were affected, there wasn't enough stock
Decrementing stock once payment is confirmed (in the webhook) is the simple option; for very limited items you might also hold a short reservation when the checkout session opens. Tell the agent explicitly which one you want.
8. Emails
You need at least two: an order confirmation and a shipping notice. Send the confirmation from the webhook, not from the success page, so it only goes out for orders that were actually paid. The email service’s API key stays on the server. If you skip the SPF/DKIM records your email provider asks for on your sending domain, your emails may end up in spam.
9. Legal pages
Not code, but part of the store. What’s required depends on where you and your customers are, but most stores need:
- A privacy policy (covering what you collect and why, plus GDPR or local equivalents where they apply)
- A cookie notice where required
- Terms of sale
- A refund and returns policy, including any statutory withdrawal period
- Seller details: legal name, address and contact information
If you sell in Turkey, expect a KVKK privacy notice, a distance sales contract and a pre-information form that the customer approves before ordering. You can have the agent add the consent checkboxes at checkout and store the consent with the order. AI can draft these texts, but have a lawyer or accountant review them before going live, and ask them about invoicing and tax obligations.
10. Deployment
Putting the Next.js app on Vercel and the database on a managed PostgreSQL service is the shortest route. Before launch:
- Enter environment variables (database URL, payment and email keys, webhook secret) in your host’s dashboard.
- Register the live webhook URL in your payment provider’s dashboard; don’t mix up test and live keys.
- Run migrations against production deliberately, and take a backup first.
- Place a small real order in production, refund it, and check the whole flow (email, stock, order status) with your own eyes.
What the AI gets wrong in ecommerce
- Computing the total in the client and sending it to the server. The most dangerous one. Search the code for a
priceortotalcoming from the client and remove it. - Marking the order paid on the success page. Without a webhook, order status shouldn’t change.
- Skipping webhook signature verification, or parsing the body as JSON before verifying, then disabling verification because “it doesn’t work”.
- Floats for money. The
0.1 + 0.2problem ends up on invoices. - No idempotency. The same webhook arrives twice, stock drops twice, two emails go out.
- An unprotected admin. It builds an
/adminpage but forgets authorisation checks on the API routes. Make sure every admin API checks the role on the server. - Going live with test keys, or the reverse, using live keys during development.
Security
- Never store, log or collect card data with your own form. Use the provider’s hosted payment page or form; that keeps your PCI DSS scope as small as possible.
- Secrets in
.env, with.envin.gitignore. Never put a secret key (likesk_...) in client code or in public environment variables (in Next.js, those prefixedNEXT_PUBLIC_). - Never paste API keys into the chat. Tell the agent the variable name, not the value.
- Read what the agent runs. In a terminal connected to a production database, don’t auto-approve migrations,
DROPs or bulkUPDATEs. - Read every diff, especially payment and auth code. A second pair of eyes is essential here; AI code review covers how to set that up.
- Collect as little personal data as you can, and keep what you collect behind limited access.
Doing it with AgentVera
Keeping several agents apart helps on a store project, and I do that with AgentVera:
- Backend and UI in separate worktrees. A Claude Code agent works on payments, webhooks and the data model while a Codex agent builds product pages and the cart UI. Each sits in its own git worktree, all in one workspace.
- Database manager. Look at the tables, orders and stock the agent created in the database manager without opening another tool; it supports PostgreSQL, MySQL and SQLite.
- DB Guard. It scans migrations the agent wrote at the end of a turn; DB Guard surfaces risks like a
DROPor anUPDATEwithoutWHEREup front. - Code review and previews. When a turn ends, code review has a second model look at it; preview environments run each worktree’s dev server at its own address. You still read the payment diff yourself before merging.
None of this gets things right for you; it makes it easier to see what changed.
Quick checklist
- Hosted vs custom decided on purpose
- Prices are integers in the smallest unit; order items copy the price
- The cart holds only IDs and quantities; the total is computed on the server
- Orders become “paid” only via a signature-verified webhook; the webhook is idempotent
- Stock is decremented with an atomic query
- The confirmation email is sent from the webhook; SPF/DKIM set up
- Privacy, terms, refund policy and seller details are live and reviewed
- No card data anywhere; secrets only on the server
- A real order placed and refunded in production
Wrapping up
You can build an ecommerce site with AI, and an agent will write most of it quickly. But what makes a store trustworthy isn’t something the agent does on its own: prices computed on the server, payments confirmed by webhook, stock decremented atomically, no card data kept, and the right legal pages. Put those rules in your first prompt, test every step in test mode, and always read the payment code before you merge it.