/** * Prepare subtotal and line-item detail content to send to PayPal */ function ipn_getLineItemDetails() { error_reporting(E_ALL); global $order, $currencies, $order_totals, $order_total_modules; $optionsST = array(); $optionsLI = array(); $onetimeSum = 0; $onetimeTax = 0; $creditsApplied = 0; $creditsTax_applied = 0; $sumOfLineItems = 0; $sumOfLineTax = 0; // prepare subtotals $optionsST['handling'] = 0; for ($i = 0, $n = sizeof($order_totals); $i < $n; $i++) { if ($order_totals[$i]['code'] == 'ot_subtotal') { $optionsST['subtotal'] = round($order_totals[$i]['value'], 2); } if ($order_totals[$i]['code'] == 'ot_tax') { $optionsST['tax_cart'] = round($order_totals[$i]['value'], 2); } if ($order_totals[$i]['code'] == 'ot_shipping') { $optionsST['shipping'] = round($order_totals[$i]['value'], 2); } if ($order_totals[$i]['code'] == 'ot_total') { $optionsST['amount'] = round($order_totals[$i]['value'], 2); } global ${$order_totals[$i]['code']}; if (isset(${$order_totals[$i]['code']}->credit_class) && ${$order_totals[$i]['code']}->credit_class == true) { $creditsApplied += round($order_totals[$i]['value'], 2); } // treat all other OT's as if they're related to handling fees if (!in_array($order_totals[$i]['code'], array('ot_total', 'ot_subtotal', 'ot_tax', 'ot_shipping')) && !(isset(${$order_totals[$i]['code']}->credit_class) && ${$order_totals[$i]['code']}->credit_class == true)) { $optionsST['handling'] += $order_totals[$i]['value']; } } // Move shipping tax amount from Tax subtotal into Shipping subtotal for submission to PayPal $module = substr($_SESSION['shipping']['id'], 0, strpos($_SESSION['shipping']['id'], '_')); if (zen_not_null($order->info['shipping_method'])) { if ($GLOBALS[$module]->tax_class > 0) { $shipping_tax_basis = !isset($GLOBALS[$module]->tax_basis) ? STORE_SHIPPING_TAX_BASIS : $GLOBALS[$module]->tax_basis; $shippingOnBilling = zen_get_tax_rate($GLOBALS[$module]->tax_class, $order->billing['country']['id'], $order->billing['zone_id']); $shippingOnDelivery = zen_get_tax_rate($GLOBALS[$module]->tax_class, $order->delivery['country']['id'], $order->delivery['zone_id']); if ($shipping_tax_basis == 'Billing') { $shipping_tax = $shippingOnBilling; } elseif ($shipping_tax_basis == 'Shipping') { $shipping_tax = $shippingOnDelivery; } else { if (STORE_ZONE == $order->billing['zone_id']) { $shipping_tax = $shippingOnBilling; } elseif (STORE_ZONE == $order->delivery['zone_id']) { $shipping_tax = $shippingOnDelivery; } else { $shipping_tax = 0; } } $taxAdjustmentForShipping = zen_calculate_tax($order->info['shipping_cost'], $shipping_tax); $optionsST['shipping'] += $taxAdjustmentForShipping; $optionsST['tax_cart'] -= $taxAdjustmentForShipping; } } ipn_logging('DEBUG Round 1', 'Subtotal Info:' . "\n" . print_r($optionsST, true)); // loop thru all products to display quantity and price. Appends *** if out-of-stock. for ($i = 0, $n = sizeof($order->products), $k = 1; $i < $n; $i++, $k++) { $optionsLI["item_number_{$k}"] = $order->products[$i]['model']; $optionsLI["quantity_{$k}"] = (int) $order->products[$i]['qty']; $optionsLI["item_name_{$k}"] = $order->products[$i]['name']; $optionsLI["item_name_{$k}"] .= zen_get_products_stock($order->products[$i]['id']) - $order->products[$i]['qty'] < 0 ? STOCK_MARK_PRODUCT_OUT_OF_STOCK : ''; // if there are attributes, loop thru them and add to description if (isset($order->products[$i]['attributes']) && sizeof($order->products[$i]['attributes']) > 0) { for ($j = 0, $n2 = sizeof($order->products[$i]['attributes']); $j < $n2; $j++) { $optionsLI["item_name_{$k}"] .= "\n " . $order->products[$i]['attributes'][$j]['option'] . ': ' . $order->products[$i]['attributes'][$j]['value']; } // end loop } // endif attribute-info $optionsLI["amount_{$k}"] = $order->products[$i]['final_price']; $optionsLI["tax_{$k}"] = zen_calculate_tax($order->products[$i]['final_price'], $order->products[$i]['tax']); $optionsLI["shipping_{$k}"] = 0; // track one-time charges if ($order->products[$i]['onetime_charges'] != 0) { $onetimeSum += $order->products[$i]['onetime_charges']; $onetimeTax += zen_calculate_tax($order->products[$i]['onetime_charges'], $order->products[$i]['tax']); } // Replace & and = with * if found. $optionsLI["item_name_{$k}"] = str_replace(array('&', '='), '*', $optionsLI["item_name_{$k}"]); $optionsLI["item_name_{$k}"] = zen_clean_html($optionsLI["item_name_{$k}"], 'strong'); $optionsLI["item_name_{$k}"] = substr($optionsLI["item_name_{$k}"], 0, 127); // reformat properly $optionsLI["item_number_{$k}"] = substr($optionsLI["item_number_{$k}"], 0, 127); } // end for loopthru all products if ($onetimeSum > 0) { $i++; $k++; $optionsLI["item_number_{$k}"] = $k; $optionsLI["item_name_{$k}"] = 'One-Time Charges'; $optionsLI["amount_{$k}"] = $onetimeSum; $optionsLI["tax_{$k}"] = $onetimeTax; $optionsLI["quantity_{$k}"] = 1; } ipn_logging('DEBUG Round 2', 'Line Item Info:' . "\n" . print_r($optionsLI, true)); // handle discounts such as gift certificates and coupons if ($creditsApplied > 0) { $optionsST['handling'] -= $creditsApplied; } // add all one-time charges $optionsST['subtotal'] += $onetimeSum; //ensure things are not negative and not carrying any bad precision $optionsST['handling'] = abs(strval($optionsST['handling'])); // subtotals have to add up to AMT // Thus, if there is a discrepancy, make adjustment to HANDLINGAMT: $st = $optionsST['subtotal'] + $optionsST['tax_cart'] + $optionsST['shipping'] + $optionsST['handling']; if ($st != $optionsST['amount']) { $optionsST['handling'] += $optionsST['amount'] - $st; } // Since the PayPal spec cannot handle mathematically mismatched values caused by one-time charges, // must drop line-item details if any one-time charges apply to this order: // And, if there are any discounts in this order, do NOT supply line-item details if ($onetimeSum > 0) { $optionsLI = array(); } // Do sanity check -- if any of the line-item subtotal math doesn't add up properly, skip line-item details, // so that the order can go through even though PayPal isn't being flexible to handle Zen Cart's diversity $optionsLI['num_cart_items'] = 0; for ($j = 1; $j < $k; $j++) { $itemAMT = $optionsLI["amount_{$j}"]; $itemTAX = $optionsLI["tax_{$j}"]; $itemQTY = $optionsLI["quantity_{$j}"]; $sumOfLineItems += $itemQTY * $itemAMT; $sumOfLineTax += round($itemQTY * $itemTAX, 2); $optionsLI['num_cart_items']++; } if ((double) strval($optionsST['subtotal']) != (double) strval($sumOfLineItems)) { $optionsLI = array(); ipn_logging('getLineItemDetails 1: Order Subtotal does not match sum of line-item prices. Line-item-details skipped.' . "\n" . (double) $optionsST['subtotal'] . ' ' . (double) $sumOfLineItems); //die('ITEMAMT != $sumOfLineItems ' . $optionsST['ITEMAMT'] . ' ' . $sumOfLineItems); } if ((double) strval($optionsST['tax_cart']) != (double) strval($sumOfLineTax)) { for ($i = 0, $n = sizeof($order->products), $k = 1; $i < $n; $i++, $k++) { $optionsLI["tax_{$k}"] = 0; } $optionsLI["tax_1"] = $optionsST["tax_cart"]; ipn_logging('getLineItemDetails 2: Tax Subtotal did not match sum of taxes for line-items. Line-item taxes skipped.' . "\n" . $optionsST['tax_cart'] . ' ' . $sumOfLineTax); //die('TAXAMT != $sumofLineTax ' . $optionsST['TAXAMT'] . ' ' . $sumOfLineTax); } // ensure all numbers are non-negative // tidy up all values so that they comply with proper format (number_format(xxxx,2) for PayPal US use ) if (is_array($optionsST)) { foreach ($optionsST as $key => $value) { $optionsST[$key] = number_format(abs(strval($value)), 2); } } if (is_array($optionsLI)) { foreach ($optionsLI as $key => $value) { if (strstr($key, 'amount')) { $optionsLI[$key] = number_format(abs(strval($value)), 2); } } } ipn_logging('getLineItemDetails 3: IPN LineItemDetails: ' . "\n" . ($creditsApplied ? 'Credits apply to this order, so all line-item details are NOT being submitted. Thus, the following data is REDUNDANT and probably VERY INACCURATE' . "\n" : '') . 'Details:' . print_r(array_merge($optionsST, $optionsLI), true) . "\n\n" . 'DEFAULT_CURRENCY = ' . DEFAULT_CURRENCY . "\nSESSION['currency'] = " . $_SESSION['currency'] . "\n" . "order->info['currency'] = " . $order->info['currency'] . "\n\$currencies->currencies[\$_SESSION['currency']]['value'] = " . $currencies->currencies[$_SESSION['currency']]['value'] . "\n" . print_r($currencies, true)); // if not default currency, do not send subtotals or line-item details if (DEFAULT_CURRENCY != $order->info['currency']) { ipn_logging('getLineItemDetails 4: Not using default currency. Thus, no line-item details can be submitted.'); return array(); } if ($currencies->currencies[$_SESSION['currency']]['value'] != 1) { ipn_logging('getLineItemDetails 5: currency val not equal to 1.0000 - cannot proceed without coping with currency conversions. Aborting line-item details.'); return array(); } // if there are any discounts in this order, do not supply subtotals or line-item details if (strval($creditsApplied) > 0) { return array(); } //$this->zcLog('getLineItemDetails 6', 'no credits - okay'); // if subtotals are not adding up correctly, then skip sending any line-item or subtotal details to PayPal $st = round(strval($optionsST['subtotal'] + $optionsST['tax_cart'] + $optionsST['shipping'] + $optionsST['handling']), 2); $stDiff = strval($optionsST['amount'] - $st); $stDiffRounded = strval(abs($st) - abs(round($optionsST['amount'], 2))); ipn_logging('getLineItemDetails 7: checking subtotals... ' . "\nitemamt: " . $optionsST['subtotal'] . "\ntaxamt: " . $optionsST['tax_cart'] . "\nshippingamt: " . $optionsST['shipping'] . "\nhandlingamt: " . $optionsST['handling'] . "\n-------------------\nsubtotal: " . number_format($st, 2) . "\namount: " . $optionsST['amount'] . "\n-------------------\ndifference: " . $stDiff . ' (abs+rounded: ' . $stDiffRounded . ')'); if ($stDiffRounded != 0) { return array(); } //die('bad subtotals'); //return array(); ipn_logging('getLineItemDetails 8: subtotals balance - okay'); if (abs($optionsST['handling']) == 0) { unset($optionsST['handling']); } // Send Subtotal and LineItem results back to be submitted to PayPal return array_merge($optionsST, $optionsLI); }
/** * Prepare subtotal and line-item detail content to send to PayPal */ function ipn_getLineItemDetails($restrictedCurrency) { global $order, $currencies, $order_totals, $order_total_modules; // if not default currency, do not send subtotals or line-item details if (DEFAULT_CURRENCY != $order->info['currency'] || $restrictedCurrency != DEFAULT_CURRENCY) { ipn_logging('getLineItemDetails 1', 'Not using default currency. Thus, no line-item details can be submitted.'); return array(); } if ($currencies->currencies[$_SESSION['currency']]['value'] != 1 || $currencies->currencies[$order->info['currency']]['value'] != 1) { ipn_logging('getLineItemDetails 2', 'currency val not equal to 1.0000 - cannot proceed without coping with currency conversions. Aborting line-item details.'); return array(); } $optionsST = array(); $optionsLI = array(); $optionsNB = array(); $numberOfLineItemsProcessed = 0; $creditsApplied = 0; $surcharges = 0; $sumOfLineItems = 0; $sumOfLineTax = 0; $optionsST['amount'] = 0; $optionsST['subtotal'] = 0; $optionsST['tax_cart'] = 0; $optionsST['shipping'] = 0; $flagSubtotalsUnknownYet = true; $subTotalLI = 0; $subTotalTax = 0; $subTotalShipping = 0; $subtotalPRE = array('no data'); $discountProblemsFlag = FALSE; $flag_treat_as_partial = FALSE; if (sizeof($order_totals)) { // prepare subtotals for ($i = 0, $n = sizeof($order_totals); $i < $n; $i++) { if ($order_totals[$i]['code'] == '') { continue; } if (in_array($order_totals[$i]['code'], array('ot_total', 'ot_subtotal', 'ot_tax', 'ot_shipping')) || strstr($order_totals[$i]['code'], 'insurance')) { if ($order_totals[$i]['code'] == 'ot_shipping') { $optionsST['shipping'] = round($order_totals[$i]['value'], 2); } if ($order_totals[$i]['code'] == 'ot_total') { $optionsST['amount'] = round($order_totals[$i]['value'], 2); } if ($order_totals[$i]['code'] == 'ot_tax') { $optionsST['tax_cart'] += round($order_totals[$i]['value'], 2); } if ($order_totals[$i]['code'] == 'ot_subtotal') { $optionsST['subtotal'] = round($order_totals[$i]['value'], 2); } } else { // handle other order totals: global ${$order_totals[$i]['code']}; if (substr($order_totals[$i]['text'], 0, 1) == '-' || isset(${$order_totals[$i]['code']}->credit_class) && ${$order_totals[$i]['code']}->credit_class == true) { // handle credits $creditsApplied += round($order_totals[$i]['value'], 2); } else { // treat all other OT's as if they're related to handling fees or other extra charges to be added/included $surcharges += $order_totals[$i]['value']; } } } if ($creditsApplied > 0) { $optionsST['subtotal'] -= $creditsApplied; } if ($surcharges > 0) { $optionsST['subtotal'] += $surcharges; } $optionsNB['creditsExist'] = $creditsApplied > 0 ? TRUE : FALSE; // Handle tax-included scenario if (DISPLAY_PRICE_WITH_TAX == 'true') { $optionsST['tax_cart'] = 0; } $subtotalPRE = $optionsST; // Move shipping tax amount from Tax subtotal into Shipping subtotal for submission to PayPal, since PayPal applies tax to each line-item individually $module = strpos($_SESSION['shipping']['id'], '_') > 0 ? substr($_SESSION['shipping']['id'], 0, strpos($_SESSION['shipping']['id'], '_')) : $_SESSION['shipping']; if (isset($GLOBALS[$module]) && zen_not_null($order->info['shipping_method']) && DISPLAY_PRICE_WITH_TAX != 'true') { if ($GLOBALS[$module]->tax_class > 0) { $shipping_tax_basis = !isset($GLOBALS[$module]->tax_basis) ? STORE_SHIPPING_TAX_BASIS : $GLOBALS[$module]->tax_basis; $shippingOnBilling = zen_get_tax_rate($GLOBALS[$module]->tax_class, $order->billing['country']['id'], $order->billing['zone_id']); $shippingOnDelivery = zen_get_tax_rate($GLOBALS[$module]->tax_class, $order->delivery['country']['id'], $order->delivery['zone_id']); if ($shipping_tax_basis == 'Billing') { $shipping_tax = $shippingOnBilling; } elseif ($shipping_tax_basis == 'Shipping') { $shipping_tax = $shippingOnDelivery; } else { if (STORE_ZONE == $order->billing['zone_id']) { $shipping_tax = $shippingOnBilling; } elseif (STORE_ZONE == $order->delivery['zone_id']) { $shipping_tax = $shippingOnDelivery; } else { $shipping_tax = 0; } } $taxAdjustmentForShipping = zen_round(zen_calculate_tax($order->info['shipping_cost'], $shipping_tax), $currencies->currencies[$_SESSION['currency']]['decimal_places']); $optionsST['shipping'] += $taxAdjustmentForShipping; $optionsST['tax_cart'] -= $taxAdjustmentForShipping; } } $flagSubtotalsUnknownYet = $optionsST['shipping'] + $optionsST['amount'] + $optionsST['tax_cart'] + $optionsST['subtotal'] == 0; } else { // if we get here, we don't have any order-total information yet because the customer has clicked Express before starting normal checkout flow // thus, we must make a note to manually calculate subtotals, rather than relying on the more robust order-total infrastructure $flagSubtotalsUnknownYet = TRUE; } $decimals = $currencies->get_decimal_places($_SESSION['currency']); // loop thru all products to prepare details of quantity and price. for ($i = 0, $n = sizeof($order->products), $k = 0; $i < $n; $i++) { // PayPal is inconsistent in how it handles zero-value line-items, so skip this entry if price is zero if ($order->products[$i]['final_price'] == 0) { continue; } else { $k++; } $optionsLI["item_number_{$k}"] = $order->products[$i]['model']; $optionsLI["item_name_{$k}"] = $order->products[$i]['name'] . ' [' . (int) $order->products[$i]['id'] . ']'; // Append *** if out-of-stock. $optionsLI["item_name_{$k}"] .= zen_get_products_stock($order->products[$i]['id']) - $order->products[$i]['qty'] < 0 ? STOCK_MARK_PRODUCT_OUT_OF_STOCK : ''; // if there are attributes, loop thru them and add to description if (isset($order->products[$i]['attributes']) && sizeof($order->products[$i]['attributes']) > 0) { for ($j = 0, $n2 = sizeof($order->products[$i]['attributes']); $j < $n2; $j++) { $optionsLI["item_name_{$k}"] .= "\n " . $order->products[$i]['attributes'][$j]['option'] . ': ' . $order->products[$i]['attributes'][$j]['value']; } // end loop } // endif attribute-info // PayPal can't handle fractional-quantity values, so convert it to qty 1 here if ($order->products[$i]['qty'] > 1 && ($order->products[$i]['qty'] != (int) $order->products[$i]['qty'] || $flag_treat_as_partial)) { $optionsLI["item_name_{$k}"] = '(' . $order->products[$i]['qty'] . ' x ) ' . $optionsLI["item_name_{$k}"]; // zen_add_tax already handles whether DISPLAY_PRICES_WITH_TAX is set $optionsLI["amount_{$k}"] = zen_round(zen_round(zen_add_tax($order->products[$i]['final_price'], $order->products[$i]['tax']), $decimals) * $order->products[$i]['qty'], $decimals); $optionsLI["quantity_{$k}"] = 1; // no line-item tax component } else { $optionsLI["quantity_{$k}"] = $order->products[$i]['qty']; $optionsLI["amount_{$k}"] = zen_round(zen_add_tax($order->products[$i]['final_price'], $order->products[$i]['tax']), $decimals); } $subTotalLI += $optionsLI["quantity_{$k}"] * $optionsLI["amount_{$k}"]; // $subTotalTax += ($optionsLI["quantity_$k"] * $optionsLI["tax_$k"]); // add line-item for one-time charges on this product if ($order->products[$i]['onetime_charges'] != 0) { $k++; $optionsLI["item_name_{$k}"] = MODULES_PAYMENT_PAYPALSTD_LINEITEM_TEXT_ONETIME_CHARGES_PREFIX . substr(htmlentities($order->products[$i]['name'], ENT_QUOTES, 'UTF-8'), 0, 120); $optionsLI["amount_{$k}"] = zen_round(zen_add_tax($order->products[$i]['onetime_charges'], $order->products[$i]['tax']), $decimals); $optionsLI["quantity_{$k}"] = 1; // $optionsLI["tax_$k"] = zen_round(zen_calculate_tax($order->products[$i]['onetime_charges'], $order->products[$i]['tax']), $decimals); $subTotalLI += $optionsLI["amount_{$k}"]; // $subTotalTax += $optionsLI["tax_$k"]; } $numberOfLineItemsProcessed = $k; } // end for loopthru all products // add line items for any surcharges added by order-total modules if ($surcharges > 0) { $numberOfLineItemsProcessed++; $k = $numberOfLineItemsProcessed; $optionsLI["item_name_{$k}"] = MODULES_PAYMENT_PAYPALSTD_LINEITEM_TEXT_SURCHARGES_SHORT; $optionsLI["amount_{$k}"] = $surcharges; $optionsLI["quantity_{$k}"] = 1; $subTotalLI += $surcharges; } // add line items for discounts such as gift certificates and coupons if ($creditsApplied > 0) { $numberOfLineItemsProcessed++; $k = $numberOfLineItemsProcessed; $optionsLI["item_name_{$k}"] = MODULES_PAYMENT_PAYPALSTD_LINEITEM_TEXT_DISCOUNTS_SHORT; $optionsLI["amount_{$k}"] = -1 * $creditsApplied; $optionsLI["quantity_{$k}"] = 1; $subTotalLI -= $creditsApplied; } // Reformat properly // Replace & and = and % with * if found. // reformat properly according to API specs // Remove HTML markup from name if found for ($k = 1, $n = $numberOfLineItemsProcessed + 1; $k < $n; $k++) { $optionsLI["item_name_{$k}"] = str_replace(array('&', '=', '%'), '*', $optionsLI["item_name_{$k}"]); $optionsLI["item_name_{$k}"] = zen_clean_html($optionsLI["item_name_{$k}"], 'strong'); $optionsLI["item_name_{$k}"] = substr($optionsLI["item_name_{$k}"], 0, 127); $optionsLI["amount_{$k}"] = round($optionsLI["amount_{$k}"], 2); if (isset($optionsLI["item_number_{$k}"])) { if ($optionsLI["item_number_{$k}"] == '') { unset($optionsLI["item_number_{$k}"]); } else { $optionsLI["item_number_{$k}"] = str_replace(array('&', '=', '%'), '*', $optionsLI["item_number_{$k}"]); $optionsLI["item_number_{$k}"] = substr($optionsLI["item_number_{$k}"], 0, 127); } } // if (isset($optionsLI["tax_$k"]) && ($optionsLI["tax_$k"] != '' || $optionsLI["tax_$k"] > 0)) { // $optionsLI["tax_$k"] = round($optionsLI["tax_$k"], 2); // } } // Sanity Check of line-item subtotals $optionsLI['num_cart_items'] = 0; for ($j = 1; $j < $k; $j++) { $itemAMT = $optionsLI["amount_{$j}"]; $itemQTY = $optionsLI["quantity_{$j}"]; $itemTAX = isset($optionsLI["tax_{$j}"]) ? $optionsLI["tax_{$j}"] : 0; $sumOfLineItems += $itemQTY * $itemAMT; $sumOfLineTax += $itemQTY * $itemTAX; $optionsLI['num_cart_items']++; } $sumOfLineItems = round($sumOfLineItems, 2); $sumOfLineTax = round($sumOfLineTax, 2); if ($sumOfLineItems == 0) { $sumOfLineTax = 0; $optionsLI = array(); $discountProblemsFlag = TRUE; if ($optionsST['shipping'] == $optionsST['amount']) { $optionsST['shipping'] = 0; } } // // Sanity check -- if tax-included pricing is causing problems, remove the numbers and put them in a comment instead: // $stDiffTaxOnly = (strval($sumOfLineItems - $sumOfLineTax - round($optionsST['amount'], 2)) + 0); // if (DISPLAY_PRICE_WITH_TAX == 'true' && $stDiffTaxOnly == 0 && ($optionsST['tax_cart'] != 0 && $sumOfLineTax != 0)) { // $optionsNB['DESC'] = 'Tax included in prices: ' . $sumOfLineTax . ' (' . $optionsST['tax_cart'] . ') '; // $optionsST['tax_cart'] = 0; // for ($k=1, $n=$numberOfLineItemsProcessed+1; $k<$n; $k++) { // if (isset($optionsLI["tax_$k"])) unset($optionsLI["tax_$k"]); // } // } // // Do sanity check -- if any of the line-item subtotal math doesn't add up properly, skip line-item details, // // so that the order can go through even though PayPal isn't being flexible to handle Zen Cart's diversity // if ((strval($subTotalTax) - strval($sumOfLineTax)) > 0.02) { // $ipn_logging('getLineItemDetails 3', 'Tax Subtotal does not match sum of taxes for line-items. Tax details are being removed from line-item submission data.' . "\n" . $sumOfLineTax . ' ' . $subTotalTax . print_r(array_merge($optionsST, $optionsLI), true)); // for ($k=1, $n=$numberOfLineItemsProcessed+1; $k<$n; $k++) { // if (isset($optionsLI["tax_$k"])) unset($optionsLI["tax_$k"]); // } // $subTotalTax = 0; // $sumOfLineTax = 0; // } // // If coupons exist and there's a calculation problem, then it's likely that taxes are incorrect, so reset L_TAXAMTn values // if ($creditsApplied > 0 && (strval($optionsST['tax_cart']) != strval($sumOfLineTax))) { // $pre = $optionsLI; // for ($k=1, $n=$numberOfLineItemsProcessed+1; $k<$n; $k++) { // if (isset($optionsLI["tax_$k"])) unset($optionsLI["tax_$k"]); // } // $ipn_logging('getLineItemDetails 4', 'Coupons/Discounts have affected tax calculations, so tax details are being removed from line-item submission data.' . "\n" . $sumOfLineTax . ' ' . $optionsST['tax_cart'] . "\n" . print_r(array_merge($optionsST, $pre, $optionsNB), true) . "\nAFTER:" . print_r(array_merge($optionsST, $optionsLI, $optionsNB), TRUE)); // $subTotalTax = 0; // $sumOfLineTax = 0; // } // disable line-item tax details, leaving only TAXAMT subtotal as tax indicator for ($k = 1, $n = $numberOfLineItemsProcessed + 1; $k < $n; $k++) { if (isset($optionsLI["tax_{$k}"])) { unset($optionsLI["tax_{$k}"]); } } // check subtotals if (strval($optionsST['subtotal']) > 0 && strval($subTotalLI) > 0 && strval($subTotalLI) != strval($optionsST['subtotal']) || strval($subTotalLI) - strval($sumOfLineItems) != 0) { $ipn_logging('getLineItemDetails 5', 'Line-item subtotals do not add up properly. Line-item-details skipped.' . "\n" . strval($sumOfLineItems) . ' ' . strval($subTotalLI) . ' ' . print_r(array_merge($optionsST, $optionsLI), true)); $optionsLI = array(); $optionsLI["item_name_0"] = MODULE_PAYMENT_PAYPAL_PURCHASE_DESCRIPTION_TITLE; $optionsLI["amount_0"] = $sumOfLineItems = $subTotalLI = $optionsST['subtotal']; } // check whether discounts are causing a problem if (strval($optionsST['subtotal']) < 0) { $pre = array_merge($optionsST, $optionsLI); $optionsST['subtotal'] = $optionsST['amount']; $optionsLI = array(); $optionsLI["item_name_0"] = MODULE_PAYMENT_PAYPAL_PURCHASE_DESCRIPTION_TITLE; $optionsLI["amount_0"] = $sumOfLineItems = $subTotalLI = $optionsST['subtotal']; if ($optionsST['amount'] < $optionsST['tax_cart']) { $optionsST['tax_cart'] = 0; } if ($optionsST['amount'] < $optionsST['shipping']) { $optionsST['shipping'] = 0; } $discountProblemsFlag = TRUE; $this->zcLog('getLineItemDetails 6', 'Discounts have caused the subtotal to calculate incorrectly. Line-item-details cannot be submitted.' . "\nBefore:" . print_r($pre, TRUE) . "\nAfter:" . print_r(array_merge($optionsST, $optionsLI), true)); } // if amount or subtotal values are 0 (ie: certain OT modules disabled), we have to get subtotals manually if ((!isset($optionsST['amount']) || $optionsST['amount'] == 0 || $flagSubtotalsUnknownYet == TRUE || $optionsST['subtotal'] == 0) && $discountProblemsFlag != TRUE) { $optionsST['subtotal'] = $sumOfLineItems; $optionsST['tax_cart'] = $sumOfLineTax; if ($subTotalShipping > 0) { $optionsST['shipping'] = $subTotalShipping; } $optionsST['amount'] = $sumOfLineItems + $optionsST['tax_cart'] + $optionsST['shipping']; } ipn_logging('getLineItemDetails 7 - subtotal comparisons', 'BEFORE line-item calcs: ' . print_r($subtotalPRE, true) . ' - AFTER doing line-item calcs: ' . print_r(array_merge($optionsST, $optionsLI, $optionsNB), true)); // if subtotals are not adding up correctly, then skip sending any line-item or subtotal details to PayPal $stAll = round(strval($optionsST['subtotal'] + $optionsST['tax_cart'] + $optionsST['shipping']), 2); $stDiff = strval($optionsST['amount'] - $stAll); $stDiffRounded = strval($stAll - round($optionsST['amount'], 2)) + 0; // unset any subtotal values that are zero if (isset($optionsST['subtotal']) && $optionsST['subtotal'] == 0) { unset($optionsST['subtotal']); } if (isset($optionsST['tax_cart']) && $optionsST['tax_cart'] == 0) { unset($optionsST['tax_cart']); } if (isset($optionsST['shipping']) && $optionsST['shipping'] == 0) { unset($optionsST['shipping']); } // tidy up all values so that they comply with proper format (number_format(xxxx,2) for PayPal US use ) if (!defined('PAYPALWPP_SKIP_LINE_ITEM_DETAIL_FORMATTING') || PAYPALWPP_SKIP_LINE_ITEM_DETAIL_FORMATTING != 'true' || in_array($order->info['currency'], array('JPY', 'NOK', 'HUF'))) { if (is_array($optionsST)) { foreach ($optionsST as $key => $value) { $optionsST[$key] = number_format($value, (int) $currencies->get_decimal_places($restrictedCurrency) == 0 ? 0 : 2); } } if (is_array($optionsLI)) { foreach ($optionsLI as $key => $value) { if (substr($key, 0, 8) == 'tax_' && ($optionsLI[$key] == '' || $optionsLI[$key] == 0)) { unset($optionsLI[$key]); } else { if (strstr($key, 'amount')) { $optionsLI[$key] = number_format($value, (int) $currencies->get_decimal_places($restrictedCurrency) == 0 ? 0 : 2); } } } } } ipn_logging('getLineItemDetails 8', 'checking subtotals... ' . "\n" . print_r(array_merge(array('calculated total' => number_format($stAll, (int) $currencies->get_decimal_places($restrictedCurrency) == 0 ? 0 : 2)), $optionsST), true) . "\n-------------------\ndifference: " . ($stDiff + 0) . ' (abs+rounded: ' . ($stDiffRounded + 0) . ')'); if ($stDiffRounded != 0) { ipn_logging('getLineItemDetails 9', 'Subtotals Bad. Skipping line-item/subtotal details'); return array(); } ipn_logging('getLineItemDetails 10', 'subtotals balance - okay' . "\nSubmitting: " . print_r(array_merge($optionsST, $optionsLI, $optionsNB), true)); // Send Subtotal and LineItem results back to be submitted to PayPal return array_merge($optionsST, $optionsLI, $optionsNB); }