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 Omnipay Laravel Package

ignited/laravel-omnipay

Laravel integration for the Omnipay payment library, giving you a clean, unified API to accept payments through PayPal, Stripe, Authorize.Net and dozens of other gateways. Configure drivers in Laravel, then purchase, authorize, capture, refund, and handle callbacks from one consistent interface.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the wrapper — and the gateway you actually need. ignited/laravel-omnipay only ships the Laravel glue plus omnipay/common. Each payment provider is a separate package:

composer require ignited/laravel-omnipay
composer require omnipay/stripe          # or omnipay/paypal, omnipay/authorizenet, ...

On Laravel 5.5+ the LaravelOmnipayServiceProvider and the Omnipay facade alias are auto-discovered. On older versions add them to config/app.php manually (Ignited\LaravelOmnipay\LaravelOmnipayServiceProvider and 'Omnipay' => Ignited\LaravelOmnipay\Facades\OmnipayFacade::class).

Publish and fill the config. This is the one file that matters:

php artisan vendor:publish --provider="Ignited\LaravelOmnipay\LaravelOmnipayServiceProvider" --tag=config
// config/omnipay.php
return [
    'default' => 'stripe',

    'gateways' => [
        'stripe' => [
            'driver'  => 'Stripe',
            'options' => [
                'apiKey'   => env('STRIPE_SECRET'),
                'testMode' => env('STRIPE_TEST_MODE', true),
            ],
        ],
        'paypal' => [
            'driver'  => 'PayPal_Express',
            'options' => [
                'username'  => env('PAYPAL_USERNAME'),
                'password'  => env('PAYPAL_PASSWORD'),
                'signature' => env('PAYPAL_SIGNATURE'),
                'testMode'  => env('PAYPAL_TEST_MODE', true),
            ],
        ],
    ],
];

driver is an Omnipay gateway name (underscored: PayPal_Express, AuthorizeNet_AIM, SecurePay_DirectPost), and options are fed straight to the gateway's initialize(), so each key maps to a setter (apiKey → setApiKey(), testMode → setTestMode()).

Where to look first. The wrapper is thin — it is ~200 lines. Nearly all your day-to-day knowledge has to come from the individual Omnipay gateway's README, not from this package. Read the config file, then read omnipay/<your-gateway>'s docs for the request/response parameters.

First use case — a one-shot charge:

use Ignited\LaravelOmnipay\LaravelOmnipay;

class CheckoutController extends Controller
{
    public function charge(Request $request, LaravelOmnipay $omnipay)
    {
        $response = $omnipay->gateway('stripe')->purchase([
            'amount'   => '49.00',          // string, major units
            'currency' => 'USD',
            'token'    => $request->input('stripeToken'),
            'description' => 'Order #'.$request->input('order_id'),
        ])->send();

        if ($response->isSuccessful()) {
            // $response->getTransactionReference()
        }
    }
}

Type-hint LaravelOmnipay rather than reaching for the facade — it's the same singleton, but it makes faking trivial in tests.

Implementation Patterns

Gateway → request → response. Every flow in this package is the same three beats, so build one internal convention around it:

$gateway  = Omnipay::gateway('stripe');       // returns an Omnipay\Common\AbstractGateway
$response = $gateway->authorize($params)->send();  // or purchase/capture/refund/void/createCard

$response is an Omnipay\Common\Message\ResponseInterface; branch on isSuccessful(), isRedirect(), isPending(), isCancelled(), and read getTransactionReference(), getMessage(), getCode(), getData().

Wrap it in your own service, not scattered facade calls. The highest-leverage move with this package is treating it as an infrastructure detail:

interface PaymentGateway {
    public function charge(Order $order, PaymentMethod $method): PaymentResult;
}

class OmnipayPaymentGateway implements PaymentGateway {
    public function __construct(private LaravelOmnipay $omnipay, private string $name) {}

    public function charge(Order $order, PaymentMethod $method): PaymentResult
    {
        $response = $this->omnipay->gateway($this->name)->purchase([
            'amount'   => $order->total->format(2, '.', ''),  // never floats
            'currency' => $order->currency,
            'token'    => $method->token,
            'transactionId' => $order->reference,             // your idempotency anchor
        ])->send();

        return PaymentResult::fromOmnipay($response);
    }
}

Bind it in a service provider, and swap in a fake (omnipay/dummy or a stub implementing your interface) for tests and local dev.

Redirect flow (PayPal Express, Sage Pay, most EU gateways). Two requests, one persisted row:

// 1. start
$response = Omnipay::gateway('paypal')->purchase([
    'amount' => '49.00', 'currency' => 'USD',
    'returnUrl' => route('checkout.return'),
    'cancelUrl' => route('checkout.cancel'),
])->send();

Payment::create(['reference' => $ref = Str::uuid(), 'amount' => 4900, 'status' => 'pending']);
$response->redirect();   // handles GET vs auto-submitting POST form for you

// 2. return
$payment = Payment::where('reference', $request->input('reference'))->firstOrFail();

$response = Omnipay::gateway('paypal')->completePurchase([
    'amount'        => number_format($payment->amount / 100, 2, '.', ''),
    'currency'      => $payment->currency,
    'transactionId' => $payment->reference,   // must match what you sent
])->send();

$response->redirect() is the right call, not redirect()->away($response->getRedirectUrl()) — several gateways require a POST with hidden fields (getRedirectMethod() / getRedirectData()).

Always re-read amount and currency from your own database on return. The return URL is user-controllable; a tampered amount passed into completePurchase is a classic underpayment bug.

Tokenisation / saved cards. createCard() (or purchase() with 'createCard' => true) returns a token via getCardReference() / getToken(). Store that; never store PAN/CVV. purchase(['cardReference' => $token]) for subsequent charges. Build the card with Omnipay\Common\CreditCard when you have raw fields and call $card->validate() before sending — it gives you far better errors than a gateway round-trip.

Multiple gateways / routing by currency or country. That's just config entries plus a runtime choice:

$name = $order->currency === 'EUR' ? 'stripe_eu' : 'stripe_us';
Omnipay::gateway($name)->purchase($params)->send();

Keep one config entry per merchant account rather than mutating options at runtime.

Post-payment operations belong in queued jobs. capture, refund, void, and fetchTransaction are idempotent-ish network calls; queue them with retries and store getTransactionReference() on the order so you can reconcile.

Testing. Two layers:

  • Unit: fake LaravelOmnipay ($this->mock(LaravelOmnipay::class) or Omnipay::shouldReceive('gateway')->andReturn($fakeGateway)) and return hand-built Omnipay\Common\Message\AbstractResponse subclasses.
  • Integration: run against the omnipay/dummy gateway in testMode so you exercise real serialization/parsing without network calls.
Omnipay::shouldReceive('gateway')->with('stripe')->andReturn($gateway = Mockery::mock(AbstractGateway::class));
$gateway->shouldReceive('purchase->send')->andReturn(new PurchaseResponse($gateway, ['success' => true]));

Gotchas and Tips

Amounts are strings in major units. '49.00', not 4900, not 49.0. Omnipay does no currency-aware conversion — amount for JPY is whole yen, for USD it's dollars-and-cents. Keep integer minor units in your DB and convert with number_format($cents / 100, 2, '.', '') at the boundary. Mixing moneyphp/money in is fine, just don't hand it floats.

testMode goes inside options, as a boolean. 'testMode' => 'false' (string) is truthy and will silently keep you in sandbox. Cast from env explicitly.

Config is resolved at boot. env() calls only work inside config/*.php. If you rotate API keys at runtime, run php artisan config:cache on deploy, and remember Omnipay::gateway() caches the resolved instance per name for the process.

That caching bites in Octane / long-lived workers. Gateway instances are memoised on the singleton, so any setters you call (e.g. setCard(), setReturnUrl()) leak into later requests. Either avoid mutating gateways, or re-resolve between requests (app()->forgetInstance(LaravelOmnipay::class)) / hook RequestTerminated.

Unknown gateway = exception, not null. Omnipay::gateway('typo') throws InvalidArgumentException. Config key is default (not gateway) in current releases — if Omnipay::gateway() with no argument fails, check the published file's key name against the release you installed.

Version alignment is the #1 support issue. This package has 2.x (Omnipay 2) and 3.x (Omnipay 3) lines, and each omnipay/* gateway package has its own 2.x/3.x split. A mismatched pair produces confusing Call to undefined method or interface errors. Pin both in composer.json.

Missing parameters throw. Omnipay validates in getData() and throws Omnipay\Common\Exception\InvalidRequestException (and InvalidCreditCardException for bad card data). Catch these separately from HTTP/transport failures and map them to validation errors — they're almost always a bug in your params array, not the customer's fault.

Debugging a failed charge. In order: $response->getMessage() (human text), $response->getCode() (gateway code), $response->getData() (raw decoded payload). Log the last one — but never log $params, which contains card data. Scrub with array_except/Arr::except before any Log::info.

Add logging at the HTTP layer, not around every call. Omnipay 3 lets you inject a PSR-18 client: $gateway->setHttpClient(new Psr18Client($stack)) — wrap Guzzle with a logging middleware and you get full request/response traces in one place for every gateway.

Use transactionId for idempotency. Pass your own order reference on every purchase/capture/refund. Gateways that support idempotency keys will deduplicate; those that don't still give you a correlation key in their dashboard when a customer double-clicks "Pay".

Webhooks are per-gateway, not per-package. This wrapper gives you nothing here. Verify signatures with the gateway's own SDK/algorithm, make the handler idempotent (unique index on transaction reference), then call fetchTransaction() to get the authoritative state rather than trusting the payload.

Extension point for ad-hoc gateways. If a gateway isn't worth a config entry (one-off, dynamic credentials), use the underlying factory instead of fighting the config: Omnipay::getFactory()->create('Stripe', $httpClient)->initialize($options).

Reconciliation job is worth writing early. A nightly fetchTransaction() sweep over recent pending payments catches the "customer closed the tab after paying" case that redirect flows lose.

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.
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
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource