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

Omnipay Bundle Laravel Package

xola/omnipay-bundle

A Symfony bundle that integrates the Omnipay payment library, providing configurable gateway services and helpers for processing payments through multiple providers with a consistent PHP API.

View on GitHub
Deep Wiki
Context7

Getting Started

Reality check first: xola/omnipay-bundle is a Symfony bundle, not a Laravel package. Laravel's package auto-discovery will not register it, and there is no facade or service provider inside. A Laravel developer will get the most value by treating it as a reference implementation of how to wire Omnipay into a framework container, then porting those patterns into a ServiceProvider + config/omnipay.php. Use omnipay/omnipay (or league/omnipay) directly as the runtime dependency.

If you are in a Symfony app alongside Laravel work:

composer require xola/omnipay-bundle omnipay/common

Then register the bundle in config/bundles.php and publish gateway config:

# config/packages/omnipay.yaml
omnipay:
    gateways:
        stripe:
            type: Stripe
            parameters:
                apiKey: '%env(STRIPE_KEY)%'
        paypal:
            type: PayPal_Express
            parameters:
                username: '%env(PAYPAL_USER)%'
                password: '%env(PAYPAL_PASS)%'
                signature: '%env(PAYPAL_SIG)%'

Where to look first (small codebase, ~4 files worth reading):

  1. DependencyInjection/Configuration.php — the config tree; this tells you every knob exposed.
  2. DependencyInjection/OmnipayExtension.php — how gateways are turned into container services.
  3. Resources/config/services.yml — service IDs you can inject (omnipay.gateway.<name>).
  4. OmnipayBundle.php — trivial; confirms there are no compiler passes to worry about.

First use case to validate the integration — a Stripe test purchase:

$response = $gateway->purchase([
    'amount'   => '10.00',
    'currency' => 'USD',
    'card'     => $card,        // Omnipay\Common\CreditCard
])->send();

if ($response->isRedirect()) {
    return redirect($response->getRedirectUrl());
}
if ($response->isSuccessful()) {
    // persist transaction
}

Laravel-native equivalent of "first use case":

// config/omnipay.php
return ['gateways' => [
    'stripe' => ['type' => 'Stripe', 'parameters' => ['apiKey' => env('STRIPE_KEY')]],
]];

// AppServiceProvider::register()
$this->app->singleton('omnipay', fn ($app) => new GatewayFactory);
$this->app->bind('omnipay.gateway.stripe', fn ($app) =>
    $app['omnipay']->create(
        config('omnipay.gateways.stripe.type'),
        config('omnipay.gateways.stripe.parameters')
    )
);

Implementation Patterns

1. Config-driven gateway selection. The bundle's real value is the idea of named gateways defined in config, injected by name. Recreate this in Laravel with a small GatewayManager rather than hardcoding new StripeGateway() in controllers:

class GatewayManager
{
    public function gateway(?string $name = null): AbstractGateway
    {
        $name ??= config('omnipay.default');
        $cfg   = config("omnipay.gateways.{$name}");
        return (new GatewayFactory)->create($cfg['type'], $cfg['parameters']);
    }
}

2. Controller → Action → Job split. Payment flows are long-running and side-effect-heavy. Pattern:

  • PaymentController@store — creates a local payments row in pending, calls purchase(), stores the transactionReference, redirects.
  • PaymentController@callback — calls completePurchase(), updates the row.
  • ProcessPaymentWebhook job — reconciles asynchronous events (Stripe webhooks, PayPal IPN).

Keeping pending rows before the gateway call is essential — the bundle does nothing to help you here, and Omnipay itself is stateless.

3. Multi-gateway / per-tenant routing. Because the Symfony bundle registers gateways as separate services, the natural Laravel analogue is a manager keyed by name, with the active gateway resolved from the tenant/currency/region on the model:

$gateway = $manager->gateway($order->tenant->gateway); // 'stripe', 'paypal', ...

4. Callback/return URL wiring. Omnipay gateways expect returnUrl and cancelUrl (or notifyUrl for webhook-style). Centralise them via a helper to avoid drift:

$gateway->purchase([
    'amount'    => $order->total,
    'currency'  => $order->currency,
    'card'      => $card,
    'returnUrl' => route('payments.callback', $order),
    'cancelUrl' => route('payments.cancel', $order),
])->send();

5. Testing with the Mock gateway. Omnipay ships Omnipay\Common\Gateway\MockGateway and FakeGateway, both of which satisfy the bundle's factory. In Laravel, bind a fake in tests:

$this->app->bind('omnipay.gateway.stripe', fn () =>
    (new GatewayFactory)->create('Mock', ['returnUrl' => '/ok'])
);

Use $response->getMockResponse() style assertions — much faster than hitting Stripe sandbox in CI.

6. Logging / HTTP client swap. Omnipay\Common\Http\ClientInterface (PSR-18 in Omnipay 3) is the extension seam. Wrap GuzzleHttp\Client to log requests/responses into your laravel.log — invaluable for debugging gateway rejections, which otherwise surface as opaque InvalidResponseExceptions.

7. Symfony→Laravel bridging. If you're porting an existing Symfony app, the bundle's omnipay.gateway.* service IDs map cleanly to Laravel container bindings; keep the same IDs (omnipay.gateway.stripe) to minimise churn in ported code.

Gotchas and Tips

Compatibility is the biggest footgun. The last release is 2020-01-22, which targets Symfony 4/5 and Omnipay 3 on PHP 7.2/7.4. On PHP 8.2+ or Symfony 6/7 you will very likely hit deprecations or hard failures in the DI extension. Laravel 10/11 (PHP 8.1+) — do not try to use the bundle itself; use omnipay/omnipay direct and copy the patterns.

Omnipay 2 ↔ 3 API breakage. Lots of blog posts and the old barryvdh/laravel-omnipay package target Omnipay 2. Differences in Omnipay 3:

  • CreditCard still exists but gateways use CardInterface.
  • completePurchase() parameter shape changed (notably for PayPal and SecurePay).
  • client is now PSR-18-ish (ClientInterface), not Guzzle 5/6-specific.
  • ResponseInterface methods renamed in places (getRedirectResponse(), etc.).

Pinning omnipay/common:^3 and matching each gateway package to Omnipay 3 is mandatory; mixing a gateway that needs Omnipay 2 will silently fail at runtime.

No Laravel facade, no auto-discovery, no config publishing. If you tried to php artisan vendor:publish, there's nothing to publish. The Symfony bundle relies on %env(VAR)% parameter resolution; Laravel uses env(), which behaves differently under config:cache — always resolve env() in config files, never in services.

Secrets in YAML. The Symfony bundle's example config puts keys in config/packages/omnipay.yaml. In Laravel, keep keys in config/omnipay.php reading env(), and never commit .env. Rotate keys if they were committed during the Omnipay 2→3 upgrade.

Exception handling is your job. Omnipay throws InvalidCreditCardException, InvalidResponseException, GatewayException — the bundle does nothing to translate them. Wrap ->send() in a try/catch that logs the request/response and returns a user-safe error; never let raw gateway messages hit the UI.

Idempotency is not built in. Omnipay has no idempotency-key concept. For purchase() retries (network timeouts, double-clicks), you must record intent in a payments table with a unique idempotency_key and check it before calling the gateway — otherwise you'll double-charge during a transient 500 from the gateway.

Webhooks vs redirects. Omnipay is fundamentally redirect/completePurchase-oriented. Modern gateways (Stripe especially) expect webhook-driven confirmation. Treat completePurchase as a hint, and always reconcile against the webhook payload in a queued job before marking an order paid. A redirect return alone is spoofable.

3-D Secure / SCA flows. Omnipay supports Secure3D via gateway-specific methods; the bundle gives you the configured gateway but nothing else. For EU/UK Stripe you need purchase() → confirm()/completePurchase() handling and to persist the 3DS ref between requests.

Debugging checklist when a payment silently fails:

  1. Dump the raw $response->getData() before casting.
  2. Swap in a logging HTTP client to see the actual request body.
  3. Verify returnUrl is an absolute https:// URL — many gateways reject relative or http:// URLs with a confusing InvalidResponseException.
  4. Confirm currency/amount units (cents vs decimal) — Stripe is smallest-unit, PayPal is decimal.
  5. Check omnipay/common version matches the gateway package's composer.json.

Extension points that survive a Symfony→Laravel port:

  • Subclass GatewayFactory to pre-register your gateways and defaults (return URLs, currency).
  • Wrap ClientInterface for logging/retry/metrics.
  • A custom AbstractGateway decorator is rarely worth it — prefer a thin application-level PaymentService that owns the domain logic.

Don't fork the bundle unless you're staying on Symfony. Nine stars, zero dependents, and a 2020 release means it's effectively unmaintained; the cost of forking for PHP 8 / Symfony 6 fixes is higher than the ~40 lines of Laravel glue it saves.

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.
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log