Stripe Payments Setup
This guide shows how to configure Stripe for PWASK, create prices, wire the webhook, and test end-to-end. PWASK supports multiple payment methods: Stripe Card, Apple Pay, and Google Pay.
Prerequisites
- Stripe account with at least one Product created.
- Supabase project with the service role key available to the app.
- CLI:
pnpm,stripeCLI (optional for local webhook testing).
1) Environment Variables
Create or update .env.local (and your hosting env) with:
# Stripe authentication
STRIPE_SECRET_KEY=sk_live_or_test_xxx # required (server only)
STRIPE_WEBHOOK_SECRET=whsec_xxx # required for webhooks
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_or_test_xxx # required (client-side)
# Prices (example IDs from Stripe Dashboard)
STRIPE_PRICE_LIFETIME=price_123
STRIPE_PRICE_ESSENTIAL_MONTHLY=price_abc
STRIPE_PRICE_ESSENTIAL_YEARLY=price_def
STRIPE_PRICE_CREATOR_MONTHLY=price_xyz
STRIPE_PRICE_CREATOR_YEARLY=price_uvx
STRIPE_PRICE_PRO_MONTHLY=price_pro_monthly
STRIPE_PRICE_PRO_YEARLY=price_pro_yearly
2) Database Migration
Add Stripe columns/indexes to the payments table:
pnpm supabase db push # or supabase db push (if you use Supabase CLI directly)
This applies supabase/migrations/20260321090000_add_stripe_columns.sql.
3) Webhook Configuration
Stripe sends events to /api/checkout/stripe/webhook. There are two methods for testing webhooks locally:
Method 1: Stripe CLI (Recommended for Local Development)
The Stripe CLI provides a local listener that forwards webhook events from Stripe to your local server, perfect for development and testing.
Installation:
# macOS (Homebrew)
brew install stripe/stripe-cli/stripe
# Linux
curl -s https://packages.stripe.com/api/v1/installation/script.sh | bash
# Windows (Scoop)
scoop bucket add stripe https://github.com/stripe/scoop-stripe-cli.git
scoop install stripe
Setup and Usage:
-
Login to Stripe CLI:
stripe loginThis opens a browser window to authenticate with your Stripe account.
-
Start the webhook listener:
stripe listen --forward-to localhost:3000/api/checkout/stripe/webhookThis command will:
- Start listening for webhook events from Stripe
- Forward them to your local development server
- Display a webhook signing secret (starting with
whsec_)
-
Copy the webhook secret to your environment:
# The CLI will display something like: whsec_xxxxxxxxxxxxxxxxxxxxx # Add it to your .env.local: STRIPE_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxxxxxxxOr set it directly in your terminal session:
export STRIPE_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxxxxxxx -
Start your development server (in a separate terminal):
pnpm dev -
Trigger test events:
# In another terminal, trigger specific webhook events stripe trigger checkout.session.completed stripe trigger payment_intent.succeeded stripe trigger invoice.payment_succeeded
What events to listen for:
The webhook handler processes these events:
checkout.session.completed- Regular card payment completed (Checkout Session)payment_intent.succeeded- Google Pay/Apple Pay payment completed (Payment Intent)invoice.payment_succeeded- Subscription invoice paidcustomer.subscription.deleted- Subscription cancelled
Viewing webhook events:
# See all webhook events in real-time
stripe listen --forward-to localhost:3000/api/checkout/stripe/webhook
# Filter specific events
stripe listen \
--events checkout.session.completed,payment_intent.succeeded \
--forward-to localhost:3000/api/checkout/stripe/webhook
Method 2: ngrok + Stripe Dashboard (Alternative)
If you prefer using ngrok or need to test with the actual Stripe Dashboard webhook configuration:
-
Start ngrok:
ngrok http 3000 -
Update your environment:
NEXT_PUBLIC_BASE_URL=https://YOUR-NGROK-URL.ngrok-free.app -
Configure webhook in Stripe Dashboard:
- Go to Stripe Dashboard → Developers → Webhooks
- Click "Add endpoint"
- URL:
https://YOUR-NGROK-URL.ngrok-free.app/api/checkout/stripe/webhook - Select events:
checkout.session.completedpayment_intent.succeeded(for Google Pay/Apple Pay)invoice.payment_succeededcustomer.subscription.deleted
- Copy the Signing secret to
STRIPE_WEBHOOK_SECRET
Production Webhook Configuration
For production, configure the webhook endpoint in Stripe Dashboard:
- URL:
https://yourdomain.com/api/checkout/stripe/webhook - Events to subscribe:
checkout.session.completedpayment_intent.succeededinvoice.payment_succeededcustomer.subscription.deleted
- Copy the Signing secret into
STRIPE_WEBHOOK_SECRET
4) Create Prices in Stripe
For each plan/billing combination you support, create a Price in the Stripe Dashboard and map it to the env vars above. For one-time lifetime payments, set STRIPE_PRICE_LIFETIME.
5) Payment Methods: Card, Apple Pay & Google Pay
Stripe Card Payment
- Standard card payment (Visa, MasterCard, Amex)
- Works on all browsers and devices
- Click "Card (Visa, MasterCard, Amex)" button to pay
Apple Pay
- Supported on: Safari on iOS 12+, Safari on macOS 12+
- Requirements: HTTPS (localhost works for development), Apple Pay merchant account configured in Stripe
- How it works: Button automatically appears on compatible devices and hides on unsupported ones
- Click "Apple Pay" button to open native Apple Pay sheet
- Users confirm payment with Face ID, Touch ID, or passcode
Google Pay
- Supported on: Chrome on Android 5+, other Android browsers with Google Pay support
- Requirements: HTTPS (localhost works for development), Google Pay merchant account configured in Stripe
- How it works: Button automatically appears on compatible devices and hides on unsupported ones
- Click "Google Pay" button to open native Google Pay sheet
- Users confirm payment with biometric or PIN
Implementation Details
The payment buttons are rendered by the StripeButton component (src/components/StripeButton.tsx):
- For Card: Always visible
- For Apple Pay: Loads
stripe.jsand checkscanUseApplePay() - For Google Pay: Loads
stripe.jsand checkscanUseGooglePay()
The backend API route (/api/checkout/stripe) handles all payment methods through Stripe Checkout, which automatically:
- Detects user's device and payment method support
- Shows the appropriate native payment UI
- Processes the transaction securely
6) Triggering Checkout
- The payment page shows all available payment methods (
/payment). - For subscriptions, ensure the
planandbillingquery params are passed (e.g.,?plan=creator&billing=monthly). - The API route
/api/checkout/stripecreates a Checkout Session and redirects users to Stripe-hosted checkout. - Each payment method (card, Apple Pay, Google Pay) uses the same API endpoint.
7) What the Webhook Does
- Verifies Stripe signature.
- Upserts users and profiles in Supabase using the service role key.
- Logs payments into
paymentswith Stripe IDs (session, subscription, payment intent, invoice). - Sends a payment confirmation email via
/api/sendmail.
8) Validation & Testing
pnpm exec tsc --noEmitandpnpm lintto ensure types/lint pass.- Run a test payment in Stripe test mode with
4242 4242 4242 4242(any future expiry, any CVC). - Test Apple Pay: Use Safari on iOS/macOS with test card
- Test Google Pay: Use Chrome on Android with test card
- Confirm the
paymentstable records the event and the email is delivered. - Verify webhook logs in Stripe Dashboard to ensure 2xx responses.
9) Production Checklist
- Use live keys and live price IDs in production env variables.
- Set the production webhook endpoint and signing secret.
- Configure Apple Pay and Google Pay merchant accounts in Stripe Dashboard.
- Rotate keys if accidentally exposed.
- Ensure CSP allows Stripe (already configured in
next.config.tsforjs.stripe.comandcheckout.stripe.com). - Keep
SUPABASE_SERVICE_ROLE_KEYandSTRIPE_SECRET_KEYonly on the server (never expose them). - Test all payment methods on production with real devices before going live.
10) Troubleshooting
Apple Pay/Google Pay buttons don't appear
- Ensure HTTPS is enabled (localhost works for dev)
- Check browser/device compatibility
- Verify
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEYis set - Check browser console for errors
Payment fails with "Unable to start Stripe checkout"
- Verify all Stripe environment variables are correct
- Check webhook logs in Stripe Dashboard
- Ensure Supabase credentials are valid
Webhook not triggering
- Verify
STRIPE_WEBHOOK_SECRETis correct - Check that webhook endpoint is configured in Stripe Dashboard
- Ensure the endpoint is accessible from the internet (not localhost)
Related Documentation
- Getting Started — Initial setup guide
- Email Configuration — Configure email notifications for payment confirmations
- PayPal Setup — Alternative payment integration
- Environment Variables — Complete environment configuration reference