/** * This will process a report text file as taken from PayPal. This will create any applicable PaypalBatch entries * and correlate with existing CreditCardPayment records. This will *only* process "Delayed Capture" transactions, * since that's all we actually care about. This will create "as unlinked" any CreditCardPayment records that had * to be created because it didn't exist (which means that this is a bad thing -- we should not have any of those). * * If there are any issues with importing, this will throw an error * * @param string $strReportText the text of the report itself from paypal * @param integer $intEntriesModified return value of the number of entries that were modified * @param integer $intEntriesAdded return value of the number of entries that were created (hopefully shouuld be zero) * @throws QCallerException */ public static function ProcessReport($strReportText, &$intEntriesModified, &$intEntriesAdded) { $intEntriesModified = 0; $intEntriesAdded = 0; // Cleanup Linebreaks $strReportText = str_replace("\r", "\n", $strReportText); while (strpos($strReportText, "\n\n") !== false) { $strReportText = str_replace("\n\n", "\n", $strReportText); } $strReportText = trim($strReportText); // Pull out the First Line (column headers) from the rest of the report $intPosition = strpos($strReportText, "\n"); $strFirstLine = substr($strReportText, 0, $intPosition); $strReportText = substr($strReportText, $intPosition + 1); // Calculate the tokens $strTokenArray = array(); $intColumnIndex = 0; foreach (explode("\t", $strFirstLine) as $strToken) { if (array_key_exists($strToken, $strTokenArray)) { throw new QCallerException('Report has a duplicate column: ' . $strToken); } $strTokenArray[$strToken] = $intColumnIndex; $intColumnIndex++; } // Ensure that the required fields exist foreach (self::$RequiredFields as $strRequiredToken) { if (!array_key_exists($strRequiredToken, $strTokenArray)) { throw new QCallerException('Report is missing required column: ' . $strRequiredToken); } } // Go through each line of the report foreach (explode("\n", $strReportText) as $strLine) { // Break out all cells, and then create a specific Values array that contain only the cells we care about, indexed by name $strCellArray = explode("\t", $strLine); $strValuesArray = array(); foreach (self::$RequiredFields as $strRequiredToken) { $strValuesArray[$strRequiredToken] = $strCellArray[$strTokenArray[$strRequiredToken]]; } // Only process "Delayed Capture" if ($strValuesArray['Type'] == 'Delayed Capture') { if (strlen(trim($strValuesArray[self::PayPalOriginalTransactionId])) == 0) { throw new QCallerException('Transaction ' . $strValuesArray[self::PayPalTransactionId] . ' does not have an Original Transaction Id'); } // Can we find the linked CCPayment Record? $objCreditCardPayment = CreditCardPayment::LoadByTransactionCode($strValuesArray[self::PayPalOriginalTransactionId]); if (!$objCreditCardPayment) { // No -- let's create this as an UNLINKED one $intEntriesAdded++; $objCreditCardPayment = new CreditCardPayment(); $objCreditCardPayment->TransactionCode = $strValuesArray[self::PayPalOriginalTransactionId]; $objCreditCardPayment->CreditCardStatusTypeId = CreditCardStatusType::Captured; $objCreditCardPayment->CreditCardLastFour = substr($strValuesArray[self::PayPalAccountNumber], strlen($strValuesArray[self::PayPalAccountNumber]) - 4); foreach (CreditCardType::$NameArray as $intId => $strName) { if (strtolower($strName) == strtolower($strValuesArray[self::PayPalTenderType])) { $objCreditCardPayment->CreditCardTypeId = $intId; } } if (!$objCreditCardPayment->CreditCardTypeId) { throw new QCallerException('Unlinked transaction contains an unknown credit card type: ' . $strValuesArray[self::PayPalTenderType]); } $objCreditCardPayment->UnlinkedFlag = true; // Setup Fields $objCreditCardPayment->AuthorizationCode = $strValuesArray[self::PayPalAuthCode]; $objCreditCardPayment->AmountCharged = $strValuesArray[self::PayPalAmount]; } // Link in the Pay Pal Batch Info (if applicable) // if (SERVER_INSTANCE == 'dev') $strValuesArray[self::PayPalBatchId] = 789; if ($intBatchNumber = trim($strValuesArray[self::PayPalBatchId])) { $objPayPalBatch = PaypalBatch::LoadByNumber($intBatchNumber); if (!$objPayPalBatch) { $objPayPalBatch = new PaypalBatch(); $objPayPalBatch->Number = $intBatchNumber; $objPayPalBatch->DateReceived = new QDateTime($strValuesArray[self::PayPalSettledDate]); $objPayPalBatch->ReconciledFlag = false; $objPayPalBatch->Save(); } if ($objCreditCardPayment->PaypalBatchId != $objPayPalBatch->Id) { $objCreditCardPayment->PaypalBatch = $objPayPalBatch; if ($objCreditCardPayment->Id) { $intEntriesModified++; } } } else { if (!is_null($objCreditCardPayment->PaypalBatchId)) { $objCreditCardPayment->PaypalBatch = null; if ($objCreditCardPayment->Id) { $intEntriesModified++; } } } // TODO: How do we account fo a "reconciled" PayPal batch that unexpectatly received another transaction not previously accounted for? // Check Fields to ensure match if ($objCreditCardPayment->AuthorizationCode != $strValuesArray[self::PayPalAuthCode]) { throw new QCallerException(sprintf('Mismatch AuthCode for Transaction %s: %s vs. %s', $strValuesArray[self::PayPalOriginalTransactionId], $strValuesArray[self::PayPalAuthCode], $objCreditCardPayment->AuthorizationCode)); } if ($objCreditCardPayment->AmountCharged != str_replace(',', '', $strValuesArray[self::PayPalAmount])) { throw new QCallerException(sprintf('Mismatch Amount for Transaction %s: %s vs. %s', $strValuesArray[self::PayPalOriginalTransactionId], $strValuesArray[self::PayPalAmount], $objCreditCardPayment->AmountCharged)); } // Update Fields $objCreditCardPayment->DateCaptured = new QDateTime($strValuesArray[self::PayPalTime]); $objCreditCardPayment->Save(); } } }