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

kitano/payment-bundle

Symfony bundle that brings payment processing into your app, wrapping multiple payment gateways behind a shared service interface so you can configure providers, create payments, and handle callbacks and status updates with minimal boilerplate.

View on GitHub
Deep Wiki
Context7

Getting Started

Reality check before you composer require: this is a Symfony bundle, not a Laravel package. There is no service provider, no config/payment.php, no facade, and no adapters for Laravel's container, HTTP layer, or Eloquent. You cannot composer require kitano/payment-bundle into a Laravel app and start taking payments. Your three choices are: (1) run it in a Symfony app, (2) treat the repo strictly as a reference implementation of a payment-flow abstraction, or (3) budget a port. For a Laravel team, (2) is the realistic default.

If you are actually on Symfony:

  1. composer require kitano/payment-bundle
  2. Add a concrete provider bundle. This package is only the orchestration layer — PaymentInstruction, PaymentSystemInterface, PaymentBridgeInterface, the flow controller, and the state model. Without one of KitanoPaymentSipsBundle (Atos/SIPS), KitanoPaymentFakeBundle, or your own adapter, nothing processes a payment.
  3. Register Kitano\PaymentBundle\KitanoPaymentBundle and the provider bundle in AppKernel.
  4. Create the schema for the PaymentInstruction Doctrine entity (check Resources/config/doctrine for the mapping).
  5. Start with KitanoPaymentFakeBundle. It gives you a working capture → redirect → return → complete loop with no acquirer credentials, so you can learn the flow and build a test harness before touching real money.

Where to look first, in order:

  • PaymentSystemInterface — the contract every provider implements (capture, complete, status translation). Read this before anything else; it is the whole abstraction in one file.
  • PaymentInstruction — the persisted state machine (amount, currency, state, extended_data).
  • PaymentInstructionManager — persistence and state transitions.
  • PaymentBridgeInterface — how the flow hands off to the system.
  • The flow controller — thin; the work lives in the classes above and the dispatched events.

First use case for a Laravel developer: don't try to run the bundle. Mirror PaymentSystemInterface as your own internal contract and stand up a Fake adapter behind it, end-to-end, before choosing a real gateway. That gets you the one genuinely valuable thing this package offers — a provider-agnostic payment lifecycle — without inheriting a Symfony 2.x-era dependency you'll have to own forever.

// Your Laravel-native equivalent, ported from the bundle's concept
interface PaymentSystem
{
    public function capture(PaymentInstruction $instruction): Response;
    public function complete(PaymentInstruction $instruction, Request $request): void;
    public function translateStatus(string $gatewayStatus): string;
}

Implementation Patterns

Instruction-as-state-machine. Every payment is a persisted record holding amount, currency, state (new → pending → completed / failed / canceled), and a free-form extended_data blob. This is the pattern worth stealing. Persist the instruction before redirecting to the gateway so a callback always has something to reconcile against:

$instruction = PaymentInstruction::create([
    'amount'   => 1999,
    'currency' => 'EUR',
    'state'    => 'new',
    'extended_data' => ['order_id' => $order->id],
]);
return $paymentSystem->capture($instruction); // HTTP redirect to gateway

Capture → complete; never trust the return URL. Your callback controller takes the gateway response, calls complete(), and the system updates state and fires events. Bind listeners to the terminal events rather than branching on $instruction->state in the controller — state checks in controllers drift the moment you add a second provider:

Event::listen(PaymentCompleted::class, fn ($e) => $e->getInstruction()->order->markPaid());
Event::listen(PaymentFailed::class,    fn ($e) => $e->getInstruction()->order->releaseStock());

Gateway-specific payloads go in extended_data. Acquirer transaction IDs, 3DS blobs, card fingerprints, the raw callback body — none of that earns a column. Keep it in the serialized blob; it survives state transitions and is readable from your translateStatus() implementation.

Verify amount and currency on return. The gateway echoes back an amount; compare it against the stored instruction before completing. Historically this relied on the system bridge doing it — if you write your own bridge, do it explicitly and loudly.

Idempotent callbacks. A gateway will retry. If the instruction is already completed, the complete() path must no-op and return 200, not re-fire payment.completed and not re-capture. Make that a first-class test, not an afterthought.

Testing workflow. Use the Fake bundle as the reference for your own integration test: assert new → completed on the return route, assert a failure status maps to failed, and assert a replayed callback is a no-op. This is the single most reusable artifact in the repo.

Custom system for a real acquirer. Implement PaymentSystemInterface: capture() returns a redirect; complete() reads the return request, validates the signature (HMAC, SIPS seal, whatever the acquirer uses), calls translateStatus(), and updates the instruction. Register it in your own container binding, and keep it behind your internal interface so the acquirer is swappable.

Gotchas and Tips

  • Not Laravel-compatible out of the box. No service provider, no publishable config, no facade, Doctrine entities instead of Eloquent. Budget for a port, not an install. If a Laravel-native integration is the actual requirement, this is the wrong artifact.
  • Symfony 2.x / PHP 5.x vintage. Expect PSR-0 autoloading, deprecated constructs, and friction on PHP 8.x. Compatibility must be proven by a time-boxed spike under your target runtime — assume it fails until it doesn't.
  • Abandoned upstream (7 stars, 0 dependents). No maintainer, no release cadence, no security advisories. For code sitting in the payment path — PCI scope, money movement — that is the dominant risk. Fork-and-own is the only viable adoption mode, and it must be staffed as permanent ownership.
  • Partial state after timeout. The gateway authorizes, your request times out, nothing is recorded. You need a reconciliation job, not just happy-path callbacks.
  • Missing idempotency keys → double capture. Capture and refund must be idempotent at your layer regardless of what the bundle does.
  • Dropped or replayed callbacks with no dedupe. Log every inbound callback with a dedupe key (gateway transaction ID) before processing.
  • Ledger divergence. With no built-in reconciliation between your PaymentInstruction states and the acquirer's ledger, drift is silent. Schedule a nightly comparison against the gateway's transaction report.
  • Silent exceptions in the flow. The bundle predates modern error-handling conventions. Wrap the system calls, log the instruction ID and gateway reference at every transition, and alert on state transitions that never complete.
  • translateStatus() must be exhaustive. Handle every status the acquirer can return, including ones you've never seen — default to a safe failed/pending rather than throwing or silently passing through.
  • extended_data is a serialized blob. Treat its format as an internal contract; if you fork, note that changing serialization breaks historical records. Don't rename keys on live instructions.
  • Out of scope entirely: subscriptions, refunds/disputes, marketplace splits, and any serious PCI-sensitive feature work. There is no provider ecosystem here to lean on.
  • Laravel recommendation: borrow the pattern (intent → authorize → capture → refund → reconcile, with explicit states and an audit log), implement it as a small domain service with a fake adapter and a callback-replay test suite, and back the real gateway with its official SDK or league/omnipay rather than resurrecting a stale orchestrator.
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.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package