* * $Id: USPSRequest.class.php 502 2009-02-10 22:09:53Z erikwinn $ *@version 0.1 * *@copyright (C) 2008 by Erik Winn *@license GPL v.2 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA * *@package Quasi * @subpackage Classes */ class USPSRequest extends ShippingRequest { /** *@var string Indicates enumeration value for size (REGULAR, LARGE, OVERSIZE) */ protected $strSize = "REGULAR"; /** *@var string Indicates enumeration value for FIRST CLASS "type" [LETTER | FLAT | PARCEL] */ protected $strFirstClassMailType = "FLAT"; /** *@var ShippingRequest */ protected $objEndiciaRequest; /** * USPSRequest Constructor * * @param ShippingMethod objShippingMethod - the method for which to obtain estimate */ public function __construct(ShippingMethod $objShippingMethod) { try { parent::__construct($objShippingMethod); } catch (QCallerException $objExc) { $objExc->IncrementOffset(); throw $objExc; } ///@todo this is currently defined in quasi_config - fixme!! $this->RemoteAccountId = USPS_USERID; $this->RemotePassword = USPS_PASSWORD; if($this->blnTestMode) { $this->strRemoteDomainName = 'testing.shippingapis.com'; $this->strRemoteCgiUrl = '/ShippingAPITest.dll?'; } else { $this->strRemoteDomainName = 'production.shippingapis.com'; $this->strRemoteCgiUrl = '/ShippingAPI.dll?'; } $this->UseSsl = false; } //Public interface .. /** * Returns a shipping label for this method to the order address * Note that we use the Endicia label server for this - it must be enabled * and you must have a configured account with Endicia for this to work! *@return resource gd image object containing label image */ public function GetLabel() { $this->objEndiciaRequest = new EndiciaRequest($this->objShippingMethod); $this->objShippingLabelImage = $this->objEndiciaRequest->GetLabel(); $this->blnHasErrors = $this->objEndiciaRequest->HasErrors; $this->strErrors = $this->objEndiciaRequest->Errors; $this->aryExtraDocumentImages = $this->objEndiciaRequest->ExtraDocumentImages; $this->aryCustomsFormImages = $this->objEndiciaRequest->CustomsFormImages; return $this->objShippingLabelImage; } /** * Returns a shipping rate for the order for this method *@return image object containing the image code */ public function GetRate() { $this->createRequest(ShippingRequestType::Rate, WebRequestType::GET); $this->submitRequest(); return $this->Rate; } //Request string creators /** * Creates a rate request - if the shipping is international this call createIntlRateRequest instead .. */ protected function createRateRequest() { if($this->Order->IsInternational) return $this->createIntlRateRequest(); $this->strApiName = 'API=RateV3'; if($this->blnTestMode) { $this->OriginZip = '10022'; $this->DestinationZip = '20008'; $this->Ounces = 5; $this->Pounds = 10; $this->Container = 'Flat Rate Box'; } $this->strRequest = '&XML='; $strXml = ''; $strXml .= ''; $strXml .= '' . $this->ServiceType . ''; $strXml .= '' . $this->strFirstClassMailType . ''; $strXml .= '' . $this->OriginZip . ''; $strXml .= '' . $this->DestinationZip . ''; $strXml .= '' . $this->Pounds . ''; $strXml .= '' . round($this->Ounces, 2) . ''; $strXml .= '' . $this->Container .''; $strXml .= '' . $this->strSize . ''; $strXml .= '' . ($this->IsMachinable ? 'True' : 'False') . ''; $strXml .= ''; $strXml = urlencode($strXml); $this->strRequest .= $strXml; } /** * Creates an international shipping rate request */ protected function createIntlRateRequest() { //USPS testing servers do not support international - force to production .. $this->strRemoteDomainName = 'production.shippingapis.com'; $this->strRemoteCgiUrl = '/ShippingAPI.dll?'; //neato - USPS has decided that it prefers "Great Britain" and not "United Kingdom" .. $strCountry = strtoupper($this->DestinationCountry); if($strCountry == "UNITED KINGDOM") $strCountry = 'GREAT BRITAIN'; $this->strApiName = 'API=IntlRate'; $this->strRequest = '&XML='; $strXml = ''; $strXml .= ''; $strXml .= '' . $this->Pounds . ''; $strXml .= '' . round($this->Ounces, 2) . ''; $strXml .= 'Package'; $strXml .= '' . $strCountry . ''; $strXml .= ''; $strXml = urlencode($strXml); $this->strRequest .= $strXml; } //Response handlers /** * Handles a rate request response */ protected function handleRateResponse() { if ($this->objShippingMethod->Order->IsInternational) $objDomDoc = $this->getDomDocument('IntlRateResponse'); else $objDomDoc = $this->getDomDocument('RateV3Response'); if($objDomDoc) { $strErrorMessage = $this->requestErrors($objDomDoc); if($strErrorMessage) { $this->blnIsAvailable = false; $this->blnHasErrors = true; $this->strErrors = $strErrorMessage; $this->Rate = 0; } else { $this->blnIsAvailable = true; if( $this->objShippingMethod->Order->IsInternational) return $this->handleIntlRateResponse($objDomDoc); $nodeList = $objDomDoc->getElementsByTagName('Postage'); $nodeList = $nodeList->item(0)->getElementsByTagName('Rate'); if($nodeList->length > 0) $this->Rate = $nodeList->item(0)->nodeValue; else $this->Rate = 0; } } else { $this->blnIsAvailable = false; $this->HasErrors = true; $this->Errors = 'Unknown USPS error ..Request:' . $this->strRequest . ' Response:' . $this->strResponse; $this->Rate = 0; } } /** * Handles an International rate request response. * USPS returns multiple rates for some areas (eg. Australia) and we need to parse the DOM to * return the correct rate for the method (service type). * @todo - more sophisticated and configurable matching by package type; currently we mostly grab the first match .. * Here is a typical response for the Service nodes: 00.07PackageROMANIA 79.951 - 3 Days\ Global Express GuaranteedMax. length 46", width 35", height 46" and max. length plus girth 108"70 00.07PackageROMANIA 79.951 - 3 Days Global Express Guaranteed Non-Document RectangularMax. length 46", width 35", height 46" and max. length plus girth 108"70 00.07PackageROMANIA 79.951 - 3 Days Global Express Guaranteed Non-Document Non-RectangularMax. length 46", width 35", height 46" and max. length plus girth 108"70 00.07PackageROMANIA 79.951 - 3 Days USPS GXG EnvelopesCardboard envelope has a dimension of 9 1/2" X 12 1/2" and GXG tyvek envelope has a dimension of 12 1/2" X 15 1/2"70 00.07PackageROMANIA 25.958 Days Express Mail International (EMS)Max.length 36", max. length plus girth 79"70 00.07PackageROMANIA 25.958 Days Express Mail International (EMS) Flat-Rate Envelope9 1/2" X 12 1/2"70 00.07PackageROMANIA 21.506 - 10 Days Priority Mail InternationalMax. length 42", Max length plus girth combined 79"70 00.07PackageROMANIA 11.956 - 10 Days Priority Mail International Flat-Rate EnvelopeUSPS-supplied Priority Mail flat-rate envelope 9 1/2" x 12 1/2." Maximum weight 4 pounds.4 00.07PackageROMANIA 38.956 - 10 Days Priority Mail International Flat-Rate BoxUSPS-supplied Priority Mail flat-rate box. Maximum weight 20 pounds.20 00.07PackageROMANIA 49.956 - 10 Days Priority Mail International Large Flat-Rate BoxUSPS-supplied Priority Mail Large flat-rate box. Maximum weight 20 pounds.20 00.07PackageROMANIA 0.94Varies First Class Mail International LettersMax. length 11.5", height 6 1/8" or more than 1/4" thick0.2188 1.20Varies First Class Mail International Large EnvelopeMax. length 15", height 12 or more than 3/4" thick4 00.07PackageROMANIA 1.40Varies First Class Mail International PackageMax. length 24", max length, height and depth (thickness) combined 36"4 * */ protected function handleIntlRateResponse($objDomDoc) { $this->Rate = 0; $nodeList = $objDomDoc->getElementsByTagName('Service'); if($nodeList->length > 0) { foreach($nodeList as $objNode) { $nodeSvcDescription = $objNode->getElementsByTagName('SvcDescription'); $nodePostage = $objNode->getElementsByTagName('Postage'); if($nodeSvcDescription->length > 0) { $strSvcDesc = $nodeSvcDescription->item(0)->nodeValue; if(false !== stripos($strSvcDesc , $this->ServiceType )) { //these are examples - not written in stone (they just happen to work for me now ..): if('First Class Mail International' == $this->ServiceType ) { if(false === stripos($strSvcDesc , 'Large Envelope' )) continue; else { $this->Rate = $nodePostage->item(0)->nodeValue; break; } } if('Priority Mail International' == $this->ServiceType ) { if(false === stripos($strSvcDesc , 'Flat-Rate Envelope' )) continue; else { $this->Rate = $nodePostage->item(0)->nodeValue; break; } } } } } } } /** * Utility function to check for request errors - returns either a string containing * server error messages or false if there were none. *@param DOMDocument objDomDoc - the server response .. *@return string | boolean error messages or false if request succeeded. */ private function requestErrors($objDomDoc) { $mixToReturn = false; $nodeListErrors = $objDomDoc->getElementsByTagName('Error'); if( $nodeListErrors->length > 0 ) { $this->blnHasErrors = true; $mixToReturn = 'Request: ' . $this->strRequest; $nodeListErrorMessages = $objDomDoc->getElementsByTagName('Description'); if( $nodeListErrorMessages->length) $mixToReturn .= ' Message: ' . $nodeListErrorMessages->item(0)->nodeValue; } return $mixToReturn; } /*************************************************************************************/ ///@todo - implement me: /** * Returns an account status report *@return string containing the status report */ public function GetAccountStatus() { throw new QCallerException(sprintf('USPSRequest: Shipping request type %s unsupported! ', ShippingRequestType::ToString($this->ShippingRequestType)) ); } /** * Returns whether this method is available for the order address *@return boolean true if method is available */ public function GetAvailability() { throw new QCallerException(sprintf('USPSRequest: Shipping request type %s unsupported! ', ShippingRequestType::ToString($this->ShippingRequestType)) ); } /** * Submits an account credit payment *@return boolean true on success */ public function CreditAccount() { throw new QCallerException(sprintf('USPSRequest: Shipping request type %s unsupported! ', ShippingRequestType::ToString($this->ShippingRequestType)) ); } //Request string creators /** * Creates a method available request */ protected function createAvailabilityRequest() { throw new QCallerException(sprintf('USPSRequest: Shipping request type %s unsupported! ', ShippingRequestType::ToString($this->ShippingRequestType)) ); } /** * Creates a label printing request */ protected function createLabelRequest() { throw new QCallerException(sprintf('USPSRequest: Shipping request type %s unsupported! ', ShippingRequestType::ToString($this->ShippingRequestType)) ); } /** * Creates a request submitting an account credit payment */ protected function createCreditAccountRequest() { throw new QCallerException(sprintf('USPSRequest: Shipping request type %s unsupported! ', ShippingRequestType::ToString($this->ShippingRequestType)) ); } /** * Creates an account status request */ protected function createAccountStatusRequest() { throw new QCallerException(sprintf('USPSRequest: Shipping request type %s unsupported! ', ShippingRequestType::ToString($this->ShippingRequestType)) ); } /** * Handles an account status request */ protected function handleAccountStatusResponse() { throw new QCallerException(sprintf('USPSRequest: Shipping request type %s unsupported! ', ShippingRequestType::ToString($this->ShippingRequestType)) ); } /** * Handles a request submitting an account credit payment */ protected function handleCreditAccountResponse() { throw new QCallerException(sprintf('USPSRequest: Shipping request type %s unsupported! ', ShippingRequestType::ToString($this->ShippingRequestType)) ); } /** * Handles a method available request */ protected function handleAvailabilityResponse() { throw new QCallerException(sprintf('USPSRequest: Shipping request type %s unsupported! ', ShippingRequestType::ToString($this->ShippingRequestType)) ); } /** * Handles a label request */ protected function handleLabelResponse() { throw new QCallerException(sprintf('USPSRequest: Shipping request type %s unsupported! ', ShippingRequestType::ToString($this->ShippingRequestType)) ); } /*************************************************************************************/ }//end class }//end define ?>