sylius/omnipay-bundle
Sylius Omnipay Bundle integrates the Omnipay PHP payment library with Sylius, letting stores configure multiple payment gateways through one unified API. It provides gateway factories, configuration, and payment handling for Sylius checkout.
Read this first: the bundle is archived and READ-ONLY. It is a Symfony 2 / Sylius 1.x integration layer for Omnipay. composer require sylius/omnipay-bundle inside a Laravel app will not register anything useful — there is no Laravel service provider, no facade, and its Symfony/PHP constraints will fight a modern composer.json. Do not adopt it for new work. The assessment below is written for a Laravel developer who either inherits a legacy Symfony/Sylius app or who wants to lift the pattern into Laravel using league/omnipay directly.
If you inherited a legacy Symfony/Sylius application that already depends on it:
composer require sylius/omnipay-bundle (expect to pin old Symfony/Omnipay versions — check the composer.json require block before guessing).new Sylius\Bundle\OmnipayBundle\SyliusOmnipayBundle() in app/AppKernel.php.DependencyInjection/Configuration.php — the authoritative schema for the sylius_omnipay.gateways config tree. Read this before writing any YAML.Resources/config/services.xml — tells you which services are public/injectable and what the gateway service IDs look like.GatewayFactory in the bundle — the seam between config and Omnipay gateway objects.sylius_omnipay:
gateways:
paypal_express:
factory: PayPal_Express
parameters:
username: "%paypal_user%"
password: "%paypal_password%"
signature: "%paypal_signature%"
testMode: true
First use case: a single purchase() charge. Pull the configured gateway, call purchase() with amount, currency, and card/token data, then branch on the response (isSuccessful(), isRedirect(), isPending()). Persist the gateway transaction reference immediately on return.
The Laravel equivalent (what most readers actually want):
composer require league/omnipay omnipay/stripe omnipay/paypal
// app/Providers/PaymentServiceProvider.php
$this->app->singleton(GatewayInterface::class, function () {
return Omnipay::create(config('services.stripe.gateway'))
->initialize([
'apiKey' => config('services.stripe.secret'),
'testMode' => app()->environment() !== 'production',
]);
});
That is functionally what the bundle does for Symfony — nothing more.
config/payments.php file plus a service-provider binding per gateway.PaymentService that normalizes parameter names (Stripe's token vs PayPal's token, amount units, returnUrl shape). This is the one class that lets you swap providers or swap Omnipay for a first-party SDK without touching checkout controllers.authorize() at checkout and capture() at fulfillment; refund() with the stored transactionReference. Not every gateway supports all three — check the gateway class' capabilities before exposing an admin button.purchase() on PayPal/redirect gateways returns a response exposing a redirect (getRedirectResponse() or getRedirectUrl() depending on gateway version). Redirect the user, persist transactionId before leaving your app, and on return call completePurchase() with the same identifying parameters. A redirect is a normal outcome — branch on isRedirect(), do not treat it as an error.amount (in minor units), currency, transactionId, returnUrl, cancelUrl, card/token. Scattering this across controllers is the #1 source of "works for Stripe, breaks for PayPal" bugs.auth middleware, a VerifyWebhookSignature middleware, and a queued ProcessPaymentWebhook job.Mock gateway (Omnipay::create('Mock')) or MockGateway subclasses to assert on outbound request parameters and return canned responses with no network. Set testMode: true and hit provider sandboxes for integration checks. In Laravel, bind the mock in a Testing service provider or via $this->mock(GatewayInterface::class).PaymentSucceeded / PaymentFailed / PaymentPending from the PaymentService keeps order state, emails, and analytics out of payment code. This mirrors the Symfony event dispatcher usage the bundle relied on.GatewayInterface (Omnipay's own) or your own PaymentGateway contract means checkout code never names a provider — exactly the abstraction the bundle provided for Sylius.Omnipay\Common\GatewayFactory; v3 uses Omnipay\Omnipay::create(). Check composer.lock before copy-pasting a snippet from GitHub — half the available examples are v2.isSuccessful() is not "money moved." Authorize returns successful with funds only held. Check getTransactionReference() is non-null and record the mode (purchase vs authorize) alongside it, or reconciliation will be ambiguous later.sylius_omnipay.gateways requires bin/console cache:clear, and the container caches gateway service definitions. In Laravel, the equivalent trap is php artisan config:cache after editing config/payments.php.signature and you get a runtime gateway exception, not a config error — validate gateway params in your own bootstrapping if you control the config.testMode must be environment-driven. Hard-coding testMode: true in a config file that gets promoted is a classic legacy-app incident. Bind it from APP_ENV.PaymentGateway interface so the extension point you actually rely on is yours, not the bundle's.getMessage() + getCode()) from failed responses; most "silent" failures are gateway-side validation errors surfaced only there. Never log card, cvv, or full request bodies.returnUrl/cancelUrl from url() in Laravel, and whitelist them in the provider dashboard.PaymentGateway interface first, migrate one provider at a time behind it, and only then remove the dependency. That seam is the only durable thing you get from this exercise — the bundle itself is replaceable glue, and so is Omnipay.How can I help you explore Laravel packages today?