Subscription & Payments

Stripe integration, plan tiers, webhooks, and feature gating

Subscription & Payments

For full endpoint details (request/response schemas, examples), see the interactive API docs at /api/doc.

What's Included

  • Three plan tiers (Free, Starter, Pro) with configurable features
  • Stripe Checkout for both recurring and one-time payments
  • Webhook handling for 5 Stripe events
  • Feature gating via FeatureAccessChecker
  • Guest and authenticated checkout support

Plan Tiers

PlanFeatures
FreeBasicAccess
StarterBasicAccess, AdvancedReports, ApiAccess
ProBasicAccess, AdvancedReports, ApiAccess, PrioritySupport, CustomIntegrations

Every user starts on the Free plan. Paid plans are activated after Stripe checkout completion.

Feature Access

Use FeatureAccessChecker to gate features in your code:

// Check if user has a feature
$checker->hasFeature($userId, Feature::AdvancedReports); // bool

// Throw if feature not available
$checker->requireFeature($userId, Feature::ApiAccess); // throws if denied

// Get the user's effective plan
$checker->getEffectivePlanType($userId); // PlanType::Free if inactive

// Get all available features
$checker->getAvailableFeatures($userId); // Feature[]

Stripe Setup

1. Create a Stripe Account

Sign up at stripe.com and get your API keys from the Developers section.

2. Set Environment Variables

STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
STRIPE_SUCCESS_URL=https://yourapp.com/payment/success
STRIPE_CANCEL_URL=https://yourapp.com/payment/cancel

3. Configure Plans

Edit config/packages/stripe.yaml to define your plan tiers and prices. See Configuration.

4. Sync Plans to Stripe

docker compose exec php bin/console app:stripe:sync-plans

This creates or updates Stripe Products and Prices to match your config. Use --dry-run to preview changes.

5. Set Up Webhook (Production)

In the Stripe Dashboard > Developers > Webhooks:

  1. Add endpoint: https://yourdomain.com/api/v1/webhooks/stripe
  2. Select events: checkout.session.completed, customer.subscription.updated, customer.subscription.deleted, invoice.payment_failed, invoice.paid
  3. Copy the signing secret to STRIPE_WEBHOOK_SECRET

6. Local Testing with Stripe CLI

# Install Stripe CLI, then:
stripe listen --forward-to localhost/api/v1/webhooks/stripe

# Copy the webhook signing secret printed by the CLI to your .env.local
STRIPE_WEBHOOK_SECRET=whsec_...

Webhook Events

Stripe EventAction
checkout.session.completedCreates subscription (free → paid upgrade)
customer.subscription.updatedUpdates plan/status
customer.subscription.deletedCancels subscription
invoice.payment_failedMarks subscription as past due
invoice.paidReactivates subscription after successful payment

Plans are fetched from Stripe and cached for 1 hour. Checkout supports both authenticated users (email pre-filled, user_id in metadata) and guest users (Stripe collects email).

Subscription Lifecycle

User signs up → Free plan (automatic)
       |
       v
Checkout → Stripe payment → Webhook → Paid plan (Starter/Pro)
                                         |
                              +----------+----------+
                              v          v          v
                          Recurring   One-time    Cancel
                           |            |           |
                      Invoice cycle  Lifetime    Downgrade
                           |          access     to Free
                      +----+----+
                      v         v
                   Paid      Failed
                    |          |
                 Active    Past Due → Expired → Free