A modest collection of PHP libraries used at SparkFun.
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.

654 lines
20 KiB

  1. <?php
  2. namespace SparkLib\UPS;
  3. use SparkLib\UPS\Ship\AddressType,
  4. SparkLib\UPS\Ship\BillShipperType,
  5. SparkLib\UPS\Ship\CodeDescriptionType,
  6. SparkLib\UPS\Ship\ContactType,
  7. SparkLib\UPS\Ship\CurrencyMonetaryType,
  8. SparkLib\UPS\Ship\DimensionsType,
  9. SparkLib\UPS\Ship\InternationalFormType,
  10. SparkLib\UPS\Ship\LabelImageFormatType,
  11. SparkLib\UPS\Ship\LabelStockSizeType,
  12. SparkLib\UPS\Ship\LabelSpecificationType,
  13. SparkLib\UPS\Ship\PackageDeclaredValueType,
  14. SparkLib\UPS\Ship\PackageServiceOptionsType,
  15. SparkLib\UPS\Ship\PackageType,
  16. SparkLib\UPS\Ship\PackageWeightType,
  17. SparkLib\UPS\Ship\PaymentInfoType,
  18. SparkLib\UPS\Ship\PhoneType,
  19. SparkLib\UPS\Ship\ProductType,
  20. SparkLib\UPS\Ship\ProducerType,
  21. SparkLib\UPS\Ship\ProductWeightType,
  22. SparkLib\UPS\Ship\RateInfoType,
  23. SparkLib\UPS\Ship\ReferenceNumberType,
  24. SparkLib\UPS\Ship\RequestType,
  25. SparkLib\UPS\Ship\ServiceAccessToken,
  26. SparkLib\UPS\Ship\ServiceType,
  27. SparkLib\UPS\Ship\ShipperType,
  28. SparkLib\UPS\Ship\ShipAddressType,
  29. SparkLib\UPS\Ship\ShipFromType,
  30. SparkLib\UPS\Ship\ShipToType,
  31. SparkLib\UPS\Ship\ShipmentChargeType,
  32. SparkLib\UPS\Ship\ShipmentType,
  33. SparkLib\UPS\Ship\ShipmentRequest,
  34. SparkLib\UPS\Ship\ShipmentServiceOptionsType,
  35. SparkLib\UPS\Ship\SoldToType,
  36. SparkLib\UPS\Ship\UltimateConsigneeType,
  37. SparkLib\UPS\Ship\UnitOfMeasurementType,
  38. SparkLib\UPS\Ship\UnitType,
  39. SparkLib\UPS\Ship\UPSSecurity,
  40. SparkLib\UPS\Ship\UsernameToken;
  41. use Exception,
  42. SoapClient,
  43. SoapHeader,
  44. SoapFault,
  45. DOMDocument;
  46. class Ship {
  47. private $_wsdl = UPS_SHIP_WSDL;
  48. private $_schema = UPS_SCHEMA;
  49. private $_client;
  50. private $_options;
  51. private $_request;
  52. private $_response;
  53. private $_destination;
  54. private $_lineItems;
  55. private $_serviceType;
  56. private $_shipper;
  57. private $_international;
  58. private $_PAK = false;
  59. private $_shipFrom;
  60. private $_shipTo;
  61. private $_shipmentMethod;
  62. private $_payment;
  63. private $_label;
  64. private $_totalValue;
  65. private $_package;
  66. private $_packages = [];
  67. private $_products = [];
  68. private $_rates = [];
  69. public $upsCodes = [
  70. 1 => 'Next Day Air',
  71. 2 => '2nd Day Air',
  72. 3 => 'Ground',
  73. 7 => 'Worldwide Express',
  74. 8 => 'Worldwide Expedited',
  75. 11 => 'Standard',
  76. 12 => '3 Day Select',
  77. 13 => 'Next Day Air Saver',
  78. 14 => 'Next Day Air Early AM',
  79. 59 => '2nd Day Air AM',
  80. 54 => 'Worldwide Express Plus',
  81. 65 => 'UPS Saver'
  82. ];
  83. public function __construct() {
  84. $Request = new RequestType();
  85. $Shipment = new ShipmentType();
  86. $LabelSpecification = new LabelSpecificationType();
  87. $this->_request = new ShipmentRequest($Request, $Shipment, $LabelSpecification);
  88. $this->_totalValue = 0;
  89. }
  90. public function setDestination($dest) {
  91. $this->_destination = $dest;
  92. }
  93. public function allowPakRates() {
  94. $this->_PAK = true;
  95. }
  96. public function createPackage() {
  97. if ($this->_package instanceOf PackageType)
  98. throw new Exception ('Add current package before creating a new one.');
  99. $this->_package = new PackageType();
  100. }
  101. public function setPackageSize($l, $w, $h, $units = 'IN') {
  102. if ($this->_package == null)
  103. throw new Exception ('Create package before setting size.');
  104. if ($this->_international === null)
  105. throw new Exception ('Set shipment destination before adding packages');
  106. $weight = $this->_package->PackageWeight->Weight;
  107. if (! isset($weight))
  108. throw new Exception ('Set package weight before setting size');
  109. if ($this->_PAK && $this->_international && $weight <= constant('\PAK_RATE_THRESHOLD')) {
  110. $this->_package->setPackaging(new CodeDescriptionType('04'));
  111. } else {
  112. $this->_package->setPackaging(new CodeDescriptionType('02'));
  113. $this->_package->setDimensions(
  114. new DimensionsType(new CodeDescriptionType($units), $l, $w, $h)
  115. );
  116. }
  117. }
  118. public function setPackageWeight($weight, $units = 'LBS') {
  119. if ($this->_package == null)
  120. throw new Exception ('Create package before setting weight.');
  121. $this->_package->setPackageWeight(
  122. new PackageWeightType(new CodeDescriptionType($units), $weight)
  123. );
  124. }
  125. public function setPackageValue($value = null) {
  126. if ($this->_package == null)
  127. throw new Exception ('Create package before setting value.');
  128. $DeclaredValue = new PackageDeclaredValueType();
  129. $DeclaredValue->setCurrencyCode('USD');
  130. if ($value)
  131. $DeclaredValue->setMonetaryValue($value);
  132. else
  133. $DeclaredValue->setMonetaryValue($this->_totalValue);
  134. $PackageServiceOptions = new PackageServiceOptionsType();
  135. $PackageServiceOptions->setDeclaredValue($DeclaredValue);
  136. $this->_package->setPackageServiceOptions($PackageServiceOptions);
  137. }
  138. public function setReferenceNumber($num) {
  139. $ReferenceNumber = new ReferenceNumberType();
  140. $ReferenceNumber->setValue($num);
  141. $ReferenceNumber->setBarCodeIndicator(true);
  142. if ($this->_international) {
  143. $this->_request->Shipment->setReferenceNumber($ReferenceNumber);
  144. } else if (count($this->_packages) == 0) {
  145. throw new Exception ('Add packages before setting reference number.');
  146. } else {
  147. foreach ($this->_packages as &$package)
  148. $package->setReferenceNumber($ReferenceNumber);
  149. }
  150. }
  151. public function addPackage() {
  152. array_push($this->_packages, $this->_package);
  153. $this->_package = null;
  154. }
  155. public function addItem($value, $qty = 1, $desc = 'Merchandise',
  156. $tariff = '', $origin = 'US') {
  157. if (count($this->_products) < 50) {
  158. // Sanitize description
  159. $desc = substr(preg_replace("/[^\w\d ]/i", '', $desc), 0, 35);
  160. // Make sure value isn't too precise
  161. $value = round($value, 6);
  162. $this->_totalValue += $value;
  163. $product = new ProductType();
  164. $measure = new UnitOfMeasurementType('PCS');
  165. $units = new UnitType($qty, $measure, $value);
  166. $product->setDescription($desc);
  167. $product->setUnit($units);
  168. $product->setCommodityCode($tariff);
  169. $product->setOriginCountryCode($origin);
  170. array_push($this->_products, $product);
  171. }
  172. }
  173. /**
  174. * Functions for handling Shipper node.
  175. */
  176. private function initShipper() {
  177. if (!isset($this->_shipper))
  178. $this->_shipper = new ShipperType();
  179. }
  180. public function setShipperAddress($street, $city, $state, $postal,
  181. $country, $account) {
  182. $this->initShipper();
  183. $this->initForms();
  184. $address = new ShipAddressType($street, $city, $state, $postal, $country);
  185. $this->_shipper->setAddress($address);
  186. $this->_shipper->setShipperNumber($account);
  187. $Address = new AddressType($street, $city, $state, null, $postal, $country);
  188. $this->_request->Shipment
  189. ->ShipmentServiceOptions
  190. ->InternationalForms
  191. ->Contacts
  192. ->Producer
  193. ->setAddress($Address);
  194. }
  195. public function setShipperPhone($phone) {
  196. $phone = preg_replace('/[^0-9]/', '', $phone);
  197. $this->initShipper();
  198. $this->_shipper->setPhone(new PhoneType($phone));
  199. }
  200. public function setShipperName($name, $company = null) {
  201. $this->initShipper();
  202. $this->initForms();
  203. if ($company) {
  204. $this->_shipper->setName($company);
  205. $this->_shipper->setAttentionName($name);
  206. $this->_shipper->setCompanyDisplayableName($company);
  207. $this->_request->Shipment
  208. ->ShipmentServiceOptions
  209. ->InternationalForms
  210. ->Contacts
  211. ->Producer
  212. ->setCompanyName($company);
  213. $this->_request->Shipment
  214. ->ShipmentServiceOptions
  215. ->InternationalForms
  216. ->Contacts
  217. ->Producer
  218. ->setAttentionName($name);
  219. } else {
  220. $this->_shipper->setName($name);
  221. $this->_shipper->setAttentionName($name);
  222. $this->_shipper->setCompanyDisplayableName($name);
  223. $this->_request->Shipment
  224. ->ShipmentServiceOptions
  225. ->InternationalForms
  226. ->Contacts
  227. ->Producer
  228. ->setCompanyName($name);
  229. $this->_request->Shipment
  230. ->ShipmentServiceOptions
  231. ->InternationalForms
  232. ->Contacts
  233. ->Producer
  234. ->setAttentionName($name);
  235. }
  236. }
  237. public function setShipperTaxID($taxId) {
  238. $this->initShipper();
  239. $this->_shipper->setTaxIdentificationNumber($taxId);
  240. }
  241. public function setShipperEmail($email) {
  242. $this->initShipper();
  243. $this->_shipper->setEMailAddress($email);
  244. }
  245. /**
  246. * Functions for handling ShipFrom node.
  247. */
  248. private function initShipFrom() {
  249. if (!isset($this->_shipFrom))
  250. $this->_shipFrom = new ShipFromType();
  251. }
  252. public function setShipFromAddress($street, $city, $state, $postal,
  253. $country) {
  254. $this->initShipFrom();
  255. $address = new ShipAddressType($street, $city, $state, $postal, $country);
  256. $this->_shipFrom->setAddress($address);
  257. }
  258. public function setShipFromPhone($phone) {
  259. $phone = preg_replace('/[^0-9]/', '', $phone);
  260. $this->initShipFrom();
  261. $this->_shipFrom->setPhone(new PhoneType($phone));
  262. }
  263. public function setShipFromName($name, $company = null) {
  264. $this->initShipFrom();
  265. if ($company) {
  266. $this->_shipFrom->setName($company);
  267. $this->_shipFrom->setAttentionName($name);
  268. $this->_shipFrom->setCompanyDisplayableName($company);
  269. } else {
  270. $this->_shipFrom->setName($name);
  271. $this->_shipFrom->setAttentionName($name);
  272. $this->_shipFrom->setCompanyDisplayableName($name);
  273. }
  274. }
  275. public function setShipFromTaxID($taxId) {
  276. $this->initShipFrom();
  277. $this->_shipFrom->setTaxIdentificationNumber($taxId);
  278. }
  279. /**
  280. * Functions for handling ShipTo node.
  281. */
  282. private function initShipTo() {
  283. if (!isset($this->_shipTo))
  284. $this->_shipTo = new ShipToType();
  285. }
  286. public function setShipToAddress($street, $city, $state, $postal, $country) {
  287. $this->initShipTo();
  288. $this->initForms();
  289. // Do some really basic validation on the postal code
  290. if ($country == 'US') {
  291. $postal = substr(preg_replace('/[^0-9]+/', '', $postal), 0, 5);
  292. $this->_international = false;
  293. } else if ($country == 'CA') {
  294. $postal = substr(preg_replace('/[^0-9A-Za-z]+/', '', $postal), 0, 6);
  295. $this->_international = true;
  296. } else {
  297. $postal = substr(preg_replace('/[^0-9A-Za-z]+/', '', $postal), 0, 9);
  298. $this->_international = true;
  299. }
  300. // Street cannot be more than 35 characters long
  301. if (is_array($street))
  302. foreach ($street as &$s)
  303. $s = substr($s, 0, 35);
  304. else
  305. $street = substr($street, 0, 35);
  306. // Only US and Canada should have the state field set.
  307. // Ireland should be set to IE because they don't have postal codes.
  308. if ($country == 'IE')
  309. $state = 'IE';
  310. else if (! ($country == 'US' || $country == 'CA'))
  311. $state = null;
  312. $ShipAddress = new ShipAddressType($street, $city, $state, $postal, $country);
  313. $this->_shipTo->setAddress($ShipAddress);
  314. $UltimateConsigneeAddress = new AddressType($street, $city, $state, null, $postal, $country);
  315. $this->_request->Shipment
  316. ->ShipmentServiceOptions
  317. ->InternationalForms
  318. ->Contacts
  319. ->UltimateConsignee
  320. ->setAddress($UltimateConsigneeAddress);
  321. $SoldToAddress = new AddressType($street, $city, $state, null, $postal, $country);
  322. $this->_request->Shipment
  323. ->ShipmentServiceOptions
  324. ->InternationalForms
  325. ->Contacts
  326. ->SoldTo
  327. ->setAddress($SoldToAddress);
  328. }
  329. public function setShipToPhone($phone) {
  330. $phone = preg_replace('/[^0-9]/', '', $phone);
  331. if ($phone) {
  332. $this->initShipTo();
  333. $this->_shipTo->setPhone(new PhoneType($phone));
  334. }
  335. }
  336. public function setShipToName($name, $company = null) {
  337. $this->initShipTo();
  338. $this->initForms();
  339. $name = substr($name, 0, 35);
  340. if ($company) {
  341. $company = substr($company, 0, 35);
  342. $this->_shipTo->setName($company);
  343. $this->_shipTo->setAttentionName($name);
  344. $this->_shipTo->setCompanyDisplayableName($company);
  345. $this->_request->Shipment
  346. ->ShipmentServiceOptions
  347. ->InternationalForms
  348. ->Contacts
  349. ->UltimateConsignee
  350. ->setCompanyName($company);
  351. $this->_request->Shipment
  352. ->ShipmentServiceOptions
  353. ->InternationalForms
  354. ->Contacts
  355. ->SoldTo
  356. ->setName($company);
  357. $this->_request->Shipment
  358. ->ShipmentServiceOptions
  359. ->InternationalForms
  360. ->Contacts
  361. ->SoldTo
  362. ->setAttentionName($name);
  363. } else {
  364. $this->_shipTo->setName($name);
  365. $this->_shipTo->setAttentionName($name);
  366. $this->_shipTo->setCompanyDisplayableName($name);
  367. $this->_request->Shipment
  368. ->ShipmentServiceOptions
  369. ->InternationalForms
  370. ->Contacts
  371. ->UltimateConsignee
  372. ->setCompanyName($name);
  373. $this->_request->Shipment
  374. ->ShipmentServiceOptions
  375. ->InternationalForms
  376. ->Contacts
  377. ->SoldTo
  378. ->setName($name);
  379. $this->_request->Shipment
  380. ->ShipmentServiceOptions
  381. ->InternationalForms
  382. ->Contacts
  383. ->SoldTo
  384. ->setAttentionName($name);
  385. }
  386. }
  387. public function setShipToEmail($email) {
  388. $this->initShipTo();
  389. $this->_shipTo->setEMailAddress($email);
  390. }
  391. public function addShipmentAccount($account) {
  392. $transportation = new ShipmentChargeType();
  393. $transportation->setType('01');
  394. $transportation->setBillShipper(new BillShipperType($account));
  395. $this->_payment = new PaymentInfoType($transportation);
  396. }
  397. public function setShippingMethod($serviceCode) {
  398. $this->_serviceType = new ServiceType($serviceCode);
  399. }
  400. private function initForms() {
  401. if (!isset($this->_request->Shipment->ShipmentServiceOptions->InternationalForms)) {
  402. $Producer = new ProducerType();
  403. $UltimateConsignee = new UltimateConsigneeType();
  404. $SoldTo = new SoldToType();
  405. $Contacts = new ContactType();
  406. $Contacts->setProducer($Producer);
  407. $Contacts->setUltimateConsignee($UltimateConsignee);
  408. $Contacts->setSoldTo($SoldTo);
  409. $InternationalForm = new InternationalFormType();
  410. $InternationalForm->setFormType('01');
  411. $InternationalForm->setReasonForExport('SALE');
  412. $InternationalForm->setCurrencyCode('USD');
  413. $InternationalForm->setContacts($Contacts);
  414. $ShipmentServiceOptions = new ShipmentServiceOptionsType();
  415. $ShipmentServiceOptions->setInternationalForms($InternationalForm);
  416. $this->_request->Shipment->setShipmentServiceOptions($ShipmentServiceOptions);
  417. }
  418. }
  419. public function setInvoice($date = null, $number = '') {
  420. $this->initForms();
  421. if ($date == null)
  422. $date = date('Ymd');
  423. else if (is_string($date))
  424. $date = date('Ymd', strtotime($date));
  425. else
  426. $date = date('Ymd', $date);
  427. $InternationalForm = new InternationalFormType();
  428. $InternationalForm->setFormType('01');
  429. $ShipmentServiceOptions = new ShipmentServiceOptionsType();
  430. $ShipmentServiceOptions->setInternationalForms($InternationalForm);
  431. $this->_request->Shipment
  432. ->ShipmentServiceOptions
  433. ->InternationalForms
  434. ->setInvoiceDate($date);
  435. $this->_request->Shipment
  436. ->ShipmentServiceOptions
  437. ->InternationalForms
  438. ->setInvoiceNumber($number);
  439. }
  440. public function useNegotiatedRates() {
  441. $rateInfo = new RateInfoType();
  442. $rateInfo->setNegotiatedRatesIndicator(true);
  443. $this->_request->Shipment->setShipmentRatingOptions($rateInfo);
  444. }
  445. private function setTotal() {
  446. if ($this->_destination == 'CA') {
  447. $invoiceLineTotal = new CurrencyMonetaryType();
  448. $invoiceLineTotal->setCurrencyCode('USD');
  449. $invoiceLineTotal->setMonetaryValue(ceil($this->_totalValue));
  450. $this->_request->Shipment->setInvoiceLineTotal($invoiceLineTotal);
  451. }
  452. }
  453. public function ship() {
  454. $this->_request->Request->setRequestOption('validate');
  455. $this->initForms();
  456. $this->_request->Shipment->setService($this->_serviceType);
  457. $this->_request->Shipment->setDescription('Electronics');
  458. $this->_request->Shipment->setShipper($this->_shipper);
  459. $this->_request->Shipment->setShipFrom($this->_shipFrom);
  460. $this->_request->Shipment->setShipTo($this->_shipTo);
  461. $this->_request->Shipment->setPackage($this->_packages);
  462. $this->_request->Shipment->setPaymentInformation($this->_payment);
  463. $this->_request->Shipment
  464. ->ShipmentServiceOptions
  465. ->InternationalForms
  466. ->setProduct($this->_products);
  467. $this->setTotal();
  468. $UsernameToken = new UsernameToken();
  469. $ServiceAccessToken = new ServiceAccessToken();
  470. $UPSSecurity = new UPSSecurity($UsernameToken, $ServiceAccessToken);
  471. $UsernameToken->setUsername(UPS_USERID);
  472. $UsernameToken->setPassword(UPS_USERPASS);
  473. $ServiceAccessToken->setAccessLicenseNumber(UPS_APIKEY);
  474. $header = new SoapHeader($this->_schema, 'UPSSecurity', $UPSSecurity);
  475. $this->_options = [
  476. 'soap_version' => 'SOAP_1_1',
  477. 'exceptions' => true,
  478. 'trace' => true,
  479. 'location' => UPS_SHIP_SERVER
  480. ];
  481. $wsdl = $this->_wsdl;
  482. $this->_client = new SoapClient($wsdl, $this->_options);
  483. $this->_client->__setSoapHeaders($header);
  484. try {
  485. $this->_response = $this->_client->ProcessShipment($this->_request, $this->_options);
  486. } catch (SoapFault $s) {
  487. if (isset($s->detail)) {
  488. $err = $s->detail->Errors->ErrorDetail->PrimaryErrorCode->Description;
  489. throw new Exception($err);
  490. }
  491. }
  492. }
  493. public function setLabelFormat($formatCode = 'ZPL') {
  494. // This is _required_, with these specific values, but has no effect
  495. $LabelStockSize = new LabelStockSizeType('6', '4');
  496. $LabelImageFormat = new LabelImageFormatType($formatCode);
  497. $this->_request->LabelSpecification->setLabelImageFormat($LabelImageFormat);
  498. $this->_request->LabelSpecification->setLabelStockSize($LabelStockSize);
  499. }
  500. public function getLabelImage() {
  501. if ($this->_response) {
  502. $img = $this->_response->ShipmentResults->PackageResults->ShippingLabel->GraphicImage;
  503. return base64_decode($img);
  504. }
  505. }
  506. public function getTrackingNumber() {
  507. if ($this->_response)
  508. return $this->_response->ShipmentResults->PackageResults->TrackingNumber;
  509. }
  510. public function getCharge() {
  511. if ($this->_response)
  512. return $this->_response->ShipmentResults->PackageResults->ServiceOptionsCharges->MonetaryValue;
  513. }
  514. public function getInvoice() {
  515. if ($this->_response) {
  516. $img = $this->_response->ShipmentResults->Form->Image->GraphicImage;
  517. return base64_decode($img);
  518. }
  519. }
  520. public function getLastRequest() {
  521. $request = $this->_client->__getLastRequest();
  522. if ($request) {
  523. $dom = new DOMDocument;
  524. $dom->preserveWhiteSpace = FALSE;
  525. $dom->formatOutput = TRUE;
  526. $dom->loadXML($request);
  527. return $dom->saveXml();
  528. }
  529. }
  530. public function getLastResponse() {
  531. $response = $this->_client->__getLastResponse();
  532. if ($response) {
  533. $dom = new DOMDocument;
  534. $dom->preserveWhiteSpace = FALSE;
  535. $dom->formatOutput = TRUE;
  536. $dom->loadXML($response);
  537. return $dom->saveXml();
  538. }
  539. }
  540. }