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

Laravel Paymongo Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

This is the Laravel wrapper for PayMongo (Philippines payment gateway: cards, GCash, GrabPay, Maya, QR Ph, installments). v3.0.1 is a small but API-surface-breaking patch, so read the upgrade note before composer update.

  1. composer require luigel/paymongo
  2. Publish config: php artisan vendor:publish --tag=paymongo-config (or the provider's tag as documented).
  3. Add keys to .env:
    PAYMONGO_SECRET_KEY=sk_test_...
    PAYMONGO_PUBLIC_KEY=pk_test_...
    
    Keep test keys in .env.testing; the config reads them at boot.
  4. First use case — take a payment and land a webhook:
    use Luigel\Paymongo\Facades\Paymongo;
    
    $intent = Paymongo::paymentIntent()->create([
        'amount'             => 10000, // centavos
        'currency'           => 'PHP',
        'payment_method_allowed' => ['card', 'gcash', 'paymaya'],
        'description'        => 'Order #1234',
    ]);
    
    Then create a payment method client-side (or via Paymongo::paymentMethod()->create()), attach it, and redirect the user to the returned next_action.redirect.url.

Look first at: the published config/paymongo.php, the Luigel\Paymongo\Facades\Paymongo facade for the resource list (paymentIntent, paymentMethod, source, checkout, link, webhook, refund, customer), and the package's webhook route/controller before you write your own.

If you only need hosted checkout, start with Paymongo::checkout()->create([...]) and skip intents entirely — it's the shortest path to a working flow.

Implementation Patterns

Intent + attach + webhook (most control). Create the intent, attach the payment method, return next_action to the front end, and treat the webhook as the source of truth for fulfilment. Never mark an order paid from the browser redirect — it can be closed mid-3DS.

Checkout Sessions / Links (least code). Paymongo::checkout()->create() gives you a checkout_url and a checkout_session_id; store the id on your order and reconcile via webhook. Links are the same idea for reusable/amount-editable payment pages.

Sources for QR Ph / e-wallets. Older flows still use Paymongo::source()->create() then paymentIntent()->create() with the source id. Prefer intents if your integration is new.

Webhook handling. Register once via artisan/console, not on every request:

Paymongo::webhook()->create([
    'url'    => route('paymongo.webhook'),
    'events' => [\Luigel\Paymongo\Enums\WebhookEventType::PAYMENT_PAID, /* ... */],
]);

Listen to the package events (e.g. PaymentPaid, PaymentFailed, SourceChargeable) or catch-all WebhookReceived. In v3.0.1 make WebhookReceived your primary hook if you were relying on any of the removed event classes — see Gotchas.

Idempotency + correlation. Store payment_intent_id / checkout_session_id on your orders table with a unique index. In the listener, look the order up by that id, not by amount or email. Wrap fulfilment in a transaction and a DB::transaction + wasRecentlyCreated/already_processed guard: PayMongo retries webhooks.

Queue the listeners. implements ShouldQueue on your listeners so the webhook endpoint returns 200 fast; PayMongo times out and retries otherwise.

Testing. Fake the HTTP layer (the package uses Laravel's HTTP client) — Http::fake([...]) with fixture payloads from PayMongo's docs is more reliable than faking the facade, because you also exercise signature/payload parsing. Add one test per event you actually listen to.

Gotchas and Tips

  • v3.0.1 removes public API in a patch release. These WebhookEventType cases and their matching Luigel\Paymongo\Events\* classes are gone: payment_intent.succeeded, payment_intent.awaiting_payment_method, subscription.activated, qr.paid, qr.expired, refund.succeeded, dispute.created, dispute.resolved, payout.deposited, payout.returned. If you used any of those classes, or matched on those strings in a switch, you'll get a fatal error / dead branch after upgrading. Grep for the strings before you bump:
    grep -rn "payment_intent.succeeded\|qr.paid\|refund.succeeded\|dispute.resolved\|payout\." app/ config/ tests/
    
    They never fired in practice, so removal is safe at runtime — the risk is purely class not found in your own code and stale array keys in a hard-coded webhook events list.
  • Pin the version. Because a patch removed classes, use "luigel/paymongo": "^3.0.1" and commit the lockfile; don't float * in a payments codebase.
  • Unknown events still arrive. The package dispatches WebhookReceived for names it doesn't recognise (including the ten above, if PayMongo ever starts sending them). Route critical logic through WebhookReceived + your own event-name switch if you want future-proofing; use the typed events for convenience only.
  • Webhook registration was failing pre-3.0.1 with parameter_invalid if you passed unsupported event types. If your php artisan paymongo:webhook (or manual POST /v1/webhooks) call is still 400ing, you're either on an older version or still passing a stale event list — the API rejects the whole request, not just the bad entry.
  • Signature verification. PayMongo signs with Paymongo-Signature (contains t= and te=/li= HMACs). Verify against the raw body — $request->all() re-encoded will not match. Exclude the webhook route from CSRF (VerifyCsrfToken::$except or bootstrap/app.php for Laravel 11+).
  • Amounts are in centavos and are integers. 10000 = ₱100.00. Rounding bugs here are silent and expensive.
  • Minimum amounts apply per payment method (cards ≈ ₱100, some e-wallets higher). Validate before creating an intent or you get a parameter_invalid from PayMongo rather than a clean form error.
  • next_action is not always present. For methods that complete inline (paid status immediately), skip the redirect; branch on $intent->status and $intent->next_action.
  • Test vs live keys are the same config key — a stray sk_live_ in a local .env will move real money. Guard with a config check in non-production.
  • Retries and ordering: webhooks can arrive out of order. Drive state transitions off the intent's status fetched from the API (Paymongo::paymentIntent()->find($id)) rather than trusting event order for terminal states.
  • Extension point: everything funnels through the HTTP client, so Http::fake() and a global middleware (logging request ids from Paymongo-Request-Id headers) are your debugging tools when support asks for a trace.
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.
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route