Fix issues cancelling paypal express subscriptions
authoreileen <emcnaughton@wikimedia.org>
Mon, 7 May 2018 04:30:51 +0000 (16:30 +1200)
committereileen <emcnaughton@wikimedia.org>
Sun, 27 May 2018 22:01:56 +0000 (10:01 +1200)
CRM/Core/Payment/PayPalImpl.php
CRM/Core/Payment/PayPalProIPN.php
tests/phpunit/CRM/Core/Payment/PayPalProIPNTest.php

index 642ee302ea06162451ecf475862e9e3ad4f0b5d7..8ab1bbec3a72056eb575540aabfd24a98813578a 100644 (file)
@@ -455,8 +455,7 @@ class CRM_Core_Payment_PayPalImpl extends CRM_Core_Payment {
      * 'ack' => 'Success',
      * 'version' => '56.0',
      * 'build' => '39949200',)
-    */
-
+     */
     $params['trxn_id'] = $result['profileid'];
     $params['payment_status_id'] = CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution', 'contribution_status_id', 'Pending');
 
index d230762395c7a94f7a6e26c8c746dc89276c31ae..6a4fe29e2984c1650c9422153b5de14283e2e62e 100644 (file)
@@ -542,6 +542,7 @@ INNER JOIN civicrm_membership_payment mp ON m.id = mp.membership_id AND mp.contr
 
   /**
    * Handle payment express IPNs.
+   *
    * For one off IPNS no actual response is required
    * Recurring is more difficult as we have limited confirmation material
    * lets look up invoice id in recur_contribution & rely on the unique transaction id to ensure no
@@ -560,23 +561,25 @@ INNER JOIN civicrm_membership_payment mp ON m.id = mp.membership_id AND mp.contr
     // as membership id etc can be derived by the load objects fn
     $objects = $ids = $input = array();
     $isFirst = FALSE;
+    $input['invoice'] = self::getValue('i', FALSE);
     $input['txnType'] = $this->retrieve('txn_type', 'String');
-    if ($input['txnType'] != 'recurring_payment') {
+    $contributionRecur = civicrm_api3('contribution_recur', 'getsingle', array(
+      'return' => 'contact_id, id, payment_processor_id',
+      'invoice_id' => $input['invoice'],
+    ));
+
+    if ($input['txnType'] !== 'recurring_payment' && $input['txnType'] !== 'recurring_payment_profile_created') {
       throw new CRM_Core_Exception('Paypal IPNS not handled other than recurring_payments');
     }
-    $input['invoice'] = self::getValue('i', FALSE);
+
     $this->getInput($input, $ids);
-    if ($this->transactionExists($input['trxn_id'])) {
+    if ($input['txnType'] === 'recurring_payment' && $this->transactionExists($input['trxn_id'])) {
       throw new CRM_Core_Exception('This transaction has already been processed');
     }
 
-    $contributionRecur = civicrm_api3('contribution_recur', 'getsingle', array(
-        'return' => 'contact_id, id',
-        'invoice_id' => $input['invoice'],
-      ));
     $ids['contact'] = $contributionRecur['contact_id'];
     $ids['contributionRecur'] = $contributionRecur['id'];
-    $result = civicrm_api3('contribution', 'getsingle', array('invoice_id' => $input['invoice']));
+    $result = civicrm_api3('contribution', 'getsingle', ['invoice_id' => $input['invoice'], 'contribution_test' => '']);
 
     $ids['contribution'] = $result['id'];
     //@todo hard - coding 'pending' for now
@@ -595,12 +598,12 @@ INNER JOIN civicrm_membership_payment mp ON m.id = mp.membership_id AND mp.contr
     // membership would be an easy add - but not relevant to my customer...
     $this->_component = $input['component'] = 'contribute';
     $input['trxn_date'] = date('Y-m-d-H-i-s', strtotime(self::retrieve('time_created', 'String')));
-    $paymentProcessorID = self::getPayPalPaymentProcessorID();
+    $paymentProcessorID = $contributionRecur['payment_processor_id'];
 
     if (!$this->validateData($input, $ids, $objects, TRUE, $paymentProcessorID)) {
       throw new CRM_Core_Exception('Data did not validate');
     }
-    return $this->recur($input, $ids, $objects, $isFirst);
+    $this->recur($input, $ids, $objects, $isFirst);
   }
 
   /**
index 5b379d2d4fab1244cf698e898e35659d9bec9fa1..8072fc9bfb8e749c9cef7ae2f02981566f54dfea 100644 (file)
@@ -285,7 +285,7 @@ class CRM_Core_Payment_PayPalProIPNTest extends CiviUnitTestCase {
       'amount_per_cycle' => '5.00',
       'payer_status' => 'unverified',
       'currency_code' => 'USD',
-      'business' => 'mpa@mainepeoplesalliance.org',
+      'business' => 'mpa@example.com',
       'address_country' => 'UNITED STATES',
       'address_city' => 'Limestone',
       'verify_sign' => 'AXi4DULbes8quzIiq2YNsdTJH5ciPPPzG9PcQvkQg4BjfvWi8aY9GgDb',
@@ -297,7 +297,7 @@ class CRM_Core_Payment_PayPalProIPNTest extends CiviUnitTestCase {
       'payment_type' => 'instant',
       'last_name' => 'Morrissette',
       'address_state' => 'ME',
-      'receiver_email' => 'info@civicrm.org',
+      'receiver_email' => 'info@example.com',
       'payment_fee' => '0.41',
       'receiver_id' => 'GTH8P7UQWWTY6',
       'txn_type' => 'recurring_payment',
@@ -371,4 +371,58 @@ class CRM_Core_Payment_PayPalProIPNTest extends CiviUnitTestCase {
     return array_merge($this->getPaypalProRecurTransaction(), array('txn_id' => 'secondone'));
   }
 
+  /**
+   * Test IPN response update for a paypal express profile creation confirmation.
+   */
+  public function testIPNPaymentExpressRecurSuccess() {
+    $this->setupRecurringPaymentProcessorTransaction(['processor_id' => '']);
+    $paypalIPN = new CRM_Core_Payment_PayPalProIPN($this->getPaypalExpressRecurSubscriptionConfirmation());
+    $paypalIPN->main();
+    $contributionRecur = $this->callAPISuccess('contribution_recur', 'getsingle', array('id' => $this->_contributionRecurID));
+    $this->assertEquals('I-JW77S1PY2032', $contributionRecur['processor_id']);
+  }
+
+  /**
+   * Get response consistent with creating a new profile.
+   *
+   * @return array
+   */
+  public function getPaypalExpressRecurSubscriptionConfirmation() {
+    return [
+      'payment_cycle' => 'Monthly',
+      'txn_type' => 'recurring_payment_profile_created',
+      'last_name' => 'buyer',
+      'next_payment_date' => '03:00:00 May 09, 2018 PDT',
+      'residence_country' => 'GB',
+      'initial_payment_amount' => '0.00',
+      'rp_invoice_id' => 'i=' . $this->_invoiceID
+        . '&m=&c=' . $this->_contributionID
+        . '&r=' . $this->_contributionRecurID
+        . '&b=' . $this->_contactID
+        . '&p=' . $this->_contributionPageID,
+      'currency_code' => 'GBP',
+      'time_created' => '12:39:01 May 09, 2018 PDT',
+      'verify_sign' => 'AUg223oCjn4HgJXKkrICawXQ3fyUA2gAd1.f1IPJ4r.9sln-nWcB-EJG',
+      'period_type' => 'Regular',
+      'payer_status' => 'verified',
+      'test_ipn' => '1',
+      'tax' => '0.00',
+      'payer_email' => 'payer@example.com',
+      'first_name' => 'test',
+      'receiver_email' => 'shop@example.com',
+      'payer_id' => 'BWXXXM8111HDS',
+      'product_type' => 1,
+      'shipping' => '0.00',
+      'amount_per_cycle' => '6.00',
+      'profile_status' => 'Active',
+      'charset' => 'windows-1252',
+      'notify_version' => '3.9',
+      'amount' => '6.00',
+      'outstanding_balance' => '0.00',
+      'recurring_payment_id' => 'I-JW77S1PY2032',
+      'product_name' => '6 Per 1 month',
+      'ipn_track_id' => '6255554274055',
+    ];
+  }
+
 }