From f468dd66dfcdb81349a7ee4860239446d7ea5e57 Mon Sep 17 00:00:00 2001 From: morbiD Date: Wed, 21 Oct 2015 16:21:26 +0100 Subject: [PATCH] Issue #2550693 by morbiD: Fixed duplicate txn_id's failing to validate/process. --- paypal_payment_ipn/includes/PayPalPaymentIPN.inc | 7 ++++ .../includes/PayPalPaymentIPNController.inc | 31 +++++++++++--- paypal_payment_ipn/paypal_payment_ipn.install | 47 +++++++++++++++++++++- .../tests/PayPalPaymentIPNControllerTest.test | 4 +- .../tests/PayPalPaymentIPNListener.test | 2 +- 5 files changed, 82 insertions(+), 9 deletions(-) diff --git a/paypal_payment_ipn/includes/PayPalPaymentIPN.inc b/paypal_payment_ipn/includes/PayPalPaymentIPN.inc index 2d66eed..a443729 100755 --- a/paypal_payment_ipn/includes/PayPalPaymentIPN.inc +++ b/paypal_payment_ipn/includes/PayPalPaymentIPN.inc @@ -30,6 +30,13 @@ class PayPalPaymentIPN { public $txn_id = ''; /** + * The PayPal payment status. + * + * @var string + */ + public $payment_status = ''; + + /** * Constructs a new instance. */ public function __construct(array $properties = array()) { diff --git a/paypal_payment_ipn/includes/PayPalPaymentIPNController.inc b/paypal_payment_ipn/includes/PayPalPaymentIPNController.inc index 41bcb2c..8cc31e4 100755 --- a/paypal_payment_ipn/includes/PayPalPaymentIPNController.inc +++ b/paypal_payment_ipn/includes/PayPalPaymentIPNController.inc @@ -26,13 +26,19 @@ abstract class PayPalPaymentIPNController { * * @param integer $txn_id * The PayPal transaction ID of the IPN to load. + * @param string $payment_status + * The PayPal payment status of the IPN to load. + * @param string $pending_reason + * The PayPal pending reason of the IPN to load. * * @return PayPalPaymentIPN|false */ - public static function load($txn_id) { + public static function load($txn_id, $payment_status, $pending_reason = '') { $ipn_data = db_select('paypal_payment_ipn', 'mpi') ->fields('mpi') ->condition('txn_id', $txn_id) + ->condition('payment_status', $payment_status) + ->condition('pending_reason', $pending_reason) ->execute() ->fetchAssoc(); @@ -52,10 +58,21 @@ abstract class PayPalPaymentIPNController { */ public static function save(PayPalPaymentIPN $ipn) { $fields = array_intersect_key(get_object_vars($ipn), get_class_vars(get_class($ipn))); + $fields['pending_reason'] = ''; + + $key = array( + 'txn_id' => $ipn->txn_id, + 'payment_status' => $ipn->payment_status, + 'pending_reason' => '', + ); + + if (isset($ipn->pending_reason)) { + $fields['pending_reason'] = $ipn->pending_reason; + $key['pending_reason'] = $ipn->pending_reason; + } + $merge_status = db_merge('paypal_payment_ipn') - ->key(array( - 'txn_id' => $ipn->txn_id, - )) + ->key($key) ->fields($fields) ->execute(); @@ -138,7 +155,11 @@ abstract class PayPalPaymentIPNController { public static function validate(array $ipn_variables) { if (isset($ipn_variables['txn_id'])) { // Make sure this IPN was not processed before. - $ipn = PayPalPaymentIPNController::load($ipn_variables['txn_id']); + $ipn = PayPalPaymentIPNController::load( + $ipn_variables['txn_id'], + $ipn_variables['payment_status'], + isset($ipn_variables['pending_reason']) ? $ipn_variables['pending_reason'] : '' + ); if (!$ipn) { // Check if the IPN matches a Payment. if (isset($ipn_variables['invoice'])) { diff --git a/paypal_payment_ipn/paypal_payment_ipn.install b/paypal_payment_ipn/paypal_payment_ipn.install index b824328..fceb169 100755 --- a/paypal_payment_ipn/paypal_payment_ipn.install +++ b/paypal_payment_ipn/paypal_payment_ipn.install @@ -28,8 +28,22 @@ function paypal_payment_ipn_schema() { 'not null' => TRUE, 'default' => '', ), + 'payment_status' => array( + 'description' => 'The status of the transaction.', + 'type' => 'varchar', + 'length' => 17, + 'not null' => TRUE, + 'default' => '', + ), + 'pending_reason' => array( + 'description' => 'The reason for a status of Pending.', + 'type' => 'varchar', + 'length' => 17, + 'not null' => TRUE, + 'default' => '', + ), ), - 'primary key' => array('txn_id'), + 'primary key' => array('txn_id', 'payment_status', 'pending_reason'), 'foreign keys' => array( 'pid' => array( 'table' => 'payment', @@ -40,3 +54,34 @@ function paypal_payment_ipn_schema() { return $schema; } + +/** + * Add payment_status and pending_reason columns to {paypal_payment_ipn} table. + */ +function paypal_payment_ipn_update_7101() { + $new_fields = array( + 'payment_status' => array( + 'description' => 'The status of the transaction.', + 'type' => 'varchar', + 'length' => 17, + 'not null' => TRUE, + 'default' => '', + ), + 'pending_reason' => array( + 'description' => 'The reason when the status is Pending.', + 'type' => 'varchar', + 'length' => 17, + 'not null' => TRUE, + 'default' => '', + ), + ); + + db_drop_primary_key('paypal_payment_ipn'); + db_add_field('paypal_payment_ipn', 'payment_status', $new_fields['payment_status']); + db_add_field('paypal_payment_ipn', 'pending_reason', $new_fields['pending_reason']); + db_add_primary_key('paypal_payment_ipn', array( + 'txn_id', + 'payment_status', + 'pending_reason', + )); +} diff --git a/paypal_payment_ipn/tests/PayPalPaymentIPNControllerTest.test b/paypal_payment_ipn/tests/PayPalPaymentIPNControllerTest.test index 3503720..733131f 100755 --- a/paypal_payment_ipn/tests/PayPalPaymentIPNControllerTest.test +++ b/paypal_payment_ipn/tests/PayPalPaymentIPNControllerTest.test @@ -55,9 +55,9 @@ class PayPalPaymentIPNControllerTest extends PayPalPaymentIPNWebTestCase { public function testLoad() { $ipn = new PayPalPaymentIPN(); PayPalPaymentIPNController::save($ipn); - $ipn_loaded = PayPalPaymentIPNController::load($ipn->txn_id); + $ipn_loaded = PayPalPaymentIPNController::load($ipn->txn_id, $ipn->payment_status); $this->assertTrue($ipn_loaded instanceof PayPalPaymentIPN, 'An IPN is loaded correctly.'); - $non_existing_ipn_loaded = PayPalPaymentIPNController::load(999); + $non_existing_ipn_loaded = PayPalPaymentIPNController::load(999, 'Completed'); $this->assertFalse($non_existing_ipn_loaded, 'A non-existing IPN is not loaded.'); } diff --git a/paypal_payment_ipn/tests/PayPalPaymentIPNListener.test b/paypal_payment_ipn/tests/PayPalPaymentIPNListener.test index 9aa00b0..020c613 100755 --- a/paypal_payment_ipn/tests/PayPalPaymentIPNListener.test +++ b/paypal_payment_ipn/tests/PayPalPaymentIPNListener.test @@ -53,7 +53,7 @@ class PayPalPaymentIPNListener extends PayPalPaymentIPNWebTestCase { $this->assertResponse(200); // Test that an IPN has been saved. - $this->assertTrue(PayPalPaymentIPNController::load($ipn_variables['txn_id'])); + $this->assertTrue(PayPalPaymentIPNController::load($ipn_variables['txn_id'], $ipn_variables['payment_status'])); $payment = entity_load_single('payment', $payment->pid); $this->assertNotEqual($original_payment_status, $payment->getStatus()->status); } -- 2.6.1.windows.1