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

Payum Lock Request Extension Bundle Laravel Package

webgriffe/payum-lock-request-extension-bundle

Symfony bundle providing a Payum extension that locks payment requests, helping prevent duplicate or concurrent processing of the same payment. Integrates into Payum flows to guard request handling with a lock, reducing risks of double charges and race conditions.

View on GitHub
Deep Wiki
Context7

Getting Started

Install

composer require webgriffe/payum-lock-request-extension-bundle

No Flex recipe is published, so register the bundle manually in config/bundles.php:

return [
    // ...
    Webgriffe\PayumLockRequestExtensionBundle\WebgriffePayumLockRequestExtensionBundle::class => ['all' => true],
];

Where to look first

  1. vendor/webgriffe/payum-lock-request-extension-bundle/src/DependencyInjection/ — confirms the config key (typically webgriffe_payum_lock_request) and which services are auto-wired.
  2. Resources/config/services.xml — shows whether the extension is registered for all Payum gateways or must be attached per gateway.
  3. vendor/webgriffe/payum-lock-request-extension/src/ — the actual extension class. The bundle is a thin wrapper; the locking logic (key derivation, acquire/release, exception type) lives here and is what you will debug.

Run bin/console config:dump-reference webgriffe_payum_lock_request to see the exact options for your installed version — the bundle is young enough that option names have moved.

First use case: prevent double capture

The classic failure this solves: a customer double-clicks "Pay", two CaptureAction executions race, and you either double-charge or throw a confusing Payum state exception. Point the extension at a Symfony Lock factory and it serialises execution per payment token.

# config/packages/payum_lock_request.yaml
webgriffe_payum_lock_request:
    lock_factory: 'lock.factory'   # any Symfony LockFactory; see below
# config/services.yaml
services:
    lock.factory:
        class: Symfony\Component\Lock\Store\RedisStore
        arguments: ['@redis.payment_lock_connection']

Then capture the exception in your controller and return HTTP 409/redirect to the "processing" page:

try {
    $gateway->execute(new Capture($token));
} catch (\Throwable $e) {
    // lock already held -> show "payment in progress"
}

Implementation Patterns

Lock on the payment token, not the cart

The extension keys the lock off the Payum token/payment details. This is usually correct: two different tokens for the same order can still race if your own code creates a second payment. If you need order-level protection, wrap your controller action in your own Symfony\Component\Lock\LockInterface keyed on the order ID in addition to this extension — do not try to override the extension's key derivation unless you fork the library.

Per-gateway vs global registration

Payum lets you register extensions globally (payum config) or per gateway (payum.gateways.<name>.extensions). Check which the bundle does. If it registers globally, every gateway (including your test offline gateway) now takes locks — usually fine, occasionally surprising in functional tests where two test cases share one Redis key.

Pair with status polling

This extension is not an idempotency layer. Use it together with a StatusAction/SyncAction flow: capture returns immediately, the client polls status, and the lock guarantees only one capture worker runs while the poller is free to read. Do not hold the lock across a long-running provider callback — locks have TTLs and an expired lock mid-capture is worse than no lock.

Symfony Lock store selection

  • flock — dev/single-host only.
  • RedisStore / PdoStore::createTable() — required for multi-container production.
  • Always set a TTL long enough for your slowest provider round-trip (PSP captures can take 30–60s under load).

Testing

Override lock_factory in config/packages/test/ with InMemoryStore. If you use Redis in tests, clear keys between tests — a leaked lock silently turns a passing test suite into a flaky one, and the failure looks like a random 409, not a lock.

Gotchas and Tips

  • The bundle is a wrapper, not the feature. Almost every real bug lives in webgriffe/payum-lock-request-extension. Read LockRequestExtension::execute() before filing an issue; the bundle just wires it.
  • Zero dependents = no community signposts. There are no blog posts, Stack Overflow answers, or recent PRs to lean on. Budget time to read the source (it's small — a few hundred lines) rather than searching.
  • Exception type is not a Symfony HTTP exception. The underlying extension throws a library exception. Map it to 409 Conflict (or a redirect) in a kernel.exception listener, otherwise users see a 500 on double-click — the exact scenario the package exists to prevent.
  • Non-blocking acquire. The lock is acquired with acquire() (non-blocking) on purpose; a blocking acquire(true) would queue a second capture behind the first and then execute it — the opposite of what you want. Don't "fix" it.
  • TTL vs release ordering. If the process dies between provider call and release(), the lock expires on TTL, not immediately. Tune TTL down for fast PSPs, up for slow ones. Monitor for lock expiry warnings.
  • Config key drift. With a last release in 2026 and no recipe, verify options with config:dump-reference rather than copy-pasting from the README — the README is frequently the first thing to fall out of date in low-traffic bundles.
  • Payum version coupling. Confirm the Payum major version (payum/payum 1.x vs 2.x) your app uses; the extension hooks the gateway execute path, which changed between majors. Composer will usually resolve correctly, but pin explicitly.
  • Extension point for custom behaviour: if you need structured "already in progress" responses, don't modify the extension — decorate your gateway's CaptureAction, or add a kernel.exception listener that inspects the lock exception and returns JSON for API callers vs an HTML flash for browser callers.
  • Log the lock key and token when acquisition fails. In production the only actionable signal is which token was contended; without it, a 409 looks like a random failure.
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.
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
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels