dev/financial#33 Add in Hook alterIPNData to allow Extensions to do custom processing...
authorSeamus Lee <seamuslee001@gmail.com>
Fri, 12 Oct 2018 00:25:47 +0000 (11:25 +1100)
committerSeamus Lee <seamuslee001@gmail.com>
Thu, 18 Oct 2018 20:40:50 +0000 (07:40 +1100)
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

CRM/Core/Payment.php
CRM/Utils/Hook.php
tests/phpunit/CRM/Core/Payment/PayPalIPNTest.php

index 63ce4d9b5a3149d9ffc0e45c2b9fdc5399ac6be2..cf31189d384d2492b47e01fc256940dfa0a109f0 100644 (file)
@@ -1393,6 +1393,9 @@ abstract class CRM_Core_Payment {
       $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.";
index 92989a902534cb7c4a605b9482377d6cb7a95cf8..e00b3f2f93e1ead053d004f412bb99fc3086506b 100644 (file)
@@ -2473,4 +2473,17 @@ abstract class CRM_Utils_Hook {
     );
   }
 
+  /**
+   * 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'
+    );
+  }
+
 }
index 9de32d4ea3a8bc052cc5f26a9f99f10d5e69fd09..c9862c05ec5e3920e3e07e880fe587c4f0ed4839 100644 (file)
@@ -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.
    *
@@ -237,6 +238,7 @@ class CRM_Core_Payment_PayPalIPNTest extends CiviUnitTestCase {
       'first_name' => 'Robert',
       'txn_id' => '8XA571746W2698126',
       'residence_country' => 'US',
+      'custom' => json_encode(['cgid' => 'test12345']),
     );
   }
 
@@ -249,4 +251,69 @@ class CRM_Core_Payment_PayPalIPNTest extends CiviUnitTestCase {
     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;
+  }
+
 }