Skip to content

Releases: cboxdk/laravel-billing

v0.8.1

Choose a tag to compare

@sylvesterdamgaard sylvesterdamgaard released this 18 Jul 07:26

Two additive, backward-compatible features (resolve within ^0.8).

Plan retirement / sunset (ADR-0016). A plan can carry a PlanRetirement (retiresAt cutoff + optional defaultSuccessorPlanId). At a subscription's next renewal on/after the cutoff it resolves: the subscriber's scheduled successor plan, a scheduled cancel, or the default successor — else UnresolvedRetirement and renew refuses (deny-by-default: never charges a retired plan). No grandfathering; no one loses paid time. PlanRetirementResolver + RetirementRenewalPolicy.

Shared retention seam. Cbox\Billing\RetentionCancellationSurvey + RetentionOffers contracts, a SaveOffer VO (FreeMonth | Discount | Pause | Downgrade | Custom, each mapping to an existing billing lever), CancellationReason/CancellationResponse, and SubscriptionCancellationRequested/SaveOfferAccepted/RetentionResolved events. Null defaults keep it a plain cancel until a host/plugin binds the rich flow.

395 tests. PHPStan level max, permissive-only.

v0.8.0

Choose a tag to compare

@sylvesterdamgaard sylvesterdamgaard released this 17 Jul 09:31

Major competitive-parity release — the pricing, usage, subscription-lifecycle and analytics breadth the mature commercial products have.

  • Pricing models: graduated / volume / package / stairstep tiers (PricingModel + PriceTier + a remainder-safe TierCalculator), alongside flat / per-unit.
  • Billable-metric aggregations: Count / Sum / Max / UniqueCount / Latest / WeightedSum via EventLog::aggregate() (in-memory + database), composed usage→quantity→tiered-price.
  • Subscription depth: free trials, a first-class state machine (Trialing / Active / PastDue / Paused / NonRenewing / Canceled, deny-by-default transitions), ramp deals, and minimum commitments + true-up.
  • Reporting depth: MRR movement waterfall (new/expansion/contraction/churn/reactivation, identity-checked), NRR/GRR, cohort retention matrix, and ARR waterfall — exact minor-unit math.

Additive/backward-compatible (new enum cases, trailing-optional VO params, an additive migration). 372 tests. Consumers on ^0.7 should widen to allow ^0.8.

v0.7.0

Choose a tag to compare

@sylvesterdamgaard sylvesterdamgaard released this 16 Jul 20:55

Billing domain events so plugins/hosts react to the lifecycle via listeners — no host wiring, no base edits.

Dispatched through the injected event dispatcher at the real lifecycle points (additive; every emitting service gained an optional trailing ?Dispatcher — no existing signature broke):

  • InvoiceIssued (DefaultInvoicer::issue) · PaymentSettled (DefaultWebhookIngest::ingest, applying path only) · CreditNoteIssued (DefaultRefunder::refund) · SubscriptionRenewed/SubscriptionChanged (SubscriptionManager) · LicenseIssued (LicenseMint::issue).

Idempotent/duplicate operations don't re-fire. Backward-compatible → consumers on ^0.6 should widen to ^0.6 || ^0.7. 317 tests. See docs/extension-points/events.md.

v0.6.0

Choose a tag to compare

@sylvesterdamgaard sylvesterdamgaard released this 16 Jul 16:40

On-prem / self-hosted software licensing — the issuer side (Cbox\Billing\Licensing).

Billing mints a signed, offline-verifiable software license from a licensable plan; the self-hosted app verifies it against a bundled public key and unlocks paid capabilities through its entitlements gate. Wraps the published crypto core cboxdk/license (EdDSA/Ed25519) — no hand-rolled crypto.

  • LicenseProfile (entitlement keys + LicenseLimits orgs/seats/environments) resolved from a plan, deny-by-default for non-licensable plans.
  • LicenseMintissue() / reissue() (renewal extends the validity window, re-pinning customer + deployment + domain binding); SubscriptionLicensePolicy derives expiry from the paid period + grace.
  • IssuedLicenseStore + RevocationRegistry ports (in-memory defaults; host supplies DB impls); RevocationPublisher emits the signed revocation list.
  • The key-holding core issuers are bound by the host app from config — this module is key-agnostic.
  • Requires cboxdk/license: ^0.1. 310 tests (real-keypair round-trip: mint → verify → revoke). PHPStan level max, permissive-only.

v0.5.0

Choose a tag to compare

@sylvesterdamgaard sylvesterdamgaard released this 16 Jul 16:25

Product shapes + term-based (registrar-style) products (ADR-0015).

A billing account (org) now sells three product shapes side by side: metered plans, recurring subscriptions, and fixed-term / registrar-style products (domains, hosting terms, certificates).

  • ProductShape (Metered | Recurring | FixedTerm | OneTime) is a first-class Catalog attribute.
  • Price gains a Term (P1Y/P2Y/P5Y) + PriceKind (Register / Renewal / Transfer / Redemption) dimension; Catalog::termPriceFor() resolves the effective, grandfathered (term × kind) price.
  • TermSubscription instances: committed term with a definite end, renew-for-a-term at the Renewal price, auto-renew, per-instance resource metadata, and the registrar lifecycle Active → Grace → Redemption → Expired plus Transfer — many instances per (org, product).
  • Term purchase / renew / redeem / transfer compose through the existing Quote → Invoice pipeline (tax, seller-of-record, credits, dunning unchanged). Entitlements stay per (org, instance).
  • Registry/EPP/DNS provisioning is out of scope — the engine owns the commercial lifecycle only.

304 tests. PHPStan level max, permissive-only, deterministic SBOM.

v0.4.0

Choose a tag to compare

@sylvesterdamgaard sylvesterdamgaard released this 16 Jul 08:12

Adds createCustomer and detachPaymentMethod to the PaymentGateway contract, completing the stored-customer / saved-method surface so a host app no longer has to reach into a gateway SDK directly for them.

  • createCustomer(string $account, ?string $email, ?string $name): string — create-or-return the gateway customer, stamping the account key into metadata for reconciliation.
  • detachPaymentMethod(string $account, string $paymentMethodId): void — idempotent detach; a no-op for vault-less gateways.

Implemented in ManualPaymentGateway (deterministic local ref; no-op detach) and the dogfooded FakePaymentGateway. Minor bump: the interface addition is breaking for external implementers. Adapter packages (laravel-billing-stripe, laravel-billing-mollie) implement these in their own v0.4.0.

v0.1.0 — the billing engine

Choose a tag to compare

@sylvesterdamgaard sylvesterdamgaard released this 15 Jul 14:17

First tagged release of Cbox Billing — the self-hostable monetization engine for Laravel, composing cboxdk/laravel-tax + cboxdk/laravel-geo.

Modules

Metering (+ pluggable EventLog: in-memory · relational · ClickHouse adapter) · double-entry Ledger (in-memory · durable DB · two-phase reserve/commit) · Money · Wallet/credits · Quote (tax + coupon + credit + progressive tax-resolution consequence-preview) · Catalog (grandfathering) · Seller-of-record (multi-entity routing) · Invoice (per-entity legal numbering) · Subscription (lifecycle + plan-change preview) · Payment (gateway-agnostic + dunning) · Pricing (coupons) · Entitlement (per-product projection to cbox-id) · Reporting (MRR/ARR/churn).

Gateway adapters ship separately: laravel-billing-stripe, -mollie; ClickHouse event log: laravel-billing-clickhouse.

PHP ^8.4 · Laravel ^13. Gate green: pint · phpstan level max · 61 tests · license-check · SBOM · audit. Status: early / pre-1.0.