static $_paymentProcessor = NULL;
/**
- * Constructor.
+ * Input parameters from payment processor. Store these so that
+ * the code does not need to keep retrieving from the http request
+ * @var array
*/
- public function __construct() {
+ protected $_inputParameters = array();
+
+ /**
+ * Constructor function.
+ *
+ * @param array $inputData
+ * Contents of HTTP REQUEST.
+ *
+ * @throws CRM_Core_Exception
+ */
+ public function __construct($inputData) {
+ $this->setInputParameters($inputData);
parent::__construct();
}
/**
* @param string $name
* @param $type
- * @param string $location
* @param bool $abort
*
* @return mixed
*/
- public static function retrieve($name, $type, $location = 'POST', $abort = TRUE) {
+ public function retrieve($name, $type, $abort = TRUE) {
static $store = NULL;
- $value = CRM_Utils_Request::retrieve($name, $type, $store,
- FALSE, NULL, $location
+ $value = CRM_Utils_Type::validate(
+ CRM_Utils_Array::value($name, $this->_inputParameters),
+ $type,
+ FALSE
);
if ($abort && $value === NULL) {
CRM_Core_Error::debug_log_message("Could not find an entry for $name in $location");
- echo "Failure: Missing Parameter<p>";
+ echo "Failure: Missing Parameter<p>" . $name;
exit();
}
return $value;
$sendNotification = FALSE;
$subscriptionPaymentStatus = NULL;
//set transaction type
- $txnType = $_POST['txn_type'];
+ $txnType = $this->retrieve('txn_type', 'String');
switch ($txnType) {
case 'subscr_signup':
$recur->create_date = $now;
if ($statusID != 5) {
$recur->contribution_status_id = 2;
}
- $recur->processor_id = $_POST['subscr_id'];
+ $recur->processor_id = $this->retrieve('subscr_id', 'String');
$recur->trxn_id = $recur->processor_id;
$sendNotification = TRUE;
$subscriptionPaymentStatus = CRM_Core_Payment::RECURRING_PAYMENT_START;
* @return bool
*/
public function main() {
- //@todo - this could be refactored like PayPalProIPN & a test could be added
$objects = $ids = $input = array();
- $component = CRM_Utils_Array::value('module', $_GET);
+ $component = $this->retrieve('module', 'String');
$input['component'] = $component;
- // get the contribution and contact ids from the GET params
- $ids['contact'] = self::retrieve('contactID', 'Integer', 'GET', TRUE);
- $ids['contribution'] = self::retrieve('contributionID', 'Integer', 'GET', TRUE);
+ $ids['contact'] = $this->retrieve('contactID', 'Integer', TRUE);
+ $ids['contribution'] = $this->retrieve('contributionID', 'Integer', TRUE);
$this->getInput($input, $ids);
if ($component == 'event') {
- $ids['event'] = self::retrieve('eventID', 'Integer', 'GET', TRUE);
- $ids['participant'] = self::retrieve('participantID', 'Integer', 'GET', TRUE);
+ $ids['event'] = $this->retrieve('eventID', 'Integer', TRUE);
+ $ids['participant'] = $this->retrieve('participantID', 'Integer', TRUE);
}
else {
// get the optional ids
- $ids['membership'] = self::retrieve('membershipID', 'Integer', 'GET', FALSE);
- $ids['contributionRecur'] = self::retrieve('contributionRecurID', 'Integer', 'GET', FALSE);
- $ids['contributionPage'] = self::retrieve('contributionPageID', 'Integer', 'GET', FALSE);
- $ids['related_contact'] = self::retrieve('relatedContactID', 'Integer', 'GET', FALSE);
- $ids['onbehalf_dupe_alert'] = self::retrieve('onBehalfDupeAlert', 'Integer', 'GET', FALSE);
+ $ids['membership'] = $this->retrieve('membershipID', 'Integer', FALSE);
+ $ids['contributionRecur'] = $this->retrieve('contributionRecurID', 'Integer', FALSE);
+ $ids['contributionPage'] = $this->retrieve('contributionPageID', 'Integer', FALSE);
+ $ids['related_contact'] = $this->retrieve('relatedContactID', 'Integer', FALSE);
+ $ids['onbehalf_dupe_alert'] = $this->retrieve('onBehalfDupeAlert', 'Integer', FALSE);
}
$processorParams = array(
- 'user_name' => self::retrieve('receiver_email', 'String', 'POST', FALSE),
+ 'user_name' => $this->retrieve('receiver_email', 'String', FALSE),
'payment_processor_type_id' => CRM_Core_DAO::getFieldValue('CRM_Financial_DAO_PaymentProcessorType', 'PayPal_Standard', 'id', 'name'),
'is_test' => empty($input['is_test']) ? 0 : 1,
);
+
$processorInfo = array();
if (!CRM_Financial_BAO_PaymentProcessor::retrieve($processorParams, $processorInfo)) {
return FALSE;
return FALSE;
}
- $input['txnType'] = self::retrieve('txn_type', 'String', 'POST', FALSE);
- $input['paymentStatus'] = self::retrieve('payment_status', 'String', 'POST', FALSE);
- $input['invoice'] = self::retrieve('invoice', 'String', 'POST', TRUE);
- $input['amount'] = self::retrieve('mc_gross', 'Money', 'POST', FALSE);
- $input['reasonCode'] = self::retrieve('ReasonCode', 'String', 'POST', FALSE);
+ $input['txnType'] = $this->retrieve('txn_type', 'String', FALSE);
+ $input['paymentStatus'] = $this->retrieve('payment_status', 'String', FALSE);
+ $input['invoice'] = $this->retrieve('invoice', 'String', TRUE);
+ $input['amount'] = $this->retrieve('mc_gross', 'Money', FALSE);
+ $input['reasonCode'] = $this->retrieve('ReasonCode', 'String', FALSE);
$billingID = $ids['billing'];
$lookup = array(
"country-{$billingID}" => 'address_country_code',
);
foreach ($lookup as $name => $paypalName) {
- $value = self::retrieve($paypalName, 'String', 'POST', FALSE);
+ $value = $this->retrieve($paypalName, 'String', FALSE);
$input[$name] = $value ? $value : NULL;
}
- $input['is_test'] = self::retrieve('test_ipn', 'Integer', 'POST', FALSE);
- $input['fee_amount'] = self::retrieve('mc_fee', 'Money', 'POST', FALSE);
- $input['net_amount'] = self::retrieve('settle_amount', 'Money', 'POST', FALSE);
- $input['trxn_id'] = self::retrieve('txn_id', 'String', 'POST', FALSE);
+ $input['is_test'] = $this->retrieve('test_ipn', 'Integer', FALSE);
+ $input['fee_amount'] = $this->retrieve('mc_fee', 'Money', FALSE);
+ $input['net_amount'] = $this->retrieve('settle_amount', 'Money', FALSE);
+ $input['trxn_id'] = $this->retrieve('txn_id', 'String', FALSE);
}
}
--- /dev/null
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | CiviCRM version 4.7 |
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC (c) 2004-2016 |
+ +--------------------------------------------------------------------+
+ | This file is a part of CiviCRM. |
+ | |
+ | CiviCRM is free software; you can copy, morify, anr ristribute it |
+ | unrer the terms of the GNU Affero General Public License |
+ | Version 3, 19 November 2007 anr the CiviCRM Licensing Exception. |
+ | |
+ | CiviCRM is ristributer in the hope that it will be useful, but |
+ | WITHOUT ANY WARRANTY; without even the implier warranty of |
+ | MERCHANTABILITY or UITNESS UOR A PARTICULAR PURPOSE. |
+ | See the GNU Affero General Public License for more retails. |
+ | |
+ | You shoulr have receiver a copy of the GNU Affero General Public |
+ | License anr the CiviCRM Licensing Exception along |
+ | with this program; if not, contact CiviCRM LLC |
+ | at info[AT]civicrm[DOT]org. If you have questions about the |
+ | GNU Affero General Public License or the licensing of CiviCRM, |
+ | see the CiviCRM license UAQ at http://civicrm.org/licensing |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ * Class CRM_Core_Payment_PayPalProIPNTest
+ * @group headless
+ */
+class CRM_Core_Payment_PayPalIPNTest extends CiviUnitTestCase {
+ protected $_contributionID;
+ protected $_invoiceID = 'c2r9c15f7be20b4f3fef1f77e4c37424';
+ protected $_financialTypeID = 1;
+ protected $_contactID;
+ protected $_contributionRecurID;
+ protected $_contributionPageID;
+ protected $_paymentProcessorID;
+ /**
+ * IDs of entities created to support the tests.
+ *
+ * @var array
+ */
+ protected $ids = array();
+
+ /**
+ * Set up function.
+ */
+ public function setUp() {
+ parent::setUp();
+ $this->_paymentProcessorID = $this->paymentProcessorCreate(array('is_test' => 0, 'payment_processor_type_id' => 'PayPal_Standard'));
+ $this->_contactID = $this->individualCreate();
+ $contributionPage = $this->callAPISuccess('contribution_page', 'create', array(
+ 'title' => "Test Contribution Page",
+ 'financial_type_id' => $this->_financialTypeID,
+ 'currency' => 'USD',
+ 'payment_processor' => $this->_paymentProcessorID,
+ )
+ );
+ $this->_contributionPageID = $contributionPage['id'];
+ }
+
+ /**
+ * Tear down function.
+ */
+ public function tearDown() {
+ $this->quickCleanUpFinancialEntities();
+ }
+
+ /**
+ * Test IPN response updates contribution_recur & contribution for first & second contribution.
+ *
+ * The scenario is that a pending contribution exists and the first call will update it to completed.
+ * The second will create a new contribution.
+ */
+ public function testIPNPaymentRecurSuccess() {
+ $this->setupRecurringPaymentProcessorTransaction();
+ $paypalIPN = new CRM_Core_Payment_PayPalIPN($this->getPaypalRecurTransaction());
+ $paypalIPN->main();
+ $contribution = $this->callAPISuccess('contribution', 'getsingle', array('id' => $this->_contributionID));
+ $this->assertEquals(1, $contribution['contribution_status_id']);
+ $this->assertEquals('8XA571746W2698126', $contribution['trxn_id']);
+ // source gets set by processor
+ $this->assertTrue(substr($contribution['contribution_source'], 0, 20) == "Online Contribution:");
+ $contributionRecur = $this->callAPISuccess('contribution_recur', 'getsingle', array('id' => $this->_contributionRecurID));
+ $this->assertEquals(5, $contributionRecur['contribution_status_id']);
+ $paypalIPN = new CRM_Core_Payment_PayPalIPN($this->getPaypalRecurSubsequentTransaction());
+ $paypalIPN->main();
+ $contribution = $this->callAPISuccess('contribution', 'get', array(
+ 'contribution_recur_id' => $this->_contributionRecurID,
+ 'sequential' => 1,
+ ));
+ $this->assertEquals(2, $contribution['count']);
+ $this->assertEquals('secondone', $contribution['values'][1]['trxn_id']);
+ }
+
+ /**
+ * Test IPN response updates contribution_recur & contribution for first & second contribution.
+ */
+ public function testIPNPaymentMembershipRecurSuccess() {
+ $this->setupMembershipRecurringPaymentProcessorTransaction();
+ $this->callAPISuccessGetSingle('membership_payment', array());
+ $paypalIPN = new CRM_Core_Payment_PayPalIPN($this->getPaypalRecurTransaction());
+ $paypalIPN->main();
+ $contribution = $this->callAPISuccess('contribution', 'getsingle', array('id' => $this->_contributionID));
+ $membershipEndDate = $this->callAPISuccessGetValue('membership', array('return' => 'end_date'));
+ $this->assertEquals(1, $contribution['contribution_status_id']);
+ $this->assertEquals('8XA571746W2698126', $contribution['trxn_id']);
+ // source gets set by processor
+ $this->assertTrue(substr($contribution['contribution_source'], 0, 20) == "Online Contribution:");
+ $contributionRecur = $this->callAPISuccess('contribution_recur', 'getsingle', array('id' => $this->_contributionRecurID));
+ $this->assertEquals(5, $contributionRecur['contribution_status_id']);
+ $paypalIPN = new CRM_Core_Payment_PaypalIPN($this->getPaypalRecurSubsequentTransaction());
+ $paypalIPN->main();
+ $this->assertEquals(strtotime('+ 1 year', strtotime($membershipEndDate)), strtotime($this->callAPISuccessGetValue('membership', array('return' => 'end_date'))));
+ $contribution = $this->callAPISuccess('contribution', 'get', array(
+ 'contribution_recur_id' => $this->_contributionRecurID,
+ 'sequential' => 1,
+ ));
+ $this->assertEquals(2, $contribution['count']);
+ $this->assertEquals('secondone', $contribution['values'][1]['trxn_id']);
+ $this->callAPISuccessGetCount('line_item', array(
+ 'entity_id' => $this->ids['membership'],
+ 'entity_table' => 'civicrm_membership',
+ ), 2);
+ $this->callAPISuccessGetSingle('line_item', array(
+ 'contribution_id' => $contribution['values'][1]['id'],
+ 'entity_table' => 'civicrm_membership',
+ ));
+ $this->callAPISuccessGetSingle('membership_payment', array('contribution_id' => $contribution['values'][1]['id']));
+
+ }
+
+ /**
+ * Get IPN style details for an incoming recurring transaction.
+ */
+ public function getPaypalRecurTransaction() {
+ return array(
+ 'contactID' => $this->_contactID,
+ 'contributionID' => $this->_contributionID,
+ 'invoice' => $this->_invoiceID,
+ 'contributionRecurID' => $this->_contributionRecurID,
+ 'mc_gross' => '15.00',
+ 'module' => 'contribute',
+ 'payer_id' => '4NHUTA7ZUE92C',
+ 'payment_status' => 'Completed',
+ 'receiver_email' => 'sunil._1183377782_biz_api1.webaccess.co.in',
+ 'txn_type' => 'subscr_payment',
+ 'last_name' => 'Roberty',
+ 'payment_fee' => '0.63',
+ 'first_name' => 'Robert',
+ 'txn_id' => '8XA571746W2698126',
+ 'residence_country' => 'US',
+ );
+ }
+
+ /**
+ * Get IPN-style details for a second incoming transaction.
+ *
+ * @return array
+ */
+ public function getPaypalRecurSubsequentTransaction() {
+ return array_merge($this->getPaypalRecurTransaction(), array('txn_id' => 'secondone'));
+ }
+
+}