From 8103c599d7b4509ccd17a47c5423bd8772fa27d6 Mon Sep 17 00:00:00 2001 From: Eileen McNaughton Date: Fri, 22 Sep 2023 17:00:47 +1200 Subject: [PATCH] Clean up on paypal & get tests working with change --- CRM/Core/Payment/PayPalProIPN.php | 123 ++++++++++++++---------------- 1 file changed, 57 insertions(+), 66 deletions(-) diff --git a/CRM/Core/Payment/PayPalProIPN.php b/CRM/Core/Payment/PayPalProIPN.php index 01b4de782c..61460ca9c3 100644 --- a/CRM/Core/Payment/PayPalProIPN.php +++ b/CRM/Core/Payment/PayPalProIPN.php @@ -228,62 +228,29 @@ class CRM_Core_Payment_PayPalProIPN extends CRM_Core_Payment_BaseIPN { $first = !$this->isContributionCompleted(); $recur = $this->getContributionRecurObject(); if (!isset($input['txnType'])) { - Civi::log()->debug('PayPalProIPN: Could not find txn_type in input request.'); + Civi::log('paypal_pro')->debug('PayPalProIPN: Could not find txn_type in input request.'); echo 'Failure: Invalid parameters

'; return; } - // make sure the invoice ids match - // make sure the invoice is valid and matches what we have in - // the contribution record - if ($recur->invoice_id != $input['invoice']) { - Civi::log()->debug('PayPalProIPN: Invoice values dont match between database and IPN request recur is ' . $recur->invoice_id . ' input is ' . $input['invoice']); - echo 'Failure: Invoice values dont match between database and IPN request recur is ' . $recur->invoice_id . " input is " . $input['invoice']; - return; - } - $now = date('YmdHis'); - $sendNotification = FALSE; - $subscriptionPaymentStatus = NULL; - //List of Transaction Type - /* - recurring_payment_profile_created RP Profile Created - recurring_payment RP Successful Payment - recurring_payment_failed RP Failed Payment - recurring_payment_profile_cancel RP Profile Cancelled - recurring_payment_expired RP Profile Expired - recurring_payment_skipped RP Profile Skipped - recurring_payment_outstanding_payment RP Successful Outstanding Payment - recurring_payment_outstanding_payment_failed RP Failed Outstanding Payment - recurring_payment_suspended RP Profile Suspended - recurring_payment_suspended_due_to_max_failed_payment RP Profile Suspended due to Max Failed Payment - */ - - //set transaction type $txnType = $this->retrieve('txn_type', 'String'); - //Changes for paypal pro recurring payment + switch ($txnType) { case 'recurring_payment_profile_created': - if (in_array(CRM_Core_PseudoConstant::getName('CRM_Contribute_BAO_ContributionRecur', 'contribution_status_id', $recur->contribution_status_id), [ - 'Pending', 'In Progress', - ], TRUE) - && !empty($recur->processor_id) + if (CRM_Core_PseudoConstant::getName('CRM_Contribute_BAO_ContributionRecur', 'contribution_status_id', $recur->contribution_status_id) === 'In Progress' ) { - echo 'already handled'; + Civi::log('paypal_pro')->debug('already handled'); return; } - $recur->create_date = $now; - $recur->contribution_status_id = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_ContributionRecur', 'contribution_status_id', 'Pending'); - $recur->processor_id = $this->retrieve('recurring_payment_id', 'String'); - $recur->trxn_id = $recur->processor_id; - $subscriptionPaymentStatus = CRM_Core_Payment::RECURRING_PAYMENT_START; - $sendNotification = TRUE; - break; + $this->processProfileCreated(); + return; case 'recurring_payment': $recur->processor_id = $this->retrieve('recurring_payment_id', 'String'); $recur->trxn_id = $recur->processor_id; + $recur->save(); if (!$first) { if ($input['paymentStatus'] !== 'Completed') { throw new CRM_Core_Exception('Ignore all IPN payments that are not completed'); @@ -308,29 +275,18 @@ class CRM_Core_Payment_PayPalProIPN extends CRM_Core_Payment_BaseIPN { } $recur->contribution_status_id = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_ContributionRecur', 'contribution_status_id', 'Completed'); $recur->end_date = $now; - $sendNotification = TRUE; - $subscriptionPaymentStatus = CRM_Core_Payment::RECURRING_PAYMENT_END; + $recur->save(); + //send recurring Notification email for user + CRM_Contribute_BAO_ContributionPage::recurringNotify( + $this->getContributionID(), + CRM_Core_Payment::RECURRING_PAYMENT_END, + $recur + ); } - + $this->single($input); break; } - $recur->save(); - - if ($sendNotification) { - //send recurring Notification email for user - CRM_Contribute_BAO_ContributionPage::recurringNotify( - $this->getContributionID(), - $subscriptionPaymentStatus, - $recur - ); - } - - if ($txnType !== 'recurring_payment') { - return; - } - - $this->single($input); } /** @@ -357,11 +313,11 @@ class CRM_Core_Payment_PayPalProIPN extends CRM_Core_Payment_BaseIPN { 'cancel_date' => 'now', 'contribution_status_id:name' => 'Failed', ])->addWhere('id', '=', $this->getContributionID())->execute(); - Civi::log()->debug('Setting contribution status to Failed'); + Civi::log('paypal_pro')->debug('Setting contribution status to Failed'); return; } if ($status === 'Pending') { - Civi::log()->debug('Returning since contribution status is Pending'); + Civi::log('paypal_pro')->debug('Returning since contribution status is Pending'); return; } if ($status === 'Refunded' || $status === 'Reversed') { @@ -369,16 +325,16 @@ class CRM_Core_Payment_PayPalProIPN extends CRM_Core_Payment_BaseIPN { 'cancel_date' => 'now', 'contribution_status_id:name' => 'Cancelled', ])->addWhere('id', '=', $this->getContributionID())->execute(); - Civi::log()->debug('Setting contribution status to Cancelled'); + Civi::log('paypal_pro')->debug('Setting contribution status to Cancelled'); return; } if ($status !== 'Completed') { - Civi::log()->debug('Returning since contribution status is not handled'); + Civi::log('paypal_pro')->debug('Returning since contribution status is not handled'); return; } if ($this->isContributionCompleted()) { - Civi::log()->debug('PayPalProIPN: Returning since contribution has already been handled.'); + Civi::log('paypal_pro')->debug('PayPalProIPN: Returning since contribution has already been handled.'); echo 'Success: Contribution has already been handled

'; return; } @@ -398,7 +354,7 @@ class CRM_Core_Payment_PayPalProIPN extends CRM_Core_Payment_BaseIPN { // entirely). The only thing the IPN class should really do is extract data from the request, validate it // & call completetransaction or call fail? (which may not exist yet). - Civi::log()->warning('Unreliable method used to get payment_processor_id for PayPal Pro IPN - this will cause problems if you have more than one instance'); + Civi::log('paypal_pro')->warning('Unreliable method used to get payment_processor_id for PayPal Pro IPN - this will cause problems if you have more than one instance'); $paymentProcessorTypeID = CRM_Core_DAO::getFieldValue('CRM_Financial_DAO_PaymentProcessorType', 'PayPal', 'id', 'name' @@ -451,7 +407,7 @@ class CRM_Core_Payment_PayPalProIPN extends CRM_Core_Payment_BaseIPN { $this->single($input); } catch (Exception $e) { - Civi::log()->debug($e->getMessage() . ' input {input}', ['input' => $input]); + Civi::log('paypal_pro')->debug($e->getMessage() . ' input {input}', ['input' => $input]); echo 'Invalid or missing data'; } } @@ -563,6 +519,15 @@ class CRM_Core_Payment_PayPalProIPN extends CRM_Core_Payment_BaseIPN { if (!$contributionRecur->find(TRUE)) { throw new CRM_Core_Exception('Failure: Could not find contribution recur record'); } + + // make sure the invoice ids match + // make sure the invoice is valid and matches what we have in + // the contribution record + $invoice = (string) $this->getValue('i'); + if ((string) $contributionRecur->invoice_id !== $invoice) { + Civi::log('paypal_pro')->debug('PayPalProIPN: Invoice values dont match between database and IPN request recur is ' . $contributionRecur->invoice_id . ' input is ' . $invoice); + throw new CRM_Core_Exception('Failure: Invoice values dont match between database and IPN request recur is ' . $contributionRecur->invoice_id . " input is " . $invoice); + } return $this->contributionRecurObject = $contributionRecur; } return $this->contributionRecurObject; @@ -609,4 +574,30 @@ class CRM_Core_Payment_PayPalProIPN extends CRM_Core_Payment_BaseIPN { return $status === 'Completed'; } + /** + * Update a recurring contribution to in progress based on an ipn profile_create notification. + * + * recurring_payment_profile_created is called when the + * subscription has been authorized and confirmed by the user, + * but before a payment has been taken. + * The recurring_payment_id is POSTed to the IPN + * and we store it in the recurring contribution's processor_id. + * + * @throws \CRM_Core_Exception + */ + private function processProfileCreated(): void { + $recur = $this->getContributionRecurObject(); + $recur->create_date = date('YmdHis'); + $recur->contribution_status_id = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_ContributionRecur', 'contribution_status_id', 'Pending'); + $recur->processor_id = $this->retrieve('recurring_payment_id', 'String'); + $recur->trxn_id = $recur->processor_id; + $recur->save(); + //send recurring Notification email for user + CRM_Contribute_BAO_ContributionPage::recurringNotify( + $this->getContributionID(), + CRM_Core_Payment::RECURRING_PAYMENT_START, + $recur + ); + } + } -- 2.25.1