/** * Private utility function for function updatePaymentStatus(): Stores only once the completed payment for a given transaction id of a $notification * * @param cbpaidPaymentBasket $paymentBasket * @param cbpaidPaymentNotification $notification notification object of the payment * @param int $now * @param boolean|string $txnIdMultiplePaymentDates FALSE: unique txn_id for each payment, TRUE: same txn_id can have multiple payment dates, additionally: 'singlepayment' will not look at txn_id at all * @param string $updateMessage Message for the history log for the payment when storing to database * @return boolean TRUE: This is really a new payment, FALSE: no, we already received and recorded it (e.g. IPN before PDT) */ private function _storePaymentOnce(&$paymentBasket, &$notification, $now, $txnIdMultiplePaymentDates, $updateMessage) { global $_CB_database; $payment = new cbpaidPayment($_CB_database); $whereArray = array('payment_basket_id' => (int) $paymentBasket->id, 'mc_gross' => (string) $notification->mc_gross); if ($notification->txn_id !== null && $txnIdMultiplePaymentDates !== 'singlepayment') { $whereArray['txn_id'] = (string) $notification->txn_id; } if ($txnIdMultiplePaymentDates === true) { $whereArray['payment_date'] = (string) $notification->payment_date; } $entry_exists = $payment->loadThisMatching($whereArray, array('id' => 'ASC')); $iAmReferencePayment = !$entry_exists || !in_array($payment->payment_status, array('Completed', 'Processed')); if ($iAmReferencePayment) { // now here we could be at this same place with 2 processes ! if IPN and PDT happen exactly same time (it happens!) if ($notification->payment_date) { $paymentDate = Application::Database()->getUtcDateTime(cbpaidTimes::getInstance()->gmStrToTime($notification->payment_date)); } else { $paymentDate = $paymentBasket->time_completed; } $payment->bindPayment($paymentBasket, $notification, $paymentDate, $now); $payment->historySetMessage($updateMessage); $payment->store(); } if (!$entry_exists && $iAmReferencePayment) { // we had to insert a new payment entry: check that it's not duplicate due to simultaneous asynchronous IPN with PDT $allPayments = $payment->loadThisMatchingList($whereArray); if (count($allPayments) !== 1) { if ($payment->id != min(array_keys($allPayments))) { // oops! it really happened: return that we are not the one which should change anything, and delete our duplicate record: $iAmReferencePayment = false; $payment->delete(); } // it happened, but we are the wining payment... } } return $iAmReferencePayment; }