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

Payment Bundle Laravel Package

idci/payment-bundle

IDCI PaymentBundle is a Symfony bundle for adding flexible payment handling to PHP applications, with abstractions for payment methods, gateway integrations, and configurable workflows to process and track transactions.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and wire it up

composer require idci/payment-bundle

Then in config/bundles.php:

IDCI\Bundle\PaymentBundle\IDCIPaymentBundle::class => ['all' => true],

Import the bundle routing (callback/notify endpoints are shipped by the bundle, so they must be reachable):

# config/routes/idci_payment.yaml
idci_payment:
    resource: "@IDCIPaymentBundle/Resources/config/routing.xml"
    prefix: /payment

The bundle persists payments and gateway configurations via Doctrine, so run migrations or doctrine:schema:update and confirm the new tables exist before writing any code.

Where to look first, in this order

  1. PaymentManager (and its interface) — this is the front door. Everything you do day-to-day (create a payment, transition a status, fetch a gateway) should go through it rather than through repositories.
  2. PaymentMethodInterface — the contract every gateway (PayPal, Stripe, manual, etc.) implements. Reading one real implementation teaches you the bundle faster than any doc.
  3. The DI tags — payment methods are registered as tagged services and collected by a registry. That tag is your extension point.
  4. The Payment / PaymentMethod entities — check the status field and the amount field type before you build anything on top.

First use case

Don't start with a real PSP. Write a ManualPaymentMethod (or "invoice") implementing PaymentMethodInterface that always returns an "accepted" or "pending" result. That gives you an end-to-end skeleton — create payment, persist, redirect, callback, transition status — without any network calls or credentials. Swap in a real gateway once the flow works.

Implementation Patterns

Custom payment method as a tagged service

The idiomatic integration is not to fork the bundle; it's to add a class implementing PaymentMethodInterface and tag it so the registry picks it up:

App\Payment\InvoicePaymentMethod:
    tags:
        - { name: idci_payment.payment_method, alias: invoice }

Keep the class stateless and inject only an HTTP client plus config. Gateway credentials should come from your PaymentMethod configuration entity (or your own parameters), not from hardcoded constants — you'll want per-environment and per-currency gateways eventually.

Create → redirect → callback

The standard three-step flow:

  1. Build a payment through the manager, passing the gateway alias and the amount, and persist it. Get back an identifier you control (your order ID), not just the bundle's internal ID.
  2. Call the method's "go to payment" step, which returns a redirect/response. Store the resulting transaction reference on the payment.
  3. The gateway calls back to the bundle's notify route, which dispatches a lifecycle event. Subscribe to that event and update your domain (order status, email, fulfilment) there — not in a controller.

Never trust the browser return URL

Treat the user-facing "return" route as cosmetic (show a spinner/receipt) and the server-to-server notify route as the source of truth. Pattern: on notify, re-fetch the payment by reference, ask the gateway for its authoritative status, then transition. This makes replay and forged returns harmless.

Status transitions, not status writes

Route every status change through the manager so the bundle can dispatch events and enforce allowed transitions. If you find yourself doing $payment->setStatus(...); $em->flush(); in a controller, you're bypassing the one thing the bundle actually gives you. If the built-in transition set doesn't cover your case (partial refunds, disputes), add your own subscriber and a small state map rather than editing the entity.

Testing

Because payment methods are plain services behind an interface, the whole flow is testable without HTTP: bind a fake PaymentMethodInterface returning a canned redirect, assert your listener transitions the payment and your order flips to "paid". For the real gateway, use its sandbox and a MockHttpClient for signature-verification paths — especially the failure paths (bad signature, duplicate notify, timeout).

Gotchas and Tips

  • Tiny ecosystem — you are the maintainer. One star, zero dependents means no Stack Overflow answers, no blog posts, and no community patches. Pin the exact version in composer.json, read the source before upgrading, and budget time for reading it up front. Vendor the diff if a release breaks you.
  • Documentation is thin. Expect to learn the API from the code. Start with the bundle's Resources/config/services.xml — it tells you every service ID, tag name, and default argument in one file.
  • Money representation. Check whether amounts are stored as integer minor units or floats before you write a single line of business logic. Getting this wrong produces rounding bugs that only appear in production, and this class of bundle is not consistent about it.
  • Entity extensibility. Bundles of this vintage often use Doctrine resolve_target_entity or mapped superclasses, meaning you may need to configure your own Payment subclass in config/packages/doctrine.yaml to add fields. Do that before you have production data; retrofitting it later is a migration headache.
  • Callback routes and firewalls. The notify endpoint must be anonymously accessible and CSRF-free, but should still be rate-limited and (ideally) IP-filtered. Put an explicit access_control entry above your catch-all rule — a forgotten permitAll here will silently 302 the gateway's POST to your login page and payments will "randomly" never confirm.
  • Idempotency. Gateways retry notifications. Guard your listener with a uniqueness constraint on the gateway transaction reference and an early return if the payment is already in a terminal status. This is the single most common bug in payment integrations.
  • Status strings vs. constants. If the bundle uses plain strings for statuses, wrap them in your own enum/constants immediately and centralise comparisons; typos here fail silently.
  • Debugging. Log every transition with payment ID, old status, new status, and event source. When a webhook misfires you'll have a 30-second answer instead of an afternoon of dump()-ing. Symfony's profiler shows the dispatched events under the Events panel — use it to confirm your subscriber actually fires before blaming the gateway.
  • Extension points, in order of preference: (1) a custom payment method service, (2) an event subscriber on the lifecycle events, (3) overriding a template or route, (4) last resort — decorating the manager service in services.yaml so the bundle's own code calls your wrapper. Decoration is clean and upgrade-safe; editing vendor code is not.
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.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console