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}.plans or, if empty, from site()->content()->plans YAML.

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 (recurring or one_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: type is recommended so fallback mode detection can distinguish recurring vs one-time events.
  • Polar: use the Polar product price ID as id; type is recommended for fallback mode detection.
  • PayPal: one-time plans must define unit_amount + currency for 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.

Kirby Klub is not affiliated with the developers of Kirby CMS. We are merely standing on the shoulder of giants.
© 2026 Bruno Meilick All rights reserved.