chargebee/laravel-react-starter-kit
A Laravel and React starter kit that wires up Chargebee subscription billing, giving developers a ready-made foundation for building SaaS apps with authentication, plans, and payment flows. Includes example UI and backend integration.
This is a starter kit, not a package — there is no composer require step and no versioned API surface. Treat it as reference architecture you copy from, not a dependency you install.
git clone https://github.com/chargebee/laravel-react-starter-kit.git my-app
cd my-app
composer install && npm install
cp .env.example .env && php artisan key:generate
Where to look first, in order:
config/ (or config/services.php) for the Chargebee site/key wiring — this tells you which env vars actually matter.routes/web.php and routes/api.php — the billing routes (checkout, portal, webhook) are the spine of the kit.app/Http/Controllers — the two or three billing controllers do 80% of the useful work.resources/js — the React side handles plan selection and checkout redirects; it's thin by design.First use case: stand up a sandbox Chargebee site, set CHARGEBEE_SITE and CHARGEBEE_API_KEY to sandbox values, seed two or three plans in the Chargebee dashboard, then walk the full loop: pricing page → checkout redirect → returning customer with an active subscription. Do not attempt production until you can explain every row in your local subscriptions table.
The real value is that it demonstrates the shape of a Chargebee + Laravel integration. Budget a day to read it and a week to harden it.
Copy-in, not fork-and-abandon. Most teams get more value cherry-picking the billing controllers, migrations, and webhook handler into an existing app than building on the kit wholesale. If you do build on it, immediately strip the demo auth scaffolding you don't need.
Treat Chargebee as the source of truth. The kit's instinct — mirror subscriptions locally, reconcile via webhooks — is correct. Use local tables for querying and gating, not for authority. Any read that answers "can this user do X?" should hit local state; anything that changes billing state should go to Chargebee and come back via webhook.
Hosted checkout + portal, not custom card forms. The kit steers toward Chargebee's hosted pages, which keeps you out of PCI scope. Pattern: a route that creates a hosted checkout session and redirects, plus a portal route for self-serve plan changes and card updates. This is the single highest-leverage pattern in the kit — copy it first.
// Pseudocode shape of the checkout pattern
$result = $chargebee->subscription()->createWithItems($customerId, [...]);
return redirect($result->subscription->hostedPage['url']);
Webhook handler as the only mutator. Route all subscription state changes through one idempotent webhook endpoint: verify signature, find-or-create the local customer, upsert subscription by Chargebee ID, log the event ID. Then dispatch domain events (SubscriptionActivated, SubscriptionCancelled) so the rest of your app never touches Chargebee directly.
Feature gating via a single helper. Add $user->subscribed('plan-slug') or a can('feature') gate reading from your local subscription row plus plan entitlements. Resist scattering if ($subscription->status === 'active') checks — the kit's demo code does this and it metastasizes.
Test-mode discipline. Keep two env blocks (CHARGEBEE_SITE sandbox vs production) and never let the webhook secret be shared between them. Use Chargebee test cards for the full lifecycle: success, decline, 3DS, trial-to-paid.
React/Inertia integration. The kit expects the frontend to know plan IDs and prices. Fetch entitlements from a small JSON endpoint on page load rather than hardcoding in React — plan IDs differ per environment and you will otherwise ship a build pointing at sandbox.
chargebee/chargebee-php version in composer.json and check the API version header when debugging "field not found" errors.VerifyCsrfToken exemptions incorrectly, signatures fail silently as 200s and your local state drifts. Log rejected signatures with the raw payload during development.CHARGEBEE_SITE, CHARGEBEE_SITE_API_KEY, and similar variations depending on commit. Grep the whole repo for env( and config( before configuring.status but doesn't model in_trial, non_renewing, or grace windows meaningfully. If you need "keep access for 3 days after failed payment," you will write it yourself off the webhook stream.BillingService wrapper around the Chargebee SDK (so the SDK doesn't leak into controllers), a BillingEvent model for the webhook log, and a Plan config file mapping Chargebee plan IDs to human-readable slugs and entitlements. Build these three before anything else.stripe-style local forwarding doesn't exist, so use ngrok or Expose for local webhook testing and keep a billing:replay {event_id} artisan command.users.id to chargebee_customer_id deliberately via a customers table, not by bolting a column onto users.How can I help you explore Laravel packages today?