Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add try catch to main loops on core ipn classes #18384

Merged
merged 1 commit into from
Sep 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 52 additions & 49 deletions CRM/Core/Payment/AuthorizeNetIPN.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,61 +35,66 @@ public function __construct($inputData) {
* @return bool|void
*/
public function main($component = 'contribute') {
try {
//we only get invoice num as a key player from payment gateway response.
//for ARB we get x_subscription_id and x_subscription_paynum
$x_subscription_id = $this->retrieve('x_subscription_id', 'String');
$ids = $objects = $input = [];

//we only get invoice num as a key player from payment gateway response.
//for ARB we get x_subscription_id and x_subscription_paynum
$x_subscription_id = $this->retrieve('x_subscription_id', 'String');
$ids = $objects = $input = [];
if ($x_subscription_id) {
// Presence of the id means it is approved.
$input['component'] = $component;

if ($x_subscription_id) {
// Presence of the id means it is approved.
$input['component'] = $component;
// load post vars in $input
$this->getInput($input, $ids);

// load post vars in $input
$this->getInput($input, $ids);
// load post ids in $ids
$this->getIDs($ids, $input);

// load post ids in $ids
$this->getIDs($ids, $input);

// Attempt to get payment processor ID from URL
if (!empty($this->_inputParameters['processor_id'])) {
$paymentProcessorID = $this->_inputParameters['processor_id'];
}
else {
// This is an unreliable method as there could be more than one instance.
// Recommended approach is to use the civicrm/payment/ipn/xx url where xx is the payment
// processor id & the handleNotification function (which should call the completetransaction api & by-pass this
// 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 AuthNet IPN - this will cause problems if you have more than one instance');
$paymentProcessorTypeID = CRM_Core_DAO::getFieldValue('CRM_Financial_DAO_PaymentProcessorType',
'AuthNet', 'id', 'name'
);
$paymentProcessorID = (int) civicrm_api3('PaymentProcessor', 'getvalue', [
'is_test' => 0,
'options' => ['limit' => 1],
'payment_processor_type_id' => $paymentProcessorTypeID,
'return' => 'id',
]);
}
// Attempt to get payment processor ID from URL
if (!empty($this->_inputParameters['processor_id'])) {
$paymentProcessorID = $this->_inputParameters['processor_id'];
}
else {
// This is an unreliable method as there could be more than one instance.
// Recommended approach is to use the civicrm/payment/ipn/xx url where xx is the payment
// processor id & the handleNotification function (which should call the completetransaction api & by-pass this
// 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 AuthNet IPN - this will cause problems if you have more than one instance');
$paymentProcessorTypeID = CRM_Core_DAO::getFieldValue('CRM_Financial_DAO_PaymentProcessorType',
'AuthNet', 'id', 'name'
);
$paymentProcessorID = (int) civicrm_api3('PaymentProcessor', 'getvalue', [
'is_test' => 0,
'options' => ['limit' => 1],
'payment_processor_type_id' => $paymentProcessorTypeID,
'return' => 'id',
]);
}

if (!$this->validateData($input, $ids, $objects, TRUE, $paymentProcessorID)) {
return FALSE;
}
if (!empty($ids['paymentProcessor']) && $objects['contributionRecur']->payment_processor_id != $ids['paymentProcessor']) {
Civi::log()->warning('Payment Processor does not match the recurring processor id.', ['civi.tag' => 'deprecated']);
}
if (!$this->validateData($input, $ids, $objects, TRUE, $paymentProcessorID)) {
return FALSE;
}
if (!empty($ids['paymentProcessor']) && $objects['contributionRecur']->payment_processor_id != $ids['paymentProcessor']) {
Civi::log()->warning('Payment Processor does not match the recurring processor id.', ['civi.tag' => 'deprecated']);
}

if ($component == 'contribute' && $ids['contributionRecur']) {
// check if first contribution is completed, else complete first contribution
$first = TRUE;
if ($objects['contribution']->contribution_status_id == 1) {
$first = FALSE;
if ($component == 'contribute' && $ids['contributionRecur']) {
// check if first contribution is completed, else complete first contribution
$first = TRUE;
if ($objects['contribution']->contribution_status_id == 1) {
$first = FALSE;
}
return $this->recur($input, $ids, $objects, $first);
}
return $this->recur($input, $ids, $objects, $first);
}
return TRUE;
}
catch (CRM_Core_Exception $e) {
Civi::log()->debug($e->getMessage());
echo 'Invalid or missing data';
}
return TRUE;
}

/**
Expand All @@ -107,9 +112,7 @@ public function recur($input, $ids, $objects, $first) {

// do a subscription check
if ($recur->processor_id != $input['subscription_id']) {
CRM_Core_Error::debug_log_message('Unrecognized subscription.');
echo 'Failure: Unrecognized subscription<p>';
return FALSE;
throw new CRM_Core_Exception('Unrecognized subscription.');
}

$contributionStatus = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name');
Expand Down
136 changes: 70 additions & 66 deletions CRM/Core/Payment/PayPalIPN.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,6 @@ public function __construct($inputData) {
public function retrieve($name, $type, $abort = TRUE) {
$value = CRM_Utils_Type::validate(CRM_Utils_Array::value($name, $this->_inputParameters), $type, FALSE);
if ($abort && $value === NULL) {
Civi::log()->debug("PayPalIPN: Could not find an entry for $name");
echo "Failure: Missing Parameter<p>" . CRM_Utils_Type::escape($name, 'String');
throw new CRM_Core_Exception("PayPalIPN: Could not find an entry for $name");
}
return $value;
Expand Down Expand Up @@ -286,84 +284,90 @@ public function single($input, $ids, $objects, $recur = FALSE, $first = FALSE) {
* @throws \CiviCRM_API3_Exception
*/
public function main() {
$objects = $ids = $input = [];
$component = $this->retrieve('module', 'String');
$input['component'] = $component;
try {
$objects = $ids = $input = [];
$component = $this->retrieve('module', 'String');
$input['component'] = $component;

$ids['contact'] = $this->retrieve('contactID', 'Integer', TRUE);
$contributionID = $ids['contribution'] = $this->retrieve('contributionID', 'Integer', TRUE);
$membershipID = $this->retrieve('membershipID', 'Integer', FALSE);
$contributionRecurID = $this->retrieve('contributionRecurID', 'Integer', FALSE);
$ids['contact'] = $this->retrieve('contactID', 'Integer', TRUE);
$contributionID = $ids['contribution'] = $this->retrieve('contributionID', 'Integer', TRUE);
$membershipID = $this->retrieve('membershipID', 'Integer', FALSE);
$contributionRecurID = $this->retrieve('contributionRecurID', 'Integer', FALSE);

$this->getInput($input);
$this->getInput($input);

if ($component == 'event') {
$ids['event'] = $this->retrieve('eventID', 'Integer', TRUE);
$ids['participant'] = $this->retrieve('participantID', 'Integer', TRUE);
}
else {
// get the optional ids
$ids['membership'] = $membershipID;
$ids['contributionRecur'] = $contributionRecurID;
$ids['contributionPage'] = $this->retrieve('contributionPageID', 'Integer', FALSE);
$ids['related_contact'] = $this->retrieve('relatedContactID', 'Integer', FALSE);
$ids['onbehalf_dupe_alert'] = $this->retrieve('onBehalfDupeAlert', 'Integer', FALSE);
}
if ($component == 'event') {
$ids['event'] = $this->retrieve('eventID', 'Integer', TRUE);
$ids['participant'] = $this->retrieve('participantID', 'Integer', TRUE);
}
else {
// get the optional ids
$ids['membership'] = $membershipID;
$ids['contributionRecur'] = $contributionRecurID;
$ids['contributionPage'] = $this->retrieve('contributionPageID', 'Integer', FALSE);
$ids['related_contact'] = $this->retrieve('relatedContactID', 'Integer', FALSE);
$ids['onbehalf_dupe_alert'] = $this->retrieve('onBehalfDupeAlert', 'Integer', FALSE);
}

$paymentProcessorID = $this->getPayPalPaymentProcessorID($input, $ids);
$paymentProcessorID = $this->getPayPalPaymentProcessorID($input, $ids);

Civi::log()->debug('PayPalIPN: Received (ContactID: ' . $ids['contact'] . '; trxn_id: ' . $input['trxn_id'] . ').');
Civi::log()->debug('PayPalIPN: Received (ContactID: ' . $ids['contact'] . '; trxn_id: ' . $input['trxn_id'] . ').');

// Debugging related to possible missing membership linkage
if ($contributionRecurID && $this->retrieve('membershipID', 'Integer', FALSE)) {
$templateContribution = CRM_Contribute_BAO_ContributionRecur::getTemplateContribution($contributionRecurID);
$membershipPayment = civicrm_api3('MembershipPayment', 'get', [
'contribution_id' => $templateContribution['id'],
'membership_id' => $membershipID,
]);
$lineItems = civicrm_api3('LineItem', 'get', [
'contribution_id' => $templateContribution['id'],
'entity_id' => $membershipID,
'entity_table' => 'civicrm_membership',
]);
Civi::log()->debug('PayPalIPN: Received payment for membership ' . (int) $membershipID
. '. Original contribution was ' . (int) $contributionID . '. The template for this contribution is '
. $templateContribution['id'] . ' it is linked to ' . $membershipPayment['count']
. 'payments for this membership. It has ' . $lineItems['count'] . ' line items linked to this membership.'
. ' it is expected the original contribution will be linked by both entities to the membership.'
);
if (empty($membershipPayment['count']) && empty($lineItems['count'])) {
Civi::log()->debug('PayPalIPN: Will attempt to compensate');
$input['membership_id'] = $this->retrieve('membershipID', 'Integer', FALSE);
}
if ($contributionRecurID) {
$recurLinks = civicrm_api3('ContributionRecur', 'get', [
// Debugging related to possible missing membership linkage
if ($contributionRecurID && $this->retrieve('membershipID', 'Integer', FALSE)) {
$templateContribution = CRM_Contribute_BAO_ContributionRecur::getTemplateContribution($contributionRecurID);
$membershipPayment = civicrm_api3('MembershipPayment', 'get', [
'contribution_id' => $templateContribution['id'],
'membership_id' => $membershipID,
'contribution_recur_id' => $contributionRecurID,
]);
Civi::log()->debug('PayPalIPN: Membership should be linked to contribution recur record ' . $contributionRecurID
. ' ' . $recurLinks['count'] . 'links found'
$lineItems = civicrm_api3('LineItem', 'get', [
'contribution_id' => $templateContribution['id'],
'entity_id' => $membershipID,
'entity_table' => 'civicrm_membership',
]);
Civi::log()->debug('PayPalIPN: Received payment for membership ' . (int) $membershipID
. '. Original contribution was ' . (int) $contributionID . '. The template for this contribution is '
. $templateContribution['id'] . ' it is linked to ' . $membershipPayment['count']
. 'payments for this membership. It has ' . $lineItems['count'] . ' line items linked to this membership.'
. ' it is expected the original contribution will be linked by both entities to the membership.'
);
if (empty($membershipPayment['count']) && empty($lineItems['count'])) {
Civi::log()->debug('PayPalIPN: Will attempt to compensate');
$input['membership_id'] = $this->retrieve('membershipID', 'Integer', FALSE);
}
if ($contributionRecurID) {
$recurLinks = civicrm_api3('ContributionRecur', 'get', [
'membership_id' => $membershipID,
'contribution_recur_id' => $contributionRecurID,
]);
Civi::log()->debug('PayPalIPN: Membership should be linked to contribution recur record ' . $contributionRecurID
. ' ' . $recurLinks['count'] . 'links found'
);
}
}
if (!$this->validateData($input, $ids, $objects, TRUE, $paymentProcessorID)) {
return;
}
}
if (!$this->validateData($input, $ids, $objects, TRUE, $paymentProcessorID)) {
return;
}

self::$_paymentProcessor = &$objects['paymentProcessor'];
if ($component == 'contribute') {
if ($ids['contributionRecur']) {
// 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) {
$first = FALSE;
self::$_paymentProcessor = &$objects['paymentProcessor'];
if ($component == 'contribute') {
if ($ids['contributionRecur']) {
// 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) {
$first = FALSE;
}
$this->recur($input, $ids, $objects, $first);
return;
}
$this->recur($input, $ids, $objects, $first);
return;
}
$this->single($input, $ids, $objects);
}
catch (CRM_Core_Exception $e) {
Civi::log()->debug($e->getMessage());
echo 'Invalid or missing data';
}
$this->single($input, $ids, $objects);
}

/**
Expand Down
Loading