Replit prompt (UPDATED)

Goal: Add a shopping cart to the top-right header (between Notifications and Settings) and wire it to Stripe Checkout using our existing Node/Express backend.
Do not touch the watermark/storage code. Cart only.

Files to create/update

Frontend (React + TypeScript)

src/context/CartContext.tsx — global cart (localStorage-backed), checkout helper.

src/components/CartDrawer.tsx — right-side drawer UI for the cart.

src/components/Header.tsx — insert a 🛒 Cart button between Notifications and Settings; mounts <CartDrawer/>.

Update any stock photo card/modal to call useCart().addItem({...}).

Backend (Node/Express)
5. routes/checkoutRoutes.js → POST /api/checkout/create-session
6. routes/stripeWebhook.js → POST /api/stripe/webhook
7. server.js wiring:

✅ app.use("/api/stripe", stripeWebhook) BEFORE express.json() (webhook needs express.raw()).

Then app.use(express.json()).

Then app.use("/api/checkout", checkoutRoutes).

Env vars (already present or add)

STRIPE_SECRET_KEY

STRIPE_PUBLISHABLE_KEY

STRIPE_WEBHOOK_SECRET

DOMAIN_URL (or FRONTEND_URL) → e.g. https://<yourapp>.replit.dev

Stripe lookups to support (use these exact keys)

Subscriptions (stock-only tiers):

stock_starter_mo ($9.99 / 5 downloads)

stock_pro_mo ($19.99 / 10 downloads)

stock_agency_mo ($39.99 / 25 downloads)

IBrandBiz core Pro bundle:

ibrandbiz_pro_monthly ($19 / includes 5 downloads)

One-off:

stock_single_699 ($6.99 single image)

Cart item shapes (frontend → backend)

One-off stock image:
{ kind: "stock", assetId: string, name: string, priceCents: 699, qty: 1, previewUrl?: string }

Subscription by lookup key:
{ kind: "price", lookupKey: "stock_starter_mo" | "stock_pro_mo" | "stock_agency_mo" | "ibrandbiz_pro_monthly", name: string, qty: 1 }

Checkout behavior

Use Stripe Checkout redirect flow.

Build line_items from the cart:

For kind:"price" → resolve Stripe Price by lookupKey.

For kind:"stock" → inline price_data with unit_amount.

Put assetIds (CSV) + userId into session.metadata.

Success URL: ${DOMAIN_URL}/thank-you?session_id={CHECKOUT_SESSION_ID}

Cancel URL: ${DOMAIN_URL}/cart

Enable allow_promotion_codes: true.

Webhook behavior

Route: POST /api/stripe/webhook with express.raw({ type: "application/json" }).

On checkout.session.completed:

Read session.metadata.assetIds and session.metadata.userId.

TODO (leave stub): grant licenses in our DB for each assetId to that userId.

Log subscription lifecycle events:

customer.subscription.created|updated|deleted (stub DB sync).

Header placement & UX

Insert the 🛒 button between the 🔔 Notifications and ⚙️ Settings buttons.

Badge shows count of items in cart.

Clicking 🛒 opens the right-side <CartDrawer/>.

Drawer shows items, subtotal for one-offs, and a Checkout button (calls /api/checkout/create-session).

Acceptance checks

Add a stock image to cart → see it in the drawer with thumbnail & $6.99.

Add a subscription (e.g., stock_starter_mo) → shows as “Subscription”.

Click Checkout → redirect to Stripe; completing purchase triggers webhook log:

✅ Checkout Complete: { userId, assetIds: [...] }

No changes to watermark/storage code or routes.