The notify flow works but has no protection against concurrent delivery: Quickpay retries callbacks, and a retry (or a callback racing the customer's browser return / an admin-triggered transition) can process the same state change twice. Two surveyed plugins ship patterns worth adopting:
CommerceWeavers SyliusSaferpayPlugin (the most complete treatment):
- A Symfony Lock + a
processing flag in payment details around callback handling — whichever of webhook/browser-return arrives second sees the flag and returns 200 immediately (SaferpayPaymentProcessor::lock()); duplicate and late callbacks are acknowledged without reprocessing
- Per-transaction webhook URLs secured by Payum notify tokens (
/webhook/{payum_token}/{order_token}) — authentication comes free from Payum's HttpRequestVerifier, and the token identifies the payment with no order-number/prefix logic at all
FluxSE SyliusPayumStripePlugin:
UpdatePaymentStateExtension, a Payum ExtensionInterface that runs after every gateway execute and applies the matching sylius_payment transition — one shared, guarded state-advancement path for webhook and browser-return alike
Relevance here
The per-payment notify token idea is exactly "direction 2" from the (closed) #53 discussion — payum-quickpay's authorize action already mints a notify token for callback_url, so the shared /payment/quickpay/notify route plus PaymentProvider lookup could eventually reduce to Payum's token-verified notify endpoint. Even without that migration, the lock/flag idempotency layer is worth having: transitions are guarded today (via GetHumanStatus re-checks), but guarding is per-operation and API-round-trip-expensive, not a concurrency control.
Tasks
The notify flow works but has no protection against concurrent delivery: Quickpay retries callbacks, and a retry (or a callback racing the customer's browser return / an admin-triggered transition) can process the same state change twice. Two surveyed plugins ship patterns worth adopting:
CommerceWeavers SyliusSaferpayPlugin (the most complete treatment):
processingflag in payment details around callback handling — whichever of webhook/browser-return arrives second sees the flag and returns 200 immediately (SaferpayPaymentProcessor::lock()); duplicate and late callbacks are acknowledged without reprocessing/webhook/{payum_token}/{order_token}) — authentication comes free from Payum'sHttpRequestVerifier, and the token identifies the payment with no order-number/prefix logic at allFluxSE SyliusPayumStripePlugin:
UpdatePaymentStateExtension, a PayumExtensionInterfacethat runs after every gateway execute and applies the matchingsylius_paymenttransition — one shared, guarded state-advancement path for webhook and browser-return alikeRelevance here
The per-payment notify token idea is exactly "direction 2" from the (closed) #53 discussion — payum-quickpay's authorize action already mints a notify token for
callback_url, so the shared/payment/quickpay/notifyroute plusPaymentProviderlookup could eventually reduce to Payum's token-verified notify endpoint. Even without that migration, the lock/flag idempotency layer is worth having: transitions are guarded today (viaGetHumanStatusre-checks), but guarding is per-operation and API-round-trip-expensive, not a concurrency control.Tasks
NotifyAction's dispatch (Symfony Lock component; key =quickpayPaymentId)