Subscription Plans
Subscriptions are driven by plan/price IDs from your provider. Klub treats the plan price ID as the gate used to unlock content. Use this page when building plan listings and subscription checkout buttons.
Plan sources
Plan sources depend on the provider:
- Stripe:
klub()->plans()lists recurring prices from the Stripe API. - Other built-in providers: plans come from
bnomei.klub.providers.{provider}.plansor, if empty, fromsite()->content()->plansYAML.
Plan YAML fields
This section applies to providers that use local plan definitions (all built-in providers except Stripe).
When you store plans in content, include these fields:
id(provider plan/price id)unit_amount(minor units)currency(ISO 4217)type(recurringorone_time)
Recommended:
productname(display name, also used for grouping)nickname(short label)
Provider-specific fields:
- Mollie:
interval(required for subscriptions),times(optional),startDate(optional,YYYY-MM-DD),method(optional:creditcard,directdebit,paypal) - Lemon Squeezy:
typeis recommended so fallback mode detection can distinguish recurring vs one-time events. - Polar: use the Polar product price ID as
id;typeis recommended for fallback mode detection. - PayPal: one-time plans must define
unit_amount+currencyfor Orders checkout amounts.
Example YAML:
- id: basic_monthly
nickname: Basic
unit_amount: 990
currency: usd
type: recurring
productname: Basic Plan
Listing plans
<?php foreach (klub()->plans() as $plan): ?>
<div>
<strong><?= $plan['product']['name'] ?? $plan['nickname'] ?></strong>
<span><?= $plan['price'] ?></span>
</div>
<?php endforeach ?>
Start subscription checkout
<form method="post" action="<?= site()->url() ?>/klub/subscribe/basic_monthly">
<input type="hidden" name="token" value="<?= csrf() ?>">
<input type="hidden" name="redirect" value="<?= url('thanks') ?>">
<button type="submit">Subscribe</button>
</form>
After the provider checkout, Klub receives the user at GET /klub/subscribe/{token} and updates gates.
If bnomei.klub.members.create is false and the logged-in user already has an active subscription for the price ID, Klub will skip checkout and redirect immediately.
Manage subscriptions
POST /klub/change/{subscriptionId}/{priceId}POST /klub/renew/{subscriptionId}POST /klub/cancel/{subscriptionId}
You can also inspect the current state and suggested action via subscriptionAction($priceId) on the provider instance.
Hooks
Subscription flows trigger klub.subscribe:*, klub.change:*, klub.renew:*, and klub.cancel:* hooks with DTOs.