Skip to Content
FeaturesMonetization & Payments

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:

VariableDescription
STRIPE_SECRET_KEYStripe secret key (sk_test_... for development)
STRIPE_WEBHOOKS_ENDPOINT_SECRETWebhook 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:

TypeDescriptionKey fields
creditsOne-time purchase that grants creditspurchase_credit_amount
subscriptionRecurring subscription tiersubscription_tier_id, subscription_tier_rank
subscription-creditsSubscription that includes a credit allowancesubscription_credit_allowance
purchaseOne-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, and active.

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:

  1. Looks up or creates the Stripe customer (linked to the Payload customer record).
  2. Creates a Checkout session with the selected price.
  3. 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:

EventWhat happens
checkout.session.completedProcesses the completed purchase — grants credits, activates subscription, or triggers fulfillment
invoice.paidConfirms successful payment for subscriptions
invoice.payment_failedHandles failed subscription payments
customer.subscription.updatedUpdates the customer’s subscription status in Payload
customer.subscription.deletedMarks the subscription as cancelled in Payload
price.createdSyncs the new price to the product’s prices array in Payload
price.updatedUpdates the price in Payload
price.deletedRemoves the price from Payload