Create live-mode products to replace the 5 test-mode price IDs in
cloud/workers/wrangler.toml:90-94. Follow every section in order.
Complete these before creating any products. Each item links to the relevant Stripe dashboard page.
| # | Setting | Where | Required value |
|---|---|---|---|
| 1 | Switch to Live mode | Top-right toggle (Dashboard home) | Live mode ON |
| 2 | Default currency | Settings → Business → Customer emails | USD |
| 3 | Statement descriptor | Settings → Business → Public details | WEBFETCH (max 22 chars) |
| 4 | Receipt emails | Settings → Business → Customer emails | "Send email receipts" ON |
| 5 | Stripe Tax | Settings → Tax → Overview | Enable if you want automatic VAT collection (EU/UK). Optional at launch — add later per plan/Section E. |
| 6 | Email domain | Settings → Business → Public details | getwebfetch.com |
| 7 | Support email | Settings → Business → Public details | support@getwebfetch.com |
| 8 | Live-mode Stripe keys | Developers → API keys | Copy Secret key (sk_live_…) — this becomes STRIPE_SECRET_KEY |
Screenshot checklist: open each page, confirm the value, proceed.
The metered overage prices attach to a Billing Meter. Create the meter before the prices, because you'll need the meter ID when creating overage prices.
| Field | Value |
|---|---|
| Display name | webfetch request |
| Event name | webfetch_request |
| Value settings — event payload key | value |
| Aggregation | Sum |
| Customer mapping — event payload key | stripe_customer_id |
Click Create meter. Copy the resulting meter ID (looks like
mtr_live_XXXXXXXXXXXXXXXX). You will select this meter when creating the
overage prices below.
Why this shape:
billing.tsposts meter events viaPOST /v1/billing/meter_eventswithpayload[value]as the unit count andpayload[stripe_customer_id]for customer mapping. Seecloud/workers/src/billing.ts:430-435.
Create products at Catalog → Products → + Add product.
| Field | Value |
|---|---|
| Name | webfetch Pro |
| Description | 10,000 fetches/mo, pooled provider keys, 100 rpm per key, all endpoints |
| Image | Upload cloud/landing/public/og-image.png |
| Statement descriptor | WEBFETCH PRO |
| Tax code | txcd_10000000 (Software as a Service) |
| Label | Type | Billing | Amount | Currency | Tax behavior | Metered? |
|---|---|---|---|---|---|---|
| Pro monthly | Recurring | Monthly | $19.00 | USD | Exclusive | No |
| Pro overage | Recurring | Monthly | $0.015 per unit | USD | Exclusive | Yes — attach meter webfetch_request |
Creating Pro monthly:
- Click Add a price.
- Pricing model: Standard pricing.
- Price:
19.00, Currency:USD, Billing period:Monthly. - Tax behavior:
Exclusive(tax added on top). - Save. Copy the
price_live_…ID → this isSTRIPE_PRICE_PRO.
Creating Pro overage:
- Click Add another price.
- Pricing model: Usage-based.
- Meter: select
webfetch_request(created above). - Price per unit:
0.015, Currency:USD. - Tax behavior:
Exclusive. - Save. Copy the
price_live_…ID → this isSTRIPE_PRICE_OVERAGE_PRO.
| Field | Value |
|---|---|
| Name | webfetch Team |
| Description | 50,000 fetches/mo, 5 seats included, pooled provider keys, 300 rpm per key |
| Image | Upload cloud/landing/public/og-image.png |
| Statement descriptor | WEBFETCH TEAM |
| Tax code | txcd_10000000 |
| Label | Type | Billing | Amount | Currency | Tax behavior | Metered? |
|---|---|---|---|---|---|---|
| Team monthly | Recurring | Monthly | $79.00 | USD | Exclusive | No |
| Team seat | Recurring | Monthly | $12.00 per unit | USD | Exclusive | No |
| Team overage | Recurring | Monthly | $0.010 per unit | USD | Exclusive | Yes — attach meter webfetch_request |
Creating Team monthly:
- Click Add a price → Standard pricing → $79.00, Monthly, USD, Exclusive.
- Save. Copy ID →
STRIPE_PRICE_TEAM.
Creating Team seat:
- Click Add another price → Standard pricing → $12.00, Monthly, USD, Exclusive.
- Save. Copy ID →
STRIPE_PRICE_TEAM_SEAT.
The checkout code at
billing.ts:76-78addsseats - 5units of this price whenseats > 5.
Creating Team overage:
- Click Add another price → Usage-based → meter
webfetch_request→ $0.010 per unit, USD, Exclusive. - Save. Copy ID →
STRIPE_PRICE_OVERAGE_TEAM.
Register after the Worker is deployed.
Stripe Dashboard → Developers → Webhooks → + Add endpoint
| Field | Value |
|---|---|
| Endpoint URL | https://api.getwebfetch.com/stripe/webhook |
| API version | 2024-06-20 |
| Events to listen for | See table below |
Events:
| Event |
|---|
checkout.session.completed |
customer.subscription.created |
customer.subscription.updated |
customer.subscription.deleted |
invoice.payment_failed |
After saving, click Reveal signing secret. Copy the whsec_… value →
this becomes STRIPE_WEBHOOK_SECRET (set via wrangler secret put STRIPE_WEBHOOK_SECRET).
Note: The Worker handles webhooks at
/stripe/webhook(registered inbillingRouterinbilling.ts:132). The deployment plan references/v1/webhooks/stripe— use/stripe/webhookinstead, which is what the code actually mounts.
STATUS (as of 2026-05-01): the value currently in
cloud/workers/wrangler.toml(STRIPE_PORTAL_CONFIG_ID = "bpc_1TMdoLIsCo7Z3L3vBZQtxTKN") is the test-mode config. Live customers cannot open the portal until you recreate it in live mode and update the binding.
The test-mode portal config is bpc_1TMdoLIsCo7Z3L3vBZQtxTKN. Recreate an
equivalent config in live mode.
Stripe Dashboard → Settings → Billing → Customer portal (toggle to Live mode in the upper-left before configuring)
Enable the following features to match test-mode config:
| Feature | Setting |
|---|---|
| Payment method update | On |
| Invoice history | On |
| Subscription cancellation | On — at period end, capture reason |
| Plan switching | On — allow customers to switch between Pro and Team |
| Subscription pause | Off (not supported in our billing model) |
| Customer information update | On — allow email update |
| Billing address collection | Optional |
Return URL: https://app.getwebfetch.com/billing
After saving, copy the portal configuration ID (bpc_live_…) →
this becomes STRIPE_PORTAL_CONFIG_ID.
After creating all 5 prices and the portal config, update
cloud/workers/wrangler.toml lines 90–99.
# before (test mode)
STRIPE_PRICE_PRO = "price_1TMd9qIsCo7Z3L3vRPcU5ADf"
STRIPE_PRICE_TEAM = "price_1TMd9rIsCo7Z3L3vlDoD5aEq"
STRIPE_PRICE_TEAM_SEAT = "price_1TMd9rIsCo7Z3L3vTas2tbYY"
STRIPE_PRICE_OVERAGE_PRO = "price_1TMdALIsCo7Z3L3vESz7Acen"
STRIPE_PRICE_OVERAGE_TEAM = "price_1TMdALIsCo7Z3L3vZcYck87Y"
STRIPE_PORTAL_CONFIG_ID = "bpc_1TMdoLIsCo7Z3L3vBZQtxTKN"
# after (live mode) — PASTE YOUR LIVE IDS HERE
STRIPE_PRICE_PRO = "price_XXXXXXXXXXXXXXXXXXXXXXXX"
STRIPE_PRICE_TEAM = "price_XXXXXXXXXXXXXXXXXXXXXXXX"
STRIPE_PRICE_TEAM_SEAT = "price_XXXXXXXXXXXXXXXXXXXXXXXX"
STRIPE_PRICE_OVERAGE_PRO = "price_XXXXXXXXXXXXXXXXXXXXXXXX"
STRIPE_PRICE_OVERAGE_TEAM = "price_XXXXXXXXXXXXXXXXXXXXXXXX"
STRIPE_PORTAL_CONFIG_ID = "bpc_XXXXXXXXXXXXXXXXXXXXXXXX"Then redeploy:
cd cloud/workers
wrangler secret put STRIPE_SECRET_KEY # paste sk_live_… key
wrangler secret put STRIPE_WEBHOOK_SECRET
wrangler deploy# Forward live webhook events to your local worker during testing.
stripe listen \
--forward-to http://localhost:8787/stripe/webhook \
--events checkout.session.completed,customer.subscription.created,\
customer.subscription.updated,customer.subscription.deleted,\
invoice.payment_failedKeep this running in a terminal, then run the steps below in another.
- Sign up at
https://app.getwebfetch.comwith a real inbox. - Confirm verification email arrives (SendGrid domain auth required first —
see
docs/SSO_SETUP.md). - Go to
/billing→ click Upgrade to Pro. - Complete Stripe Checkout with test card
4242 4242 4242 4242, any future expiry, any CVC. Use your live Stripe key — this charges the card for real. For smoke testing only, use a real card you own and immediately cancel. - Return to
/billing—planshould flip toprowithin 10 seconds (webhook latency). If it doesn't, checkwrangler tailand the Stripe Dashboard → Events tab for webhook delivery status. - Create a new API key in the dashboard.
- Run a pooled search:
Every listed provider should return
curl -H "Authorization: Bearer wf_YOUR_PRO_KEY" \ "https://api.getwebfetch.com/v1/search?q=nature&providers=unsplash,pexels,pixabay,flickr" \ | jq '.providerReports'
count > 0with no401or403errors. - Check
/v1/usage— the request counter should have incremented. - In Stripe Dashboard → Billing → Meters →
webfetch_request→ View events — confirm a meter event was recorded for the request.
# Simulate a subscription update (replace ws_xxx with a real workspace_id):
stripe trigger customer.subscription.updated \
--override subscription:metadata.workspace_id=ws_xxx
# Test duplicate delivery (idempotency):
stripe events resend evt_XXXXXXXX
# Expect: second delivery returns 200 but is a no-op (dedup via webhook_events table).stripe trigger invoice.payment_failedCheck that the dashboard /billing page shows the past_due banner (B2
fix from the launch-hardening plan).