⚡ Stripe Subscription Paywall — Val Town Template

A complete, remixable template for gating content behind a Stripe subscription on Val Town.

What's included

LayerHow
AuthGoogle Sign-In via LastLogin — zero API keys needed
PaymentsStripe Checkout (Apple Pay, Google Pay, all cards — automatic)
Billing PortalStripe Customer Portal for self-serve manage / cancel
DatabaseVal Town SQLite — email ↔ stripe_customer_id ↔ subscription_status
WebhooksHandles checkout.session.completed, subscription.updated, subscription.deleted

Flow

Visitor → Landing page → Sign In (Google) → Paywall → Stripe Checkout → Premium Dashboard
                                                                            ↕
                                                                    Stripe Customer Portal
                                                                    (manage / cancel)

Setup (5 min)

1. Fork this project

  • Create a Product with a recurring Price (e.g. $9/month)
  • Create a WebhookYOUR_VAL_URL/api/webhook
    • Events: checkout.session.completed, customer.subscription.updated, customer.subscription.deleted
  • Enable Apple Pay / Google Pay in Settings → Payment Methods
  • Configure Customer Portal in Settings → Billing → Customer Portal

3. Set env vars in your Val:

VariableValue
STRIPE_SECRET_KEYsk_test_...
STRIPE_PRICE_IDprice_...
STRIPE_WEBHOOK_SECRETwhsec_...

4. Test with card 4242 4242 4242 4242 (any future expiry, any CVC)

File structure

main.tsx          — HTTP handler + routing (entry point)
db.ts             — SQLite database layer
stripe.ts         — Stripe API helpers (checkout, portal, webhooks)
styles.ts         — All CSS (design tokens, page-specific styles)
pages/
  landing.tsx     — Public landing page with pricing
  dashboard.tsx   — Premium content (subscribers) / paywall (non-subscribers)
  setup.tsx       — Setup instructions (shown when env vars are missing)

Routes

RouteMethodPurpose
/GETLanding / Dashboard / Setup (context-dependent)
/api/checkoutPOSTCreates Stripe Checkout Session
/api/portalPOSTCreates Stripe Customer Portal Session
/api/webhookPOSTReceives Stripe webhook events
/api/userGETReturns subscription status (JSON)
/auth/loginGETGoogle auth (LastLogin middleware)
/auth/logoutGETLogout (LastLogin middleware)

Customizing

Change pricing

Edit STRIPE_PRICE_ID env var. The $9/mo shown in the UI is just placeholder text in pages/dashboard.tsx and pages/landing.tsx.

Replace premium content

Edit PremiumContent component in pages/dashboard.tsx.

Change the look

Edit styles.ts — all CSS variables and page styles are in one place.

Add multiple tiers

Store tier info in the plan_name column and gate content by tier instead of just active/inactive.

How Apple Pay / Google Pay work

Stripe Checkout automatically shows these buttons when:

  1. The customer's browser/device supports them
  2. You've enabled them in Stripe Dashboard → Settings → Payment Methods

No extra code needed.