From 3d8638eb077d320181d5e5c15558d6a0f375f201 Mon Sep 17 00:00:00 2001 From: O K Date: Mon, 8 Dec 2025 13:11:17 +0200 Subject: [PATCH] add NONSTANDARD handling --- usps_api_bridge.php | 72 +++++++++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 32 deletions(-) diff --git a/usps_api_bridge.php b/usps_api_bridge.php index e21ced9..46c4d6c 100644 --- a/usps_api_bridge.php +++ b/usps_api_bridge.php @@ -139,9 +139,6 @@ class Usps_Api_Bridge extends Module return $helper->generateForm([$fields_form]); } -/** - * THE CORE BRIDGE LOGIC - */ public function calculateRate($params, $shipping_cost, $products, $originalModule) { require_once(dirname(__FILE__) . '/classes/UspsV3Client.php'); @@ -152,26 +149,25 @@ class Usps_Api_Bridge extends Module return false; } - // 2. Identify which Service (Method) PrestaShop is asking for + // 2. Identify Service $carrierId = $params->id_carrier; - $sql = 'SELECT code FROM `' . _DB_PREFIX_ . 'uspsl_method` WHERE id_carrier = ' . (int)$carrierId; $methodCode = Db::getInstance()->getValue($sql); if (!$methodCode) { - return false; + return false; } // 3. Map Old Code to New API Enum $newApiClass = $this->mapServiceCodeToApiClass($methodCode); if (!$newApiClass) { - $this->log("Mapping failed for code: " . $methodCode); + $this->log("Mapping failed for legacy code: " . $methodCode); return false; } - // 4. Pack the Products + // 4. Pack Products $packedBoxes = $originalModule->getHelper()->getCarrierHelper()->packProducts($products, $params->id); - + if (empty($packedBoxes)) { $this->log("Box packer returned empty."); return false; @@ -181,31 +177,36 @@ class Usps_Api_Bridge extends Module $client = new UspsV3Client($token, (bool)Configuration::get('USPS_BRIDGE_LIVE_MODE')); $totalPrice = 0; - // 6. Get Origin/Dest addresses + // 6. Address Data $originZip = $this->getOriginZip($originalModule); $destAddress = new Address($params->id_address_delivery); - - // Clean zip codes (USPS V3 expects 5 digits for domestic) + $originZip = substr(preg_replace('/[^0-9]/', '', $originZip), 0, 5); $destZip = substr(preg_replace('/[^0-9]/', '', $destAddress->postcode), 0, 5); - $isInternational = ($destAddress->id_country != Country::getByIso('US')); - // 7. Loop through every box and get a rate + // 7. Loop through boxes foreach ($packedBoxes as $packedBox) { - - // BoxPacker uses Grams and Millimeters by default - // USPS requires Pounds and Inches - - // Weight: Grams -> Pounds + + // Weight Conversion (Grams -> Pounds) $weightInLbs = $this->convertUnit($packedBox->getWeight(), 'g', 'lbs', 3); - // Dimensions: Millimeters -> Inches - $box = $packedBox->getBox(); + // USPS requires minimum 0.001 lbs. 0 causes errors. + if ($weightInLbs <= 0) $weightInLbs = 0.1; + + // Dimensions (mm -> Inches) + $box = $packedBox->getBox(); $length = $this->convertUnit($box->getOuterLength(), 'mm', 'in', 2); $width = $this->convertUnit($box->getOuterWidth(), 'mm', 'in', 2); $height = $this->convertUnit($box->getOuterDepth(), 'mm', 'in', 2); + // Determine Processing Category (Crucial for V3 API) + // Machinable: Length <= 22", Width <= 18", Height <= 15", Weight >= 6oz (0.375lbs) and <= 25lbs + $category = 'MACHINABLE'; + if ($length > 22 || $width > 18 || $height > 15 || $weightInLbs > 25 || $weightInLbs < 0.375) { + $category = 'NONSTANDARD'; + } + // Build Payload $payload = [ 'originZIPCode' => $originZip, @@ -216,40 +217,47 @@ class Usps_Api_Bridge extends Module 'mailClass' => $newApiClass, 'priceType' => 'COMMERCIAL', 'mailingDate' => date('Y-m-d', strtotime('+1 day')), - 'processingCategory' => 'MACHINABLE', - 'rateIndicator' => 'SP' + 'processingCategory' => $category, + 'rateIndicator' => 'SP' // Single Piece ]; - // -- HANDLE FLAT RATES -- - $rateIndicator = $this->mapBoxToRateIndicator($box->getReference()); - if ($rateIndicator) { - $payload['rateIndicator'] = $rateIndicator; + // Flat Rate Override + $flatRateIndicator = $this->mapBoxToRateIndicator($box->getReference()); + if ($flatRateIndicator) { + $payload['rateIndicator'] = $flatRateIndicator; + // Dimensions technically ignored for Flat Rate, but required by API schema + // Processing category usually irrelevant for Flat Rate but must be valid Enum } if ($isInternational) { $payload['destinationCountryCode'] = Country::getIsoById($destAddress->id_country); $payload['originZIPCode'] = $originZip; - + // Remove domestic specific fields + unset($payload['destinationEntryFacilityType']); + unset($payload['destinationZIPCode']); + $response = $client->getInternationalRate($payload); } else { $payload['destinationZIPCode'] = $destZip; $payload['destinationEntryFacilityType'] = 'NONE'; - + $response = $client->getDomesticRate($payload); } - // Process Response + // Log Payload on Error for Debugging if (isset($response['error'])) { $this->log("API Error: " . $response['error']); + $this->log("Payload causing error: " . json_encode($payload)); return false; } + // Parse Price if (isset($response['totalBasePrice'])) { $totalPrice += (float)$response['totalBasePrice']; } elseif (isset($response['rateOptions'][0]['totalBasePrice'])) { $totalPrice += (float)$response['rateOptions'][0]['totalBasePrice']; } else { - $this->log("API Response missing price: " . print_r($response, true)); + $this->log("API Response missing price. Full response: " . json_encode($response)); return false; } } @@ -275,7 +283,7 @@ class Usps_Api_Bridge extends Module // Normalize to base unit (grams or mm) $baseValue = $value * (isset($units[$from]) ? $units[$from] : 1); - + // Convert to target unit $converted = $baseValue / (isset($units[$to]) ? $units[$to] : 1);