saucebase / billing
Stripe billing for Saucebase: checkout, subscriptions, trials, a pricing page and invoices.
Requires
- saucebase/core: ^2.0
- stripe/stripe-php: ^22.0
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
- dev-main
- v3.1.4
- v3.1.3
- v3.1.2
- v3.1.1
- v3.1.0
- v3.0.0
- v2.1.4
- v2.1.3
- v2.1.2
- v2.1.1
- v2.1.0
- v2.0.0
- v1.7.0
- v1.6.2
- v1.6.1
- v1.6.0
- v1.5.0
- v1.4.0
- v1.3.0
- v1.2.1
- v1.2.0
- v1.1.0
- v1.0.3
- v1.0.2
- v1.0.1
- v1.0.0
- v0.0.12
- v0.0.11
- v0.0.10
- v0.0.9
- v0.0.8
- v0.0.7
- v0.0.6
- v0.0.5
- v0.0.4
- v0.0.3
- v0.0.2
- v0.0.1
- dev-dev-22-10
- dev-test/mutation-gaps
- dev-chore/mit-license
- dev-dev-v3
- dev-sc-72-inertia-modal
- dev-retire-settings-module
- dev-dev-v2
- dev-dev
- dev-feat/fe-multi-framework
- dev-feat/internachi-modular
- dev-demo
This package is auto-updated.
Last update: 2026-10-23 20:24:44 UTC
README
Stripe billing for Saucebase: checkout, subscriptions, trials, a pricing page and invoices.
A module for Saucebase, the AI-ready Laravel SaaS kit. Modules are Composer packages you add to a Saucebase app; once installed, the code is yours to change.
Features
- Stripe checkout, straight to Stripe or through your own checkout page
- Subscriptions that cancel at period end and can be resumed
- Customer portal for cards, invoices and plan changes
- Free trials, one per customer, with or without a card up front
- Grace period after a failed payment, with emails at each step
- Plan kinds: free, subscription, lifetime and one-off
- Entitlements: each plan turns features on and sets limits your app checks
- Pricing page at
/pricing, built from your plans - Billing settings: current plan, invoices and saved card
- Catalogue sync with Stripe, both ways
- Webhooks that are safe to retry and handle events out of order
- Emails for every subscription and payment change
- Admin panel for products, subscriptions and customers, with a revenue dashboard
- Vue and React
Install
New to Saucebase? Start a new app and pick billing when it asks for modules:
curl -fsSL https://install.saucebase.dev | bash
It installs PHP and Composer if they're missing. With the CLI already installed, run saucebase new my-app instead.
Already have a Saucebase app? Add it with:
composer require saucebase/billing git apply modules/billing/patches/user.patch php artisan migrate php artisan modules:seed --module=billing npm run build
Then create your plans in Stripe and import them with php artisan billing:sync-catalog. They arrive hidden: describe them in the admin panel, under Billing → Products, and turn on Visible.
Manual install
If a patch doesn't apply, because your file has changed, make its change by hand:
user.patch makes users billing owners, which subscriptions need:
// app/Models/User.php use Modules\Billing\Contracts\BillingOwner; use Modules\Billing\Traits\Billable; class User extends Authenticatable implements BillingOwner { use Billable; }
Configuration
Add your Stripe keys to .env:
STRIPE_SECRET_KEY= STRIPE_PUBLISHABLE_KEY= STRIPE_WEBHOOK_SECRET=
In Stripe, under Developers → Webhooks, add https://<your-app>/billing/webhooks/stripe with these events. Its signing secret is STRIPE_WEBHOOK_SECRET.
checkout.session.completed
checkout.session.async_payment_succeeded
customer.subscription.updated
customer.subscription.deleted
customer.subscription.trial_will_end
invoice.paid
invoice.payment_succeeded
invoice.payment_failed
charge.refunded
payment_method.attached
payment_method.detached
customer.updated
Locally, forward them with stripe listen --all-snapshot --forward-to localhost/billing/webhooks/stripe.
The rest is in the admin panel, under Settings → Billing: currency, checkout flow, grace period and trials.
Show plans on the homepage (optional)
The plans live at /pricing. To also list them on the homepage, use core's home::content.after slot. Paths below are relative to the module's resources/js/. A flat install keeps one stack there; a checkout with both keeps them in vue/ and react/, so use the folder of the stack you run.
-
Share the plans on the homepage only, from
shareInertiaData()insrc/Providers/BillingServiceProvider.php(core's hook for shared props, called fromboot()). This is the same payloadBillingPlansControllerbuilds:use Modules\Billing\Models\Product; use Modules\Billing\Services\BillingOwners; use Modules\Billing\Services\PlanActions; protected function shareInertiaData(): void { Inertia::share('billing.home', function () { if (! request()->routeIs('index')) { return null; } $products = Product::displayable()->get(); $user = request()->user(); return [ 'products' => $products, ...$this->app->make(PlanActions::class)->for($this->app->make(BillingOwners::class)->for($user), $products, $user), ]; }); }
-
Add
components/HomePricing, next toProductSection:<!-- Vue: components/HomePricing.vue --> <script setup lang="ts"> import { usePage } from '@inertiajs/vue3'; import { computed } from 'vue'; import ProductSection from './ProductSection.vue'; const home = computed(() => usePage().props.billing?.home); </script> <template> <ProductSection v-if="home?.products.length" v-bind="home" /> </template>
// React: components/HomePricing.tsx import { usePage } from '@inertiajs/react'; import ProductSection from './ProductSection'; export function HomePricing() { const home = usePage().props.billing?.home; return home?.products.length ? <ProductSection {...home} /> : null; }
-
Register it inside
setup()in the module'sappentry. A lowerorderrenders first. Use your stack's alias,@saucebase/vueor@saucebase/react:// registerGlobalComponent is already imported there, from '@saucebase/<stack>/lib/globalComponents' import HomePricing from './components/HomePricing.vue'; // React: import { HomePricing } from './components/HomePricing'; // inside setup() registerGlobalComponent('home::content.after', HomePricing, { order: 10 });
Run npm run build afterwards.
Demo data
php artisan modules:seed --module=billing --demo
Adds sample plans (pushed to Stripe when your keys are set), sample customers, and a staff account (billing@saucebase.dev) that only sees billing in the admin. All use the DEMO_PASSWORD from .env.
License
MIT. See LICENSE.