Skip to content

Harden the notify flow: idempotency, callback races, and per-payment notify tokens #109

Description

@loevgaard

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

  • Add lock + processing-flag idempotency around NotifyAction's dispatch (Symfony Lock component; key = quickpayPaymentId)
  • Return 200 for duplicate/in-flight callbacks so Quickpay stops retrying
  • Evaluate migrating the callback URL to Payum notify tokens (spike; would subsume the prefix/lookup machinery — coordinate with the reconcile command Add a console command that reconciles pending payments by polling Quickpay #101, which must keep working for payments created before the switch)
  • Concurrency test: two simultaneous callbacks for the same payment produce one state change

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions