X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=CRM%2FCore%2FPayment%2FPayPalProIPN.php;h=87b568654665899a9c937da608522eddf916e00c;hb=3a317d6d94f518d5f41dc4d217b7b38650ada74d;hp=ff8cf081785cdc81a0e1e81cba5397e2b5dec0ea;hpb=3533840e0cf516ccb57b808d2babf6bedbbc2ac6;p=civicrm-core.git diff --git a/CRM/Core/Payment/PayPalProIPN.php b/CRM/Core/Payment/PayPalProIPN.php index ff8cf08178..87b5686546 100644 --- a/CRM/Core/Payment/PayPalProIPN.php +++ b/CRM/Core/Payment/PayPalProIPN.php @@ -148,21 +148,22 @@ class CRM_Core_Payment_PayPalProIPN extends CRM_Core_Payment_BaseIPN { * * @param array $input * @param array $ids - * @param array $objects + * @param \CRM_Contribute_BAO_ContributionRecur $recur + * @param \CRM_Contribute_BAO_Contribution $contribution * @param bool $first * + * @throws \API_Exception * @throws \CRM_Core_Exception * @throws \CiviCRM_API3_Exception + * @throws \Civi\API\Exception\UnauthorizedException */ - public function recur($input, $ids, $objects, $first) { + public function recur($input, $ids, $recur, $contribution, $first) { if (!isset($input['txnType'])) { Civi::log()->debug('PayPalProIPN: Could not find txn_type in input request.'); echo 'Failure: Invalid parameters

'; return; } - $recur = &$objects['contributionRecur']; - // make sure the invoice ids match // make sure the invoice is valid and matches what we have in // the contribution record @@ -256,46 +257,42 @@ class CRM_Core_Payment_PayPalProIPN extends CRM_Core_Payment_BaseIPN { $recur->save(); if ($sendNotification) { - $autoRenewMembership = FALSE; - if ($recur->id && - isset($ids['membership']) && $ids['membership'] - ) { - $autoRenewMembership = TRUE; - } //send recurring Notification email for user CRM_Contribute_BAO_ContributionPage::recurringNotify($subscriptionPaymentStatus, $ids['contact'], $ids['contributionPage'], $recur, - $autoRenewMembership + !empty($ids['membership']) ); } - if ($txnType != 'recurring_payment') { + if ($txnType !== 'recurring_payment') { return; } // CRM-13737 - am not aware of any reason why payment_date would not be set - this if is a belt & braces - $objects['contribution']->receive_date = !empty($input['payment_date']) ? date('YmdHis', strtotime($input['payment_date'])) : $now; + $contribution->receive_date = !empty($input['payment_date']) ? date('YmdHis', strtotime($input['payment_date'])) : $now; $this->single($input, [ - 'related_contact' => $ids['related_contact'] ?? NULL, - 'participant' => !empty($objects['participant']) ? $objects['participant']->id : NULL, - 'contributionRecur' => !empty($objects['contributionRecur']) ? $objects['contributionRecur']->id : NULL, - ], $objects, TRUE, $first); + 'participant' => $ids['participant'] ?? NULL, + 'contributionRecur' => $recur->id ?? NULL, + ], $contribution, TRUE, $first); } /** * @param array $input * @param array $ids - * @param array $objects + * @param \CRM_Contribute_BAO_Contribution $contribution * @param bool $recur * @param bool $first * * @return void + * @throws \API_Exception + * @throws \CRM_Core_Exception + * @throws \CiviCRM_API3_Exception + * @throws \Civi\API\Exception\UnauthorizedException */ - public function single($input, $ids, $objects, $recur = FALSE, $first = FALSE) { - $contribution = &$objects['contribution']; + public function single($input, $ids, $contribution, $recur = FALSE, $first = FALSE) { // make sure the invoice is valid and matches what we have in the contribution record if ((!$recur) || ($recur && $first)) { @@ -322,7 +319,11 @@ class CRM_Core_Payment_PayPalProIPN extends CRM_Core_Payment_BaseIPN { $status = $input['paymentStatus']; if ($status === 'Denied' || $status === 'Failed' || $status === 'Voided') { - $this->failed($objects); + Contribution::update(FALSE)->setValues([ + 'cancel_date' => 'now', + 'contribution_status_id:name' => 'Failed', + ])->addWhere('id', '=', $contribution->id)->execute(); + Civi::log()->debug("Setting contribution status to Failed"); return; } if ($status === 'Pending') { @@ -350,7 +351,7 @@ class CRM_Core_Payment_PayPalProIPN extends CRM_Core_Payment_BaseIPN { return; } - CRM_Contribute_BAO_Contribution::completeOrder($input, $ids, $objects['contribution']); + CRM_Contribute_BAO_Contribution::completeOrder($input, $ids, $contribution); } /** @@ -394,7 +395,7 @@ class CRM_Core_Payment_PayPalProIPN extends CRM_Core_Payment_BaseIPN { $this->handlePaymentExpress(); return; } - $objects = $ids = $input = []; + $ids = $input = []; $this->_component = $input['component'] = self::getValue('m'); $input['invoice'] = self::getValue('i', TRUE); // get the contribution and contact ids from the GET params @@ -440,28 +441,60 @@ INNER JOIN civicrm_membership_payment mp ON m.id = mp.membership_id AND mp.contr $paymentProcessorID = self::getPayPalPaymentProcessorID(); } - if (!$this->validateData($input, $ids, $objects, TRUE, $paymentProcessorID)) { - return; + // Check if the contribution exists + // make sure contribution exists and is valid + $contribution = new CRM_Contribute_BAO_Contribution(); + $contribution->id = $ids['contribution']; + if (!$contribution->find(TRUE)) { + throw new CRM_Core_Exception('Failure: Could not find contribution record for ' . (int) $contribution->id, NULL, ['context' => "Could not find contribution record: {$contribution->id} in IPN request: " . print_r($input, TRUE)]); + } + + // make sure contact exists and is valid + // use the contact id from the contribution record as the id in the IPN may not be valid anymore. + $contact = new CRM_Contact_BAO_Contact(); + $contact->id = $contribution->contact_id; + $contact->find(TRUE); + if ($contact->id != $ids['contact']) { + // If the ids do not match then it is possible the contact id in the IPN has been merged into another contact which is why we use the contact_id from the contribution + CRM_Core_Error::debug_log_message("Contact ID in IPN {$ids['contact']} not found but contact_id found in contribution {$contribution->contact_id} used instead"); + echo "WARNING: Could not find contact record: {$ids['contact']}

"; + $ids['contact'] = $contribution->contact_id; + } + + // CRM-19478: handle oddity when p=null is set in place of contribution page ID, + if (!empty($ids['contributionPage']) && !is_numeric($ids['contributionPage'])) { + // We don't need to worry if about removing contribution page id as it will be set later in + // CRM_Contribute_BAO_Contribution::loadRelatedObjects(..) using $objects['contribution']->contribution_page_id + unset($ids['contributionPage']); } + $ids['paymentProcessor'] = $paymentProcessorID; + $contribution->loadRelatedObjects($input, $ids); + $input['payment_processor_id'] = $paymentProcessorID; if ($ids['contributionRecur']) { + $contributionRecur = new CRM_Contribute_BAO_ContributionRecur(); + $contributionRecur->id = $ids['contributionRecur']; + if (!$contributionRecur->find(TRUE)) { + CRM_Core_Error::debug_log_message("Could not find contribution recur record: {$ids['ContributionRecur']} in IPN request: " . print_r($input, TRUE)); + echo "Failure: Could not find contribution recur record: {$ids['ContributionRecur']}

"; + return; + } // check if first contribution is completed, else complete first contribution $first = TRUE; $completedStatusId = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed'); - if ($objects['contribution']->contribution_status_id == $completedStatusId) { + if ($contribution->contribution_status_id == $completedStatusId) { $first = FALSE; } - $this->recur($input, $ids, $objects, $first); + $this->recur($input, $ids, $contributionRecur, $contribution, $first); return; } $this->single($input, [ - 'related_contact' => $ids['related_contact'] ?? NULL, - 'participant' => !empty($objects['participant']) ? $objects['participant']->id : NULL, - 'contributionRecur' => !empty($objects['contributionRecur']) ? $objects['contributionRecur']->id : NULL, - ], $objects, FALSE, FALSE); + 'participant' => $ids['participant'] ?? NULL, + 'contributionRecur' => $ids['contributionRecur'] ?? NULL, + ], $contribution, FALSE, FALSE); } catch (CRM_Core_Exception $e) { Civi::log()->debug($e->getMessage()); @@ -571,10 +604,39 @@ INNER JOIN civicrm_membership_payment mp ON m.id = mp.membership_id AND mp.contr $input['trxn_date'] = date('Y-m-d H:i:s', strtotime(self::retrieve('time_created', 'String'))); $paymentProcessorID = $contributionRecur['payment_processor_id']; - if (!$this->validateData($input, $ids, $objects, TRUE, $paymentProcessorID)) { - throw new CRM_Core_Exception('Data did not validate'); + // Check if the contribution exists + // make sure contribution exists and is valid + $contribution = new CRM_Contribute_BAO_Contribution(); + $contribution->id = $ids['contribution']; + if (!$contribution->find(TRUE)) { + throw new CRM_Core_Exception('Failure: Could not find contribution record for ' . (int) $contribution->id, NULL, ['context' => "Could not find contribution record: {$contribution->id} in IPN request: " . print_r($input, TRUE)]); + } + + if (!empty($ids['contributionRecur'])) { + $contributionRecur = new CRM_Contribute_BAO_ContributionRecur(); + $contributionRecur->id = $ids['contributionRecur']; + if (!$contributionRecur->find(TRUE)) { + CRM_Core_Error::debug_log_message("Could not find contribution recur record: {$ids['ContributionRecur']} in IPN request: " . print_r($input, TRUE)); + echo "Failure: Could not find contribution recur record: {$ids['ContributionRecur']}

"; + return FALSE; + } + } + + $objects['contribution'] = &$contribution; + + // CRM-19478: handle oddity when p=null is set in place of contribution page ID, + if (!empty($ids['contributionPage']) && !is_numeric($ids['contributionPage'])) { + // We don't need to worry if about removing contribution page id as it will be set later in + // CRM_Contribute_BAO_Contribution::loadRelatedObjects(..) using $objects['contribution']->contribution_page_id + unset($ids['contributionPage']); } - $this->recur($input, $ids, $objects, $isFirst); + + $contribution = &$objects['contribution']; + $ids['paymentProcessor'] = $paymentProcessorID; + $contribution->loadRelatedObjects($input, $ids); + $objects = array_merge($objects, $contribution->_relatedObjects); + + $this->recur($input, $ids, $objects['contributionRecur'], $objects['contribution'], $isFirst); } /**