[MediaWiki-commits] [Gerrit] Cancel duplicate authorizations for a single order ID - change (wikimedia...SmashPig)
jenkins-bot has submitted this change and it was merged. Change subject: Cancel duplicate authorizations for a single order ID .. Cancel duplicate authorizations for a single order ID Donors can somehow submit the Adyen iframe multiple times without our generating new order IDs / merchant references. If we get duplicate authorizations, mark the donor details as captured after capturing the first, then cancel all subsequent authorizations. We send the pending queue message before the donor sees the credit card form, so we assume that if no message exists, this is a duplicate authorization and the original has already been sent to Civi. Bug: T129935 Change-Id: I2b338164461130c3afd0d91efbbd7cfbeec1b680 --- M CrmLink/Messages/DonationInterfaceMessage.php M PaymentProviders/Adyen/Jobs/ProcessCaptureRequestJob.php M PaymentProviders/Adyen/Tests/MockAdyenPaymentsAPI.php M PaymentProviders/Adyen/Tests/phpunit/CaptureJobTest.php M PaymentProviders/Amazon/Tests/phpunit/ApiTest.php M Tests/BaseSmashPigUnitTestCase.php 6 files changed, 285 insertions(+), 54 deletions(-) Approvals: Awight: Looks good to me, approved jenkins-bot: Verified diff --git a/CrmLink/Messages/DonationInterfaceMessage.php b/CrmLink/Messages/DonationInterfaceMessage.php index 6db0360..3f9c391 100644 --- a/CrmLink/Messages/DonationInterfaceMessage.php +++ b/CrmLink/Messages/DonationInterfaceMessage.php @@ -6,6 +6,7 @@ * Message sent to the 'cc-limbo' queue when a payment has been initiated and sent off to the gateway. */ class DonationInterfaceMessage extends KeyedOpaqueStorableObject { + public $captured = ''; public $city = ''; public $city_2 = ''; public $comment = ''; diff --git a/PaymentProviders/Adyen/Jobs/ProcessCaptureRequestJob.php b/PaymentProviders/Adyen/Jobs/ProcessCaptureRequestJob.php index d6d5987..7323071 100644 --- a/PaymentProviders/Adyen/Jobs/ProcessCaptureRequestJob.php +++ b/PaymentProviders/Adyen/Jobs/ProcessCaptureRequestJob.php @@ -26,10 +26,10 @@ protected $avsResult; protected $cvvResult; // Actions to take after examining capture request and queue message - const ACTION_IGNORE = 'ignore'; // no donor info or auth already captured, do nothing const ACTION_PROCESS = 'process'; // all clear to capture payment const ACTION_REJECT = 'reject'; // very likely fraud - cancel the authorization const ACTION_REVIEW = 'review'; // potential fraud - do not capture now + const ACTION_DUPLICATE = 'duplicate'; // probable duplicate - cancel the authorization public static function factory( Authorisation $authMessage ) { $obj = new ProcessCaptureRequestJob(); @@ -54,7 +54,11 @@ ); // Determine if a message exists in the pending queue; if it does not then - // this payment has already been sent to the verified queue. + // this payment has already been sent to the verified queue. If it does, + // we need to check $capture_requested in case we have requested a capture + // but have not yet received notification of capture success. Either case can + // occur when a donor submits their credit card details multiple times against + // a single order ID. We should cancel all the duplicate authorizations. Logger::debug( 'Attempting to locate associated message in pending queue.' ); /** * @var \SmashPig\Core\DataStores\KeyedOpaqueDataStore @@ -64,51 +68,58 @@ $success = true; $action = $this->determineAction( $queueMessage ); - if ( $action == self::ACTION_PROCESS ) { - // Tell the pending queue to keep the message around for the RecordCaptureJob - $pendingQueue->queueIgnoreObject(); - - // Attempt to capture the payment - $api = $this->getApi(); - Logger::info( - "Attempting capture API call for currency '{$this->currency}', " . - "amount '{$this->amount}', reference '{$this->pspReference}'." - ); - $captureResult = $api->capture( $this->currency, $this->amount, $this->pspReference ); - - if ( $captureResult ) { - // Success! + switch( $action ) { + case self::ACTION_PROCESS: + // Attempt to capture the payment + $api = $this->getApi(); Logger::info( - "Successfully captured payment! Returned reference: '{$captureResult}'. " . - 'Leaving pending
[MediaWiki-commits] [Gerrit] Cancel duplicate authorizations for a single order ID - change (wikimedia...SmashPig)
Ejegg has uploaded a new change for review. https://gerrit.wikimedia.org/r/277427 Change subject: Cancel duplicate authorizations for a single order ID .. Cancel duplicate authorizations for a single order ID Donors can somehow submit the Adyen iframe multiple times without our generating new order IDs / merchant references. If we get duplicate authorizations, mark the donor details as captured after capturing the first, then cancel all subsequent authorizations. We send the pending queue message before the donor sees the credit card form, so we assume that if no message exists, this is a duplicate authorization and the original has already been sent to Civi. Bug: T129935 Change-Id: I2b338164461130c3afd0d91efbbd7cfbeec1b680 --- M CrmLink/Messages/DonationInterfaceMessage.php M PaymentProviders/Adyen/Jobs/ProcessCaptureRequestJob.php 2 files changed, 77 insertions(+), 46 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/wikimedia/fundraising/SmashPig refs/changes/27/277427/1 diff --git a/CrmLink/Messages/DonationInterfaceMessage.php b/CrmLink/Messages/DonationInterfaceMessage.php index 6db0360..3f9c391 100644 --- a/CrmLink/Messages/DonationInterfaceMessage.php +++ b/CrmLink/Messages/DonationInterfaceMessage.php @@ -6,6 +6,7 @@ * Message sent to the 'cc-limbo' queue when a payment has been initiated and sent off to the gateway. */ class DonationInterfaceMessage extends KeyedOpaqueStorableObject { + public $captured = ''; public $city = ''; public $city_2 = ''; public $comment = ''; diff --git a/PaymentProviders/Adyen/Jobs/ProcessCaptureRequestJob.php b/PaymentProviders/Adyen/Jobs/ProcessCaptureRequestJob.php index d0cc7e6..7600795 100644 --- a/PaymentProviders/Adyen/Jobs/ProcessCaptureRequestJob.php +++ b/PaymentProviders/Adyen/Jobs/ProcessCaptureRequestJob.php @@ -26,10 +26,10 @@ protected $avsResult; protected $cvvResult; // Actions to take after examining capture request and queue message - const ACTION_IGNORE = 'ignore'; // no donor info or auth already captured, do nothing const ACTION_PROCESS = 'process'; // all clear to capture payment const ACTION_REJECT = 'reject'; // very likely fraud - cancel the authorization const ACTION_REVIEW = 'review'; // potential fraud - do not capture now + const ACTION_DUPLICATE = 'duplicate'; // probable duplicate - cancel the authorization public static function factory( Authorisation $authMessage ) { $obj = new ProcessCaptureRequestJob(); @@ -54,58 +54,69 @@ ); // Determine if a message exists in the pending queue; if it does not then - // this payment has already been sent to the verified queue. + // this payment has already been sent to the verified queue. If it does, + // we need to check $capture_requested in case we have requested a capture + // but have not yet received notification of capture success. Either case can + // occur when a donor submits their credit card details multiple times against + // a single order ID. We should cancel all the duplicate authorizations. Logger::debug( 'Attempting to locate associated message in pending queue.' ); $pendingQueue = Configuration::getDefaultConfig()->object( 'data-store/pending' ); $queueMessage = $pendingQueue->queueGetObject( null, $this->correlationId ); $success = true; $action = $this->determineAction( $queueMessage ); - if ( $action == self::ACTION_PROCESS ) { - // Tell the pending queue to keep the message around for the RecordCaptureJob - $pendingQueue->queueIgnoreObject(); - - // Attempt to capture the payment - $api = new AdyenPaymentsAPI( $this->account ); - Logger::info( - "Attempting capture API call for currency '{$this->currency}', " . - "amount '{$this->amount}', reference '{$this->pspReference}'." - ); - $captureResult = $api->capture( $this->currency, $this->amount, $this->pspReference ); - - if ( $captureResult ) { - // Success! + switch( $action ) { + case self::ACTION_PROCESS: + // Attempt to capture the payment + $api = new AdyenPaymentsAPI( $this->account ); Logger::info( - "Successfully captured payment! Returned reference: '{$captureResult}'. " . - 'Leaving pending message in queue for