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

Cashier Paystack Laravel Package

veeqtoh/cashier-paystack

Laravel Cashier for Paystack: a fluent billing library that brings subscriptions, customers, payment methods, single charges, invoices, refunds, and webhook handling to your Laravel app with minimal configuration.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit

The package follows the canonical Laravel Cashier pattern established by laravel/cashier (Stripe): a service provider registers the package, publishes a config file (config/paystack.php) and migrations, and exposes a Billable trait that is composed into an existing Eloquent model (typically User). This is a well-understood, low-ceremony integration shape for our stack:

  • Payments logic is attached to the domain model rather than a separate bounded context — acceptable for a monolith or modular monolith, but it hard-couples billing to the users table and to one subscription per "name" per user.
  • The package mutates the users table (adding paystack_id, card_brand, card_last_four, trial_ends_at, etc.) and introduces a subscriptions table. This is a schema-level commitment on our most trafficked table.
  • It is a payment gateway adapter, not a billing platform: no proration engine, no dunning/retry orchestration, no multi-gateway abstraction, no revenue reporting. Anything beyond "create/cancel/resume a Paystack plan and read status" is ours to build.
  • Paystack-specific semantics (plan code + plan name as a two-argument identity, Kobo-denominated amounts, card auth_token-driven charging) leak into application code and must be understood by whoever owns checkout.

Integration feasibility

High feasibility on paper. The install path is a four-command sequence (composer require, vendor:publish, migrate, env keys) plus a one-line trait addition, and the README documents the full happy path including subscriptions, trials, single charges, refunds, payment methods, webhooks, and invoice PDFs. For a Laravel team already familiar with Cashier concepts, a spike should be measurable in days, not weeks.

The gating question is not feasibility but supportability: we would be adopting a package with 6 GitHub stars, 0 dependent packages, a single apparent maintainer, and a release history that only began after the predecessor (webong/cashier-paystack) was abandoned. The README is comprehensive; the signals around it are thin.

Technical risk

Risk Severity Notes
Maintainer/bus factor High Solo maintainer, 6 stars, 0 dependents. If it stalls, we inherit a fork. Precedent: this package exists because its predecessor was abandoned.
API coverage vs. Cashier baseline Medium README covers the Stripe-Cashier surface, but parity is unverified — no evidence of upstream contract tests, Paystack sandbox CI, or coverage metrics.
Webhook correctness High Billing state is reconciled via Paystack webhooks. Signature verification, replay/idempotency, and CSRF exclusion are the classic failure points, and the README's "Webhooks & CSRF Protection" section is the least concrete part of the doc.
Schema coupling Medium Adding columns to users and a new subscriptions table is cheap now, expensive later. Migrations on a large users table require planning.
Currency/market scope Low–Medium Defaults to NGN, supports GHS via Cashier::useCurrency. Fine if our go-to-market is Nigeria/Ghana; a blocker if we need multi-currency, multi-gateway, or non-African acquirers.
Money-unit bugs Medium Charges are in Kobo (minor units). Mixed major/minor unit handling in app code is a recurring source of 100× billing defects.
Version/versioning discipline Medium Version badges and a CHANGELOG section exist, but no documented semver policy or upgrade guide content visible. A breaking change in a 0.x-style package is a live possibility.
Licensing Low MIT — no legal friction, forkable.

Key questions

  1. Is Paystack our only payment rail for the foreseeable 24 months, or will we need Stripe/Adyen/Flutterwave alongside? (Determines whether we should build a thin internal payment port over this package from day one.)
  2. Who owns the fork if the maintainer disappears — do we have PHP capacity to take custodianship, and what is the trigger (e.g., >90 days without a release, or a blocking Paystack API change)?
  3. What is our required Cashier parity list? Specifically: proration, metered/usage billing, multiple concurrent subscriptions per user, multi-currency, tax/VAT, and payment-method update flows — none are demonstrated in the README.
  4. What are Paystack's webhook retry semantics and does the package de-duplicate events? What is our reconciliation story when a webhook is dropped (nightly verifyTransaction sweep)?
  5. Do we accept the users-table mutation, or do we need billing state in a separate schema for compliance/data-residency reasons?
  6. What is the security posture of the maintainer's release pipeline (2FA, signed tags, Packagist ownership)? Can we pin by commit hash?

Integration Approach

Stack fit

Good fit if we are on Laravel 10.x with PHP 8.x and MySQL/Postgres, which matches the declared minimums. The trait-based design means billing status becomes available anywhere the User model is available — convenient for middleware (subscribed('main')), Blade conditionals, and API resources. The package is a poor fit if we run multi-tenant with per-tenant Paystack credentials (config is global/env-driven) or if the billing surface is large enough to warrant a dedicated service.

Recommendation: adopt inside a thin internal billing module (App\Billing) that wraps Cashier calls behind an interface (createSubscription, cancel, onGracePeriod, chargeOnce). This costs ~1–2 days and converts an unhedged vendor bet into a swappable dependency.

Migration path

Three scenarios, in increasing cost:

  1. Greenfield billing (best case). Install, publish, migrate, add trait, build checkout + webhook handlers. No data backfill. Sequenced in ~2 sprints including QA against Paystack sandbox.
  2. From webong/cashier-paystack. This is the package's stated purpose, but "complete re-write" implies namespace, trait, and possibly table-shape changes. We must produce an explicit column-by-column and method-by-method diff before committing; existing subscriptions rows need a mapping exercise and a rollback plan.
  3. From laravel/cashier (Stripe). No automated path. Gateway IDs, plan identifiers, and the subscriptions table shape differ. Treat as a re-implementation with historical-data import, not a migration.

Compatibility

  • Confirm our User model doesn't already define conflicting accessors/mutators for trial_ends_at or card fields; the README explicitly requires a date mutator for trial_ends_at, which suggests Laravel 10 $casts may need hand-holding.
  • Confirm primary key type (bigint vs uuid) compatibility with the published migrations — Cashier-style packages often assume bigint.
  • Confirm the config file name (paystack.php) doesn't collide with an existing Paystack SDK integration (e.g., unicodeveloper/laravel-paystack, which many Nigerian Laravel teams already have installed). Running two Paystack clients is a real and common footgun.
  • Pin the composer constraint tightly (~x.y) and commit composer.lock.

Sequencing

  1. Spike (3–5 days): sandbox install, end-to-end subscription with test card, webhook capture via ngrok, trial→charge transition, cancellation and grace period. Exit criteria: all six flows green and webhook signature verification confirmed.
  2. Fork/hedge decision (gate): if the spike surfaces parity gaps or if the maintainer is unresponsive, fork to a private registry before writing production code — not after.
  3. Data model (1 sprint): migrations on a replica first, users table index/lock analysis, backfill strategy if applicable, and a billing-state read model for support tooling.
  4. Checkout + subscription lifecycle (1–2 sprints): server-side initialization only (never trust client amounts), idempotency keys on charge creation, explicit Kobo handling in a single serialization boundary.
  5. Webhooks (parallel, before launch): CSRF exemption scoped to the webhook route only, signature verification, event de-duplication table, dead-letter queue, and an operator runbook.
  6. Trials, invoices, refunds (subsequent): invoice PDF generation is CPU/file-storage-bearing — decide storage and retention up front.
  7. Observability & reconciliation (must precede GA): nightly job that reconciles local subscription state against Paystack's API; alerts on divergence.

Operational Impact

Maintenance

This is the dominant cost. We inherit the maintenance of a package with one maintainer, no dependents, and a lineage of abandonment. Concretely:

  • Budget ~1–2 engineer-days per quarter for upgrade testing plus a standing fork-readiness checklist (private fork, CI, Packagist mirror) reviewed semi-annually.
  • The package must be treated as internal code we happen to source externally: pinned version, locked, reviewed diffs on upgrade, no auto-merge of dependency bumps.
  • Any Paystack API change (new required fields, deprecations, auth changes) is a potential break with no upstream SLA. Our mitigation is monitoring Paystack's changelog ourselves.

Support

No vendor support, no SLA, no commercial backing. Internal first-line support must be our own engineers. Documentation is a strong README but not a support artifact — it is silent on failure recovery, webhook retry handling, error taxonomy, and what Paystack error codes surface as exceptions. We will need to build and own:

  • A billing runbook (common Paystack error codes → customer-facing message).
  • An operator console or admin actions: force-resync from Paystack, replay webhook, void subscription.
  • A "who do I escalate to" path that terminates in our own team, not an upstream maintainer.

Scaling

  • Volume: Laravel/Paystack integrations are typically low-QPS (checkout + webhooks). The package itself is unlikely to be the bottleneck; Paystack's API rate limits and webhook delivery are.
  • Webhook throughput: webhooks arrive as unauthenticated HTTP and must respond fast. Enqueue immediately and process asynchronously; do not do Paystack API calls or heavy DB writes synchronously in the webhook handler. Without this, a burst during a billing-cycle rollover (e.g., 1st-of-month renewals) can cascade into timeouts and dropped events.
  • Database: subscription-status checks (subscribed('main')) sit on hot paths — index subscriptions(user_id, name) and cache status per request. Adding columns to a large users table requires online-DDL or a shadow-table approach.
  • Invoices: PDF generation is the one CPU/file-heavy operation; move to a queue and object storage from day one.

Failure modes

  • Webhook loss / divergence: the highest-probability, highest-impact failure. A dropped subscription.create or charge.success leaves a paying customer marked unpaid (or vice versa). Mitigation: idempotent handlers keyed on Paystack event reference, nightly reconciliation sweep, alert on divergence count > 0.
  • Webhook spoofing: missing or bypassable signature verification means subscription forgery. Mitigation: verify x-paystack-signature (HMAC-SHA512 with secret key) in middleware; test it explicitly; scope CSRF exemption to the single route.
  • Trial conversion failure: card declines at trial expiry produce silent churn. Mitigation: pre-expiry notifications, retry/dunning policy (which we must build — the package does not provide it), and a past_due-style state in our own model.
  • Auth-token invalidation: charging a stored card auth_token fails when the card expires or the issuer forces re-authentication. Mitigation: surface a card-update flow (README documents retrieval/deletion of payment methods but not update-and-retry); treat as a product requirement, not a package gap to ignore.
  • Money-unit errors: Kobo/major-unit confusion produces 100× over- or under-charges. Mitigation: single conversion helper, unit tests with known amounts, server-side recalculation of all totals.
  • Upstream abandonment: no releases for 6+ months while our Laravel version advances. Mitigation: the fork-readiness checklist above; keep the App\Billing wrapper so the blast radius is one adapter, not the codebase.
  • Namespace/config collision with an existing Paystack SDK: silent misconfiguration. Mitigation: verify at spike time.

Ramp-up

Low for Laravel engineers already fluent in Cashier idioms — the API is deliberately Stripe-Cashier-shaped, so newSubscription(...)->trialDays(10)->create($token) reads as familiar. Expect 2–4 days to productive contribution including sandbox setup and webhook testing.

Higher for the team's collective operational ramp-up: understanding Paystack's object model (plans, plan codes, authorization codes, transaction references), webhook lifecycle, and reconciliation. That is a 1–2 week organizational learning curve and is independent of the package — it would exist with any Paystack integration.

Documentation gaps to close internally on day one: webhook signature verification specifics, error-code mapping, the trial-without-payment-method path (generic trials vs. trialDays), and invoice PDF storage/retention. Notably, several README passages use "Paystack Paystack code" — a copy-paste artifact from the Stripe docs — which signals the docs were adapted rather than written against verified behavior. Treat every code sample as unverified until the spike proves it.

Verdict: Technically sound and well-shaped for a Laravel + Paystack monolith, and cheap to trial. Adoption is justified only behind an internal abstraction layer, with a private fork prepared in advance and reconciliation/observability built before general availability. The 6-star/0-dependent profile is not a reason to reject the package; it is a reason to refuse to depend on it unmargined.

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.
gmostafa/php-graphql-client
xeviant/paystack
snowsoft/faker
meditationsteps/blogetc
animalyoung/laravel-influxdb
onix-solutions/sms-telera
lonely-lights/prosetta
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge
simple-as-fuck/php-performance-log