eugenefvdm/billing
A Laravel/PHP billing package by eugenefvdm that provides a foundation for handling invoices, payments, and subscription billing inside your application. Framework-native and lightweight, it aims to keep core billing logic organized and easy to integrate.
Architecture fit. eugenefvdm/billing is a Laravel-native package (service provider, publishable config, migrations, Eloquent traits) that owns plans, subscriptions, and payment records. The v0.4 release note — "Only activate sub when EFT money received" — is the load-bearing detail: it moves the activation boundary from intent-to-pay (subscription created/invoiced) to settlement-confirmed (funds reconciled). That reframes the package from a checkout convenience into a system of record on a finance-critical path. It fits best where the host app already has an authoritative EFT settlement source — a bank feed, payment-provider webhook, finance approval workflow, or ERP export — and can supply a discrete, auditable "money received" signal. It fits poorly if the only signal available is a human reading bank statements with no structured reference field.
Integration feasibility. The code change is likely a composer update plus a config/migration publish and a handful of gated call sites. The real effort is process: every place that currently creates an EFT subscription and implicitly treats it as live must be re-examined, and a confirmation entry point (admin action, reconciliation command, or webhook handler) must be wired to the package's activation path. Treat this as a workflow integration with an upgrade attached. Feasibility is moderate and bounded — days of engineering, weeks of validation and finance/support alignment.
Technical risk. Concentrated in lifecycle correctness: pending subscriptions that never activate, duplicate or double-fired activation from retried confirmations, receipts arriving before the subscription record exists, out-of-order or partial payments, and pre-existing subscriptions stranded in a now-invalid state. Because the package is pre-1.0 with effectively zero adoption, there is also dependency risk: undocumented behavior, sparse release notes, no community signal on edge cases, and a single maintainer. Do not assume semver stability; pin the version and verify the activation contract against source before committing.
Key questions.
activated_at timestamp, an event, a service method?lockForUpdate, unique constraint on payment reference)?Stack fit. Good for a standard Laravel app that already uses service providers, migrations, queues, and events. The package should not be the only thing that knows money arrived — the host app needs its own receipt record (with reference, amount, value date, confirming actor/source) and the package should be the thing that translates that record into a subscription state change. Keep a thin host-owned action class (e.g. ConfirmEftPayment) as the seam: it survives package upgrades and is the single place to add audit logging, partial-payment policy, and retry behavior.
Migration path. Start with an inventory, not a composer command. Enumerate every path that creates an EFT subscription — storefront checkout, admin panel, API endpoints, imports, legacy queued jobs — and for each, identify the current activation trigger. Then:
isActive()-style check (status + timestamps), never on the mere existence of a subscription row; audit subscribed(), middleware, policies, and Blade conditionals.Compatibility. Confirm PHP/Laravel version support, database migration requirements (new columns, indexes, nullable/nullability changes), any changed config keys, and whether existing rows are valid post-upgrade. Because this is v0.x, treat every minor bump as potentially behavior-changing and lock the version in composer.json with a rationale recorded. Validate on a staging copy of production data, not an empty schema.
Sequencing.
travel()/freezeTime(); Event::fake() assertions that activation side effects are queued afterCommit().Sequence the technical work before the finance/support runbook, so the runbook describes behavior that actually exists.
Maintenance. Increases materially — this package now sits between revenue and entitlement. The host app inherits responsibility for reconciliation jobs, the confirmation seam, audit logging, and upgrade vigilance on a pre-1.0 dependency with no community. Budget for re-reading source on every version bump rather than relying on changelogs.
Support. Finance and support need a single shared view answering four questions per customer: is there a receipt, what reference, what status, and who confirmed it. Build the admin surface for this early — a "paid but inactive" ticket is the dominant support case and is unrecoverable to triage without a reference/status/latency view. Manual override must be authorized, attributed, and logged.
Scaling. Primary pressures are queue throughput for confirmation side effects, webhook/ingest concurrency, database indexes on subscription status and payment reference, and reconciliation batch size against statement volume. Reconciliation should be chunked and resumable, not a single long transaction. Cache invalidation is a real concern: any cached entitlement or "is subscribed" flag must be busted on activation and on pending expiry.
Failure modes to design against.
update() reconciliation skips model events → active subscription with no provisioning or welcome mail.Ramp-up. Operational readiness should include: dashboards for pending-EFT age, activation latency, failed/unmatched reconciliations, duplicate-event suppression count, and manual overrides; an alert on reconciliation job failure and on pending-EFT age breaching policy; a runbook with explicit playbooks for "paid but inactive" and "active without payment"; a rollback/feature-flag plan; and a named escalation path spanning engineering, finance, and support. Because the release note is one line and adoption is zero, treat every conclusion here as provisional until the activation contract, migration behavior, and event model are verified in source — and record that verification as a gate before rollout, not after.
How can I help you explore Laravel packages today?