Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Saas Bundle Laravel Package

simpleweb/saas-bundle

SimpleWeb SaaS Bundle is a PHP package that bundles common SaaS building blocks such as plans, subscriptions, billing, and account management, helping Laravel/PHP developers add subscription features faster and focus on their core product.

View on GitHub
Deep Wiki
Context7

Getting Started

Read this first: the package is a Symfony 2 bundle, not a Laravel package. There is no composer require simpleweb/saas-bundle path into a Laravel app — its Symfony/Doctrine constraints are PHP 5.x/7.0-era and will conflict on PHP 8+/Laravel 10+. On a Laravel codebase, your day-to-day use is as a reference model to port, not a dependency to install. Everything below assumes that framing unless you are explicitly on a legacy Symfony 2 host.

Minimal steps to get value in a day:

  1. Clone the repo and skim in this order: Entity/ (the domain model), DependencyInjection/Configuration.php (what's actually configurable — the README is likely stale), Event/ + EventListener/ (hook points), Gateway/ (how provider calls are shaped).
  2. Extract the domain vocabulary into a one-page model: Plan, Subscription (with states like trialing → active → past_due → canceled), Invoice, PaymentMethod, Team/Member/Invitation. Note every state transition the code implies.
  3. Map each concept onto your Laravel target: Eloquent model or enum, migration, and the service/job that owns the transition.
  4. First use case (the vertical slice): seed two Plan records, create a Subscription for a user/team against one, and gate a single feature on that subscription's status. That one path exercises models, plan lookup, state, and gating — and tells you in hours whether the bundle's design is worth more of your time.

If you are on a Symfony 2 host: register Simpleweb\SaaSBundle\SimplewebSaaSBundle in AppKernel, import the bundle extension config, override the entity class parameters with your own subclasses/mapped superclasses, and use doctrine:migrations:diff (never schema:update) to materialize tables.

Implementation Patterns

Plan catalog is immutable and versioned. Never edit a Plan row that has subscribers — a price change silently rewrites the meaning of historical invoices. Create a new Plan (v2) and migrate subscribers explicitly, recording which plan version an invoice was billed under.

Denormalize entitlements onto the reader. The bundle resolves subscription state through the DB; feature checks run on every request. On subscription state-change, write plan_id / active_until / a limits snapshot onto your users/teams row (or a cached entitlement payload) and gate on that column. Let the billing domain be the writer; let your app be the fast reader. In Laravel, this is a listener on your SubscriptionUpdated event writing to a denormalized column, with a scheduled reconciliation job as backup.

Port the state machine before the code. Extract the explicit states and legal transitions from the source, then model them as a PHP enum plus guarded transition methods ($subscription->markPastDue(), ->cancelAtPeriodEnd()). This is the part genuinely worth borrowing — the invariants around trial expiry, mid-cycle upgrades (proration), and cancellation timing.

Replace gateway adapters with Cashier. The bundle's Gateway/ layer targets a decade-old provider API surface. Do not port it. Use Laravel Cashier (Stripe/Paddle) for the gateway, and port only the domain model and the event semantics.

Swap the event dispatcher idiomatically. Symfony's dispatcher hooks become Laravel events + queued listeners: SubscriptionCreated, SubscriptionRenewed, PaymentFailed, SubscriptionCanceled. Make anything that sends an email, writes an invoice, or calls the gateway a queued job with retries — the bundle assumes synchronous, in-request billing, which is exactly what you should not carry forward.

Webhook ingestion is your own responsibility. Build it natively: verify signatures, persist the raw event with an idempotency key, and make the handler a no-op on replay. Then reconcile — a scheduled job that compares local subscription state against the gateway's truth and repairs drift.

Seed plans, don't create them at runtime. Plans are catalog data. Use a seeder/fixture with stable slugs (pro-monthly, team-annual) that code can reference, rather than database IDs or free-text names.

Gotchas and Tips

  • The stack framing is the biggest gotcha. Any attempt to bridge this bundle into a modern Laravel process — a Symfony sidecar, a container shim, an ORM bridge — costs more than reimplementing the subscription logic. Mine the design, write the code.
  • Zero dependents, no maintainer signal. There is no upstream triage, no security patching, and no forks to lean on. Treat every read as a one-way extraction; never vendor the files into your repo without a license check and a clear note that you now own them forever.
  • Don't trust the README. Read DependencyInjection/Configuration.php for the real option tree. Old bundles drift badly from their docs — the config class is the source of truth for class overrides, table prefixes, and gateway credentials.
  • Entity extension is via mapped superclasses. If you ever do run this on Symfony 2, subclass and remap rather than editing vendor entities. Use migrations:diff; schema:update will silently drop columns and is a data-loss footgun.
  • Money handling. If the source stores amounts as floats or as strings without a currency/scale, fix that in your port — integer minor units plus an ISO currency code, always. Never port a float money type forward.
  • Time and proration. Mid-cycle upgrades, trial-to-paid conversion, and "cancel at period end" are all timezone- and boundary-sensitive. Store UTC, store both current_period_start and current_period_end, and write tests for the exact boundary instants. Porting the behavior without these invariants is how you get double charges.
  • Deprecated gateway assumptions. The Gateway/ code likely calls endpoints that no longer exist. Do not "fix" it — replace it. Check which gateway the bundle assumes and confirm its current API shape before assuming any code is reusable.
  • Extension points worth studying: the bundle's events/listeners and its subscription state transitions. Those are the parts with transferable value; the DI wiring, routing, and gateway calls are not.
  • Debugging a legacy run: when something misbehaves on a Symfony 2 host, check the compiled DI container and the Doctrine mapping cache before the domain code — stale cache is the usual culprit, and clearing it (cache:clear) will save you an hour of reading entities.

Bottom line for day-to-day: use it as a 1–2 day architectural checklist — plans, subscription states, trials, proration, invoices, dunning, cancellations — then implement the resulting model against Laravel Cashier or a small bespoke module you own and can maintain.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo