Skip to content

Commit

Permalink
dev/financial#33 Add in Hook alterIPNData to allow Extensions to do c…
Browse files Browse the repository at this point in the history
…ustom processing of the IPN Data

Add in unit test of alterIPNData hook and ensure that it is called in all styles of IPN Notifications

Shift hook call to be done in handlePaymentMethod function as per Eileen's comment and remove the hook from deprecated pathways and passbyrefeence in hook

Change name to be postIPNProcess to be more explicit about the purpose of the hook
  • Loading branch information
seamuslee001 committed Oct 18, 2018
1 parent df466fe commit 2d39b9c
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 0 deletions.
3 changes: 3 additions & 0 deletions CRM/Core/Payment.php
Original file line number Diff line number Diff line change
Expand Up @@ -1393,6 +1393,9 @@ public static function handlePaymentMethod($method, $params = array()) {
$extension_instance_found = TRUE;
}

// Call IPN alterIPNData hook to allow for custom processing of IPN data.
$IPNParams = array_merge($_GET, $_REQUEST);
CRM_Utils_Hook::postIPNProcess($IPNParams);
if (!$extension_instance_found) {
$message = "No extension instances of the '%1' payment processor were found.<br />" .
"%2 method is unsupported in legacy payment processors.";
Expand Down
13 changes: 13 additions & 0 deletions CRM/Utils/Hook.php
Original file line number Diff line number Diff line change
Expand Up @@ -2473,4 +2473,17 @@ public static function alterMailingRecipients(&$mailingObject, &$criteria, $cont
);
}

/**
* ALlow Extensions to custom process IPN hook data such as sending Google Analyitcs information based on the IPN
* @param array $IPNData - Array of IPN Data
* @return mixed
*/
public static function postIPNProcess(&$IPNData) {
return self::singleton()->invoke(array('IPNData'),
$IPNData, self::$_nullObject, self::$_nullObject,
self::$_nullObject, self::$_nullObject, self::$_nullObject,
'civicrm_postIPNProcess'
);
}

}
67 changes: 67 additions & 0 deletions tests/phpunit/CRM/Core/Payment/PayPalIPNTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class CRM_Core_Payment_PayPalIPNTest extends CiviUnitTestCase {
protected $_contributionRecurID;
protected $_contributionPageID;
protected $_paymentProcessorID;
protected $_customFieldID;
/**
* IDs of entities created to support the tests.
*
Expand Down Expand Up @@ -237,6 +238,7 @@ public function getPaypalTransaction() {
'first_name' => 'Robert',
'txn_id' => '8XA571746W2698126',
'residence_country' => 'US',
'custom' => json_encode(['cgid' => 'test12345']),
);
}

Expand All @@ -249,4 +251,69 @@ public function getPaypalRecurSubsequentTransaction() {
return array_merge($this->getPaypalRecurTransaction(), array('txn_id' => 'secondone'));
}

/**
* Test IPN response updates contribution and invoice is attached in mail reciept
* Test also AlterIPNData intercepts at the right point and allows for custom processing
* The scenario is that a pending contribution exists and the IPN call will update it to completed.
* And also if Tax and Invoicing is enabled, this unit test ensure that invoice pdf is attached with email recipet
*/
public function testhookAlterIPNDataOnIPNPaymentSuccess() {

$pendingStatusID = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Pending');
$completedStatusID = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Completed');
$params = array(
'payment_processor_id' => $this->_paymentProcessorID,
'contact_id' => $this->_contactID,
'trxn_id' => NULL,
'invoice_id' => $this->_invoiceID,
'contribution_status_id' => $pendingStatusID,
'is_email_receipt' => TRUE,
);
$this->_contributionID = $this->contributionCreate($params);
$this->createCustomField();
$contribution = $this->callAPISuccess('contribution', 'get', array('id' => $this->_contributionID, 'sequential' => 1));
// assert that contribution created before handling payment via paypal standard has no transaction id set and pending status
$this->assertEquals(NULL, $contribution['values'][0]['trxn_id']);
$this->assertEquals($pendingStatusID, $contribution['values'][0]['contribution_status_id']);
$this->hookClass->setHook('civicrm_postIPNProcess', array($this, 'hookCiviCRMAlterIPNData'));
global $_REQUEST;
$_REQUEST = array('q' => CRM_Utils_System::url('civicrm/payment/ipn/' . $this->_paymentProcessorID)) + $this->getPaypalTransaction();

$mut = new CiviMailUtils($this, TRUE);
$payment = CRM_Core_Payment::handlePaymentMethod('PaymentNotification', ['processor_id' => $this->_paymentProcessorID]);

$contribution = $this->callAPISuccess('contribution', 'get', array('id' => $this->_contributionID, 'sequential' => 1));
// assert that contribution is completed after getting response from paypal standard which has transaction id set and completed status
$this->assertEquals($_REQUEST['txn_id'], $contribution['values'][0]['trxn_id']);
$this->assertEquals($completedStatusID, $contribution['values'][0]['contribution_status_id']);
$this->assertEquals('test12345', $contribution['values'][0]['custom_' . $this->_customFieldID]);
}

/**
* Store Custom data passed in from the PayPalIPN in a custom field
*/
public function hookCiviCRMAlterIPNData($data) {
if (!empty($data['custom'])) {
$customData = json_decode($data['custom'], TRUE);
$customField = $this->callAPISuccess('custom_field', 'get', ['label' => 'TestCustomFieldIPNHook']);
$this->callAPISuccess('contribution', 'create', ['id' => $this->_contributionID, 'custom_' . $customField['id'] => $customData['cgid']]);
}
}

/**
* @return array
*/
protected function createCustomField() {
$customGroup = $this->customGroupCreate(array('extends' => 'Contribution'));
$fields = array(
'label' => 'TestCustomFieldIPNHook',
'data_type' => 'String',
'html_type' => 'Text',
'custom_group_id' => $customGroup['id'],
);
$field = CRM_Core_BAO_CustomField::create($fields);
$this->_customFieldID = $field->id;
return $customGroup;
}

}

0 comments on commit 2d39b9c

Please sign in to comment.