A QCodo powered CMS
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

755 lines
28 KiB

  1. <?php
  2. if(!defined('QUINTACMS') ) die('No Quinta.');
  3. if (!defined("FEDEXREQUEST.CLASS.PHP")){
  4. define("FEDEXREQUEST.CLASS.PHP",1);
  5. /**
  6. * Class FedexRequest - provides services for Fedex Direct Connect
  7. * This class implements the FEDEX SOAP API for web service requests.
  8. *
  9. * NOTE: ServiceType as configured in the database must be one of the following:
  10. * - EUROPE_FIRST_INTERNATIONAL_PRIORITY
  11. * - FEDEX_1_DAY_FREIGHT
  12. * - FEDEX_2_DAY
  13. * - FEDEX_2_DAY_FREIGHT
  14. * - FEDEX_3_DAY_FREIGHT
  15. * - FEDEX_EXPRESS_SAVER
  16. * - FEDEX_GROUND
  17. * - FIRST_OVERNIGHT
  18. * - GROUND_HOME_DELIVERY
  19. * - INTERNATIONAL_ECONOMY
  20. * - INTERNATIONAL_ECONOMY_FREIGHT
  21. * - INTERNATIONAL_FIRST
  22. * - INTERNATIONAL_PRIORITY
  23. * - INTERNATIONAL_PRIORITY_FREIGHT
  24. * - PRIORITY_OVERNIGHT
  25. * - STANDARD_OVERNIGHT
  26. *
  27. *@todo - document this class ..
  28. *
  29. *@author Erik Winn <sidewalksoftware@gmail.com>
  30. *
  31. *@version 0.3
  32. *
  33. *@package Quinta
  34. * @subpackage Classes
  35. */
  36. class FedexRequest extends ShippingRequest
  37. {
  38. /**
  39. * NOTE: LabelStockType must be one of the following:
  40. * - PAPER_4X6
  41. * - PAPER_4X8
  42. * - PAPER_4X9
  43. * - PAPER_7X4.75
  44. * - PAPER_8.5X11_BOTTOM_HALF_LABEL
  45. * - PAPER_8.5X11_TOP_HALF_LABEL
  46. * - STOCK_4X6
  47. * - STOCK_4X6.75_LEADING_DOC_TAB
  48. * - STOCK_4X6.75_TRAILING_DOC_TAB
  49. * - STOCK_4X8
  50. * - STOCK_4X9_LEADING_DOC_TAB
  51. * - STOCK_4X9_TRAILING_DOC_TAB
  52. * @var string
  53. */
  54. protected $strLabelStockType = 'PAPER_4X6';
  55. /**
  56. * NOTE: LabelFormatType must be one of the following:
  57. * COMMON2D
  58. * LABEL_DATA_ONLY
  59. *@var string paper format for label
  60. */
  61. protected $strLabelFormatType = 'COMMON2D';
  62. /**
  63. * NOTE: ImageType must be one of the following:
  64. * DPL
  65. * EPL2
  66. * PDF
  67. * PNG
  68. * ZPLII
  69. *@var string image format for label
  70. */
  71. protected $strImageType = 'PNG';
  72. /**
  73. *@var string Account number provided by Fedex
  74. */
  75. protected $strAccountNumber;
  76. /**
  77. *@var string Meter number provided by Fedex
  78. */
  79. protected $strMeterNumber;
  80. /**
  81. *@var string strPayorType - who pays for the shipping
  82. */
  83. protected $strPayorType = 'SENDER';
  84. /**
  85. * NOTE: DropoffType must be one of the following:
  86. * - BUSINESS_SERVICE_CENTER
  87. * - DROP_BOX
  88. * - REGULAR_PICKUP
  89. * - REQUEST_COURIER
  90. * - STATION
  91. *@var string Drop off method
  92. */
  93. protected $strDropoffType = 'REGULAR_PICKUP';
  94. /**
  95. * NOTE: PackagingType must be one of the following:
  96. * - FEDEX_10KG_BOX
  97. * - FEDEX_25KG_BOX
  98. * - FEDEX_BOX
  99. * - FEDEX_ENVELOPE
  100. * - FEDEX_PAK
  101. * - FEDEX_TUBE
  102. * - YOUR_PACKAGING
  103. *@var string Packaging type
  104. */
  105. protected $strPackagingType = 'YOUR_PACKAGING';
  106. /**
  107. *@var string Indicates units of weight (LB | KG)
  108. */
  109. protected $strWeightUnits = 'LB';
  110. /**
  111. *@var string Indicates units of length (IN | CM)
  112. */
  113. protected $strLengthUnits = 'IN';
  114. /**
  115. * FedEx combines units (eg. pounds and ounces ) into one figure ..
  116. *@var float fltWeight - weight of package in designated units ..
  117. */
  118. protected $fltWeight;
  119. /**
  120. * FedexRequest Constructor
  121. *
  122. * @param ShippingMethod objShippingMethod - the method for which to obtain estimate
  123. */
  124. public function __construct(ShippingMethod $objShippingMethod){
  125. parent::__construct($objShippingMethod);
  126. //now unused ..
  127. $this->strRemoteCgiUrl = '/GatewayDC';
  128. if($objShippingMethod->TestMode){///@todo defined in config - fixme!
  129. $this->strRemoteDomainName = 'gatewaybeta.fedex.com';
  130. $this->strRemoteAccountId = FEDEX_TESTKEY;
  131. $this->strRemotePassword = FEDEX_TESTPASSWORD;
  132. $this->strAccountNumber = FEDEX_TESTACCOUNT_NUMBER;
  133. $this->strMeterNumber = FEDEX_TESTMETER_NUMBER;
  134. $this->strRemoteDomainName = 'gatewaybeta.fedex.com'; //unused ..
  135. }else{
  136. $this->strRemoteDomainName = 'gateway.fedex.com';
  137. $this->strRemoteAccountId = FEDEX_KEY;
  138. $this->strRemotePassword = FEDEX_PASSWORD;
  139. $this->strAccountNumber = FEDEX_ACCOUNT_NUMBER;
  140. $this->strMeterNumber = FEDEX_METER_NUMBER;
  141. $this->strRemoteDomainName = 'gateway.fedex.com'; //unused ..
  142. }
  143. $this->fltWeight = $objShippingMethod->Pounds;
  144. $this->fltWeight += $objShippingMethod->Ounces / 16;
  145. }
  146. //Public interface ..
  147. /**
  148. * Returns a shipping rate for the order for this method
  149. *@return image object containing the image code
  150. */
  151. public function GetRate(){
  152. $this->intShippingRequestType = ShippingRequestType::Rate;
  153. //Example code does this - not sure why or if it is needed ..
  154. ini_set("soap.wsdl_cache_enabled", "0");
  155. if($this->blnTestMode)
  156. $this->strWsdlUri = __QUINTA_CORE__ . '/utilities/FDXRateService_v5_test.wsdl';
  157. else
  158. $this->strWsdlUri = __QUINTA_CORE__ . '/utilities/FDXRateService_v5.wsdl';
  159. $this->strSoapFunction = 'getRates';
  160. $this->createSoapRateRequest();
  161. try{
  162. $this->submitSoapRequest();
  163. } catch (SoapFault $objFault) {
  164. // exit(var_dump($objFault));
  165. throw new Exception($objFault->faultstring);
  166. }
  167. $this->handleRateResponse();
  168. return $this->Rate;
  169. }
  170. /**
  171. * Utility function to format the mult-dimensional array of RateRequest (labels) request data
  172. * to be passed on the the Soap client function.
  173. *@return array - suitable for passing to the Soap function for RateRequest (labels) requests
  174. */
  175. protected function createSoapRateRequest(){
  176. $arySoapParamsToReturn['WebAuthenticationDetail'] = $this->createWebAuthDetailArray();
  177. $arySoapParamsToReturn['ClientDetail'] = $this->createClientDetailArray();
  178. $arySoapParamsToReturn['TransactionDetail'] = $this->createTransactionDetailArray();
  179. $arySoapParamsToReturn['Version'] = $this->createVersionDetailArray('crs');
  180. $aryRequestedShipment = $this->createRequestedShipmentDetailArray();
  181. $aryRequestedShipment['PackageDetail'] = 'INDIVIDUAL_PACKAGES';
  182. $arySoapParamsToReturn['RequestedShipment'] = $aryRequestedShipment;
  183. $this->arySoapRequest = $arySoapParamsToReturn;
  184. }
  185. /**
  186. * Parses the rate request SOAP response from FEDEX server
  187. * @todo - handle errors more elegantly, make more robust ..
  188. */
  189. protected function handleRateResponse(){
  190. $this->Rate = 0;
  191. //first check for Fedex errors
  192. if('FAILURE' == $this->mixSoapResponse->HighestSeverity
  193. || 'ERROR' == $this->mixSoapResponse->HighestSeverity )
  194. {
  195. $this->blnIsAvailable = false;
  196. $this->blnHasErrors = true;
  197. $mixNotifications = $this->mixSoapResponse->Notifications;
  198. if(is_array($mixNotifications))
  199. foreach($mixNotifications as $objNotification)
  200. $this->strErrors .= $objNotification->Severity . ':' . $objNotification->Message . "\n";
  201. else
  202. $this->strErrors .= $mixNotifications->Severity . ' : ' .$mixNotifications->Message . "\n";
  203. //Service is not allowed to destination - eg. fedex_2_day to Australia .. not really
  204. // an error, just not available so reset HasErrors:
  205. if( false !== stripos( $this->strErrors, 'Service is not allowed' ) )
  206. $this->blnHasErrors = false;
  207. } else {
  208. $this->blnIsAvailable = true;
  209. $mixDetails = $this->mixSoapResponse->RateReplyDetails->RatedShipmentDetails;
  210. if(is_array($mixDetails))
  211. $this->Rate = $mixDetails[0]->ShipmentRateDetail->TotalNetCharge->Amount;
  212. else
  213. $this->Rate = $mixDetails->ShipmentRateDetail->TotalNetCharge->Amount;
  214. }
  215. if($this->blnIsAvailable && !$this->Rate){
  216. $this->blnIsAvailable = false;
  217. $this->HasErrors = true;
  218. $this->strErrors = 'Unknown Fedex Error.';
  219. }
  220. return $this->Rate;
  221. }
  222. /**
  223. * Returns a shipping label for this method to the order address
  224. * Note: this function uses the SOAP interface provided from Fedex - partially due to time constraints
  225. * and partially to remind me to reimplement these service request classes to use SOAP; all of the
  226. * web services seem to be providing WSDLs now and it is builtin to PHP so it makes sense. Later.
  227. * This quick example implements the "new" style Fedex API.
  228. *@return string containing label image or null on failure ..
  229. */
  230. public function GetLabel(){
  231. $this->intShippingRequestType = ShippingRequestType::Label;
  232. //Example code does this - not sure why or if it is needed ..
  233. ini_set("soap.wsdl_cache_enabled", "0");
  234. if($this->blnTestMode)
  235. $this->strWsdlUri = __QUINTA_CORE__ . '/utilities/FDXShipService_v5_test.wsdl';
  236. else
  237. $this->strWsdlUri = __QUINTA_CORE__ . '/utilities/FDXShipService_v5.wsdl';
  238. $this->strSoapFunction = 'processShipment';
  239. $this->createSoapShipRequest();
  240. try{
  241. $this->submitSoapRequest();
  242. } catch (SoapFault $objFault) {
  243. // exit(var_dump($objFault));
  244. throw new Exception($objFault->faultstring . ' - '
  245. . $objFault->detail->fault->details->ValidationFailureDetail->message);
  246. }
  247. return $this->handleLabelResponse();
  248. }
  249. /**
  250. * Handles a label response returning the image. On failure the image is null and errors are
  251. * stored in strErrors. The image is the string containing the formatted image from Fedex - this
  252. * can be written directly to a file as eg. image.png or image.pdf ..
  253. * @return string containing the image code returned from Fedex
  254. */
  255. protected function handleLabelResponse(){
  256. $this->objShippingLabelImage = null;
  257. //first check for Fedex errors
  258. if('FAILURE' == $this->mixSoapResponse->HighestSeverity
  259. || 'ERROR' == $this->mixSoapResponse->HighestSeverity )
  260. {
  261. $this->blnHasErrors = true;
  262. //Note: this part is unclear - Notifications can be an array or object?? May not work correctly ..
  263. if(is_array($this->mixSoapResponse->Notifications))
  264. foreach($this->mixSoapResponse->Notifications as $objNotification)
  265. $this->strErrors .= $objNotification->Severity . ':' . $objNotification->Message . "\n";
  266. else
  267. $this->strErrors .= $this->mixSoapResponse->Notifications->Severity
  268. . ': ' . $this->mixSoapResponse->Notifications->Message . "\n";
  269. } else {
  270. $objPackageDetails = $this->mixSoapResponse->CompletedShipmentDetail->CompletedPackageDetails;
  271. $this->objShippingLabelImage = $objPackageDetails->Label->Parts->Image;
  272. $strFinalPrice = $this->mixSoapResponse->CompletedShipmentDetail->ShipmentRating;
  273. if(!empty($strFinalPrice))
  274. {
  275. $this->objShippingMethod->Order->ShippingCost = $strFinalPrice;
  276. $this->objShippingMethod->Order->Save(false,true);
  277. }
  278. if($this->Order->IsInternational){
  279. if(property_exists($objPackageDetails, 'PackageDocuments')){
  280. $aryPackageDocuments = $objPackageDetails->PackageDocuments;
  281. foreach($aryPackageDocuments as $objDocument){
  282. $this->aryExtraDocumentImages[] = new ExtraDocumentImage(
  283. $objDocument->Parts->Image,
  284. $objDocument->Type,
  285. $objDocument->CopiesToPrint
  286. );
  287. }
  288. }
  289. }
  290. $strTrackingNumber = $objPackageDetails->TrackingId->TrackingNumber;
  291. if(!empty($strTrackingNumber)){
  292. if(! TrackingNumber::LoadByOrderIdNumber($this->objShippingMethod->OrderId, $strTrackingNumber )){
  293. $objTrackingNumber = new TrackingNumber();
  294. $objTrackingNumber->OrderId = $this->objShippingMethod->OrderId;
  295. $objTrackingNumber->Number = $strTrackingNumber;
  296. $objTrackingNumber->Save();
  297. }
  298. }
  299. }
  300. return $this->objShippingLabelImage;
  301. }
  302. /**
  303. * Utility function to format the mult-dimensional array of ShipRequest (labels) request data
  304. * to be passed on the the Soap client function.
  305. *@return array - suitable for passing to the Soap function for ShipRequest (labels) requests
  306. */
  307. protected function createSoapShipRequest(){
  308. $arySoapParamsToReturn['WebAuthenticationDetail'] = $this->createWebAuthDetailArray();
  309. $arySoapParamsToReturn['ClientDetail'] = $this->createClientDetailArray();
  310. $arySoapParamsToReturn['TransactionDetail'] = $this->createTransactionDetailArray();
  311. $arySoapParamsToReturn['Version'] = $this->createVersionDetailArray('ship');
  312. $aryRequestedShipment = $this->createRequestedShipmentDetailArray();
  313. $aryRequestedShipment['LabelSpecification'] = array('LabelFormatType' => $this->strLabelFormatType,
  314. 'ImageType' => $this->strImageType,
  315. 'LabelStockType' => $this->strLabelStockType,
  316. );
  317. $arySoapParamsToReturn['RequestedShipment'] = $aryRequestedShipment;
  318. $this->arySoapRequest = $arySoapParamsToReturn;
  319. }
  320. protected function createWebAuthDetailArray(){
  321. return array('UserCredential' => array('Key' => $this->strRemoteAccountId,
  322. 'Password' => $this->strRemotePassword
  323. )
  324. );
  325. }
  326. protected function createClientDetailArray(){
  327. return array('AccountNumber' => $this->strAccountNumber,
  328. 'MeterNumber' => $this->strMeterNumber
  329. );
  330. }
  331. protected function createTransactionDetailArray(){
  332. return array('CustomerTransactionId' => STORE_NAME . ' Order ' . $this->Order->Id );
  333. }
  334. protected function createVersionDetailArray($strService){
  335. return array('ServiceId' => $strService, 'Major' => '5', 'Intermediate' => '0', 'Minor' => '0');
  336. }
  337. protected function createShipperDetailArray(){
  338. $strSenderCountryCode = CountryType::ToIsoCode2( $this->OriginCountryId);
  339. $arySenderStreets = array();
  340. if(STORE_ADDRESS1)
  341. $arySenderStreets[] = STORE_ADDRESS1;
  342. if(STORE_ADDRESS2)
  343. $arySenderStreets[] = STORE_ADDRESS2;
  344. if(! count($arySenderStreets))
  345. throw new Exception('FedexRequest: Shipper address must have at least one street line!');
  346. return array('Contact' => array('PersonName' => STORE_OWNER,
  347. 'CompanyName' => STORE_NAME,
  348. 'PhoneNumber' => STORE_PHONE,
  349. ),
  350. 'Address' => array('StreetLines' =>$arySenderStreets,
  351. 'City' => STORE_CITY,
  352. 'StateOrProvinceCode' => STORE_STATE,
  353. 'PostalCode' => STORE_POSTAL_CODE,
  354. 'CountryCode' => $strSenderCountryCode,
  355. ),
  356. );
  357. }
  358. protected function createRecipientDetailArray(){
  359. $strRecipientName = $this->Order->FullShippingName;
  360. $strRecipientCompany = $this->Order->ShippingCompany;
  361. $strRecipientCity = $this->Order->ShippingCity;
  362. $strRecipientPhone = $this->Order->Account->Person->PhoneNumber;
  363. $aryRecipientStreets = array();
  364. if($this->Order->ShippingStreet1)
  365. $aryRecipientStreets[] = $this->Order->ShippingStreet1;
  366. if($this->Order->ShippingStreet2)
  367. $aryRecipientStreets[] = $this->Order->ShippingStreet2;
  368. if(! count($aryRecipientStreets))
  369. throw new Exception('FedexRequest: Recipient address must have at least one street line!');
  370. $aryToReturn = array('Contact' => array('PersonName' => $strRecipientName,
  371. 'CompanyName' => $strRecipientCompany,
  372. 'PhoneNumber' => $strRecipientPhone,
  373. ),
  374. 'Address' => array('StreetLines' => $aryRecipientStreets,
  375. 'City' => $strRecipientCity,
  376. //Fedex barfs if the state or province is over 2 chars ..
  377. 'StateOrProvinceCode' => substr($this->DestinationStateCode, 0, 2),
  378. 'PostalCode' => $this->DestinationZip,
  379. 'CountryCode' => $this->DestinationCountryCode,
  380. ),
  381. );
  382. return $aryToReturn;
  383. }
  384. protected function createRequestedShipmentDetailArray(){
  385. $strSenderCountryCode = CountryType::ToIsoCode2( $this->OriginCountryId);
  386. $aryToReturn = array( 'ShipTimestamp' => date('c'),
  387. 'DropoffType' => $this->strDropoffType,
  388. 'ServiceType' => $this->ServiceType,
  389. 'PackagingType' => $this->strPackagingType,
  390. 'TotalWeight' => array(
  391. 'Value' => number_format($this->Weight, 1),
  392. 'Units' => $this->strWeightUnits,
  393. ),
  394. 'Shipper' => $this->createShipperDetailArray(),
  395. 'Recipient' => $this->createRecipientDetailArray(),
  396. 'ShippingChargesPayment' => array(
  397. 'PaymentType' => $this->strPayorType,
  398. 'Payor' => array('AccountNumber' => $this->strAccountNumber,
  399. 'CountryCode' => $strSenderCountryCode,
  400. ),
  401. ),
  402. 'RateRequestTypes' => array('ACCOUNT'), // valid values ACCOUNT and LIST
  403. 'PackageCount' => 1,
  404. 'RequestedPackages' => $this->createRequestedPackagesDetailArray(),
  405. );
  406. if($this->Order->IsInternational && ShippingRequestType::Label == $this->intShippingRequestType)
  407. $aryToReturn['InternationalDetail'] = $this->createInternationalDetailArray();
  408. return $aryToReturn;
  409. }
  410. protected function createInternationalDetailArray(){
  411. //start with "Contact" and "Address" populated ..
  412. $aryToReturn = $this->createShipperDetailArray();
  413. $aryToReturn['CustomsValue'] = array('Amount' => number_format($this->TotalValue, 2),
  414. 'Currency' => 'USD',
  415. );
  416. $aryToReturn['DocumentContent'] = 'DOCUMENTS_ONLY';
  417. $aryToReturn['DutiesPayment'] = array('PaymentType' => 'SENDER',
  418. 'Payor' => array('AccountNumber' => $this->strAccountNumber,
  419. 'CountryCode' => $this->DestinationCountryCode,
  420. ),
  421. );
  422. $aryCommodities = array();
  423. foreach( OrderItem::LoadArrayByOrderId( $this->Order->Id ) as $objOrderItem ){
  424. $objProduct = $objOrderItem->Product;
  425. $fltWeight = $objProduct->Weight / 16;
  426. if('KG' == $this->strWeightUnits )
  427. $fltWeight = $fltWeight / 2.2;
  428. $fltTotalAmount = $objProduct->RetailPrice * $objOrderItem->Quantity;
  429. $aryCommodities[] = array('NumberOfPieces' => $objOrderItem->Quantity,
  430. 'Description' => $objProduct->ShortDescription,
  431. 'CountryOfManufacture' => 'US',
  432. 'Weight' => array('Value' => $fltWeight,
  433. 'Units' => $this->strWeightUnits,
  434. ),
  435. 'Quantity' => $objOrderItem->Quantity,
  436. 'QuantityUnits' => 'EA',
  437. 'UnitPrice' => array('Amount' => $objProduct->RetailPrice,
  438. 'Currency' => 'USD',
  439. ),
  440. 'CustomsValue' => array('Amount' => $fltTotalAmount,
  441. 'Currency' => 'USD',
  442. ),
  443. );
  444. }
  445. if(empty($aryCommodities))
  446. throw new Exception('No order items for international Order #' . $this->Order->Id);
  447. $aryToReturn['Commodities'] = $aryCommodities;
  448. return $aryToReturn;
  449. }
  450. protected function createRequestedPackagesDetailArray(){
  451. return array('0' => array('SequenceNumber' => '1',
  452. 'Weight' => array(
  453. 'Value' => number_format($this->Weight, 1),
  454. 'Units' => $this->strWeightUnits,
  455. ),
  456. ),
  457. );
  458. }
  459. public function __get($strName){
  460. switch ($strName){
  461. case 'Weight':
  462. return $this->fltWeight ;
  463. case 'WeightUnits':
  464. return $this->strWeightUnits ;
  465. case 'LengthUnits':
  466. return $this->strLengthUnits ;
  467. case 'MeterNumber':
  468. return $this->strMeterNumber ;
  469. default:
  470. try {
  471. return parent::__get($strName);
  472. } catch (QCallerException $objExc) {
  473. $objExc->IncrementOffset();
  474. throw $objExc;
  475. }
  476. }
  477. }
  478. public function __set($strName, $mixValue){
  479. switch ($strName){
  480. case 'Weight':
  481. try {
  482. return ($this->fltWeight = QType::Cast($mixValue, QType::Float ));
  483. } catch (QInvalidCastException $objExc) {
  484. $objExc->IncrementOffset();
  485. throw $objExc;
  486. }
  487. case 'WeightUnits':
  488. try {
  489. return ($this->strWeightUnits = QType::Cast($mixValue, QType::String ));
  490. } catch (QInvalidCastException $objExc) {
  491. $objExc->IncrementOffset();
  492. throw $objExc;
  493. }
  494. case 'LengthUnits':
  495. try {
  496. return ($this->strLengthUnits = QType::Cast($mixValue, QType::String ));
  497. } catch (QInvalidCastException $objExc) {
  498. $objExc->IncrementOffset();
  499. throw $objExc;
  500. }
  501. case 'MeterNumber':
  502. try {
  503. return ($this->strMeterNumber = QType::Cast($mixValue, QType::String ));
  504. } catch (QInvalidCastException $objExc) {
  505. $objExc->IncrementOffset();
  506. throw $objExc;
  507. }
  508. default:
  509. try {
  510. return (parent::__set($strName, $mixValue));
  511. } catch (QCallerException $objExc) {
  512. $objExc->IncrementOffset();
  513. throw $objExc;
  514. }
  515. }
  516. }
  517. /*************************************************************************************/
  518. ///@todo - implement me:
  519. /**
  520. * Returns an account status report
  521. *@return string containing the status report
  522. */
  523. public function GetAccountStatus()
  524. {
  525. throw new QCallerException(sprintf('FEDEXRequest: Shipping request type %s unsupported! ',
  526. ShippingRequestType::ToString($this->ShippingRequestType)) );
  527. }
  528. /**
  529. * Returns whether this method is available for the order address
  530. *@return boolean true if method is available
  531. */
  532. public function GetAvailability()
  533. {
  534. throw new QCallerException(sprintf('FEDEXRequest: Shipping request type %s unsupported! ',
  535. ShippingRequestType::ToString($this->ShippingRequestType)) );
  536. }
  537. /**
  538. * Submits an account credit payment
  539. *@return boolean true on success
  540. */
  541. public function CreditAccount()
  542. {
  543. throw new QCallerException(sprintf('FEDEXRequest: Shipping request type %s unsupported! ',
  544. ShippingRequestType::ToString($this->ShippingRequestType)) );
  545. }
  546. //Request string creators
  547. /**
  548. * Creates a method available request
  549. */
  550. protected function createAvailabilityRequest()
  551. {
  552. throw new QCallerException(sprintf('FEDEXRequest: Shipping request type %s unsupported! ',
  553. ShippingRequestType::ToString($this->ShippingRequestType)) );
  554. }
  555. /**
  556. * Creates a label printing request
  557. */
  558. protected function createLabelRequest()
  559. {
  560. throw new QCallerException(sprintf('FEDEXRequest: Shipping request type %s unsupported! ',
  561. ShippingRequestType::ToString($this->ShippingRequestType)) );
  562. }
  563. /**
  564. * Creates a request submitting an account credit payment
  565. */
  566. protected function createCreditAccountRequest()
  567. {
  568. throw new QCallerException(sprintf('FEDEXRequest: Shipping request type %s unsupported! ',
  569. ShippingRequestType::ToString($this->ShippingRequestType)) );
  570. }
  571. /**
  572. * Creates an account status request
  573. */
  574. protected function createAccountStatusRequest()
  575. {
  576. throw new QCallerException(sprintf('FEDEXRequest: Shipping request type %s unsupported! ',
  577. ShippingRequestType::ToString($this->ShippingRequestType)) );
  578. }
  579. /**
  580. * Handles an account status request
  581. */
  582. protected function handleAccountStatusResponse()
  583. {
  584. throw new QCallerException(sprintf('FEDEXRequest: Shipping request type %s unsupported! ',
  585. ShippingRequestType::ToString($this->ShippingRequestType)) );
  586. }
  587. /**
  588. * Handles a request submitting an account credit payment
  589. */
  590. protected function handleCreditAccountResponse()
  591. {
  592. throw new QCallerException(sprintf('FEDEXRequest: Shipping request type %s unsupported! ',
  593. ShippingRequestType::ToString($this->ShippingRequestType)) );
  594. }
  595. /**
  596. * Handles a method available request
  597. */
  598. protected function handleAvailabilityResponse()
  599. {
  600. throw new QCallerException(sprintf('FEDEXRequest: Shipping request type %s unsupported! ',
  601. ShippingRequestType::ToString($this->ShippingRequestType)) );
  602. }
  603. /*************************************************************************************/
  604. /*********************** Old XML POST version functions - DEPRECATED **********************************
  605. These are left as examples and in case some would prefer to use them (as PHP SOAP is a bit buggy ..)
  606. public function GetRate()
  607. {
  608. $this->createRequest(ShippingRequestType::Rate, WebRequestType::POST);
  609. $this->submitRequest();
  610. return $this->Rate;
  611. }*/
  612. /**
  613. * Creates the POST string for the rate request - DEPRECATED
  614. */
  615. protected function createRateRequest()
  616. {
  617. $this->strRequest = '';
  618. $blnAddOriginStateAndZip = ( CountryType::UnitedStates == $this->OriginCountryId
  619. ||CountryType::Canada == $this->OriginCountryId );
  620. $blnAddDestinationStateAndZip = ( CountryType::UnitedStates == $this->DestinationCountryId
  621. ||CountryType::Canada == $this->DestinationCountryId );
  622. $str = $this->createXMLOpenTags('FDXRateRequest');
  623. $str .= '<RequestHeader>';
  624. // $str .= '<CustomerTransactionIdentifier>Express Rate</CustomerTransactionIdentifier>';
  625. $str .= '<AccountNumber>' . $this->strAccountNumber . '</AccountNumber>';
  626. $str .= '<MeterNumber>' . $this->strMeterNumber . '</MeterNumber>';
  627. $str .= '<CarrierCode>' . $this->Carrier . '</CarrierCode>';
  628. $str .= '</RequestHeader>';
  629. $str .= '<DropoffType>' . $this->strDropoffType . '</DropoffType>';
  630. $str .= '<Service>' . $this->ServiceType . '</Service>';
  631. $str .= '<Packaging>'.$this->strPackagingType . '</Packaging>';
  632. $str .= '<WeightUnits>'. $this->strWeightUnits . '</WeightUnits>';
  633. $str .= '<Weight>' . number_format($this->Weight, 1) . '</Weight>';
  634. $str .= '<OriginAddress>';
  635. if($blnAddOriginStateAndZip)
  636. {
  637. $str .= '<StateOrProvinceCode>' . $this->OriginStateCode . '</StateOrProvinceCode>';
  638. $str .= '<PostalCode>' . $this->OriginZip.'</PostalCode>';
  639. }
  640. $str .= '<CountryCode>' . $this->OriginCountryCode . '</CountryCode>';
  641. $str .= '</OriginAddress>';
  642. $str .= '<DestinationAddress>';
  643. if($blnAddDestinationStateAndZip)
  644. {
  645. $str .= '<StateOrProvinceCode>' . $this->DestinationStateCode . '</StateOrProvinceCode>';
  646. $str .= '<PostalCode>' . $this->DestinationZip.'</PostalCode>';
  647. }
  648. $str .= '<CountryCode>' . $this->DestinationCountryCode . '</CountryCode>';
  649. $str .= '</DestinationAddress>';
  650. $str .= '<Payment>';
  651. $str .= '<PayorType>' . $this->strPayorType . '</PayorType>';
  652. $str .= '</Payment>';
  653. $str .= '<PackageCount>1</PackageCount>';
  654. $str .= '</FDXRateRequest>';
  655. $this->strRequest = $str;
  656. }
  657. /**
  658. * Parses the rate request response from FEDEX server - XML POST VERSION: DEPRECATED.
  659. * @todo - handle errors more elegantly, make more robust ..
  660. */
  661. /* protected function handleRateResponse()
  662. {
  663. $objDomDoc = $this->getDomDocument('FDXRateReply');
  664. if($objDomDoc)
  665. {
  666. $strErrorMessage = $this->requestErrors($objDomDoc);
  667. if($strErrorMessage)
  668. {
  669. $this->blnHasErrors = true;
  670. $this->strErrors = $strErrorMessage;
  671. $this->Rate = 0;
  672. } else {
  673. $nodeList = $objDomDoc->getElementsByTagName('NetCharge');
  674. if($nodeList->length > 0)
  675. $this->Rate = $nodeList->item(0)->nodeValue;
  676. else
  677. $this->Rate = 0;
  678. }
  679. } else {
  680. $this->HasErrors = true;
  681. $this->Errors = 'Unknown FEDEX error ..Request:' . $this->strRequest . ' Response:' . $this->strResponse;
  682. $this->Rate = 0;
  683. }
  684. //debugging:
  685. // if(!$this->Rate)
  686. // {
  687. // $this->Errors = 'Unknown FEDEX error ..Request:' . $this->strRequest . ' Response:' . $this->strResponse;
  688. // die($this->Errors);
  689. // }
  690. }*/
  691. /**
  692. * Utility function to check for request errors - returns either a string containing
  693. * server error messages or false if there were none.
  694. *@param DOMDocument objDomDoc - the server response ..
  695. *@return string | boolean error messages or false if request succeeded.
  696. */
  697. private function requestErrors($objDomDoc)
  698. {
  699. $mixToReturn = false;
  700. $nodeListErrors = $objDomDoc->getElementsByTagName('Error');
  701. if( $nodeListErrors->length > 0 )
  702. {
  703. $this->blnHasErrors = true;
  704. $mixToReturn = 'Request: ' . $this->strRequest;
  705. $nodeListErrorMessages = $objDomDoc->getElementsByTagName('Message');
  706. if( $nodeListErrorMessages->length)
  707. $mixToReturn .= ' Message: ' . $nodeListErrorMessages->item(0)->nodeValue;
  708. }
  709. return $mixToReturn;
  710. }
  711. private function createXMLOpenTags($strApi)
  712. {
  713. $strToReturn = '<?xml version="1.0" encoding="UTF-8" ?>';
  714. $strToReturn .= sprintf('<%s xmlns:api="http://www.fedex.com/fsmapi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="%s.xsd">',
  715. $strApi, $strApi);
  716. return $strToReturn;
  717. }
  718. }//end class
  719. }//end define
  720. ?>