Monetization & Payments
Monetization is built around a Products collection and the Stripe plugin. Products sync bidirectionally with Stripe — product details push to Stripe, and prices sync back via webhooks. The frontend provides checkout flows, a billing portal, and an optional paywall system.
Stripe setup
Required environment variables:
| Variable | Description |
|---|---|
STRIPE_SECRET_KEY | Stripe secret key (sk_test_... for development) |
STRIPE_WEBHOOKS_ENDPOINT_SECRET | Webhook signing secret (whsec_...) from your Stripe webhook endpoint |
Webhooks: In production, add an endpoint in the Stripe Dashboard → Webhooks pointing to https://your-domain.com/api/webhooks/stripe and subscribe to checkout.session.completed, invoice.paid, invoice.payment_failed, customer.subscription.updated, customer.subscription.deleted, price.created, price.updated, price.deleted. For local development, use the Stripe CLI to forward webhooks: stripe listen --forward-to localhost:3000/api/webhooks/stripe and set the printed signing secret as STRIPE_WEBHOOKS_ENDPOINT_SECRET.
Products collection
Each product has a monetization type that determines its behavior:
| Type | Description | Key fields |
|---|---|---|
| credits | One-time purchase that grants credits | purchase_credit_amount |
| subscription | Recurring subscription tier | subscription_tier_id, subscription_tier_rank |
| subscription-credits | Subscription that includes a credit allowance | subscription_credit_allowance |
| purchase | One-time purchase (e.g. a digital product) | purchase_fulfillment_id, allow_guest_checkout |
All product types share:
- Name and description — synced to Stripe.
- Active toggle — controls visibility.
- Features — list of feature strings (synced to Stripe’s
marketing_features). - Prices — read-only in Payload. Created and managed in the Stripe Dashboard; synced to Payload via webhooks (
price.created,price.updated,price.deleted). Each price has:stripe_price_id,amount,currency,interval, andactive.
Stripe sync
Payload → Stripe
When you create or update a product in Payload, the Stripe plugin syncs the name, description, and active status to the corresponding Stripe product. A custom afterChange hook also pushes:
- Monetization metadata (type, credit amounts, tier info) to Stripe product metadata.
- The features list to Stripe’s
marketing_features.
If the Stripe product doesn’t exist yet, it’s created automatically.
Stripe → Payload
Prices are managed in the Stripe Dashboard and synced to Payload via webhooks. When you create a price in Stripe, the price.created webhook writes it to the product’s prices array in Payload. Updates and deletions sync the same way.
This means you set up your pricing (amounts, currencies, intervals) in Stripe, and Payload reflects them automatically.
Checkout flow
Authenticated checkout
The createCheckout server action creates a Stripe Checkout session for the authenticated customer. It:
- Looks up or creates the Stripe customer (linked to the Payload customer record).
- Creates a Checkout session with the selected price.
- Redirects the user to Stripe’s hosted checkout page.
The frontend uses a CheckoutButton component that triggers this flow.
Guest checkout
For products with allow_guest_checkout enabled, the createGuestCheckout action creates a Checkout session without requiring sign-in. The customer’s email is collected on the Stripe checkout page.
After checkout
Stripe sends a checkout.session.completed webhook. The webhook handler updates the customer’s record in Payload (e.g. granting credits, activating a subscription, or triggering fulfillment for purchases).
Billing portal
The createPortalSession action opens Stripe’s Customer Portal where customers can manage their subscriptions, update payment methods, and view invoices. This is available from the user’s dashboard.
Paywall
The paywall system lets you gate content behind credit or subscription requirements.
Configuration
A PaywallConfig defines the gating rules:
- Credits — require a minimum credit balance (
enableCredits,credits). - Subscription tier — require a minimum subscription rank (
enableSubscription,minimum_tier_rank,subscription_tier). - Redirect — optionally redirect users who don’t meet the requirements (
autoRedirect,redirectUrl). - Toast — show a notification when access is denied (
showToast).
Usage
The PaywallForm component renders a gating UI that checks the current customer’s credits and subscription tier against the config. If the customer meets the requirements, the protected content is shown; otherwise, the paywall form prompts them to purchase or upgrade.
Webhook events
The following Stripe webhook events are handled:
| Event | What happens |
|---|---|
checkout.session.completed | Processes the completed purchase — grants credits, activates subscription, or triggers fulfillment |
invoice.paid | Confirms successful payment for subscriptions |
invoice.payment_failed | Handles failed subscription payments |
customer.subscription.updated | Updates the customer’s subscription status in Payload |
customer.subscription.deleted | Marks the subscription as cancelled in Payload |
price.created | Syncs the new price to the product’s prices array in Payload |
price.updated | Updates the price in Payload |
price.deleted | Removes the price from Payload |