# Build a Shopping Cart in Lovable

- Tool: Lovable Prompts
- Last updated: June 2026

## TL;DR

Paste the starter prompt into Lovable Build mode and get a working single-seller storefront: product catalog, persistent cart (DB-backed for signed-in users, localStorage for guests), Stripe Checkout, and a webhook that creates orders and decrements stock atomically. The raw-body webhook pattern and stock-decrement timing are the two non-negotiable pieces. Full chain: ~100–180 credits on Pro $25/mo, about one full day.

## Frequently asked questions

### Why won't Stripe work in Lovable preview?

Lovable's preview runs in an iframe on Lovable's domain. When your checkout edge function returns a Stripe Checkout URL and your code does window.location.href = url, the iframe blocks the redirect to checkout.stripe.com as a cross-origin navigation. The Stripe webhook also cannot reach the preview URL since it is behind Lovable's internal proxy and not a publicly reachable endpoint. Both issues are solved by publishing your project (top-right Publish button) and testing on the deployed URL. Register the webhook at Stripe Dashboard → Webhooks with your production URL before any Stripe testing.

### Where in the flow should I decrement stock — at add-to-cart, checkout, or webhook?

Always in the webhook, after payment succeeds. Decrement at add-to-cart and you permanently lock inventory for every abandoned cart. Decrement at checkout submit and customers whose Stripe payment fails leave stock as sold. Decrement in the webhook and you only touch stock after Stripe confirms the money cleared. The decrement_stock() function in the starter prompt raises an exception if stock goes below zero, which the webhook handler catches and uses to trigger an automatic Stripe refund — this is the only way to handle the edge case of two simultaneous purchases of the last unit.

### How do I merge a guest cart when the user signs in mid-checkout?

The cart merge follow-up prompt adds this to useCart.ts. After a successful sign-in, mergeGuestCart() reads the localStorage cart, upserts each item into the user's DB cart (adding qtys on conflict rather than duplicating), then clears localStorage. The key is the upsert conflict behavior on (cart_id, product_id, variant_id) — if the user already had the same item in a previous signed-in session, the quantities should sum, not create a duplicate line item. Call mergeGuestCart() in Signin.tsx immediately after signInWithPassword succeeds, before navigating anywhere.

### What's the deal with constructEventAsync and Lovable's edge function?

Stripe's webhook signature verification requires HMAC with SHA-256, which uses the WebCrypto API. In Deno (which Lovable Edge Functions run on), WebCrypto is async-only. Stripe's synchronous constructEvent() tries to call WebCrypto synchronously and throws 'SubtleCryptoProvider cannot be used in a synchronous context'. The fix is to use constructEventAsync() with await. The second gotcha: you must pass the raw text body — the exact bytes Stripe signed. If you call req.json() before constructEventAsync, the bytes change and the signature fails. Line 1 of your webhook handler must always be const rawBody = await req.text().

### Can I add product variants (size, color) to the schema later?

The starter prompt includes the variants table from the beginning. The VariantSelector component renders variant chips on the product detail page, and both the cart and webhook handle the optional variant_id field. If you initially seed products without variants, they work fine — variant_id is nullable throughout. To add variants to an existing product, use the admin Products page to add variant rows in the edit Sheet. The decrement_stock function handles both cases: if p_variant is not null, it decrements variants.stock; otherwise it decrements products.stock.

### How do I handle abandoned carts and email recovery?

The starter prompt does not include abandoned cart recovery — it is a significant addition. The pattern: on checkout page load, save the customer_email to the cart row. Create a pg_cron job that runs every hour and finds carts updated more than 1 hour ago with no corresponding completed order. For each, send a Resend email to the stored email: 'You left something in your cart — here's a link to pick up where you left off.' This requires collecting customer_email before the Stripe redirect, which means adding an email field to your Checkout.tsx before the Stripe button. Budget ~40 credits for this follow-up.

### Should I really build this instead of just using Shopify?

For physical goods at any real volume, just use Shopify — $39/mo gets you tax tables, shipping rate engines, fraud detection, and 1,000+ themes you don't want to recreate. Build in Lovable when: you have fewer than 20 SKUs and want owned checkout without Shopify's fee structure, you're integrating commerce into an existing Lovable app where Shopify embed feels wrong, or you sell digital goods and Lemon Squeezy's 5% Merchant of Record fee also feels heavy. If your build outgrows this prompt kit and you need custom architecture, RapidDev builds production-grade Lovable apps at $13K-$25K — book a free 30-minute consultation at rapidevelopers.com.

---

Source: https://www.rapidevelopers.com/lovable-prompts/lovable-prompts-for-building-shopping-cart
© RapidDev — https://www.rapidevelopers.com/lovable-prompts/lovable-prompts-for-building-shopping-cart
