Virex Docs

Guides, setup, comparison, and practical continuation for owned repos.

Billing and checkout · Stripe

Stripe setup for a Virex-generated or Virex-continued repo

Stripe is the payments layer that turns a prepared pricing page or checkout shell into a real charging flow. This page walks through every step: creating the account, adding the right products, wiring the keys, and testing before going live.

StripeBilling and checkout

When to add Stripe

Only wire Stripe in when the product actually sells something

A generated repo can look ready to charge without actually charging anyone. Stripe is how that gap closes — once you have real products and keys behind it.

Good fit

SaaS with paid plans, subscription products, gated accounts, one-time purchase checkouts, digital-product stores, and any marketplace that needs real revenue.

Can wait

Landing pages without sign-up, portfolios, internal tools, early prototypes, and anything where 'paid' is still a future decision. You can always add Stripe later.

Setup sequence

From zero to live charging in seven steps

Work through these in order. Each step unlocks the next. Don't skip to live keys until test mode passes end-to-end.

  1. 1
    Create your Stripe account

    Sign up at stripe.com with the business email you want connected to payouts. Complete the basic business profile (name, country, tax type) — you only need the minimum to start testing.

  2. 2
    Create your products and prices inside Stripe

    Open Stripe Dashboard → Products → Add product. Create one product per plan / item with the correct price and recurrence (monthly subscription, one-time, metered, etc.). Copy the `price_...` ID of each price — you'll need them later.

  3. 3
    Grab the three keys you need

    In Stripe Dashboard → Developers → API keys, copy the Publishable key (pk_test_...) and the Secret key (sk_test_...). In Developers → Webhooks, create an endpoint (URL comes in step 5) and copy the Signing secret (whsec_...).

  4. 4
    Add the keys to your repo's .env.local

    Open the generated repo locally. The README tells you which env vars the code expects. Paste the three keys in, plus the price IDs, plus your app URL. Never commit this file.

  5. 5
    Add the webhook endpoint URL in Stripe

    For local testing use `stripe listen --forward-to localhost:3000/api/stripe/webhook`. For production, set the endpoint URL to `https://yourdomain.com/api/stripe/webhook`. Subscribe it to the events your code handles (usually checkout.session.completed, customer.subscription.updated, invoice.paid).

  6. 6
    Test the full flow in test mode

    Use Stripe test card 4242 4242 4242 4242 with any future date and any CVC. Run the checkout end-to-end, confirm the webhook fires, check the profile updates in your database. Only move on when every step passes.

  7. 7
    Switch to live mode

    In Stripe Dashboard, toggle Live mode, recreate your products with live prices (IDs differ between test and live), and swap test keys for live keys in your production environment. Repeat the webhook step with the live signing secret.

Environment variables

What to add to your .env.local

These are the variables the generated Stripe wiring expects. Your repo's README may use slightly different names — always prefer the README over this list if they disagree.

.env.local (test mode example)
# Public key — safe to expose on the client
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_xxxxxxxxxxxx

# Server-side secret — NEVER expose on the client
STRIPE_SECRET_KEY=sk_test_xxxxxxxxxxxx

# Webhook signing secret — verifies incoming Stripe events
STRIPE_WEBHOOK_SECRET=whsec_xxxxxxxxxxxx

# Price IDs for each plan (copy from Stripe Dashboard → Products)
STRIPE_PRICE_STARTER=price_xxxxxxxxxxxx
STRIPE_PRICE_PREMIUM=price_xxxxxxxxxxxx
STRIPE_PRICE_PRO=price_xxxxxxxxxxxx

# The app URL Stripe redirects back to after checkout
NEXT_PUBLIC_APP_URL=http://localhost:3000

Keep the keys out of git

Commit .env.local.example with placeholder values so teammates see which vars are needed, but .env.local itself should stay in .gitignore. If a real key slips into a commit, rotate it in Stripe Dashboard immediately — it cannot be un-exposed.

Connecting through the Virex workspace

Connect Stripe from inside the app, without touching code

Your Virex workspace has a Stripe connect surface for the builds you plan to sell through your own Stripe account. Use it when you want Virex to verify the keys and hold the connection state for you.

Where to find it

  • Log in and open the Dashboard.
  • Scroll to the Revenue / Stripe section on the right of the dashboard.
  • Click the Connect Stripe action.
  • You'll be redirected to Stripe to authorise the connection, then back to the workspace.
  • Once connected, the Stripe card shows Connected state, account email, and payouts status.

Two different ways to use Stripe

Connect in the Virex dashboard if you want Virex to manage the keys + state for you. Add keys manually in the repo's .env.local if you want full control and are comfortable with server environment variables. Both are valid — pick one and stay with it.

Adding Stripe after the fact

If a build already exists and now needs payments

Stripe can be added to a project that didn't start with billing. The path is the same — only the starting state differs.

  • Open the build in the Virex workspace and run Edit / Repair with a clear request: 'Add a pricing page with three tiers, a Stripe checkout for each, and a webhook endpoint for subscription events.'
  • The engine uses the medium or large edit tier depending on scope, and generates the pricing UI, checkout route, and webhook handler.
  • After the edit lands, follow the same seven-step setup sequence above to wire your own products and keys.
  • Test in test mode first. Every time.

Practical tips

Small things that save hours later

Test cards beyond 4242

Stripe has a full library of test cards for specific failure modes: 4000 0000 0000 0002 (card declined), 4000 0000 0000 9995 (insufficient funds), 4000 0025 0000 3155 (requires 3D Secure). Test the failure paths too, not just the happy path.

Webhooks are the source of truth

Never trust the client redirect after checkout. The webhook is what actually confirms the charge. Grant access on webhook receipt, not on 'success' URL visit — users can fake the success URL, they cannot fake a signed webhook.

Use Stripe CLI for local webhook testing

Install stripe CLI, run `stripe login`, then `stripe listen --forward-to localhost:3000/api/stripe/webhook`. Copy the whsec_... it prints to your .env.local. Every real webhook now forwards to your local server so you can debug.

Keep test and live products separate

Test and live mode have completely separate product lists and price IDs. When you switch to live, recreate the same products there and update your env vars. Don't try to re-use test price IDs — they won't resolve in live mode.

Add tax later, not first

Stripe Tax, VAT, and jurisdiction handling are worth their own setup pass after the core checkout works. Don't try to solve the tax problem before the first test payment goes through.

Retention: cancel_at_period_end

When a user cancels, set cancel_at_period_end=true instead of cancelling immediately. They keep access until the paid period ends, which reduces refund requests and gives them a natural window to change their mind.

When something goes wrong

The errors you'll probably hit at least once

'No such price' in checkout

The STRIPE_PRICE_* env var is referencing a price ID from the other mode. Test keys only see test prices, live keys only see live prices. Re-check which mode you're in.

Webhook returns 400 / signature verification failed

The STRIPE_WEBHOOK_SECRET doesn't match the endpoint you're calling. In local dev this happens when you restart `stripe listen` — it rotates the whsec. Copy the new one into .env.local and restart your app.

User paid but didn't get access

Almost always a webhook issue. Check Stripe Dashboard → Developers → Events → find the checkout.session.completed event → view the attempts. If it shows 5xx responses, your webhook handler crashed; if it shows no attempts, your endpoint URL is wrong.

'Stripe is not available in your country'

Certain Stripe features (especially Connect for marketplaces) are region-restricted. Check stripe.com/global for your region's feature list before designing around them.

Stripe is a recommended standard, not a lock-in

Projects that don't sell anything don't need it. Projects that use a different processor (Paddle, Lemon Squeezy, custom) can swap Stripe for the equivalent without touching the rest of the Virex workflow.