luigel/laravel-paymongo
Laravel integration for PayMongo, the Philippine payment gateway. Provides an expressive wrapper for payment intents, sources, checkout sessions, webhooks, and related APIs, with configuration, facades, and fluent methods to accept GCash, cards, and other payment methods.
Architecture fit. This is a thin, conventional Laravel package wrapping the PayMongo REST API: a service provider, a facade, a publishable config file, a package-owned webhook route/controller, and a translation layer that maps PayMongo webhook payloads into Laravel events (Luigel\Paymongo\Events\*) plus a generic WebhookReceived. It does not own persistence, does not introduce its own queue or scheduler, and defers to Laravel's event dispatcher and HTTP client. That is the right shape for a payment-gateway adapter: low architectural surface area, no schema changes, and it composes with the host app's existing job/queue/listener conventions. It is an integration dependency, not a framework — treat it as a boundary adapter to be isolated behind an application-level payment service.
Integration feasibility. Straightforward: composer require, publish config, set public/secret keys and webhook signing secret via env, register the webhook URL in the PayMongo dashboard, and add listeners. The main effort is not installation but the surrounding discipline — idempotency keys, a persisted webhook/event log, signature verification, and reconciliation against PayMongo's API for anything money-affecting.
Technical risk. The headline risk in v3.0.1 is process, not runtime: a patch-level release ships a breaking API change. It removes nine WebhookEventType enum cases and their corresponding Luigel\Paymongo\Events\* classes (payment_intent.succeeded, payment_intent.awaiting_payment_method, subscription.activated, qr.paid, qr.expired, refund.succeeded, dispute.created, dispute.resolved, payout.deposited, payout.returned). Concretely:
match/switch arm, Event::listen registration, use statement, or config referencing those classes will fail at class-load/compile time or throw \UnhandledMatchError after upgrade — i.e., fatal errors, not deprecations.WebhookEventType::from($string) over a value read from storage, a test fixture, or a replayed payload can now throw \ValueError.WebhookReceived. That preserves forward compatibility with PayMongo adding or renaming events, and it is the hook a TPM should insist the implementation subscribes to for anything not explicitly enumerated.Key questions.
3.0.1) rather than ^3.0 and expect future removals in patch releases.match blocks generated from webhook logs?event_type columns, queue payloads already serialized) containing removed event strings that could hit ::from() on replay or retry?WebhookReceived path, and is it wired to an idempotent, logged consumer so we never depend on the enumerated event classes for money-critical flows?Stack fit. Compatible with a standard Laravel application (service provider auto-discovery, config publish, facade). Requirements are the usual PHP + Laravel version floor plus guzzlehttp/guzzle; verify against our actual PHP/Laravel matrix before committing. It is a leaf dependency — no framework coupling beyond events and config — so it isolates cleanly behind an application-owned PaymentGateway interface.
Migration path. Low effort, moderate care:
Luigel\Paymongo\Events\... FQCNs; include tests, factories, and any match on WebhookEventType.WebhookReceived listener with an internal event_type string switch, or drop the arm entirely if it was unreachable.ValueError.phpstan/psalm) and the full test suite — this is the step that surfaces class-not-found errors, not runtime.Compatibility. Behaviourally compatible: webhook registration now succeeds where it previously failed with parameter_invalid, which is a real fix — the old code was asking PayMongo to subscribe to event types it does not support, so webhook provisioning was likely broken or partially broken for anyone who passed those types. That is worth confirming: if webhook registration was failing in production, this release is a fix for a live defect, not just a cleanup, which raises its priority.
Sequencing. Do not bundle with other payment work. Order: (a) inventory removed references and persisted strings, (b) add an idempotent WebhookReceived consumer with a persisted webhook log if one does not exist, (c) upgrade in a branch behind a passing test suite, (d) staging webhook replay including a deliberately unknown event_type to prove the fallback path, (e) production with webhook delivery monitored for 24–48h, (f) reconcile a live transaction, refund, and payout against the PayMongo dashboard.
Maintenance. The upgrade itself is near-zero maintenance; the ongoing cost is ownership of the WebhookReceived mapping layer, which we now own because the package no longer enumerates those types. Budget a small amount of recurring effort to keep our internal event taxonomy current as PayMongo changes. Package-level risk: single-maintainer open source with an apparent willingness to break API in patch releases — pin versions, review changelogs on every bump, and keep the wrapper thin enough to fork.
Support. Upstream support is community/GitHub-issue based; no SLA. Production payment incidents will be diagnosed by our team, so invest in local observability: log raw payloads (redacted), signature-verification outcomes, and dispatch results. Confirm the PayMongo-side webhook dashboard shows successful delivery after the upgrade — that is the fastest signal that registration now works.
Scaling. Webhook endpoints are the scaling pressure point: verify signature before any DB work, acknowledge fast (2xx) and enqueue, enforce idempotency on PayMongo's event ID, and cap concurrent processing per merchant. The package is stateless and adds no meaningful per-request overhead; throughput is bounded by our queue and DB, not by the adapter. API-call paths should use timeouts, retries with backoff on 5xx/429, and a circuit breaker around non-idempotent calls.
Failure modes.
UnhandledMatchError from surviving references to removed classes — caught in CI if the suite is thorough, otherwise in production.ValueError on replayed/stored event strings — the subtle one, since it appears during retry/backfill rather than at request time.WebhookReceived only. If our handlers previously assumed an enumerated class would fire, that payment state transition silently never happens. This is the primary residual risk and the reason the generic consumer must be the primary path.Ramp-up. Low. A Laravel-fluent engineer can read the package in an hour; the learning curve is PayMongo's domain model and webhook semantics, not the code. New team members should be pointed at the webhook log, the WebhookReceived consumer, and the signature-verification code — those three artefacts are the real system. Expect under a day of onboarding for the integration, and treat the upgrade to v3.0.1 as a half-day change including verification.
How can I help you explore Laravel packages today?