<?php /** * @version $Id: usps.php 12739 2009-10-20 19:38:22Z alex $ * @package In-Commerce * @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved. * @license Commercial License * This software is protected by copyright law and international treaties. * Unauthorized reproduction or unlicensed usage of the code of this program, * or any portion of it may result in severe civil and criminal penalties, * and will be prosecuted to the maximum extent possible under the law * See http://www.in-portal.org/commercial-license for copyright notices and details. */ defined('FULL_PATH') or die('restricted access!'); define('MODULE_SHIPPING_USPS_TEXT_TITLE', 'United States Postal Service'); define('MODULE_SHIPPING_USPS_TEXT_DESCRIPTION', 'You will need to have registered an account with USPS. Click <a target="_blank" href="https://secure.shippingapis.com/registration/"><strong>HERE</strong></a> for registration details. USPS expects you to use pounds as weight measure for your products.'); define('MODULE_SHIPPING_USPS_TEXT_ERROR', 'An error occured with the USPS shipping calculations.<br>If you prefer to use USPS as your shipping method, please contact the store owner.'); define('MODULE_SHIPPING_USPS_TEXT_DAY', 'Day'); define('MODULE_SHIPPING_USPS_TEXT_DAYS', 'Days'); define('MODULE_SHIPPING_USPS_TEXT_WEEKS', 'Weeks'); define('MODULE_SHIPPING_USPS_STATUS', 'True'); // Do you want to offer USPS shipping? define('MODULE_SHIPPING_USPS_SERVER', 'production'); // An account at USPS is needed to use the Production server // production othervise value may be 'test' define('MODULE_SHIPPING_USPS_HANDLING', '0'); // Handling fee for this shipping method define('MODULE_SHIPPING_USPS_TAX_CLASS', '0'); // Use the following tax class on the shipping fee define('MODULE_SHIPPING_USPS_ZONE', '0'); // If a zone is selected, only enable this shipping method for that zone. define('MODULE_SHIPPING_USPS_SORT_ORDER', '0'); // Sort order of display. define('MODULE_SHIPPING_USPS_TYPES', 'PRIORITY, PARCEL'); // EXPRESS, FIRST CLASS, BMP, MEDIA 'Select the domestic services to be offered: define('MODULE_SHIPPING_USPS_TYPES_INTL', 'EXPRESS MAIL INTERNATIONAL (EMS), EXPRESS MAIL INT, EXPRESS MAIL INT FLAT RATE ENV, PRIORITY MAIL INT, PRIORITY MAIL INT FLAT RATE ENV, PRIORITY MAILINT FLAT RATE BOX, FIRST-CLASS MAIL INT');// 'GLOBAL EXPRESS, GLOBAL EXPRESS NON-DOC RECT, GLOBAL EXPRESS NON-DOC NON-RECT, Select the international services to be offered: define('MODULE_SHIPPING_USPS_OPTIONS', 'Display weight, Display transit time'); // //configuration values for insurance define('MODULE_SHIPPING_USPS_INS1', '1.65');// 'US/Canada insurance for totals $.01-$50.00 define('MODULE_SHIPPING_USPS_INS2', '2.05');// 'US/Canada insurance for totals $50.01-$100 define('MODULE_SHIPPING_USPS_INS3', '2.45');// 'US/Canada insurance for totals $100.01-$200 define('MODULE_SHIPPING_USPS_INS4', '4.60');// 'US/Canada insurance for totals $200.01-$300 define('MODULE_SHIPPING_USPS_INS5', '.90');// 'US/Canada insurance for every $100 over $300 (add) define('MODULE_SHIPPING_USPS_INS6', '2.40');// 'International insurance for totals $.01-$50.00 define('MODULE_SHIPPING_USPS_INS7', '3.30');// 'International insurance for totals $50.01-$100 define('MODULE_SHIPPING_USPS_INS8', '4.20');// 'International insurance for totals $100.01-$200 define('MODULE_SHIPPING_USPS_INS9', '5.10');// 'International insurance for totals $200.01-$300 define('MODULE_SHIPPING_USPS_INS10', '.90');// 'International insurance for every $100 over $300 (add) define('MODULE_SHIPPING_USPS_INSURE', 'True');// 'Insure packages shipped by USPS? define('MODULE_SHIPPING_USPS_INSURE_TAX', 'True');// 'Insure tax on packages shipped by USPS? define('USPS_LOG_FILE', WRITEABLE .'/user_files/usps.log'); class USPS extends ShippingQuoteEngine { var $countries, $pounds, $ounces, $insurance_cost = 0, $shipping_origin_country, $store_first_name, $store_last_name, $company_name, $store_name, $store_address1, $store_address2, $store_city, $store_state, $store_zip5, $store_zip4, $store_phone, $usps_userid; var $order = Array(); var $types = Array(); var $intl_types = Array(); function USPS() { parent::kBase(); // EXPRESS, FIRST CLASS, PRIORITY, PARCEL, BMP, MEDIA $this->types = Array( 'EXPRESS' => 'Express Mail', 'FIRST CLASS' => 'First Class Mail', 'PRIORITY' => 'Priority Mail', 'PARCEL' => 'Parcel Post', 'BPM' => 'Bound Printed Matter', 'MEDIA' => 'Media Mail' ); $this->intl_types = Array( // 'GLOBAL EXPRESS' => 'Global Express Guaranteed', // 'GLOBAL EXPRESS NON-DOC RECT' => 'Global Express Guaranteed Non-Document Rectangular', // 'GLOBAL EXPRESS NON-DOC NON-RECT' => 'Global Express Guaranteed Non-Document Non-Rectangular', 'EXPRESS MAIL INT' => 'Express Mail International (EMS)', 'EXPRESS MAIL INT FLAT RATE ENV' => 'Express Mail International (EMS) Flat Rate Envelope', 'PRIORITY MAIL INT' => 'Priority Mail International', 'PRIORITY MAIL INT FLAT RATE ENV' => 'Priority Mail International Flat Rate Envelope', 'PRIORITY MAIL INT FLAT RATE BOX' => 'Priority Mail International Flat Rate Box', 'FIRST-CLASS MAIL INT' => 'First-Class Mail International' ); // get 2-symbol country code $country = $this->Application->ConfigValue('Comm_Shipping_Country'); if ($country != '') { $db =& $this->Application->GetADODBConnection(); $this->shipping_origin_country = $db->GetOne( 'SELECT DestAbbr2 FROM '.TABLE_PREFIX.'StdDestinations WHERE DestAbbr = '.$db->qstr($country).' AND destType = 1 ' ); } $contact_name = trim($this->_prepare_xml_param($this->Application->ConfigValue('Comm_Contacts_Name'))); $split_pos = strpos($contact_name, ' '); if ($split_pos === false) { $this->store_first_name = $contact_name; $this->store_last_name = ''; } else { $this->store_first_name = substr($contact_name, 0, $split_pos); $this->store_last_name = trim(substr($contact_name, $split_pos)); } $this->company_name = $this->_prepare_xml_param($this->Application->ConfigValue('Comm_CompanyName')); $this->store_name = $this->_prepare_xml_param($this->Application->ConfigValue('Comm_StoreName')); $this->store_address1 = $this->_prepare_xml_param($this->Application->ConfigValue('Comm_Shipping_AddressLine1')); $this->store_address2 = $this->_prepare_xml_param($this->Application->ConfigValue('Comm_Shipping_AddressLine2')); if ($this->store_address2 == '') { $this->store_address2 = $this->store_address1; $this->store_address1 = ''; } $this->store_city = $this->_prepare_xml_param($this->Application->ConfigValue('Comm_Shipping_City')); $this->store_state = $this->_prepare_xml_param($this->Application->ConfigValue('Comm_Shipping_State')); $zip = $this->_prepare_xml_param($this->Application->ConfigValue('Comm_Shipping_ZIP')); $this->store_zip5 = substr($zip, 0, 5); $this->store_zip4 = trim(substr($zip, 6), '-'); $this->store_phone = $this->_prepare_xml_param($this->Application->ConfigValue('Comm_Contacts_Phone')); // get username and password fron config. $a_params = $this->LoadParams(); $this->usps_userid = $a_params['AccountLogin']; // Note by Erik: DO NOT CHANGE THIS ARRAY. It's values are sent to USPS service and any changes may impact class main functionality. $this->countries = array( 'AF' => 'Afghanistan', 'AL' => 'Albania', 'DZ' => 'Algeria', 'AD' => 'Andorra', 'AO' => 'Angola', 'AI' => 'Anguilla', 'AG' => 'Antigua and Barbuda', 'AR' => 'Argentina', 'AM' => 'Armenia', 'AW' => 'Aruba', 'AU' => 'Australia', 'AT' => 'Austria', 'AZ' => 'Azerbaijan', 'BS' => 'Bahamas', 'BH' => 'Bahrain', 'BD' => 'Bangladesh', 'BB' => 'Barbados', 'BY' => 'Belarus', 'BE' => 'Belgium', 'BZ' => 'Belize', 'BJ' => 'Benin', 'BM' => 'Bermuda', 'BT' => 'Bhutan', 'BO' => 'Bolivia', 'BA' => 'Bosnia-Herzegovina', 'BW' => 'Botswana', 'BR' => 'Brazil', 'VG' => 'British Virgin Islands', 'BN' => 'Brunei Darussalam', 'BG' => 'Bulgaria', 'BF' => 'Burkina Faso', 'MM' => 'Burma', 'BI' => 'Burundi', 'KH' => 'Cambodia', 'CM' => 'Cameroon', 'CA' => 'Canada', 'CV' => 'Cape Verde', 'KY' => 'Cayman Islands', 'CF' => 'Central African Republic', 'TD' => 'Chad', 'CL' => 'Chile', 'CN' => 'China', 'CX' => 'Christmas Island (Australia)', 'CC' => 'Cocos Island (Australia)', 'CO' => 'Colombia', 'KM' => 'Comoros', 'CG' => 'Congo (Brazzaville),Republic of the', 'ZR' => 'Congo, Democratic Republic of the', 'CK' => 'Cook Islands (New Zealand)', 'CR' => 'Costa Rica', 'CI' => 'Cote d\'Ivoire (Ivory Coast)', 'HR' => 'Croatia', 'CU' => 'Cuba', 'CY' => 'Cyprus', 'CZ' => 'Czech Republic', 'DK' => 'Denmark', 'DJ' => 'Djibouti', 'DM' => 'Dominica', 'DO' => 'Dominican Republic', 'TP' => 'East Timor (Indonesia)', 'EC' => 'Ecuador', 'EG' => 'Egypt', 'SV' => 'El Salvador', 'GQ' => 'Equatorial Guinea', 'ER' => 'Eritrea', 'EE' => 'Estonia', 'ET' => 'Ethiopia', 'FK' => 'Falkland Islands', 'FO' => 'Faroe Islands', 'FJ' => 'Fiji', 'FI' => 'Finland', 'FR' => 'France', 'GF' => 'French Guiana', 'PF' => 'French Polynesia', 'GA' => 'Gabon', 'GM' => 'Gambia', 'GE' => 'Georgia, Republic of', 'DE' => 'Germany', 'GH' => 'Ghana', 'GI' => 'Gibraltar', 'GB' => 'Great Britain and Northern Ireland', 'GR' => 'Greece', 'GL' => 'Greenland', 'GD' => 'Grenada', 'GP' => 'Guadeloupe', 'GT' => 'Guatemala', 'GN' => 'Guinea', 'GW' => 'Guinea-Bissau', 'GY' => 'Guyana', 'HT' => 'Haiti', 'HN' => 'Honduras', 'HK' => 'Hong Kong', 'HU' => 'Hungary', 'IS' => 'Iceland', 'IN' => 'India', 'ID' => 'Indonesia', 'IR' => 'Iran', 'IQ' => 'Iraq', 'IE' => 'Ireland', 'IL' => 'Israel', 'IT' => 'Italy', 'JM' => 'Jamaica', 'JP' => 'Japan', 'JO' => 'Jordan', 'KZ' => 'Kazakhstan', 'KE' => 'Kenya', 'KI' => 'Kiribati', 'KW' => 'Kuwait', 'KG' => 'Kyrgyzstan', 'LA' => 'Laos', 'LV' => 'Latvia', 'LB' => 'Lebanon', 'LS' => 'Lesotho', 'LR' => 'Liberia', 'LY' => 'Libya', 'LI' => 'Liechtenstein', 'LT' => 'Lithuania', 'LU' => 'Luxembourg', 'MO' => 'Macao', 'MK' => 'Macedonia, Republic of', 'MG' => 'Madagascar', 'MW' => 'Malawi', 'MY' => 'Malaysia', 'MV' => 'Maldives', 'ML' => 'Mali', 'MT' => 'Malta', 'MQ' => 'Martinique', 'MR' => 'Mauritania', 'MU' => 'Mauritius', 'YT' => 'Mayotte (France)', 'MX' => 'Mexico', 'MD' => 'Moldova', 'MC' => 'Monaco (France)', 'MN' => 'Mongolia', 'MS' => 'Montserrat', 'MA' => 'Morocco', 'MZ' => 'Mozambique', 'NA' => 'Namibia', 'NR' => 'Nauru', 'NP' => 'Nepal', 'NL' => 'Netherlands', 'AN' => 'Netherlands Antilles', 'NC' => 'New Caledonia', 'NZ' => 'New Zealand', 'NI' => 'Nicaragua', 'NE' => 'Niger', 'NG' => 'Nigeria', 'KP' => 'North Korea (Korea, Democratic People\'s Republic of)', 'NO' => 'Norway', 'OM' => 'Oman', 'PK' => 'Pakistan', 'PA' => 'Panama', 'PG' => 'Papua New Guinea', 'PY' => 'Paraguay', 'PE' => 'Peru', 'PH' => 'Philippines', 'PN' => 'Pitcairn Island', 'PL' => 'Poland', 'PT' => 'Portugal', 'QA' => 'Qatar', 'RE' => 'Reunion', 'RO' => 'Romania', 'RU' => 'Russia', 'RW' => 'Rwanda', 'SH' => 'Saint Helena', 'KN' => 'Saint Kitts (St. Christopher and Nevis)', 'LC' => 'Saint Lucia', 'PM' => 'Saint Pierre and Miquelon', 'VC' => 'Saint Vincent and the Grenadines', 'SM' => 'San Marino', 'ST' => 'Sao Tome and Principe', 'SA' => 'Saudi Arabia', 'SN' => 'Senegal', 'YU' => 'Serbia-Montenegro', 'SC' => 'Seychelles', 'SL' => 'Sierra Leone', 'SG' => 'Singapore', 'SK' => 'Slovak Republic', 'SI' => 'Slovenia', 'SB' => 'Solomon Islands', 'SO' => 'Somalia', 'ZA' => 'South Africa', 'GS' => 'South Georgia (Falkland Islands)', 'KR' => 'South Korea (Korea, Republic of)', 'ES' => 'Spain', 'LK' => 'Sri Lanka', 'SD' => 'Sudan', 'SR' => 'Suriname', 'SZ' => 'Swaziland', 'SE' => 'Sweden', 'CH' => 'Switzerland', 'SY' => 'Syrian Arab Republic', 'TW' => 'Taiwan', 'TJ' => 'Tajikistan', 'TZ' => 'Tanzania', 'TH' => 'Thailand', 'TG' => 'Togo', 'TK' => 'Tokelau (Union) Group (Western Samoa)', 'TO' => 'Tonga', 'TT' => 'Trinidad and Tobago', 'TN' => 'Tunisia', 'TR' => 'Turkey', 'TM' => 'Turkmenistan', 'TC' => 'Turks and Caicos Islands', 'TV' => 'Tuvalu', 'UG' => 'Uganda', 'UA' => 'Ukraine', 'AE' => 'United Arab Emirates', 'UY' => 'Uruguay', 'UZ' => 'Uzbekistan', 'VU' => 'Vanuatu', 'VA' => 'Vatican City', 'VE' => 'Venezuela', 'VN' => 'Vietnam', 'WF' => 'Wallis and Futuna Islands', 'WS' => 'Western Samoa', 'YE' => 'Yemen', 'ZM' => 'Zambia', 'ZW' => 'Zimbabwe' ); $this->countryinsure = array( 'AF' => 0, 'AL' => 0, 'DZ' => 2185, 'AD' => 5000, 'AO' => 0, 'AI' => 415, 'AG' => 60, 'AR' => 5000, 'AM' => 1350, 'AW' => 830, 'AU' => 3370, 'AT' => 5000, 'AZ' => 5000, 'BS' => 2795, 'BH' => 0, 'BD' => 5000, 'BB' => 220, 'BY' => 1323, 'BE' => 5000, 'BZ' => 1600, 'BJ' => 170, 'BM' => 440, 'BT' => 440, 'BO' => 0, 'BA' => 5000, 'BW' => 145, 'BR' => 5000, 'VG' => 165, 'BN' => 4405, 'BG' => 1030, 'BF' => 530, 'MM' => 4045, 'BI' => 790, 'KH' => 0, 'CM' => 5000, 'CA' => 675, 'CV' => 0, 'KY' => 0, 'CF' => 4405, 'TD' => 440, 'CL' => 0, 'CN' => 1130, 'CX' => 3370, 'CC' => 3370, 'CO' => 0, 'KM' => 690, 'CG' => 1685, 'ZR' => 0, 'CK' => 980, 'CR' => 0, 'CI' => 5000, 'HR' => 5000, 'CU' => 0, 'CY' => 5000, 'CZ' => 5000, 'DK' => 5000, 'DJ' => 880, 'DM' => 0, 'DO' => 0, 'TP' => 0, 'EC' => 0, 'EG' => 1685, 'SV' => 0, 'GQ' => 0, 'ER' => 0, 'EE' => 2020, 'ET' => 1000, 'FK' => 510, 'FO' => 5000, 'FJ' => 600, 'FI' => 5000, 'FR' => 5000, 'GF' => 5000, 'PF' => 1015, 'GA' => 485, 'GM' => 2575, 'GE' => 1350, 'DE' => 5000, 'GH' => 5000, 'GI' => 5000, 'GB' => 857, 'GR' => 5000, 'GL' => 5000, 'GD' => 350, 'GP' => 5000, 'GT' => 0, 'GN' => 875, 'GW' => 21, 'GY' => 10, 'HT' => 0, 'HN' => 0, 'HK' => 5000, 'HU' => 5000, 'IS' => 5000, 'IN' => 2265, 'ID' => 0, 'IR' => 0, 'IQ' => 0, 'IE' => 5000, 'IL' => 0, 'IT' => 5000, 'JM' => 0, 'JP' => 5000, 'JO' => 0, 'KZ' => 5000, 'KE' => 815, 'KI' => 0, 'KW' => 1765, 'KG' => 1350, 'LA' => 0, 'LV' => 1350, 'LB' => 440, 'LS' => 440, 'LR' => 440, 'LY' => 0, 'LI' => 5000, 'LT' => 5000, 'LU' => 5000, 'MO' => 4262, 'MK' => 2200, 'MG' => 675, 'MW' => 50, 'MY' => 1320, 'MV' => 0, 'ML' => 950, 'MT' => 5000, 'MQ' => 5000, 'MR' => 635, 'MU' => 270, 'YT' => 5000, 'MX' => 0, 'MD' => 1350, 'MC' => 5000, 'MN' => 440, 'MS' => 2200, 'MA' => 5000, 'MZ' => 0, 'NA' => 4405, 'NR' => 220, 'NP' => 0, 'NL' => 5000, 'AN' => 830, 'NC' => 1615, 'NZ' => 980, 'NI' => 440, 'NE' => 810, 'NG' => 205, 'KP' => 0, 'NO' => 0, 'OM' => 575, 'PK' => 270, 'PA' => 0, 'PG' => 445, 'PY' => 0, 'PE' => 0, 'PH' => 270, 'PN' => 0, 'PL' => 1350, 'PT' => 5000, 'QA' => 2515, 'RE' => 5000, 'RO' => 5000, 'RU' => 5000, 'RW' => 0, 'SH' => 170, 'KN' => 210, 'LC' => 400, 'PM' => 5000, 'VC' => 130, 'SM' => 5000, 'ST' => 440, 'SA' => 0, 'SN' => 865, 'YU' => 5000, 'SC' => 0, 'SL' => 0, 'SG' => 4580, 'SK' => 5000, 'SI' => 4400, 'SB' => 0, 'SO' => 440, 'ZA' => 1760, 'GS' => 510, 'KR' => 5000, 'ES' => 5000, 'LK' => 35, 'SD' => 0, 'SR' => 535, 'SZ' => 560, 'SE' => 5000, 'CH' => 5000, 'SY' => 3080, 'TW' => 1350, 'TJ' => 1350, 'TZ' => 230, 'TH' => 1350, 'TG' => 2190, 'TK' => 295, 'TO' => 515, 'TT' => 930, 'TN' => 2200, 'TR' => 880, 'TM' => 675, 'TC' => 0, 'TV' => 4715, 'UG' => 0, 'UA' => 5000, 'AE' => 5000, 'UY' => 0, 'UZ' => 5000, 'VU' => 0, 'VA' => 5000, 'VE' => 0, 'VN' => 0, 'WF' => 1615, 'WS' => 295, 'YE' => 0, 'ZM' => 540, 'ZW' => 600, 'US' => 5000 ); } function SetInsurance() { $this->insurance_cost = 0; // Insurance module by Kevin Shelton // divide the value of the order among the packages based on the order total or subtotal depending on whether or not you have configured to insure tax $shipping_weight = $this->order['ShippingWeight']; $shipping_num_boxes = $this->order['ShippingNumBoxes']; $costperpkg = $this->order['SubTotal'] / $shipping_num_boxes; // retrieve the maximum allowed insurance for the destination country and if the package value exceeds it then set package value to the maximum allowed $maxins = $this->countryinsure[$this->order['ShippingCountry']]; if ($costperpkg > $maxins) $costperpkg = $maxins; // if insurance not allowed for destination or insurance is turned off add nothing to shipping cost if (($maxins == 0) || (MODULE_SHIPPING_USPS_INSURE == 'False')) { $insurance = 0; } // US and Canada share the same insurance calculation (though not the same maximum) else if (($this->order['ShippingCountry'] == 'US') || ($this->order['ShippingCountry'] == 'CA')) { if ($costperpkg<=50) { $insurance=MODULE_SHIPPING_USPS_INS1; } else if ($costperpkg<=100) { $insurance=MODULE_SHIPPING_USPS_INS2; } else if ($costperpkg<=200) { $insurance=MODULE_SHIPPING_USPS_INS3; } else if ($costperpkg<=300) { $insurance=MODULE_SHIPPING_USPS_INS4; } else { $insurance = MODULE_SHIPPING_USPS_INS4 + ((ceil($costperpkg/100) -3) * MODULE_SHIPPING_USPS_INS5); } } // if insurance allowed and is not US or Canada then calculate international insurance else { if ($costperpkg<=50) { $insurance=MODULE_SHIPPING_USPS_INS6; } else if ($costperpkg<=100) { $insurance=MODULE_SHIPPING_USPS_INS7; } else if ($costperpkg<=200) { $insurance=MODULE_SHIPPING_USPS_INS8; } else if ($costperpkg<=300) { $insurance=MODULE_SHIPPING_USPS_INS9; } else { $insurance = MODULE_SHIPPING_USPS_INS9 + ((ceil($costperpkg/100) - 3) * MODULE_SHIPPING_USPS_INS10); } } // usps doesnt accept zero weight $shipping_weight = ($shipping_weight < 0.1 ? 0.1 : $shipping_weight); $shipping_pounds = floor ($shipping_weight); $shipping_ounces = round(16 * ($shipping_weight - floor($shipping_weight))); $this->_setWeight($shipping_pounds, $shipping_ounces); // Added by Kevin Chen (kkchen@uci.edu); Fixes the Parcel Post Bug July 1, 2004 // Refer to http://www.usps.com/webtools/htm/Domestic-Rates.htm documentation // Thanks Ryan if($shipping_pounds > 35 || ($shipping_pounds == 0 && $shipping_ounces < 6)){ $this->_setMachinable('False'); } else{ $this->_setMachinable('True'); } $this->insurance_cost = $insurance; // End Kevin Chen July 1, 2004 } function _setService($service) { $this->service = $service; } function _setWeight($pounds, $ounces=0) { $this->pounds = $pounds; $this->ounces = $ounces; } function _setContainer($container) { $this->container = $container; } function _setSize($size) { $this->size = $size; } function _setMachinable($machinable) { $this->machinable = $machinable; } function PhoneClean($phone) { $res = ereg_replace("[(]|[)]|[\-]|[ ]|[#]|[\.]|[a-z](.*)|[A-Z](.*)", "", $phone); if ( strlen($res) > 10 ) $res = substr($res, 0, 10); return $res != '' ? $res : $phone; } function GetQuote($method = '') { if ( isset($this->types[$method]) || in_array($method, $this->intl_types)) { $this->_setService($method); } $this -> _setContainer('None'); $this -> _setSize('REGULAR'); $this -> SetInsurance(); // ??? if ($this->order['ShippingCountry'] == $this->shipping_origin_country) { $request='<?xml version="1.0"?>'; // PASSWORD="'.$this->usps_password.'" $request.= '<RateV3Request USERID="'.$this->usps_userid.'">'; $services_count = 0; if (isset($this->service)) { $this->types = array($this->service => $this->types[$this->service]); } $dest_zip = str_replace(' ', '', $this->order['ShippingZip']); $dest_zip = substr($dest_zip, 0, 5); reset($this->types); $allowed_types = explode(", ", MODULE_SHIPPING_USPS_TYPES); while (list($key, $value) = each($this->types)) { if ( !in_array($key, $allowed_types) ) continue; $request .= '<Package ID="'.$services_count.'">'. '<Service>'.$key.'</Service>'. '<ZipOrigination>'.$this->store_zip5.'</ZipOrigination>'. '<ZipDestination>'.$dest_zip.'</ZipDestination>'. '<Pounds>'.$this->pounds.'</Pounds>'. '<Ounces>'.$this->ounces.'</Ounces>'. '<Size>'.$this->size.'</Size>'. '<Machinable>'.$this->machinable.'</Machinable>'. '</Package>'; $services_count++; } $request .= '</RateV3Request>'; $api_query = 'RateV3'; } else { $request = '<IntlRateRequest USERID="'.$this->usps_userid.'">'. '<Package ID="0">'. '<Pounds>'.$this->pounds.'</Pounds>'. '<Ounces>'.$this->ounces.'</Ounces>'. '<MailType>Package</MailType>'. '<Country>'.$this->countries[$this->order['ShippingCountry']].'</Country>'. '</Package>'. '</IntlRateRequest>'; $api_query = 'IntlRate'; } $request = 'API='.$api_query.'&XML=' . urlencode($request); $body = $this->PostQuery($request); $body = str_replace(chr(146), '', $body); // for bad ` // check for errors if (strpos($body, '<Error>') !== false) { $errors = Array (); preg_match_all('/<Number>(.*?)<\/Number>/s', $body, $error_numbers); preg_match_all('/<Description>(.*?)<\/Description>/s', $body, $error_descriptions); foreach ($error_numbers[1] as $index => $error_number) { $errors[$index] = $error_descriptions[1][$index]; if ($this->Application->isDebugMode()) { $errors[$index] .= ' (' . $error_number . ')'; } } $errors = array_unique($errors); // we may have same errors on many packages, so don't show duplicates return Array('error' => implode('<br/>', $errors)); } // parse response $xml_helper =& $this->Application->recallObject('kXMLHelper'); /* @var $xml_helper kXMLHelper */ $root_node =& $xml_helper->Parse($body); /* @var $root_node kXMLNode */ $rates = Array(); // Domestic shipping if ($this->order['ShippingCountry'] == $this->shipping_origin_country) { $i = 0; $postage_node =& $root_node->FindChild('Package'); do { // $parcel_node =& $postage_node->firstChild; $service = $postage_node->FindChildValue('MailService'); if ( $service != '' ) { $i++; $rates[$i] = Array(); $rates[$i]['Title'] = $service; $rates[$i]['Rate'] = $this->insurance_cost + $postage_node->FindChildValue('Rate'); } } while ( $postage_node =& $postage_node->NextSibling()); } else { // for International Rates !!! $allowed_types = array(); foreach( explode(", ", MODULE_SHIPPING_USPS_TYPES_INTL) as $value ) { $allowed_types[$value] = $this->intl_types[$value]; } $i = 0; $service_node =& $root_node->FindChild('Service'); do { $service = trim($service_node->FindChildValue('SvcDescription')); if( !in_array($service, $allowed_types) ) continue; $i++; if ( $service_node->FindChildValue('MaxWeight') >= $this->pounds ) { $rates[$i] = Array(); $rates[$i]['Title'] = $service; $rates[$i]['MaxDimensions'] = $service_node->FindChildValue('MaxDimensions'); $rates[$i]['MaxWeight'] = $service_node->FindChildValue('MaxWeight'); $rates[$i]['SvcCommitments'] = $service_node->FindChildValue('SvcCommitments'); $rates[$i]['Rate'] = $this->insurance_cost + $service_node->FindChildValue('Postage'); } } while ( $service_node =& $service_node->NextSibling()); } // print_r($rates); // die('here'); return $rates; } function PostOrder() { $request=''; $base_request = ''; $this->SetInsurance(); // $this->order['ShippingCountry'] = $this->GetUSPSCountry($this->order['ShippingCountry']); // Domestic Order if ($this->order['ShippingCountry'] == $this->shipping_origin_country) { // $dest_zip = str_replace(' ', '', $this->order['ShippingZip5']); $this->order['ShippingZip5'] = substr($this->order['ShippingZip5'], 0, 5); $WeightInOunces = floor($this->pounds * 16 + $this->ounces); $base_request =' <Option>1</Option> <ImageParameters></ImageParameters> <FromName>'.$this->store_name.'</FromName> <FromFirm>'.$this->company_name.'</FromFirm> <FromAddress1>'.$this->store_address1.'</FromAddress1> <FromAddress2>'.$this->store_address2.'</FromAddress2> <FromCity>'.$this->store_city.'</FromCity> <FromState>'.$this->store_state.'</FromState> <FromZip5>'.$this->store_zip5.'</FromZip5> <FromZip4>'.$this->store_zip4.'</FromZip4> <ToName>'.$this->order['FirstName'].' '.$this->order['LastName'].'</ToName> <ToFirm>'.$this->order['ShippingCompany'].'</ToFirm> <ToAddress1>'.$this->order['ShippingAddress2'].'</ToAddress1> <ToAddress2>'.$this->order['ShippingAddress1'].'</ToAddress2> <ToCity>'.$this->order['ShippingCity'].'</ToCity> <ToState>'.$this->order['ShippingState'].'</ToState> <ToZip5>'.$this->order['ShippingZip5'].'</ToZip5> <ToZip4>'.$this->order['ShippingZip4'].'</ToZip4> <WeightInOunces>'.$WeightInOunces.'</WeightInOunces> <ServiceType>'.$this->order['ShippingService'].'</ServiceType> <ImageType>PDF</ImageType> <LabelDate>'.date('m/d/Y',time()).'</LabelDate> <CustomerRefNo></CustomerRefNo> <AddressServiceRequested></AddressServiceRequested> <SenderName></SenderName><SenderEMail></SenderEMail> <RecipientName></RecipientName> <RecipientEMail></RecipientEMail> '; $api_query = 'DeliveryConfirmationV3'; $xml_request = 'DeliveryConfirmationV3.0Request'; } else { // International Order(s) $shipping_service = strtolower($this->order['ShippingService']); $base_request = '<Option/> <ImageParameters/> <FromFirstName>'.$this->store_first_name.'</FromFirstName> <FromLastName>'.$this->store_last_name.'</FromLastName> <FromFirm>'.$this->company_name.'</FromFirm> <FromAddress1>'.$this->store_address1.'</FromAddress1> <FromAddress2>'.$this->store_address2.'</FromAddress2> <FromCity>'.$this->store_city.'</FromCity> <FromState>'.$this->store_state.'</FromState> <FromZip5>'.$this->store_zip5.'</FromZip5> <FromPhone>'.$this->PhoneClean($this->store_phone).'</FromPhone> <ToName>'.$this->order['FirstName'].' '.$this->order['LastName'].'</ToName> <ToFirm>'.$this->order['ShippingCompany'].'</ToFirm> <ToAddress1></ToAddress1> <ToAddress2>'.$this->order['ShippingAddress2'].'</ToAddress2> <ToAddress3>'.$this->order['ShippingAddress1'].'</ToAddress3> <ToCity>'.$this->order['ShippingCity'].'</ToCity>'; if ( $this->order['ShippingProvince'] != '' ) { $base_request.=' <ToProvince>'.$this->order['ShippingProvince'].'</ToProvince>'; } $base_request.=' <ToCountry>'.$this->countries[$this->order['ShippingCountry']].'</ToCountry> <ToPostalCode>'.$this->order['ShippingZip'].'</ToPostalCode> <ToPOBoxFlag>N</ToPOBoxFlag> <ToPhone>'.$this->PhoneClean($this->order['ShippingPhone']).'</ToPhone> <ToFax>'.$this->PhoneClean($this->order['ShippingFax']).'</ToFax> <ToEmail>'.$this->order['Email'].'</ToEmail> <ShippingContents>'; // add items foreach ( $this->order['Items'] as $k => $value ) { $base_request.=' <ItemDetail> <Description>Computer Parts</Description> <Quantity>'.$value['Qty'].'</Quantity> <Value>'.($value['Price'] * $value['Qty']).'</Value> <NetPounds>'.$value['NetPounds'].'</NetPounds> <NetOunces>'.$value['NetOunces'].'</NetOunces> <HSTariffNumber>123456</HSTariffNumber> <CountryOfOrigin>United States</CountryOfOrigin> </ItemDetail>'; } // end add items $base_request.=' </ShippingContents> <GrossPounds>'.$this->pounds.'</GrossPounds> <GrossOunces>'.$this->ounces.'</GrossOunces> <ContentType>MERCHANDISE</ContentType> <Agreement>Y</Agreement> <InvoiceNumber>'.$this->order['InvoiceNumber'].'</InvoiceNumber> <ImageType>PDF</ImageType> <ImageLayout>ALLINONEFILE</ImageLayout> <LabelDate>'.date('m/d/Y',time()).'</LabelDate> '; if (strpos($shipping_service, 'express') !== false) { $xml_request = 'ExpressMailIntlRequest'; $api_query = 'ExpressMailIntl'; } elseif (strpos($shipping_service, 'priority') !== false) { $xml_request = 'PriorityMailIntlRequest'; $api_query = 'PriorityMailIntl'; } else { $xml_request = 'FirstClassMailIntlRequest'; $api_query = 'FirstClassMailIntl'; } } $request.= '<'.$xml_request.' USERID="'.$this->usps_userid.'">'; $request.= $base_request; $request.= '</'.$xml_request.'>'; // die($request); $request = 'API='.$api_query.'&XML='.urlencode($request); $body = $this->PostQuery($request, 1); // check for errors if (strpos($body, '<Error>') !== false) { $errors = Array (); preg_match_all('/<Number>(.*?)<\/Number>/s', $body, $error_numbers); preg_match_all('/<Description>(.*?)<\/Description>/s', $body, $error_descriptions); foreach ($error_numbers[1] as $index => $error_number) { $errors[$index] = Array ('error_number' => $error_number, 'error_description' => $error_descriptions[1][$index]); } // TODO: find a way to return other error messages in same package as well return $errors[0]; } // parse response $xml_helper =& $this->Application->recallObject('kXMLHelper'); $root_node =& $xml_helper->Parse($body); /* @var $root_node kXMLNode */ $Postage = 0; $label_file = $TrackingNumber = $PostnetBarCode = ''; // Domestic shipping if ($this->order['ShippingCountry'] == $this->shipping_origin_country ) { $delivery_node =& $root_node->FindChild('DeliveryConfirmationV3.0Response'); do { $TrackingNumber = $delivery_node->FindChildValue('DeliveryConfirmationNumber'); $PostnetBarCode = $delivery_node->FindChildValue('Postnet'); $DeliveryConfirmationLabel = base64_decode($delivery_node->FindChildValue('DeliveryConfirmationLabel')); } while ( $delivery_node =& $delivery_node->NextSibling()); } else { if (strpos($shipping_service, 'express') !== false) { $node_title = 'ExpressMailIntlResponse'; } elseif (strpos($shipping_service, 'priority') !== false) { $node_title = 'PriorityMailIntlResponse'; } else { $node_title = 'FirstClassMailIntlResponse'; } $delivery_node =& $root_node->FindChild($node_title); $PostnetBarCode = $delivery_node->FindChildValue('BarcodeNumber'); $Postage = $delivery_node->FindChildValue('Postage'); $DeliveryConfirmationLabel = base64_decode($delivery_node->FindChildValue('LabelImage')); } if ( $TrackingNumber != '' ) { $label_file = USPS_LABEL_FOLDER.$TrackingNumber.".pdf"; } elseif ( $PostnetBarCode != '' ) { $label_file = USPS_LABEL_FOLDER.$PostnetBarCode.".pdf"; } if ( $label_file != '' ) { if (!$handle = fopen($label_file, 'a')) echo "Cannot open file ($label_file)"; if ( @fwrite($handle, $DeliveryConfirmationLabel) === FALSE) echo "Cannot write to file ($label_file)"; } return array('TrackingNumber' => $TrackingNumber, 'PostnetBarCode' => $PostnetBarCode, 'Postage' => $Postage); } function GetUSPSCountry($country) { $country = $this->Application->Conn->GetOne('SELECT DestAbbr2 FROM '.TABLE_PREFIX.'StdDestinations WHERE DestAbbr = '.$this->Application->Conn->qstr($country)); if ( $country == '' ) $country = 'US'; return $country; } function GetShippingQuotes($params = null) { $weights = Kg2Pounds($params['packages']['0']['weight']); $weight = ''; $weight = $weights[0]; if ( $weights[1] != '' ) { $weight.='.'.$weights[1]; } $country = $this->GetUSPSCountry($params['dest_country']); $this->order = Array(); $this->order['ShippingWeight'] = $weight; $this->order['ShippingNumBoxes'] = 1; $this->order['ShippingZip'] = $params['dest_postal']; $this->order['SubTotal'] = $params['amount']; $this->order['ShippingCountry'] = $country; $shipping_types = Array(); $rates = $this->GetQuote(); if ( !isset($rates['error']) ) { $this->Application->RemoveVar('usps_errors'); $i = 1; foreach ($rates as $k => $rate ) { $shipping_types['USPS_'.$i] = Array( 'ShippingId' => 'USPS_'.$i, 'TotalCost' => $rate['Rate'], 'ShippingName' => $rate['Title'], 'Type' => '1', 'CODFlat' => '0', 'CODPercent' => '0', 'PortalGroups' => ',15,', 'InsuranceFee' => '', 'COD' => '0', 'SelectedOnly' => '0', 'Code' => $rate['Title'] ); $i++; } } else { // for Front-End (shipping screen) and Admin (Shipping Tab on editing order) $this->Application->StoreVar('usps_errors', $rates['error']); } $this->Application->StoreVar('current_usps_shipping_types', serialize($shipping_types)); return $shipping_types; } function TrackOrder($TrackingNumber='') { if ( $TrackingNumber != '' ) { // http://testing.shippingapis.com/ShippingAPITest.dll?API=TrackV2&XML=<TrackFieldRequest USERID="402INTEC7634"><TrackID ID="EJ958083578US"></TrackID></TrackFieldRequest> $request = '<TrackRequest USERID="'.$this->usps_userid.'"><TrackID ID="'.$TrackingNumber.'"></TrackID></TrackRequest>'; $api_query = 'TrackV2'; $request = 'API='.$api_query.'&XML='.urlencode($request); $body = $this->PostQuery($request); // check for errors if (strpos($body, '<Error>') !== false) { $errors = Array (); preg_match_all('/<Number>(.*?)<\/Number>/s', $body, $error_numbers); preg_match_all('/<Description>(.*?)<\/Description>/s', $body, $error_descriptions); foreach ($error_numbers[1] as $index => $error_number) { $errors[$index] = $error_descriptions[1][$index]; if ($this->Application->isDebugMode()) { $errors[$index] .= ' (' . $error_number . ')'; } } $errors = array_unique($errors); // we may have same errors on many packages, so don't show duplicates return Array('error' => implode('<br/>', $errors)); } $xml_helper =& $this->Application->recallObject('kXMLHelper'); $root_node =& $xml_helper->Parse($body); /* @var $root_node kXMLNode */ // Tracking Shipping $delivery_node =& $root_node->FindChild('TrackInfo'); $TrackSummary = $delivery_node->FindChildValue('TrackSummary'); // echo ' TrackSummary ('.$TrackingNumber.') = '.$TrackSummary.'<br>'; return strpos($TrackSummary, 'delivered') !== false ? 1 : 0; } else return false; } function ProcessTrackOrders() { $sql = sprintf('SELECT `OrderId`, `ShippingTracking` FROM %s WHERE `Status` > 3 AND `Delivered` = 0', TABLE_PREFIX.'Orders'); $orders = $this->Application->Conn->Query($sql); foreach ( $orders as $k => $order ) { // try to track order if ( $order['ShippingTracking'] != '' && $this->TrackOrder($order['ShippingTracking']) ) { $update_order = sprintf("UPDATE %s SET `Delivered` = 1 WHERE %s = %s", TABLE_PREFIX.'Orders', $this->Application->getUnitOption('ord', 'IDField'), $order[$this->Application->getUnitOption('ord', 'IDField')] ); $this->Application->Conn->Query($update_order); } } } function PostQuery($request, $secure=0) { switch (MODULE_SHIPPING_USPS_SERVER) { case 'production': $usps_server = $secure > 0 ? 'https://secure.shippingapis.com' : 'http://production.shippingapis.com' ; $api_dll = 'ShippingAPI.dll'; break; case 'test': $usps_server = $secure > 0 ? 'https://secure.shippingapis.com' : 'http://testing.shippingapis.com'; $api_dll = 'ShippingAPITest.dll'; break; } $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $usps_server.'/'.$api_dll.'?'.$request); curl_setopt($curl, CURLOPT_HEADER, 0); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); $body = curl_exec($curl); curl_close($curl); if (defined('USPS_LOG_FILE')) { $filename = USPS_LOG_FILE; if ( !$fp = fopen($filename, "a") ) echo("Failed opening file $filename"); } $request_url = sprintf("Date %s : IP %s\n\nPost\n\n%s\n\nReplay\n\n%s\n\n", date("m/d/Y H:i:s",time()), $_SERVER['REMOTE_ADDR'], $usps_server.'/'.$api_dll.'?'.urldecode($request), $body ); if (defined('USPS_LOG_FILE')) { if (!fwrite($fp, $request_url)) echo("Failed writing to file $filename"); fclose($fp); } return $body; } function GetAvailableTypes() { return array(); $conn =& $this->Application->GetADODBConnection(); $types = $conn->Query('SELECT * FROM '.TABLE_PREFIX.'ShippingType'); $ret = array(); foreach ($types as $a_type) { $a_type['_ClassName'] = get_class($this); $a_type['_Id'] = 'CUST_'.$a_type['ShippingID']; $a_type['_Name'] = '(Custom) '.$a_type['Name']; $ret[] = $a_type; } return $ret; } function LoadParams() { $sql = 'SELECT Properties FROM '.$this->Application->getUnitOption('sqe', 'TableName').' WHERE ClassName="USPS"'; $db =& $this->Application->GetADODBConnection(); return unserialize($db->GetOne($sql)); } function _prepare_xml_param($value) { return strip_tags($value); } }