Financial - Make Payment::create idempotent for a repeated trxn_id - #36426
Open
mattwire wants to merge 1 commit into
Open
Financial - Make Payment::create idempotent for a repeated trxn_id#36426mattwire wants to merge 1 commit into
mattwire wants to merge 1 commit into
Conversation
A payment processor webhook racing a synchronous front-end/back-office confirmation of the same charge (e.g. paying an existing pending contribution via CRM_Contribute_Form_Contribution_Confirm) can both read the contribution as not-yet-completed and both go on to record a payment for it. Add a per-contribution lock via Civi::lockManager() around create(), and treat a second payment carrying an already-recorded trxn_id as a no-op success (returning the existing FinancialTrxn) rather than creating a duplicate. Every caller gets this for free with no changes needed at the call site. Includes a regression test using APIv4 Payment::create/get. Alternative to the reject-with-exception approach in fix-payment-create-race-lock - see that branch for comparison.
|
🤖 Thank you for contributing to CiviCRM! ❤️ We will need to test and review this PR. 👷 Introduction for new contributors...
PR commands & links...
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
A payment processor webhook racing a synchronous front-end/back-office confirmation of the same charge (e.g. paying an existing pending contribution via CRM_Contribute_Form_Contribution_Confirm) can both read the contribution as not-yet-completed and both go on to record a payment for it. Add a per-contribution lock via Civi::lockManager() around create(), and treat a second payment carrying an already-recorded trxn_id as a no-op success (returning the existing FinancialTrxn) rather than creating a duplicate. Every caller gets this for free with no changes needed at the call site. Includes a regression test using APIv4 Payment::create/get.
Alternative to the reject-with-exception approach in fix-payment-create-race-lock - see that branch for comparison.
Before
Two payments recorded (or crash for one of them) if user-side and webhook record payment at the same time.
After
One payment recorded and both sides think they recorded it - desired outcome.
Technical Details
Adds a lock and relies on trxn_id being unique (which is enforced by DB entity).
The downstream code has 15 seconds to complete otherwise it will throw a CRM_Core_Exception. If downstream code completes in time the payment record is returned just the same as for the first caller.
Comments
This should be a cleaner solution than #36401 because it doesn't require code changes anywhere else.