Carrier.php 81 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Usps\Model;
  7. use Magento\Framework\App\ObjectManager;
  8. use Magento\Framework\Xml\Security;
  9. use Magento\Quote\Model\Quote\Address\RateRequest;
  10. use Magento\Shipping\Helper\Carrier as CarrierHelper;
  11. use Magento\Shipping\Model\Carrier\AbstractCarrierOnline;
  12. use Magento\Shipping\Model\Rate\Result;
  13. use Magento\Usps\Helper\Data as DataHelper;
  14. /**
  15. * USPS shipping
  16. * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
  17. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  18. */
  19. class Carrier extends AbstractCarrierOnline implements \Magento\Shipping\Model\Carrier\CarrierInterface
  20. {
  21. /** @deprecated */
  22. const CONTAINER_VARIABLE = 'VARIABLE';
  23. /** @deprecated */
  24. const CONTAINER_FLAT_RATE_BOX = 'FLAT RATE BOX';
  25. /** @deprecated */
  26. const CONTAINER_FLAT_RATE_ENVELOPE = 'FLAT RATE ENVELOPE';
  27. /** @deprecated */
  28. const CONTAINER_RECTANGULAR = 'RECTANGULAR';
  29. /** @deprecated */
  30. const CONTAINER_NONRECTANGULAR = 'NONRECTANGULAR';
  31. /**
  32. * USPS size
  33. */
  34. const SIZE_REGULAR = 'REGULAR';
  35. const SIZE_LARGE = 'LARGE';
  36. /**
  37. * Default api revision
  38. *
  39. * @var int
  40. */
  41. const DEFAULT_REVISION = 2;
  42. /**
  43. * Code of the carrier
  44. *
  45. * @var string
  46. */
  47. const CODE = 'usps';
  48. /**
  49. * Ounces in one pound for conversion
  50. */
  51. const OUNCES_POUND = 16;
  52. /**
  53. * Code of the carrier
  54. *
  55. * @var string
  56. */
  57. protected $_code = self::CODE;
  58. /**
  59. * Weight precision
  60. *
  61. * @var int
  62. */
  63. private static $weightPrecision = 10;
  64. /**
  65. * Rate request data
  66. *
  67. * @var \Magento\Quote\Model\Quote\Address\RateRequest|null
  68. */
  69. protected $_request = null;
  70. /**
  71. * Rate result data
  72. *
  73. * @var Result|null
  74. */
  75. protected $_result = null;
  76. /**
  77. * Default cgi gateway url
  78. *
  79. * @var string
  80. */
  81. protected $_defaultGatewayUrl = 'http://production.shippingapis.com/ShippingAPI.dll';
  82. /**
  83. * Container types that could be customized for USPS carrier
  84. *
  85. * @var string[]
  86. */
  87. protected $_customizableContainerTypes = ['VARIABLE', 'RECTANGULAR', 'NONRECTANGULAR'];
  88. /**
  89. * Carrier helper
  90. *
  91. * @var \Magento\Shipping\Helper\Carrier
  92. */
  93. protected $_carrierHelper;
  94. /**
  95. * @var \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory
  96. */
  97. protected $_productCollectionFactory;
  98. /**
  99. * @var \Magento\Framework\HTTP\ZendClientFactory
  100. */
  101. protected $_httpClientFactory;
  102. /**
  103. * @inheritdoc
  104. */
  105. protected $_debugReplacePrivateDataKeys = [
  106. 'USERID'
  107. ];
  108. /**
  109. * @var DataHelper
  110. */
  111. private $dataHelper;
  112. /**
  113. * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
  114. * @param \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory
  115. * @param \Psr\Log\LoggerInterface $logger
  116. * @param Security $xmlSecurity
  117. * @param \Magento\Shipping\Model\Simplexml\ElementFactory $xmlElFactory
  118. * @param \Magento\Shipping\Model\Rate\ResultFactory $rateFactory
  119. * @param \Magento\Quote\Model\Quote\Address\RateResult\MethodFactory $rateMethodFactory
  120. * @param \Magento\Shipping\Model\Tracking\ResultFactory $trackFactory
  121. * @param \Magento\Shipping\Model\Tracking\Result\ErrorFactory $trackErrorFactory
  122. * @param \Magento\Shipping\Model\Tracking\Result\StatusFactory $trackStatusFactory
  123. * @param \Magento\Directory\Model\RegionFactory $regionFactory
  124. * @param \Magento\Directory\Model\CountryFactory $countryFactory
  125. * @param \Magento\Directory\Model\CurrencyFactory $currencyFactory
  126. * @param \Magento\Directory\Helper\Data $directoryData
  127. * @param \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry
  128. * @param \Magento\Shipping\Helper\Carrier $carrierHelper
  129. * @param \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory
  130. * @param \Magento\Framework\HTTP\ZendClientFactory $httpClientFactory
  131. * @param array $data
  132. *
  133. * @SuppressWarnings(PHPMD.ExcessiveParameterList)
  134. */
  135. public function __construct(
  136. \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
  137. \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory,
  138. \Psr\Log\LoggerInterface $logger,
  139. Security $xmlSecurity,
  140. \Magento\Shipping\Model\Simplexml\ElementFactory $xmlElFactory,
  141. \Magento\Shipping\Model\Rate\ResultFactory $rateFactory,
  142. \Magento\Quote\Model\Quote\Address\RateResult\MethodFactory $rateMethodFactory,
  143. \Magento\Shipping\Model\Tracking\ResultFactory $trackFactory,
  144. \Magento\Shipping\Model\Tracking\Result\ErrorFactory $trackErrorFactory,
  145. \Magento\Shipping\Model\Tracking\Result\StatusFactory $trackStatusFactory,
  146. \Magento\Directory\Model\RegionFactory $regionFactory,
  147. \Magento\Directory\Model\CountryFactory $countryFactory,
  148. \Magento\Directory\Model\CurrencyFactory $currencyFactory,
  149. \Magento\Directory\Helper\Data $directoryData,
  150. \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry,
  151. CarrierHelper $carrierHelper,
  152. \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory,
  153. \Magento\Framework\HTTP\ZendClientFactory $httpClientFactory,
  154. array $data = []
  155. ) {
  156. $this->_carrierHelper = $carrierHelper;
  157. $this->_productCollectionFactory = $productCollectionFactory;
  158. $this->_httpClientFactory = $httpClientFactory;
  159. parent::__construct(
  160. $scopeConfig,
  161. $rateErrorFactory,
  162. $logger,
  163. $xmlSecurity,
  164. $xmlElFactory,
  165. $rateFactory,
  166. $rateMethodFactory,
  167. $trackFactory,
  168. $trackErrorFactory,
  169. $trackStatusFactory,
  170. $regionFactory,
  171. $countryFactory,
  172. $currencyFactory,
  173. $directoryData,
  174. $stockRegistry,
  175. $data
  176. );
  177. }
  178. /**
  179. * Collect and get rates
  180. *
  181. * @param RateRequest $request
  182. * @return \Magento\Quote\Model\Quote\Address\RateResult\Error|bool|Result
  183. */
  184. public function collectRates(RateRequest $request)
  185. {
  186. if (!$this->canCollectRates()) {
  187. return $this->getErrorMessage();
  188. }
  189. $this->setRequest($request);
  190. $this->_result = $this->_getQuotes();
  191. $this->_updateFreeMethodQuote($request);
  192. return $this->getResult();
  193. }
  194. /**
  195. * Prepare and set request to this instance
  196. *
  197. * @param \Magento\Quote\Model\Quote\Address\RateRequest $request
  198. * @return $this
  199. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  200. * @SuppressWarnings(PHPMD.NPathComplexity)
  201. * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
  202. */
  203. public function setRequest(\Magento\Quote\Model\Quote\Address\RateRequest $request)
  204. {
  205. $this->_request = $request;
  206. $r = new \Magento\Framework\DataObject();
  207. if ($request->getLimitMethod()) {
  208. $r->setService($request->getLimitMethod());
  209. } else {
  210. $r->setService('ALL');
  211. }
  212. if ($request->getUspsUserid()) {
  213. $userId = $request->getUspsUserid();
  214. } else {
  215. $userId = $this->getConfigData('userid');
  216. }
  217. $r->setUserId($userId);
  218. if ($request->getUspsContainer()) {
  219. $container = $request->getUspsContainer();
  220. } else {
  221. $container = $this->getConfigData('container');
  222. }
  223. $r->setContainer($container);
  224. if ($request->getUspsSize()) {
  225. $size = $request->getUspsSize();
  226. } else {
  227. $size = $this->getConfigData('size');
  228. }
  229. $r->setSize($size);
  230. if ($request->getGirth()) {
  231. $girth = $request->getGirth();
  232. } else {
  233. $girth = $this->getConfigData('girth');
  234. }
  235. $r->setGirth($girth);
  236. if ($request->getHeight()) {
  237. $height = $request->getHeight();
  238. } else {
  239. $height = $this->getConfigData('height');
  240. }
  241. $r->setHeight($height);
  242. if ($request->getLength()) {
  243. $length = $request->getLength();
  244. } else {
  245. $length = $this->getConfigData('length');
  246. }
  247. $r->setLength($length);
  248. if ($request->getWidth()) {
  249. $width = $request->getWidth();
  250. } else {
  251. $width = $this->getConfigData('width');
  252. }
  253. $r->setWidth($width);
  254. if ($request->getUspsMachinable()) {
  255. $machinable = $request->getUspsMachinable();
  256. } else {
  257. $machinable = $this->getConfigData('machinable');
  258. }
  259. $r->setMachinable($machinable);
  260. if ($request->getOrigPostcode()) {
  261. $r->setOrigPostal($request->getOrigPostcode());
  262. } else {
  263. $r->setOrigPostal(
  264. $this->_scopeConfig->getValue(
  265. \Magento\Sales\Model\Order\Shipment::XML_PATH_STORE_ZIP,
  266. \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
  267. $request->getStoreId()
  268. )
  269. );
  270. }
  271. if ($request->getOrigCountryId()) {
  272. $r->setOrigCountryId($request->getOrigCountryId());
  273. } else {
  274. $r->setOrigCountryId(
  275. $this->_scopeConfig->getValue(
  276. \Magento\Sales\Model\Order\Shipment::XML_PATH_STORE_COUNTRY_ID,
  277. \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
  278. $request->getStoreId()
  279. )
  280. );
  281. }
  282. if ($request->getDestCountryId()) {
  283. $destCountry = $request->getDestCountryId();
  284. } else {
  285. $destCountry = self::USA_COUNTRY_ID;
  286. }
  287. $r->setDestCountryId($destCountry);
  288. if (!$this->_isUSCountry($destCountry)) {
  289. $r->setDestCountryName($this->_getCountryName($destCountry));
  290. }
  291. if ($request->getDestPostcode()) {
  292. $r->setDestPostal($request->getDestPostcode());
  293. }
  294. $weight = $this->getTotalNumOfBoxes($request->getPackageWeight());
  295. $r->setWeightPounds(floor($weight));
  296. $ounces = ($weight - floor($weight)) * self::OUNCES_POUND;
  297. $r->setWeightOunces(sprintf('%.' . self::$weightPrecision . 'f', $ounces));
  298. if ($request->getFreeMethodWeight() != $request->getPackageWeight()) {
  299. $r->setFreeMethodWeight($request->getFreeMethodWeight());
  300. }
  301. $r->setValue($request->getPackageValue());
  302. $r->setValueWithDiscount($request->getPackageValueWithDiscount());
  303. $r->setBaseSubtotalInclTax($request->getBaseSubtotalInclTax());
  304. $this->setRawRequest($r);
  305. return $this;
  306. }
  307. /**
  308. * Get result of request
  309. *
  310. * @return Result|null
  311. */
  312. public function getResult()
  313. {
  314. return $this->_result;
  315. }
  316. /**
  317. * @inheritdoc
  318. *
  319. * Starting from 23.02.2018 USPS doesn't allow to create free shipping labels via their API.
  320. */
  321. public function isShippingLabelsAvailable()
  322. {
  323. return false;
  324. }
  325. /**
  326. * Get quotes
  327. *
  328. * @return Result
  329. */
  330. protected function _getQuotes()
  331. {
  332. return $this->_getXmlQuotes();
  333. }
  334. /**
  335. * Set free method request
  336. *
  337. * @param string $freeMethod
  338. * @return void
  339. */
  340. protected function _setFreeMethodRequest($freeMethod)
  341. {
  342. $r = $this->_rawRequest;
  343. $weight = $this->getTotalNumOfBoxes($r->getFreeMethodWeight());
  344. $r->setWeightPounds(floor($weight));
  345. $ounces = ($weight - floor($weight)) * self::OUNCES_POUND;
  346. $r->setWeightOunces(sprintf('%.' . self::$weightPrecision . 'f', $ounces));
  347. $r->setService($freeMethod);
  348. }
  349. /**
  350. * Build RateV3 request, send it to USPS gateway and retrieve quotes in XML format
  351. *
  352. * @link http://www.usps.com/webtools/htm/Rate-Calculators-v2-3.htm
  353. * @return Result
  354. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  355. * @SuppressWarnings(PHPMD.NPathComplexity)
  356. * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
  357. */
  358. protected function _getXmlQuotes()
  359. {
  360. $r = $this->_rawRequest;
  361. // The origin address(shipper) must be only in USA
  362. if (!$this->_isUSCountry($r->getOrigCountryId())) {
  363. $responseBody = '';
  364. return $this->_parseXmlResponse($responseBody);
  365. }
  366. if ($this->_isUSCountry($r->getDestCountryId())) {
  367. $xml = $this->_xmlElFactory->create(
  368. ['data' => '<?xml version="1.0" encoding="UTF-8"?><RateV4Request/>']
  369. );
  370. $xml->addAttribute('USERID', $r->getUserId());
  371. // according to usps v4 documentation
  372. $xml->addChild('Revision', '2');
  373. $package = $xml->addChild('Package');
  374. $package->addAttribute('ID', 0);
  375. $service = $this->getCode('service_to_code', $r->getService());
  376. if (!$service) {
  377. $service = $r->getService();
  378. }
  379. if (strpos($r->getContainer(), 'FLAT RATE ENVELOPE') !== false ||
  380. strpos($r->getContainer(), 'FLAT RATE BOX') !== false
  381. ) {
  382. $service = 'Priority';
  383. }
  384. $package->addChild('Service', $service);
  385. // no matter Letter, Flat or Parcel, use Parcel
  386. if ($r->getService() == 'FIRST CLASS' || $r->getService() == 'FIRST CLASS HFP COMMERCIAL') {
  387. $package->addChild('FirstClassMailType', 'PARCEL');
  388. }
  389. $package->addChild('ZipOrigination', $r->getOrigPostal());
  390. //only 5 chars available
  391. $package->addChild('ZipDestination', substr($r->getDestPostal(), 0, 5));
  392. $package->addChild('Pounds', $r->getWeightPounds());
  393. $package->addChild('Ounces', $r->getWeightOunces());
  394. // Because some methods don't accept VARIABLE and (NON)RECTANGULAR containers
  395. $package->addChild('Container', $r->getContainer());
  396. $package->addChild('Size', $r->getSize());
  397. if ($r->getSize() == 'LARGE') {
  398. $package->addChild('Width', $r->getWidth());
  399. $package->addChild('Length', $r->getLength());
  400. $package->addChild('Height', $r->getHeight());
  401. if ($r->getContainer() == 'NONRECTANGULAR' || $r->getContainer() == 'VARIABLE') {
  402. $package->addChild('Girth', $r->getGirth());
  403. }
  404. }
  405. $package->addChild('Machinable', $r->getMachinable());
  406. $api = 'RateV4';
  407. } else {
  408. $xml = $this->_xmlElFactory->create(
  409. ['data' => '<?xml version = "1.0" encoding = "UTF-8"?><IntlRateV2Request/>']
  410. );
  411. $xml->addAttribute('USERID', $r->getUserId());
  412. // according to usps v4 documentation
  413. $xml->addChild('Revision', '2');
  414. $package = $xml->addChild('Package');
  415. $package->addAttribute('ID', 0);
  416. $package->addChild('Pounds', $r->getWeightPounds());
  417. $package->addChild('Ounces', $r->getWeightOunces());
  418. $package->addChild('MailType', 'All');
  419. $package->addChild('ValueOfContents', $r->getValue());
  420. $package->addChild('Country', $r->getDestCountryName());
  421. $package->addChild('Container', $r->getContainer());
  422. $package->addChild('Size', $r->getSize());
  423. $width = $length = $height = $girth = '';
  424. if ($r->getSize() == 'LARGE') {
  425. $width = $r->getWidth();
  426. $length = $r->getLength();
  427. $height = $r->getHeight();
  428. if ($r->getContainer() == 'NONRECTANGULAR') {
  429. $girth = $r->getGirth();
  430. }
  431. }
  432. $package->addChild('Width', $width);
  433. $package->addChild('Length', $length);
  434. $package->addChild('Height', $height);
  435. $package->addChild('Girth', $girth);
  436. $package->addChild('OriginZip', $r->getOrigPostal());
  437. $package->addChild('AcceptanceDateTime', date('c'));
  438. $package->addChild('DestinationPostalCode', $r->getDestPostal());
  439. $api = 'IntlRateV2';
  440. }
  441. $request = $xml->asXML();
  442. $responseBody = $this->_getCachedQuotes($request);
  443. if ($responseBody === null) {
  444. $debugData = ['request' => $this->filterDebugData($request)];
  445. try {
  446. $url = $this->getConfigData('gateway_url');
  447. if (!$url) {
  448. $url = $this->_defaultGatewayUrl;
  449. }
  450. $client = $this->_httpClientFactory->create();
  451. $client->setUri($url);
  452. $client->setConfig(['maxredirects' => 0, 'timeout' => 30]);
  453. $client->setParameterGet('API', $api);
  454. $client->setParameterGet('XML', $request);
  455. $response = $client->request();
  456. $responseBody = $response->getBody();
  457. $debugData['result'] = $responseBody;
  458. $this->_setCachedQuotes($request, $responseBody);
  459. } catch (\Exception $e) {
  460. $debugData['result'] = ['error' => $e->getMessage(), 'code' => $e->getCode()];
  461. $responseBody = '';
  462. }
  463. $this->_debug($debugData);
  464. }
  465. return $this->_parseXmlResponse($responseBody);
  466. }
  467. /**
  468. * Parse calculated rates
  469. *
  470. * @param string $response
  471. * @return Result
  472. * @link http://www.usps.com/webtools/htm/Rate-Calculators-v2-3.htm
  473. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  474. * @SuppressWarnings(PHPMD.NPathComplexity)
  475. */
  476. protected function _parseXmlResponse($response)
  477. {
  478. $r = $this->_rawRequest;
  479. $costArr = [];
  480. $priceArr = [];
  481. if (strlen(trim($response)) > 0) {
  482. if (strpos(trim($response), '<?xml') === 0) {
  483. if (strpos($response, '<?xml version="1.0"?>') !== false) {
  484. $response = str_replace(
  485. '<?xml version="1.0"?>',
  486. '<?xml version="1.0" encoding="ISO-8859-1"?>',
  487. $response
  488. );
  489. }
  490. $xml = $this->parseXml($response);
  491. if (is_object($xml)) {
  492. $allowedMethods = explode(',', $this->getConfigData('allowed_methods'));
  493. $serviceCodeToActualNameMap = [];
  494. /**
  495. * US Rates
  496. */
  497. if ($this->_isUSCountry($r->getDestCountryId())) {
  498. if (is_object($xml->Package) && is_object($xml->Package->Postage)) {
  499. foreach ($xml->Package->Postage as $postage) {
  500. $serviceName = $this->_filterServiceName((string)$postage->MailService);
  501. $_serviceCode = $this->getCode('method_to_code', $serviceName);
  502. $serviceCode = $_serviceCode ? $_serviceCode : (string)$postage->attributes()->CLASSID;
  503. $serviceCodeToActualNameMap[$serviceCode] = $serviceName;
  504. if (in_array($serviceCode, $allowedMethods)) {
  505. $costArr[$serviceCode] = (string)$postage->Rate;
  506. $priceArr[$serviceCode] = $this->getMethodPrice(
  507. (string)$postage->Rate,
  508. $serviceCode
  509. );
  510. }
  511. }
  512. asort($priceArr);
  513. }
  514. } else {
  515. /*
  516. * International Rates
  517. */
  518. if (is_object($xml->Package) && is_object($xml->Package->Service)) {
  519. foreach ($xml->Package->Service as $service) {
  520. $serviceName = $this->_filterServiceName((string)$service->SvcDescription);
  521. $serviceCode = 'INT_' . (string)$service->attributes()->ID;
  522. $serviceCodeToActualNameMap[$serviceCode] = $serviceName;
  523. if (!$this->isServiceAvailable($service)) {
  524. continue;
  525. }
  526. if (in_array($serviceCode, $allowedMethods)) {
  527. $costArr[$serviceCode] = (string)$service->Postage;
  528. $priceArr[$serviceCode] = $this->getMethodPrice(
  529. (string)$service->Postage,
  530. $serviceCode
  531. );
  532. }
  533. }
  534. asort($priceArr);
  535. }
  536. }
  537. }
  538. }
  539. }
  540. $result = $this->_rateFactory->create();
  541. if (empty($priceArr)) {
  542. $error = $this->_rateErrorFactory->create();
  543. $error->setCarrier('usps');
  544. $error->setCarrierTitle($this->getConfigData('title'));
  545. $error->setErrorMessage($this->getConfigData('specificerrmsg'));
  546. $result->append($error);
  547. } else {
  548. foreach ($priceArr as $method => $price) {
  549. $rate = $this->_rateMethodFactory->create();
  550. $rate->setCarrier('usps');
  551. $rate->setCarrierTitle($this->getConfigData('title'));
  552. $rate->setMethod($method);
  553. $rate->setMethodTitle(
  554. isset(
  555. $serviceCodeToActualNameMap[$method]
  556. ) ? $serviceCodeToActualNameMap[$method] : $this->getCode(
  557. 'method',
  558. $method
  559. )
  560. );
  561. $rate->setCost($costArr[$method]);
  562. $rate->setPrice($price);
  563. $result->append($rate);
  564. }
  565. }
  566. return $result;
  567. }
  568. /**
  569. * Get configuration data of carrier
  570. *
  571. * @param string $type
  572. * @param string $code
  573. * @return array|false
  574. * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
  575. */
  576. public function getCode($type, $code = '')
  577. {
  578. $codes = [
  579. 'method' => [
  580. '0_FCLE' => __('First-Class Mail Large Envelope'),
  581. '0_FCL' => __('First-Class Mail Letter'),
  582. '0_FCP' => __('First-Class Package Service - Retail'),
  583. '0_FCPC' => __('First-Class Mail Postcards'),
  584. '1' => __('Priority Mail'),
  585. '2' => __('Priority Mail Express Hold For Pickup'),
  586. '3' => __('Priority Mail Express'),
  587. '4' => __('Retail Ground'),
  588. '6' => __('Media Mail'),
  589. '7' => __('Library Mail'),
  590. '13' => __('Priority Mail Express Flat Rate Envelope'),
  591. '15' => __('First-Class Mail Large Postcards'),
  592. '16' => __('Priority Mail Flat Rate Envelope'),
  593. '17' => __('Priority Mail Medium Flat Rate Box'),
  594. '22' => __('Priority Mail Large Flat Rate Box'),
  595. '23' => __('Priority Mail Express Sunday/Holiday Delivery'),
  596. '25' => __('Priority Mail Express Sunday/Holiday Delivery Flat Rate Envelope'),
  597. '27' => __('Priority Mail Express Flat Rate Envelope Hold For Pickup'),
  598. '28' => __('Priority Mail Small Flat Rate Box'),
  599. '29' => __('Priority Mail Padded Flat Rate Envelope'),
  600. '30' => __('Priority Mail Express Legal Flat Rate Envelope'),
  601. '31' => __('Priority Mail Express Legal Flat Rate Envelope Hold For Pickup'),
  602. '32' => __('Priority Mail Express Sunday/Holiday Delivery Legal Flat Rate Envelope'),
  603. '33' => __('Priority Mail Hold For Pickup'),
  604. '34' => __('Priority Mail Large Flat Rate Box Hold For Pickup'),
  605. '35' => __('Priority Mail Medium Flat Rate Box Hold For Pickup'),
  606. '36' => __('Priority Mail Small Flat Rate Box Hold For Pickup'),
  607. '37' => __('Priority Mail Flat Rate Envelope Hold For Pickup'),
  608. '38' => __('Priority Mail Gift Card Flat Rate Envelope'),
  609. '39' => __('Priority Mail Gift Card Flat Rate Envelope Hold For Pickup'),
  610. '40' => __('Priority Mail Window Flat Rate Envelope'),
  611. '41' => __('Priority Mail Window Flat Rate Envelope Hold For Pickup'),
  612. '42' => __('Priority Mail Small Flat Rate Envelope'),
  613. '43' => __('Priority Mail Small Flat Rate Envelope Hold For Pickup'),
  614. '44' => __('Priority Mail Legal Flat Rate Envelope'),
  615. '45' => __('Priority Mail Legal Flat Rate Envelope Hold For Pickup'),
  616. '46' => __('Priority Mail Padded Flat Rate Envelope Hold For Pickup'),
  617. '47' => __('Priority Mail Regional Rate Box A'),
  618. '48' => __('Priority Mail Regional Rate Box A Hold For Pickup'),
  619. '49' => __('Priority Mail Regional Rate Box B'),
  620. '50' => __('Priority Mail Regional Rate Box B Hold For Pickup'),
  621. '53' => __('First-Class Package Service Hold For Pickup'),
  622. '57' => __('Priority Mail Express Sunday/Holiday Delivery Flat Rate Boxes'),
  623. '58' => __('Priority Mail Regional Rate Box C'),
  624. '59' => __('Priority Mail Regional Rate Box C Hold For Pickup'),
  625. '61' => __('First-Class Package Service'),
  626. '62' => __('Priority Mail Express Padded Flat Rate Envelope'),
  627. '63' => __('Priority Mail Express Padded Flat Rate Envelope Hold For Pickup'),
  628. '64' => __('Priority Mail Express Sunday/Holiday Delivery Padded Flat Rate Envelope'),
  629. 'INT_1' => __('Priority Mail Express International'),
  630. 'INT_2' => __('Priority Mail International'),
  631. 'INT_4' => __('Global Express Guaranteed (GXG)'),
  632. 'INT_5' => __('Global Express Guaranteed Document'),
  633. 'INT_6' => __('Global Express Guaranteed Non-Document Rectangular'),
  634. 'INT_7' => __('Global Express Guaranteed Non-Document Non-Rectangular'),
  635. 'INT_8' => __('Priority Mail International Flat Rate Envelope'),
  636. 'INT_9' => __('Priority Mail International Medium Flat Rate Box'),
  637. 'INT_10' => __('Priority Mail Express International Flat Rate Envelope'),
  638. 'INT_11' => __('Priority Mail International Large Flat Rate Box'),
  639. 'INT_12' => __('USPS GXG Envelopes'),
  640. 'INT_13' => __('First-Class Mail International Letter'),
  641. 'INT_14' => __('First-Class Mail International Large Envelope'),
  642. 'INT_15' => __('First-Class Package International Service'),
  643. 'INT_16' => __('Priority Mail International Small Flat Rate Box'),
  644. 'INT_17' => __('Priority Mail Express International Legal Flat Rate Envelope'),
  645. 'INT_18' => __('Priority Mail International Gift Card Flat Rate Envelope'),
  646. 'INT_19' => __('Priority Mail International Window Flat Rate Envelope'),
  647. 'INT_20' => __('Priority Mail International Small Flat Rate Envelope'),
  648. 'INT_21' => __('First-Class Mail International Postcard'),
  649. 'INT_22' => __('Priority Mail International Legal Flat Rate Envelope'),
  650. 'INT_23' => __('Priority Mail International Padded Flat Rate Envelope'),
  651. 'INT_24' => __('Priority Mail International DVD Flat Rate priced box'),
  652. 'INT_25' => __('Priority Mail International Large Video Flat Rate priced box'),
  653. 'INT_27' => __('Priority Mail Express International Padded Flat Rate Envelope'),
  654. ],
  655. 'service_to_code' => [
  656. '0_FCLE' => 'First Class',
  657. '0_FCL' => 'First Class',
  658. '0_FCP' => 'First Class',
  659. '0_FCPC' => 'First Class',
  660. '1' => 'Priority',
  661. '2' => 'Priority Express',
  662. '3' => 'Priority Express',
  663. '4' => 'Retail Ground',
  664. '6' => 'Media',
  665. '7' => 'Library',
  666. '13' => 'Priority Express',
  667. '15' => 'First Class',
  668. '16' => 'Priority',
  669. '17' => 'Priority',
  670. '22' => 'Priority',
  671. '23' => 'Priority Express',
  672. '25' => 'Priority Express',
  673. '27' => 'Priority Express',
  674. '28' => 'Priority',
  675. '29' => 'Priority',
  676. '30' => 'Priority Express',
  677. '31' => 'Priority Express',
  678. '32' => 'Priority Express',
  679. '33' => 'Priority',
  680. '34' => 'Priority',
  681. '35' => 'Priority',
  682. '36' => 'Priority',
  683. '37' => 'Priority',
  684. '38' => 'Priority',
  685. '39' => 'Priority',
  686. '40' => 'Priority',
  687. '41' => 'Priority',
  688. '42' => 'Priority',
  689. '43' => 'Priority',
  690. '44' => 'Priority',
  691. '45' => 'Priority',
  692. '46' => 'Priority',
  693. '47' => 'Priority',
  694. '48' => 'Priority',
  695. '49' => 'Priority',
  696. '50' => 'Priority',
  697. '53' => 'First Class',
  698. '57' => 'Priority Express',
  699. '58' => 'Priority',
  700. '59' => 'Priority',
  701. '61' => 'First Class',
  702. '62' => 'Priority Express',
  703. '63' => 'Priority Express',
  704. '64' => 'Priority Express',
  705. 'INT_1' => 'Priority Express',
  706. 'INT_2' => 'Priority',
  707. 'INT_4' => 'Priority Express',
  708. 'INT_5' => 'Priority Express',
  709. 'INT_6' => 'Priority Express',
  710. 'INT_7' => 'Priority Express',
  711. 'INT_8' => 'Priority',
  712. 'INT_9' => 'Priority',
  713. 'INT_10' => 'Priority Express',
  714. 'INT_11' => 'Priority',
  715. 'INT_12' => 'Priority Express',
  716. 'INT_13' => 'First Class',
  717. 'INT_14' => 'First Class',
  718. 'INT_15' => 'First Class',
  719. 'INT_16' => 'Priority',
  720. 'INT_17' => 'Priority',
  721. 'INT_18' => 'Priority',
  722. 'INT_19' => 'Priority',
  723. 'INT_20' => 'Priority',
  724. 'INT_21' => 'First Class',
  725. 'INT_22' => 'Priority',
  726. 'INT_23' => 'Priority',
  727. 'INT_24' => 'Priority',
  728. 'INT_25' => 'Priority',
  729. 'INT_27' => 'Priority Express',
  730. ],
  731. 'method_to_code' => [
  732. 'First-Class Mail Large Envelope' => '0_FCLE',
  733. 'First-Class Mail Letter' => '0_FCL',
  734. 'First-Class Package Service - Retail' => '0_FCP',
  735. ],
  736. 'first_class_mail_type' => ['LETTER' => __('Letter'), 'FLAT' => __('Flat'), 'PARCEL' => __('Parcel')],
  737. 'container' => [
  738. 'VARIABLE' => __('Variable'),
  739. 'SM FLAT RATE BOX' => __('Small Flat-Rate Box'),
  740. 'MD FLAT RATE BOX' => __('Medium Flat-Rate Box'),
  741. 'LG FLAT RATE BOX' => __('Large Flat-Rate Box'),
  742. 'FLAT RATE ENVELOPE' => __('Flat-Rate Envelope'),
  743. 'SM FLAT RATE ENVELOPE' => __('Small Flat-Rate Envelope'),
  744. 'WINDOW FLAT RATE ENVELOPE' => __('Window Flat-Rate Envelope'),
  745. 'GIFT CARD FLAT RATE ENVELOPE' => __('Gift Card Flat-Rate Envelope'),
  746. 'LEGAL FLAT RATE ENVELOPE' => __('Legal Flat-Rate Envelope'),
  747. 'PADDED FLAT RATE ENVELOPE' => __('Padded Flat-Rate Envelope'),
  748. 'RECTANGULAR' => __('Rectangular'),
  749. 'NONRECTANGULAR' => __('Non-rectangular'),
  750. ],
  751. 'containers_filter' => [
  752. [
  753. 'containers' => ['VARIABLE'],
  754. 'filters' => [
  755. 'within_us' => [
  756. 'method' => [
  757. '13', '27', '16', '22', '17', '28', '2', '3', '1', '33', '34', '35',
  758. '36', '37', '42', '43', '53', '4', '6', '15', '23', '25', '57'
  759. ],
  760. ],
  761. 'from_us' => [
  762. 'method' => [
  763. 'INT_10', 'INT_8', 'INT_11', 'INT_9', 'INT_16', 'INT_20', 'INT_4',
  764. 'INT_12', 'INT_1', 'INT_2', 'INT_13', 'INT_14', 'INT_15'
  765. ],
  766. ],
  767. ],
  768. ],
  769. [
  770. 'containers' => ['SM FLAT RATE BOX'],
  771. 'filters' => [
  772. 'within_us' => [
  773. 'method' => ['28', '57'],
  774. ],
  775. 'from_us' => [
  776. 'method' => ['INT_16', 'INT_24'],
  777. ],
  778. ]
  779. ],
  780. [
  781. 'containers' => ['MD FLAT RATE BOX'],
  782. 'filters' => [
  783. 'within_us' => [
  784. 'method' => ['17', '57'],
  785. ],
  786. 'from_us' => [
  787. 'method' => ['INT_9', 'INT_24'],
  788. ],
  789. ]
  790. ],
  791. [
  792. 'containers' => ['LG FLAT RATE BOX'],
  793. 'filters' => [
  794. 'within_us' => [
  795. 'method' => ['22', '57'],
  796. ],
  797. 'from_us' => [
  798. 'method' => ['INT_11', 'INT_24', 'INT_25'],
  799. ],
  800. ]
  801. ],
  802. [
  803. 'containers' => ['SM FLAT RATE ENVELOPE'],
  804. 'filters' => [
  805. 'within_us' => [
  806. 'method' => ['42', '43'],
  807. ],
  808. 'from_us' => [
  809. 'method' => ['INT_20'],
  810. ],
  811. ]
  812. ],
  813. [
  814. 'containers' => ['WINDOW FLAT RATE ENVELOPE'],
  815. 'filters' => [
  816. 'within_us' => [
  817. 'method' => ['40', '41'],
  818. ],
  819. 'from_us' => [
  820. 'method' => ['INT_19'],
  821. ],
  822. ]
  823. ],
  824. [
  825. 'containers' => ['GIFT CARD FLAT RATE ENVELOPE'],
  826. 'filters' => [
  827. 'within_us' => [
  828. 'method' => ['38', '39'],
  829. ],
  830. 'from_us' => [
  831. 'method' => ['INT_18'],
  832. ],
  833. ]
  834. ],
  835. [
  836. 'containers' => ['PADDED FLAT RATE ENVELOPE'],
  837. 'filters' => [
  838. 'within_us' => [
  839. 'method' => ['62', '63', '64', '46', '29'],
  840. ],
  841. 'from_us' => [
  842. 'method' => ['INT_27', 'INT_23'],
  843. ],
  844. ]
  845. ],
  846. [
  847. 'containers' => ['LEGAL FLAT RATE ENVELOPE'],
  848. 'filters' => [
  849. 'within_us' => [
  850. 'method' => ['44', '45', '30', '31', '32'],
  851. ],
  852. 'from_us' => [
  853. 'method' => ['INT_17', 'INT_22'],
  854. ],
  855. ]
  856. ],
  857. [
  858. 'containers' => ['FLAT RATE ENVELOPE'],
  859. 'filters' => [
  860. 'within_us' => [
  861. 'method' => ['16', '13', '27', '16', '15', '37', '42', '43', '25', '62'],
  862. ],
  863. 'from_us' => [
  864. 'method' => [
  865. 'INT_10', 'INT_8', 'INT_14', 'INT_20', 'INT_17', 'INT_18', 'INT_19', 'INT_22', 'INT_27'
  866. ],
  867. ],
  868. ]
  869. ],
  870. [
  871. 'containers' => ['RECTANGULAR'],
  872. 'filters' => [
  873. 'within_us' => [
  874. 'method' => ['3', '1', '4', '6', '7', '61'],
  875. ],
  876. 'from_us' => [
  877. 'method' => ['INT_12', 'INT_1', 'INT_2', 'INT_15'],
  878. ],
  879. ]
  880. ],
  881. [
  882. 'containers' => ['NONRECTANGULAR'],
  883. 'filters' => [
  884. 'within_us' => [
  885. 'method' => ['3', '1', '4', '6', '7'],
  886. ],
  887. 'from_us' => [
  888. 'method' => ['INT_4', 'INT_1', 'INT_2', 'INT_15'],
  889. ],
  890. ]
  891. ],
  892. ],
  893. 'size' => ['REGULAR' => __('Regular'), 'LARGE' => __('Large')],
  894. 'machinable' => ['true' => __('Yes'), 'false' => __('No')],
  895. 'delivery_confirmation_types' => ['True' => __('Not Required'), 'False' => __('Required')],
  896. ];
  897. if (!isset($codes[$type])) {
  898. return false;
  899. } elseif ('' === $code) {
  900. return $codes[$type];
  901. }
  902. if (!isset($codes[$type][$code])) {
  903. return false;
  904. } else {
  905. return $codes[$type][$code];
  906. }
  907. }
  908. /**
  909. * Get tracking
  910. *
  911. * @param string|string[] $trackings
  912. * @return Result|null
  913. */
  914. public function getTracking($trackings)
  915. {
  916. $this->setTrackingReqeust();
  917. if (!is_array($trackings)) {
  918. $trackings = [$trackings];
  919. }
  920. $this->_getXmlTracking($trackings);
  921. return $this->_result;
  922. }
  923. /**
  924. * Set tracking request
  925. *
  926. * @return void
  927. */
  928. protected function setTrackingReqeust()
  929. {
  930. $r = new \Magento\Framework\DataObject();
  931. $userId = $this->getConfigData('userid');
  932. $r->setUserId($userId);
  933. $this->_rawTrackRequest = $r;
  934. }
  935. /**
  936. * Send request for tracking
  937. *
  938. * @param string[] $trackings
  939. * @return void
  940. */
  941. protected function _getXmlTracking($trackings)
  942. {
  943. $r = $this->_rawTrackRequest;
  944. foreach ($trackings as $tracking) {
  945. $xml = $this->_xmlElFactory->create(
  946. ['data' => '<?xml version = "1.0" encoding = "UTF-8"?><TrackRequest/>']
  947. );
  948. $xml->addAttribute('USERID', $r->getUserId());
  949. $trackid = $xml->addChild('TrackID');
  950. $trackid->addAttribute('ID', $tracking);
  951. $api = 'TrackV2';
  952. $request = $xml->asXML();
  953. $debugData = ['request' => $this->filterDebugData($request)];
  954. try {
  955. $url = $this->getConfigData('gateway_url');
  956. if (!$url) {
  957. $url = $this->_defaultGatewayUrl;
  958. }
  959. $client = $this->_httpClientFactory->create();
  960. $client->setUri($url);
  961. $client->setConfig(['maxredirects' => 0, 'timeout' => 30]);
  962. $client->setParameterGet('API', $api);
  963. $client->setParameterGet('XML', $request);
  964. $response = $client->request();
  965. $responseBody = $response->getBody();
  966. $debugData['result'] = $responseBody;
  967. } catch (\Exception $e) {
  968. $debugData['result'] = ['error' => $e->getMessage(), 'code' => $e->getCode()];
  969. $responseBody = '';
  970. }
  971. $this->_debug($debugData);
  972. $this->_parseXmlTrackingResponse($tracking, $responseBody);
  973. }
  974. }
  975. /**
  976. * Parse xml tracking response
  977. *
  978. * @param string $trackingvalue
  979. * @param string $response
  980. * @return void
  981. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  982. * @SuppressWarnings(PHPMD.UnusedLocalVariable)
  983. */
  984. protected function _parseXmlTrackingResponse($trackingvalue, $response)
  985. {
  986. $errorTitle = __('For some reason we can\'t retrieve tracking info right now.');
  987. $resultArr = [];
  988. if (strlen(trim($response)) > 0) {
  989. if (strpos(trim($response), '<?xml') === 0) {
  990. $xml = $this->parseXml($response);
  991. if (is_object($xml)) {
  992. if (isset($xml->Number) && isset($xml->Description) && (string)$xml->Description != '') {
  993. $errorTitle = (string)$xml->Description;
  994. } elseif (isset($xml->TrackInfo)
  995. && isset($xml->TrackInfo->Error)
  996. && isset($xml->TrackInfo->Error->Description)
  997. && (string)$xml->TrackInfo->Error->Description != ''
  998. ) {
  999. $errorTitle = (string)$xml->TrackInfo->Error->Description;
  1000. } else {
  1001. $errorTitle = __(
  1002. 'Sorry, something went wrong. Please try again or contact us and we\'ll try to help.'
  1003. );
  1004. }
  1005. if (isset($xml->TrackInfo) && isset($xml->TrackInfo->TrackSummary)) {
  1006. $resultArr['tracksummary'] = (string)$xml->TrackInfo->TrackSummary;
  1007. }
  1008. }
  1009. }
  1010. }
  1011. if (!$this->_result) {
  1012. $this->_result = $this->_trackFactory->create();
  1013. }
  1014. $defaults = $this->getDefaults();
  1015. if ($resultArr) {
  1016. $tracking = $this->_trackStatusFactory->create();
  1017. $tracking->setCarrier('usps');
  1018. $tracking->setCarrierTitle($this->getConfigData('title'));
  1019. $tracking->setTracking($trackingvalue);
  1020. $tracking->setTrackSummary($resultArr['tracksummary']);
  1021. $this->_result->append($tracking);
  1022. } else {
  1023. $error = $this->_trackErrorFactory->create();
  1024. $error->setCarrier('usps');
  1025. $error->setCarrierTitle($this->getConfigData('title'));
  1026. $error->setTracking($trackingvalue);
  1027. $error->setErrorMessage($errorTitle);
  1028. $this->_result->append($error);
  1029. }
  1030. }
  1031. /**
  1032. * Get tracking response
  1033. *
  1034. * @return string
  1035. */
  1036. public function getResponse()
  1037. {
  1038. $statuses = '';
  1039. if ($this->_result instanceof \Magento\Shipping\Model\Tracking\Result) {
  1040. if ($trackings = $this->_result->getAllTrackings()) {
  1041. foreach ($trackings as $tracking) {
  1042. if ($data = $tracking->getAllData()) {
  1043. if (!empty($data['track_summary'])) {
  1044. $statuses .= __($data['track_summary']);
  1045. } else {
  1046. $statuses .= __('Empty response');
  1047. }
  1048. }
  1049. }
  1050. }
  1051. }
  1052. if (empty($statuses)) {
  1053. $statuses = __('Empty response');
  1054. }
  1055. return $statuses;
  1056. }
  1057. /**
  1058. * Get allowed shipping methods
  1059. *
  1060. * @return array
  1061. */
  1062. public function getAllowedMethods()
  1063. {
  1064. $allowed = explode(',', $this->getConfigData('allowed_methods'));
  1065. $arr = [];
  1066. foreach ($allowed as $k) {
  1067. $arr[$k] = $this->getCode('method', $k);
  1068. }
  1069. return $arr;
  1070. }
  1071. /**
  1072. * Return USPS county name by country ISO 3166-1-alpha-2 code
  1073. *
  1074. * Return false for unknown countries
  1075. *
  1076. * @param string $countryId
  1077. * @return string|false
  1078. * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
  1079. */
  1080. protected function _getCountryName($countryId)
  1081. {
  1082. $countries = [
  1083. 'AD' => 'Andorra',
  1084. 'AE' => 'United Arab Emirates',
  1085. 'AF' => 'Afghanistan',
  1086. 'AG' => 'Antigua and Barbuda',
  1087. 'AI' => 'Anguilla',
  1088. 'AL' => 'Albania',
  1089. 'AM' => 'Armenia',
  1090. 'AN' => 'Netherlands Antilles',
  1091. 'AO' => 'Angola',
  1092. 'AR' => 'Argentina',
  1093. 'AT' => 'Austria',
  1094. 'AU' => 'Australia',
  1095. 'AW' => 'Aruba',
  1096. 'AX' => 'Aland Island (Finland)',
  1097. 'AZ' => 'Azerbaijan',
  1098. 'BA' => 'Bosnia-Herzegovina',
  1099. 'BB' => 'Barbados',
  1100. 'BD' => 'Bangladesh',
  1101. 'BE' => 'Belgium',
  1102. 'BF' => 'Burkina Faso',
  1103. 'BG' => 'Bulgaria',
  1104. 'BH' => 'Bahrain',
  1105. 'BI' => 'Burundi',
  1106. 'BJ' => 'Benin',
  1107. 'BM' => 'Bermuda',
  1108. 'BN' => 'Brunei Darussalam',
  1109. 'BO' => 'Bolivia',
  1110. 'BR' => 'Brazil',
  1111. 'BS' => 'Bahamas',
  1112. 'BT' => 'Bhutan',
  1113. 'BW' => 'Botswana',
  1114. 'BY' => 'Belarus',
  1115. 'BZ' => 'Belize',
  1116. 'CA' => 'Canada',
  1117. 'CC' => 'Cocos Island (Australia)',
  1118. 'CD' => 'Congo, Democratic Republic of the',
  1119. 'CF' => 'Central African Republic',
  1120. 'CG' => 'Congo, Republic of the',
  1121. 'CH' => 'Switzerland',
  1122. 'CI' => 'Ivory Coast (Cote d Ivoire)',
  1123. 'CK' => 'Cook Islands (New Zealand)',
  1124. 'CL' => 'Chile',
  1125. 'CM' => 'Cameroon',
  1126. 'CN' => 'China',
  1127. 'CO' => 'Colombia',
  1128. 'CR' => 'Costa Rica',
  1129. 'CU' => 'Cuba',
  1130. 'CV' => 'Cape Verde',
  1131. 'CX' => 'Christmas Island (Australia)',
  1132. 'CY' => 'Cyprus',
  1133. 'CZ' => 'Czech Republic',
  1134. 'DE' => 'Germany',
  1135. 'DJ' => 'Djibouti',
  1136. 'DK' => 'Denmark',
  1137. 'DM' => 'Dominica',
  1138. 'DO' => 'Dominican Republic',
  1139. 'DZ' => 'Algeria',
  1140. 'EC' => 'Ecuador',
  1141. 'EE' => 'Estonia',
  1142. 'EG' => 'Egypt',
  1143. 'ER' => 'Eritrea',
  1144. 'ES' => 'Spain',
  1145. 'ET' => 'Ethiopia',
  1146. 'FI' => 'Finland',
  1147. 'FJ' => 'Fiji',
  1148. 'FK' => 'Falkland Islands',
  1149. 'FM' => 'Micronesia, Federated States of',
  1150. 'FO' => 'Faroe Islands',
  1151. 'FR' => 'France',
  1152. 'GA' => 'Gabon',
  1153. 'GB' => 'United Kingdom of Great Britain and Northern Ireland',
  1154. 'GD' => 'Grenada',
  1155. 'GE' => 'Georgia, Republic of',
  1156. 'GF' => 'French Guiana',
  1157. 'GH' => 'Ghana',
  1158. 'GI' => 'Gibraltar',
  1159. 'GL' => 'Greenland',
  1160. 'GM' => 'Gambia',
  1161. 'GN' => 'Guinea',
  1162. 'GP' => 'Guadeloupe',
  1163. 'GQ' => 'Equatorial Guinea',
  1164. 'GR' => 'Greece',
  1165. 'GS' => 'South Georgia (Falkland Islands)',
  1166. 'GT' => 'Guatemala',
  1167. 'GW' => 'Guinea-Bissau',
  1168. 'GY' => 'Guyana',
  1169. 'HK' => 'Hong Kong',
  1170. 'HN' => 'Honduras',
  1171. 'HR' => 'Croatia',
  1172. 'HT' => 'Haiti',
  1173. 'HU' => 'Hungary',
  1174. 'ID' => 'Indonesia',
  1175. 'IE' => 'Ireland',
  1176. 'IL' => 'Israel',
  1177. 'IN' => 'India',
  1178. 'IQ' => 'Iraq',
  1179. 'IR' => 'Iran',
  1180. 'IS' => 'Iceland',
  1181. 'IT' => 'Italy',
  1182. 'JM' => 'Jamaica',
  1183. 'JO' => 'Jordan',
  1184. 'JP' => 'Japan',
  1185. 'KE' => 'Kenya',
  1186. 'KG' => 'Kyrgyzstan',
  1187. 'KH' => 'Cambodia',
  1188. 'KI' => 'Kiribati',
  1189. 'KM' => 'Comoros',
  1190. 'KN' => 'Saint Kitts (Saint Christopher and Nevis)',
  1191. 'KP' => 'North Korea (Korea, Democratic People\'s Republic of)',
  1192. 'KR' => 'South Korea (Korea, Republic of)',
  1193. 'KW' => 'Kuwait',
  1194. 'KY' => 'Cayman Islands',
  1195. 'KZ' => 'Kazakhstan',
  1196. 'LA' => 'Laos',
  1197. 'LB' => 'Lebanon',
  1198. 'LC' => 'Saint Lucia',
  1199. 'LI' => 'Liechtenstein',
  1200. 'LK' => 'Sri Lanka',
  1201. 'LR' => 'Liberia',
  1202. 'LS' => 'Lesotho',
  1203. 'LT' => 'Lithuania',
  1204. 'LU' => 'Luxembourg',
  1205. 'LV' => 'Latvia',
  1206. 'LY' => 'Libya',
  1207. 'MA' => 'Morocco',
  1208. 'MC' => 'Monaco (France)',
  1209. 'MD' => 'Moldova',
  1210. 'MG' => 'Madagascar',
  1211. 'MK' => 'Macedonia, Republic of',
  1212. 'ML' => 'Mali',
  1213. 'MM' => 'Burma',
  1214. 'MN' => 'Mongolia',
  1215. 'MO' => 'Macao',
  1216. 'MQ' => 'Martinique',
  1217. 'MR' => 'Mauritania',
  1218. 'MS' => 'Montserrat',
  1219. 'MT' => 'Malta',
  1220. 'MU' => 'Mauritius',
  1221. 'MV' => 'Maldives',
  1222. 'MW' => 'Malawi',
  1223. 'MX' => 'Mexico',
  1224. 'MY' => 'Malaysia',
  1225. 'MZ' => 'Mozambique',
  1226. 'NA' => 'Namibia',
  1227. 'NC' => 'New Caledonia',
  1228. 'NE' => 'Niger',
  1229. 'NG' => 'Nigeria',
  1230. 'NI' => 'Nicaragua',
  1231. 'NL' => 'Netherlands',
  1232. 'NO' => 'Norway',
  1233. 'NP' => 'Nepal',
  1234. 'NR' => 'Nauru',
  1235. 'NZ' => 'New Zealand',
  1236. 'OM' => 'Oman',
  1237. 'PA' => 'Panama',
  1238. 'PE' => 'Peru',
  1239. 'PF' => 'French Polynesia',
  1240. 'PG' => 'Papua New Guinea',
  1241. 'PH' => 'Philippines',
  1242. 'PK' => 'Pakistan',
  1243. 'PL' => 'Poland',
  1244. 'PM' => 'Saint Pierre and Miquelon',
  1245. 'PN' => 'Pitcairn Island',
  1246. 'PT' => 'Portugal',
  1247. 'PY' => 'Paraguay',
  1248. 'QA' => 'Qatar',
  1249. 'RE' => 'Reunion',
  1250. 'RO' => 'Romania',
  1251. 'RS' => 'Serbia',
  1252. 'RU' => 'Russia',
  1253. 'RW' => 'Rwanda',
  1254. 'SA' => 'Saudi Arabia',
  1255. 'SB' => 'Solomon Islands',
  1256. 'SC' => 'Seychelles',
  1257. 'SD' => 'Sudan',
  1258. 'SE' => 'Sweden',
  1259. 'SG' => 'Singapore',
  1260. 'SH' => 'Saint Helena',
  1261. 'SI' => 'Slovenia',
  1262. 'SK' => 'Slovak Republic',
  1263. 'SL' => 'Sierra Leone',
  1264. 'SM' => 'San Marino',
  1265. 'SN' => 'Senegal',
  1266. 'SO' => 'Somalia',
  1267. 'SR' => 'Suriname',
  1268. 'ST' => 'Sao Tome and Principe',
  1269. 'SV' => 'El Salvador',
  1270. 'SY' => 'Syrian Arab Republic',
  1271. 'SZ' => 'Eswatini',
  1272. 'TC' => 'Turks and Caicos Islands',
  1273. 'TD' => 'Chad',
  1274. 'TG' => 'Togo',
  1275. 'TH' => 'Thailand',
  1276. 'TJ' => 'Tajikistan',
  1277. 'TK' => 'Tokelau (Union Group) (Western Samoa)',
  1278. 'TL' => 'East Timor (Timor-Leste, Democratic Republic of)',
  1279. 'TM' => 'Turkmenistan',
  1280. 'TN' => 'Tunisia',
  1281. 'TO' => 'Tonga',
  1282. 'TR' => 'Turkey',
  1283. 'TT' => 'Trinidad and Tobago',
  1284. 'TV' => 'Tuvalu',
  1285. 'TW' => 'Taiwan',
  1286. 'TZ' => 'Tanzania',
  1287. 'UA' => 'Ukraine',
  1288. 'UG' => 'Uganda',
  1289. 'UY' => 'Uruguay',
  1290. 'UZ' => 'Uzbekistan',
  1291. 'VA' => 'Vatican City',
  1292. 'VC' => 'Saint Vincent and the Grenadines',
  1293. 'VE' => 'Venezuela',
  1294. 'VG' => 'British Virgin Islands',
  1295. 'VN' => 'Vietnam',
  1296. 'VU' => 'Vanuatu',
  1297. 'WF' => 'Wallis and Futuna Islands',
  1298. 'WS' => 'Western Samoa',
  1299. 'YE' => 'Yemen',
  1300. 'YT' => 'Mayotte (France)',
  1301. 'ZA' => 'South Africa',
  1302. 'ZM' => 'Zambia',
  1303. 'ZW' => 'Zimbabwe',
  1304. 'US' => 'United States',
  1305. ];
  1306. if (isset($countries[$countryId])) {
  1307. return $countries[$countryId];
  1308. }
  1309. return false;
  1310. }
  1311. /**
  1312. * Clean service name from unsupported strings and characters
  1313. *
  1314. * @param string $name
  1315. * @return string
  1316. */
  1317. protected function _filterServiceName($name)
  1318. {
  1319. $name = (string)preg_replace(
  1320. ['~<[^/!][^>]+>.*</[^>]+>~sU', '~\<!--.*--\>~isU', '~<[^>]+>~is'],
  1321. '',
  1322. html_entity_decode($name)
  1323. );
  1324. $name = str_replace('*', '', $name);
  1325. return $name;
  1326. }
  1327. /**
  1328. * Form XML for US shipment request
  1329. * As integration guide it is important to follow appropriate sequence for tags e.g.: <FromLastName /> must be
  1330. * after <FromFirstName />
  1331. *
  1332. * @param \Magento\Framework\DataObject $request
  1333. * @return string
  1334. * @deprecated 100.2.1 This method should not be used anymore.
  1335. * @see \Magento\Usps\Model\Carrier::_doShipmentRequest method doc block.
  1336. */
  1337. protected function _formUsExpressShipmentRequest(\Magento\Framework\DataObject $request)
  1338. {
  1339. $packageParams = $request->getPackageParams();
  1340. $packageWeight = $request->getPackageWeight();
  1341. if ($packageParams->getWeightUnits() != \Zend_Measure_Weight::OUNCE) {
  1342. $packageWeight = round(
  1343. $this->_carrierHelper->convertMeasureWeight(
  1344. (float)$request->getPackageWeight(),
  1345. $packageParams->getWeightUnits(),
  1346. \Zend_Measure_Weight::OUNCE
  1347. )
  1348. );
  1349. }
  1350. list($fromZip5, $fromZip4) = $this->_parseZip($request->getShipperAddressPostalCode());
  1351. list($toZip5, $toZip4) = $this->_parseZip($request->getRecipientAddressPostalCode(), true);
  1352. $rootNode = 'ExpressMailLabelRequest';
  1353. // the wrap node needs for remove xml declaration above
  1354. $xmlWrap = $this->_xmlElFactory->create(['data' => '<?xml version = "1.0" encoding = "UTF-8"?><wrap/>']);
  1355. $xml = $xmlWrap->addChild($rootNode);
  1356. $xml->addAttribute('USERID', $this->getConfigData('userid'));
  1357. $xml->addAttribute('PASSWORD', $this->getConfigData('password'));
  1358. $xml->addChild('Option');
  1359. $xml->addChild('Revision');
  1360. $xml->addChild('EMCAAccount');
  1361. $xml->addChild('EMCAPassword');
  1362. $xml->addChild('ImageParameters');
  1363. $xml->addChild('FromFirstName', $request->getShipperContactPersonFirstName());
  1364. $xml->addChild('FromLastName', $request->getShipperContactPersonLastName());
  1365. $xml->addChild('FromFirm', $request->getShipperContactCompanyName());
  1366. $xml->addChild('FromAddress1', $request->getShipperAddressStreet2());
  1367. $xml->addChild('FromAddress2', $request->getShipperAddressStreet1());
  1368. $xml->addChild('FromCity', $request->getShipperAddressCity());
  1369. $xml->addChild('FromState', $request->getShipperAddressStateOrProvinceCode());
  1370. $xml->addChild('FromZip5', $fromZip5);
  1371. $xml->addChild('FromZip4', $fromZip4);
  1372. $xml->addChild('FromPhone', $request->getShipperContactPhoneNumber());
  1373. $xml->addChild('ToFirstName', $request->getRecipientContactPersonFirstName());
  1374. $xml->addChild('ToLastName', $request->getRecipientContactPersonLastName());
  1375. $xml->addChild('ToFirm', $request->getRecipientContactCompanyName());
  1376. $xml->addChild('ToAddress1', $request->getRecipientAddressStreet2());
  1377. $xml->addChild('ToAddress2', $request->getRecipientAddressStreet1());
  1378. $xml->addChild('ToCity', $request->getRecipientAddressCity());
  1379. $xml->addChild('ToState', $request->getRecipientAddressStateOrProvinceCode());
  1380. $xml->addChild('ToZip5', $toZip5);
  1381. $xml->addChild('ToZip4', $toZip4);
  1382. $xml->addChild('ToPhone', $request->getRecipientContactPhoneNumber());
  1383. $xml->addChild('WeightInOunces', $packageWeight);
  1384. $xml->addChild('WaiverOfSignature', $packageParams->getDeliveryConfirmation());
  1385. $xml->addChild('POZipCode');
  1386. $xml->addChild('ImageType', 'PDF');
  1387. $xml = $xmlWrap->{$rootNode}->asXML();
  1388. return $xml;
  1389. }
  1390. /**
  1391. * Form XML for US Signature Confirmation request
  1392. * As integration guide it is important to follow appropriate sequence for tags e.g.: <FromLastName /> must be
  1393. * after <FromFirstName />
  1394. *
  1395. * @param \Magento\Framework\DataObject $request
  1396. * @param string $serviceType
  1397. * @return string
  1398. * @throws \Exception
  1399. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  1400. */
  1401. protected function _formUsSignatureConfirmationShipmentRequest(\Magento\Framework\DataObject $request, $serviceType)
  1402. {
  1403. switch ($serviceType) {
  1404. case 'PRIORITY':
  1405. case 'Priority':
  1406. $serviceType = 'Priority';
  1407. break;
  1408. case 'FIRST CLASS':
  1409. case 'First Class':
  1410. $serviceType = 'First Class';
  1411. break;
  1412. case 'STANDARD':
  1413. case 'Standard Post':
  1414. case 'Retail Ground':
  1415. $serviceType = 'Retail Ground';
  1416. break;
  1417. case 'MEDIA':
  1418. case 'Media':
  1419. $serviceType = 'Media Mail';
  1420. break;
  1421. case 'LIBRARY':
  1422. case 'Library':
  1423. $serviceType = 'Library Mail';
  1424. break;
  1425. default:
  1426. throw new \Exception(__('Service type does not match'));
  1427. }
  1428. $packageParams = $request->getPackageParams();
  1429. $packageWeight = $request->getPackageWeight();
  1430. if ($packageParams->getWeightUnits() != \Zend_Measure_Weight::OUNCE) {
  1431. $packageWeight = round(
  1432. $this->_carrierHelper->convertMeasureWeight(
  1433. (float)$request->getPackageWeight(),
  1434. $packageParams->getWeightUnits(),
  1435. \Zend_Measure_Weight::OUNCE
  1436. )
  1437. );
  1438. }
  1439. list($fromZip5, $fromZip4) = $this->_parseZip($request->getShipperAddressPostalCode());
  1440. list($toZip5, $toZip4) = $this->_parseZip($request->getRecipientAddressPostalCode(), true);
  1441. if ($this->getConfigData('mode')) {
  1442. $rootNode = 'SignatureConfirmationV3.0Request';
  1443. } else {
  1444. $rootNode = 'SigConfirmCertifyV3.0Request';
  1445. }
  1446. // the wrap node needs for remove xml declaration above
  1447. $xmlWrap = $this->_xmlElFactory->create(['data' => '<?xml version = "1.0" encoding = "UTF-8"?><wrap/>']);
  1448. $xml = $xmlWrap->addChild($rootNode);
  1449. $xml->addAttribute('USERID', $this->getConfigData('userid'));
  1450. $xml->addChild('Option', 1);
  1451. $xml->addChild('ImageParameters');
  1452. $xml->addChild('FromName', $request->getShipperContactPersonName());
  1453. $xml->addChild('FromFirm', $request->getShipperContactCompanyName());
  1454. $xml->addChild('FromAddress1', $request->getShipperAddressStreet2());
  1455. $xml->addChild('FromAddress2', $request->getShipperAddressStreet1());
  1456. $xml->addChild('FromCity', $request->getShipperAddressCity());
  1457. $xml->addChild('FromState', $request->getShipperAddressStateOrProvinceCode());
  1458. $xml->addChild('FromZip5', $fromZip5);
  1459. $xml->addChild('FromZip4', $fromZip4);
  1460. $xml->addChild('ToName', $request->getRecipientContactPersonName());
  1461. $xml->addChild('ToFirm', $request->getRecipientContactCompanyName());
  1462. $xml->addChild('ToAddress1', $request->getRecipientAddressStreet2());
  1463. $xml->addChild('ToAddress2', $request->getRecipientAddressStreet1());
  1464. $xml->addChild('ToCity', $request->getRecipientAddressCity());
  1465. $xml->addChild('ToState', $request->getRecipientAddressStateOrProvinceCode());
  1466. $xml->addChild('ToZip5', $toZip5);
  1467. $xml->addChild('ToZip4', $toZip4);
  1468. $xml->addChild('WeightInOunces', $packageWeight);
  1469. $xml->addChild('ServiceType', $serviceType);
  1470. $xml->addChild('WaiverOfSignature', $packageParams->getDeliveryConfirmation());
  1471. $xml->addChild('ImageType', 'PDF');
  1472. $xml = $xmlWrap->{$rootNode}->asXML();
  1473. return $xml;
  1474. }
  1475. /**
  1476. * Convert decimal weight into pound-ounces format
  1477. *
  1478. * @param float $weightInPounds
  1479. * @return float[]
  1480. */
  1481. protected function _convertPoundOunces($weightInPounds)
  1482. {
  1483. $weightInOunces = ceil($weightInPounds * self::OUNCES_POUND);
  1484. $pounds = floor($weightInOunces / self::OUNCES_POUND);
  1485. $ounces = $weightInOunces % self::OUNCES_POUND;
  1486. return [$pounds, $ounces];
  1487. }
  1488. /**
  1489. * Form XML for international shipment request
  1490. * As integration guide it is important to follow appropriate sequence for tags e.g.: <FromLastName /> must be
  1491. * after <FromFirstName />
  1492. *
  1493. * @param \Magento\Framework\DataObject $request
  1494. * @return string
  1495. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  1496. * @SuppressWarnings(PHPMD.NPathComplexity)
  1497. * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
  1498. * @deprecated 100.2.1 Should not be used anymore.
  1499. * @see \Magento\Usps\Model\Carrier::_doShipmentRequest doc block.
  1500. */
  1501. protected function _formIntlShipmentRequest(\Magento\Framework\DataObject $request)
  1502. {
  1503. $packageParams = $request->getPackageParams();
  1504. $height = $packageParams->getHeight();
  1505. $width = $packageParams->getWidth();
  1506. $length = $packageParams->getLength();
  1507. $girth = $packageParams->getGirth();
  1508. $packageWeight = $request->getPackageWeight();
  1509. if ($packageParams->getWeightUnits() != \Zend_Measure_Weight::POUND) {
  1510. $packageWeight = $this->_carrierHelper->convertMeasureWeight(
  1511. (float)$request->getPackageWeight(),
  1512. $packageParams->getWeightUnits(),
  1513. \Zend_Measure_Weight::POUND
  1514. );
  1515. }
  1516. if ($packageParams->getDimensionUnits() != \Zend_Measure_Length::INCH) {
  1517. $length = round(
  1518. $this->_carrierHelper->convertMeasureDimension(
  1519. (float)$packageParams->getLength(),
  1520. $packageParams->getDimensionUnits(),
  1521. \Zend_Measure_Length::INCH
  1522. )
  1523. );
  1524. $width = round(
  1525. $this->_carrierHelper->convertMeasureDimension(
  1526. (float)$packageParams->getWidth(),
  1527. $packageParams->getDimensionUnits(),
  1528. \Zend_Measure_Length::INCH
  1529. )
  1530. );
  1531. $height = round(
  1532. $this->_carrierHelper->convertMeasureDimension(
  1533. (float)$packageParams->getHeight(),
  1534. $packageParams->getDimensionUnits(),
  1535. \Zend_Measure_Length::INCH
  1536. )
  1537. );
  1538. }
  1539. if ($packageParams->getGirthDimensionUnits() != \Zend_Measure_Length::INCH) {
  1540. $girth = round(
  1541. $this->_carrierHelper->convertMeasureDimension(
  1542. (float)$packageParams->getGirth(),
  1543. $packageParams->getGirthDimensionUnits(),
  1544. \Zend_Measure_Length::INCH
  1545. )
  1546. );
  1547. }
  1548. $container = $request->getPackagingType();
  1549. switch ($container) {
  1550. case 'VARIABLE':
  1551. $container = 'VARIABLE';
  1552. break;
  1553. case 'FLAT RATE ENVELOPE':
  1554. $container = 'FLATRATEENV';
  1555. break;
  1556. case 'FLAT RATE BOX':
  1557. $container = 'FLATRATEBOX';
  1558. break;
  1559. case 'RECTANGULAR':
  1560. $container = 'RECTANGULAR';
  1561. break;
  1562. case 'NONRECTANGULAR':
  1563. $container = 'NONRECTANGULAR';
  1564. break;
  1565. default:
  1566. $container = 'VARIABLE';
  1567. }
  1568. $shippingMethod = $request->getShippingMethod();
  1569. list($fromZip5, $fromZip4) = $this->_parseZip($request->getShipperAddressPostalCode());
  1570. // the wrap node needs for remove xml declaration above
  1571. $xmlWrap = $this->_xmlElFactory->create(['data' => '<?xml version = "1.0" encoding = "UTF-8"?><wrap/>']);
  1572. $method = '';
  1573. $service = $this->getCode('service_to_code', $shippingMethod);
  1574. if ($service == 'Priority') {
  1575. $method = 'Priority';
  1576. $rootNode = 'PriorityMailIntlRequest';
  1577. $xml = $xmlWrap->addChild($rootNode);
  1578. } else {
  1579. if ($service == 'First Class') {
  1580. $method = 'FirstClass';
  1581. $rootNode = 'FirstClassMailIntlRequest';
  1582. $xml = $xmlWrap->addChild($rootNode);
  1583. } else {
  1584. $method = 'Express';
  1585. $rootNode = 'ExpressMailIntlRequest';
  1586. $xml = $xmlWrap->addChild($rootNode);
  1587. }
  1588. }
  1589. $xml->addAttribute('USERID', $this->getConfigData('userid'));
  1590. $xml->addAttribute('PASSWORD', $this->getConfigData('password'));
  1591. $xml->addChild('Option');
  1592. $xml->addChild('Revision', self::DEFAULT_REVISION);
  1593. $xml->addChild('ImageParameters');
  1594. $xml->addChild('FromFirstName', $request->getShipperContactPersonFirstName());
  1595. $xml->addChild('FromLastName', $request->getShipperContactPersonLastName());
  1596. $xml->addChild('FromFirm', $request->getShipperContactCompanyName());
  1597. $xml->addChild('FromAddress1', $request->getShipperAddressStreet2());
  1598. $xml->addChild('FromAddress2', $request->getShipperAddressStreet1());
  1599. $xml->addChild('FromCity', $request->getShipperAddressCity());
  1600. $xml->addChild('FromState', $request->getShipperAddressStateOrProvinceCode());
  1601. $xml->addChild('FromZip5', $fromZip5);
  1602. $xml->addChild('FromZip4', $fromZip4);
  1603. $xml->addChild('FromPhone', $request->getShipperContactPhoneNumber());
  1604. if ($method != 'FirstClass') {
  1605. if ($request->getReferenceData()) {
  1606. $referenceData = $request->getReferenceData() . ' P' . $request->getPackageId();
  1607. } else {
  1608. $referenceData = $request->getOrderShipment()->getOrder()->getIncrementId() .
  1609. ' P' .
  1610. $request->getPackageId();
  1611. }
  1612. $xml->addChild('FromCustomsReference', 'Order #' . $referenceData);
  1613. }
  1614. $xml->addChild('ToFirstName', $request->getRecipientContactPersonFirstName());
  1615. $xml->addChild('ToLastName', $request->getRecipientContactPersonLastName());
  1616. $xml->addChild('ToFirm', $request->getRecipientContactCompanyName());
  1617. $xml->addChild('ToAddress1', $request->getRecipientAddressStreet1());
  1618. $xml->addChild('ToAddress2', $request->getRecipientAddressStreet2());
  1619. $xml->addChild('ToCity', $request->getRecipientAddressCity());
  1620. $xml->addChild('ToProvince', $request->getRecipientAddressStateOrProvinceCode());
  1621. $xml->addChild('ToCountry', $this->_getCountryName($request->getRecipientAddressCountryCode()));
  1622. $xml->addChild('ToPostalCode', $request->getRecipientAddressPostalCode());
  1623. $xml->addChild('ToPOBoxFlag', 'N');
  1624. $xml->addChild('ToPhone', $request->getRecipientContactPhoneNumber());
  1625. $xml->addChild('ToFax');
  1626. $xml->addChild('ToEmail');
  1627. if ($method != 'FirstClass') {
  1628. $xml->addChild('NonDeliveryOption', 'Return');
  1629. }
  1630. if ($method == 'FirstClass') {
  1631. if (stripos($shippingMethod, 'Letter') !== false) {
  1632. $xml->addChild('FirstClassMailType', 'LETTER');
  1633. } else {
  1634. if (stripos($shippingMethod, 'Flat') !== false) {
  1635. $xml->addChild('FirstClassMailType', 'FLAT');
  1636. } else {
  1637. $xml->addChild('FirstClassMailType', 'PARCEL');
  1638. }
  1639. }
  1640. }
  1641. if ($method != 'FirstClass') {
  1642. $xml->addChild('Container', $container);
  1643. }
  1644. $shippingContents = $xml->addChild('ShippingContents');
  1645. $packageItems = $request->getPackageItems();
  1646. // get countries of manufacture
  1647. $countriesOfManufacture = [];
  1648. $productIds = [];
  1649. foreach ($packageItems as $itemShipment) {
  1650. $item = new \Magento\Framework\DataObject();
  1651. $item->setData($itemShipment);
  1652. $productIds[] = $item->getProductId();
  1653. }
  1654. $productCollection = $this->_productCollectionFactory->create()->addStoreFilter(
  1655. $request->getStoreId()
  1656. )->addFieldToFilter(
  1657. 'entity_id',
  1658. ['in' => $productIds]
  1659. )->addAttributeToSelect(
  1660. 'country_of_manufacture'
  1661. );
  1662. foreach ($productCollection as $product) {
  1663. $countriesOfManufacture[$product->getId()] = $product->getCountryOfManufacture();
  1664. }
  1665. $packagePoundsWeight = $packageOuncesWeight = 0;
  1666. // for ItemDetail
  1667. foreach ($packageItems as $itemShipment) {
  1668. $item = new \Magento\Framework\DataObject();
  1669. $item->setData($itemShipment);
  1670. $itemWeight = $item->getWeight() * $item->getQty();
  1671. if ($packageParams->getWeightUnits() != \Zend_Measure_Weight::POUND) {
  1672. $itemWeight = $this->_carrierHelper->convertMeasureWeight(
  1673. $itemWeight,
  1674. $packageParams->getWeightUnits(),
  1675. \Zend_Measure_Weight::POUND
  1676. );
  1677. }
  1678. if (!empty($countriesOfManufacture[$item->getProductId()])) {
  1679. $countryOfManufacture = $this->_getCountryName($countriesOfManufacture[$item->getProductId()]);
  1680. } else {
  1681. $countryOfManufacture = '';
  1682. }
  1683. $itemDetail = $shippingContents->addChild('ItemDetail');
  1684. $itemDetail->addChild('Description', $item->getName());
  1685. $ceiledQty = ceil($item->getQty());
  1686. if ($ceiledQty < 1) {
  1687. $ceiledQty = 1;
  1688. }
  1689. $individualItemWeight = $itemWeight / $ceiledQty;
  1690. $itemDetail->addChild('Quantity', $ceiledQty);
  1691. $itemDetail->addChild('Value', sprintf('%.2F', $item->getCustomsValue() * $item->getQty()));
  1692. list($individualPoundsWeight, $individualOuncesWeight) = $this->_convertPoundOunces($individualItemWeight);
  1693. $itemDetail->addChild('NetPounds', $individualPoundsWeight);
  1694. $itemDetail->addChild('NetOunces', sprintf('%.2F', $individualOuncesWeight));
  1695. $itemDetail->addChild('HSTariffNumber', 0);
  1696. $itemDetail->addChild('CountryOfOrigin', $countryOfManufacture);
  1697. list($itemPoundsWeight, $itemOuncesWeight) = $this->_convertPoundOunces($itemWeight);
  1698. $packagePoundsWeight += $itemPoundsWeight;
  1699. $packageOuncesWeight += $itemOuncesWeight;
  1700. }
  1701. $additionalPackagePoundsWeight = floor($packageOuncesWeight / self::OUNCES_POUND);
  1702. $packagePoundsWeight += $additionalPackagePoundsWeight;
  1703. $packageOuncesWeight -= $additionalPackagePoundsWeight * self::OUNCES_POUND;
  1704. if ($packagePoundsWeight + $packageOuncesWeight / self::OUNCES_POUND < $packageWeight) {
  1705. list($packagePoundsWeight, $packageOuncesWeight) = $this->_convertPoundOunces($packageWeight);
  1706. }
  1707. $xml->addChild('GrossPounds', $packagePoundsWeight);
  1708. $xml->addChild('GrossOunces', $packageOuncesWeight);
  1709. if ($packageParams->getContentType() == 'OTHER' && $packageParams->getContentTypeOther() != null) {
  1710. $xml->addChild('ContentType', $packageParams->getContentType());
  1711. $xml->addChild('ContentTypeOther ', $packageParams->getContentTypeOther());
  1712. } else {
  1713. $xml->addChild('ContentType', $packageParams->getContentType());
  1714. }
  1715. $xml->addChild('Agreement', 'y');
  1716. $xml->addChild('ImageType', 'PDF');
  1717. $xml->addChild('ImageLayout', 'ALLINONEFILE');
  1718. if ($method == 'FirstClass') {
  1719. $xml->addChild('Container', $container);
  1720. }
  1721. // set size
  1722. if ($packageParams->getSize()) {
  1723. $xml->addChild('Size', $packageParams->getSize());
  1724. }
  1725. // set dimensions
  1726. $xml->addChild('Length', $length);
  1727. $xml->addChild('Width', $width);
  1728. $xml->addChild('Height', $height);
  1729. if ($girth) {
  1730. $xml->addChild('Girth', $girth);
  1731. }
  1732. $xml = $xmlWrap->{$rootNode}->asXML();
  1733. return $xml;
  1734. }
  1735. /**
  1736. * Do shipment request to carrier web service, obtain Print Shipping Labels and process errors in response
  1737. *
  1738. * @param \Magento\Framework\DataObject $request
  1739. * @return \Magento\Framework\DataObject
  1740. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  1741. * @deprecated 100.2.1 This method must not be used anymore. Starting from 23.02.2018 USPS elimates API usage for
  1742. * free shipping labels generating.
  1743. */
  1744. protected function _doShipmentRequest(\Magento\Framework\DataObject $request)
  1745. {
  1746. $this->_prepareShipmentRequest($request);
  1747. $result = new \Magento\Framework\DataObject();
  1748. $service = $this->getCode('service_to_code', $request->getShippingMethod());
  1749. $recipientUSCountry = $this->_isUSCountry($request->getRecipientAddressCountryCode());
  1750. if ($recipientUSCountry && $service == 'Priority Express') {
  1751. $requestXml = $this->_formUsExpressShipmentRequest($request);
  1752. $api = 'ExpressMailLabel';
  1753. } else {
  1754. if ($recipientUSCountry) {
  1755. $requestXml = $this->_formUsSignatureConfirmationShipmentRequest($request, $service);
  1756. if ($this->getConfigData('mode')) {
  1757. $api = 'SignatureConfirmationV3';
  1758. } else {
  1759. $api = 'SignatureConfirmationCertifyV3';
  1760. }
  1761. } else {
  1762. if ($service == 'First Class') {
  1763. $requestXml = $this->_formIntlShipmentRequest($request);
  1764. $api = 'FirstClassMailIntl';
  1765. } else {
  1766. if ($service == 'Priority') {
  1767. $requestXml = $this->_formIntlShipmentRequest($request);
  1768. $api = 'PriorityMailIntl';
  1769. } else {
  1770. $requestXml = $this->_formIntlShipmentRequest($request);
  1771. $api = 'ExpressMailIntl';
  1772. }
  1773. }
  1774. }
  1775. }
  1776. $debugData = ['request' => $this->filterDebugData($requestXml)];
  1777. $url = $this->getConfigData('gateway_secure_url');
  1778. if (!$url) {
  1779. $url = $this->_defaultGatewayUrl;
  1780. }
  1781. $client = $this->_httpClientFactory->create();
  1782. $client->setUri($url);
  1783. $client->setConfig(['maxredirects' => 0, 'timeout' => 30]);
  1784. $client->setParameterGet('API', $api);
  1785. $client->setParameterGet('XML', $requestXml);
  1786. $response = $client->request()->getBody();
  1787. $response = $this->parseXml($response);
  1788. if ($response !== false) {
  1789. if ($response->getName() == 'Error') {
  1790. $debugData['result'] = [
  1791. 'error' => $response->Description,
  1792. 'code' => $response->Number,
  1793. 'xml' => $response->asXML(),
  1794. ];
  1795. $this->_debug($debugData);
  1796. $result->setErrors($debugData['result']['error']);
  1797. } else {
  1798. if ($recipientUSCountry && $service == 'Priority Express') {
  1799. $labelContent = base64_decode((string)$response->EMLabel);
  1800. $trackingNumber = (string)$response->EMConfirmationNumber;
  1801. } elseif ($recipientUSCountry) {
  1802. $labelContent = base64_decode((string)$response->SignatureConfirmationLabel);
  1803. $trackingNumber = (string)$response->SignatureConfirmationNumber;
  1804. } else {
  1805. $labelContent = base64_decode((string)$response->LabelImage);
  1806. $trackingNumber = (string)$response->BarcodeNumber;
  1807. }
  1808. $result->setShippingLabelContent($labelContent);
  1809. $result->setTrackingNumber($trackingNumber);
  1810. }
  1811. }
  1812. $result->setGatewayResponse($response);
  1813. $debugData['result'] = $response;
  1814. $this->_debug($debugData);
  1815. return $result;
  1816. }
  1817. /**
  1818. * Return container types of carrier
  1819. *
  1820. * @param \Magento\Framework\DataObject|null $params
  1821. * @return array|bool
  1822. */
  1823. public function getContainerTypes(\Magento\Framework\DataObject $params = null)
  1824. {
  1825. if ($params === null) {
  1826. return $this->_getAllowedContainers();
  1827. }
  1828. return $this->_isUSCountry($params->getCountryRecipient()) ? [] : $this->_getAllowedContainers($params);
  1829. }
  1830. /**
  1831. * Return all container types of carrier
  1832. *
  1833. * @return array|bool
  1834. */
  1835. public function getContainerTypesAll()
  1836. {
  1837. return $this->getCode('container');
  1838. }
  1839. /**
  1840. * Return structured data of containers witch related with shipping methods
  1841. *
  1842. * @return array|bool
  1843. */
  1844. public function getContainerTypesFilter()
  1845. {
  1846. return $this->getCode('containers_filter');
  1847. }
  1848. /**
  1849. * Return delivery confirmation types of carrier
  1850. *
  1851. * @param \Magento\Framework\DataObject|null $params
  1852. * @return array
  1853. */
  1854. public function getDeliveryConfirmationTypes(\Magento\Framework\DataObject $params = null)
  1855. {
  1856. if ($params == null) {
  1857. return [];
  1858. }
  1859. $countryRecipient = $params->getCountryRecipient();
  1860. if ($this->_isUSCountry($countryRecipient)) {
  1861. return $this->getCode('delivery_confirmation_types');
  1862. } else {
  1863. return [];
  1864. }
  1865. }
  1866. /**
  1867. * Check whether girth is allowed for the USPS
  1868. *
  1869. * @param null|string $countyDest
  1870. * @param null|string $carrierMethodCode
  1871. * @return bool
  1872. */
  1873. public function isGirthAllowed($countyDest = null, $carrierMethodCode = null)
  1874. {
  1875. return $this->_isUSCountry($countyDest)
  1876. && $this->getDataHelper()->displayGirthValue($carrierMethodCode) ? false : true;
  1877. }
  1878. /**
  1879. * Return content types of package
  1880. *
  1881. * @param \Magento\Framework\DataObject $params
  1882. * @return array
  1883. */
  1884. public function getContentTypes(\Magento\Framework\DataObject $params)
  1885. {
  1886. $countryShipper = $params->getCountryShipper();
  1887. $countryRecipient = $params->getCountryRecipient();
  1888. if ($countryShipper == self::USA_COUNTRY_ID && $countryRecipient != self::USA_COUNTRY_ID) {
  1889. return [
  1890. 'MERCHANDISE' => __('Merchandise'),
  1891. 'SAMPLE' => __('Sample'),
  1892. 'GIFT' => __('Gift'),
  1893. 'DOCUMENTS' => __('Documents'),
  1894. 'RETURN' => __('Return'),
  1895. 'OTHER' => __('Other')
  1896. ];
  1897. }
  1898. return [];
  1899. }
  1900. /**
  1901. * Parse zip from string to zip5-zip4
  1902. *
  1903. * @param string $zipString
  1904. * @param bool $returnFull
  1905. * @return string[]
  1906. */
  1907. protected function _parseZip($zipString, $returnFull = false)
  1908. {
  1909. $zip4 = '';
  1910. $zip5 = '';
  1911. $zip = [$zipString];
  1912. if (preg_match('/[\\d\\w]{5}\\-[\\d\\w]{4}/', $zipString) != 0) {
  1913. $zip = explode('-', $zipString);
  1914. }
  1915. $count = count($zip);
  1916. for ($i = 0; $i < $count; ++$i) {
  1917. if (strlen($zip[$i]) == 5) {
  1918. $zip5 = $zip[$i];
  1919. } elseif (strlen($zip[$i]) == 4) {
  1920. $zip4 = $zip[$i];
  1921. }
  1922. }
  1923. if (empty($zip5) && empty($zip4) && $returnFull) {
  1924. $zip5 = $zipString;
  1925. }
  1926. return [$zip5, $zip4];
  1927. }
  1928. /**
  1929. * Check availability of post service
  1930. *
  1931. * @param \SimpleXMLElement $service
  1932. * @return boolean
  1933. */
  1934. private function isServiceAvailable(\SimpleXMLElement $service)
  1935. {
  1936. // Allow services which which don't provide any ExtraServices
  1937. if (empty($service->ExtraServices->children()->count())) {
  1938. return true;
  1939. }
  1940. foreach ($service->ExtraServices->children() as $child) {
  1941. if (filter_var($child->Available, FILTER_VALIDATE_BOOLEAN)) {
  1942. return true;
  1943. }
  1944. }
  1945. return false;
  1946. }
  1947. /**
  1948. * Replace sensitive fields.
  1949. *
  1950. * Replace sensitive fields, which specified as attributes of xml node.
  1951. * For followed xml:
  1952. * ```xml
  1953. * <RateV4Request USERID="1">
  1954. * <Revision>2</Revision>
  1955. * </RateV4Request>
  1956. * ```xml
  1957. * the `USERID` attribute value will be replaced by specified mask
  1958. *
  1959. * @param string $data
  1960. * @return string
  1961. * @SuppressWarnings(PHPMD.UnusedLocalVariable)
  1962. */
  1963. protected function filterDebugData($data)
  1964. {
  1965. try {
  1966. $xml = new \SimpleXMLElement($data);
  1967. $attributes = $xml->attributes();
  1968. /** @var \SimpleXMLElement $attribute */
  1969. foreach ($attributes as $key => $attribute) {
  1970. if (in_array((string) $key, $this->_debugReplacePrivateDataKeys)) {
  1971. $attributes[$key] = self::DEBUG_KEYS_MASK;
  1972. }
  1973. }
  1974. $data = $xml->asXML();
  1975. } catch (\Exception $e) {
  1976. }
  1977. return $data;
  1978. }
  1979. /**
  1980. * Gets Data helper object
  1981. *
  1982. * @return DataHelper
  1983. * @deprecated 100.2.0
  1984. */
  1985. private function getDataHelper()
  1986. {
  1987. if (!$this->dataHelper) {
  1988. $this->dataHelper = ObjectManager::getInstance()->get(DataHelper::class);
  1989. }
  1990. return $this->dataHelper;
  1991. }
  1992. }