Builder.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. <?php
  2. /**
  3. * This file is part of the Klarna Core module
  4. *
  5. * (c) Klarna Bank AB (publ)
  6. *
  7. * For the full copyright and license information, please view the NOTICE
  8. * and LICENSE files that were distributed with this source code.
  9. */
  10. namespace Klarna\Core\Model\Api;
  11. use Klarna\Core\Api\BuilderInterface;
  12. use Klarna\Core\Helper\ConfigHelper;
  13. use Klarna\Core\Helper\KlarnaConfig;
  14. use Klarna\Core\Model\Checkout\Orderline\Collector;
  15. use Magento\Customer\Api\Data\AddressInterface;
  16. use Magento\Customer\Model\Data\Address as CustomerAddress;
  17. use Magento\Directory\Helper\Data as DirectoryHelper;
  18. use Magento\Framework\DataObject;
  19. use Magento\Framework\DataObjectFactory;
  20. use Magento\Framework\Event\ManagerInterface as EventManager;
  21. use Magento\Framework\Stdlib\DateTime;
  22. use Magento\Framework\Url;
  23. use Magento\Quote\Model\Quote\Address;
  24. use Magento\Store\Api\Data\StoreInterface;
  25. /**
  26. * Base class to generate API configuration
  27. *
  28. * @method Builder setShippingUnitPrice($integer)
  29. * @method int getShippingUnitPrice()
  30. * @method Builder setShippingTaxRate($integer)
  31. * @method int getShippingTaxRate()
  32. * @method Builder setShippingTotalAmount($integer)
  33. * @method int getShippingTotalAmount()
  34. * @method Builder setShippingTaxAmount($integer)
  35. * @method int getShippingTaxAmount()
  36. * @method Builder setShippingTitle($string)
  37. * @method string getShippingTitle()
  38. * @method Builder setShippingReference($integer)
  39. * @method int getShippingReference()
  40. * @method Builder setDiscountUnitPrice($integer)
  41. * @method int getDiscountUnitPrice()
  42. * @method Builder setDiscountTaxRate($integer)
  43. * @method int getDiscountTaxRate()
  44. * @method Builder setDiscountTotalAmount($integer)
  45. * @method int getDiscountTotalAmount()
  46. * @method Builder setDiscountTaxAmount($integer)
  47. * @method int getDiscountTaxAmount()
  48. * @method Builder setDiscountTitle($integer)
  49. * @method int getDiscountTitle()
  50. * @method Builder setDiscountReference($integer)
  51. * @method int getDiscountReference()
  52. * @method Builder setTaxUnitPrice($integer)
  53. * @method int getTaxUnitPrice()
  54. * @method Builder setTaxTotalAmount($integer)
  55. * @method int getTaxTotalAmount()
  56. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  57. */
  58. abstract class Builder extends DataObject implements BuilderInterface
  59. {
  60. /**
  61. * @var string
  62. */
  63. public $prefix = '';
  64. /**
  65. * @var Collector
  66. */
  67. protected $orderLineCollector = null;
  68. /**
  69. * @var EventManager
  70. */
  71. protected $eventManager;
  72. /**
  73. * @var array
  74. */
  75. protected $orderLines = [];
  76. /**
  77. * @var \Magento\Sales\Model\AbstractModel|\Magento\Quote\Model\Quote
  78. */
  79. protected $object = null;
  80. /**
  81. * @var array
  82. */
  83. protected $request = [];
  84. /**
  85. * @var bool
  86. */
  87. protected $inRequestSet = false;
  88. /**
  89. * @var ConfigHelper
  90. */
  91. protected $configHelper;
  92. /**
  93. * @var Url
  94. */
  95. protected $url;
  96. /**
  97. * @var DirectoryHelper
  98. */
  99. protected $directoryHelper;
  100. /**
  101. * @var \Magento\Framework\Stdlib\DateTime\DateTime
  102. */
  103. protected $coreDate;
  104. /**
  105. * @var KlarnaConfig
  106. */
  107. protected $klarnaConfig;
  108. /**
  109. * @var DataObjectFactory
  110. */
  111. protected $dataObjectFactory;
  112. /**
  113. * @var DataObject\Copy
  114. */
  115. private $objCopyService;
  116. /**
  117. * @var \Magento\Customer\Model\AddressRegistry
  118. */
  119. private $addressRegistry;
  120. /**
  121. * Init
  122. *
  123. * @param EventManager $eventManager
  124. * @param Collector $collector
  125. * @param Url $url
  126. * @param ConfigHelper $configHelper
  127. * @param DirectoryHelper $directoryHelper
  128. * @param DateTime\DateTime $coreDate
  129. * @param DataObject\Copy $objCopyService
  130. * @param \Magento\Customer\Model\AddressRegistry $addressRegistry
  131. * @param KlarnaConfig $klarnaConfig
  132. * @param DataObjectFactory $dataObjectFactory
  133. * @param array $data
  134. * @SuppressWarnings(PHPMD.ExcessiveParameterList)
  135. */
  136. public function __construct(
  137. EventManager $eventManager,
  138. Collector $collector,
  139. Url $url,
  140. ConfigHelper $configHelper,
  141. DirectoryHelper $directoryHelper,
  142. \Magento\Framework\Stdlib\DateTime\DateTime $coreDate,
  143. \Magento\Framework\DataObject\Copy $objCopyService,
  144. \Magento\Customer\Model\AddressRegistry $addressRegistry,
  145. KlarnaConfig $klarnaConfig,
  146. DataObjectFactory $dataObjectFactory,
  147. array $data = []
  148. ) {
  149. parent::__construct($data);
  150. $this->eventManager = $eventManager;
  151. $this->orderLineCollector = $collector;
  152. $this->url = $url;
  153. $this->configHelper = $configHelper;
  154. $this->directoryHelper = $directoryHelper;
  155. $this->coreDate = $coreDate;
  156. $this->objCopyService = $objCopyService;
  157. $this->addressRegistry = $addressRegistry;
  158. $this->klarnaConfig = $klarnaConfig;
  159. $this->dataObjectFactory = $dataObjectFactory;
  160. }
  161. /**
  162. * Generate order body
  163. *
  164. * @param string $type
  165. * @return $this
  166. * @throws \Magento\Framework\Exception\LocalizedException
  167. */
  168. public function generateRequest($type = self::GENERATE_TYPE_CREATE)
  169. {
  170. $this->collectOrderLines($this->getObject()->getStore());
  171. return $this;
  172. }
  173. /**
  174. * Collect order lines
  175. *
  176. * @param StoreInterface $store
  177. * @return $this
  178. * @throws \Magento\Framework\Exception\LocalizedException
  179. */
  180. public function collectOrderLines(StoreInterface $store)
  181. {
  182. /** @var \Klarna\Core\Model\Checkout\Orderline\AbstractLine $model */
  183. foreach ($this->getOrderLinesCollector()->getCollectors($store) as $model) {
  184. $model->collect($this);
  185. }
  186. return $this;
  187. }
  188. /**
  189. * Get totals collector model
  190. *
  191. * @return \Klarna\Core\Model\Checkout\Orderline\Collector
  192. */
  193. public function getOrderLinesCollector()
  194. {
  195. return $this->orderLineCollector;
  196. }
  197. /**
  198. * Get the object used to generate request
  199. *
  200. * @return \Magento\Sales\Model\AbstractModel|\Magento\Quote\Model\Quote
  201. */
  202. public function getObject()
  203. {
  204. return $this->object;
  205. }
  206. /**
  207. * Set the object used to generate request
  208. *
  209. * @param \Magento\Sales\Model\AbstractModel|\Magento\Quote\Model\Quote $object
  210. *
  211. * @return $this
  212. */
  213. public function setObject($object)
  214. {
  215. $this->object = $object;
  216. return $this;
  217. }
  218. /**
  219. * Get request
  220. *
  221. * @return array
  222. */
  223. abstract public function getRequest();
  224. /**
  225. * Set generated request
  226. *
  227. * @param array $request
  228. * @param string $type
  229. *
  230. * @return $this
  231. */
  232. public function setRequest(array $request, $type = self::GENERATE_TYPE_CREATE)
  233. {
  234. $this->request = $this->cleanNulls($request);
  235. if (!$this->inRequestSet) {
  236. $this->inRequestSet = true;
  237. $this->eventManager->dispatch(
  238. $this->prefix . "_builder_set_request_{$type}",
  239. [
  240. 'builder' => $this
  241. ]
  242. );
  243. $this->eventManager->dispatch(
  244. $this->prefix . '_builder_set_request',
  245. [
  246. 'builder' => $this
  247. ]
  248. );
  249. $this->inRequestSet = false;
  250. }
  251. return $this;
  252. }
  253. /**
  254. * Remove items that are not allowed to be null
  255. *
  256. * @param array $request
  257. * @return array
  258. */
  259. protected function cleanNulls(array $request)
  260. {
  261. $disallowNulls = [
  262. 'customer',
  263. 'billing_address',
  264. 'shipping_address',
  265. 'external_payment_methods'
  266. ];
  267. foreach ($disallowNulls as $key) {
  268. if (empty($request[$key])) {
  269. unset($request[$key]);
  270. }
  271. }
  272. return $request;
  273. }
  274. /**
  275. * Get order lines as array
  276. *
  277. * @param StoreInterface $store
  278. * @param bool $orderItemsOnly
  279. *
  280. * @return array
  281. * @throws \Magento\Framework\Exception\LocalizedException
  282. */
  283. public function getOrderLines(StoreInterface $store, $orderItemsOnly = false)
  284. {
  285. /** @var \Klarna\Core\Model\Checkout\Orderline\AbstractLine $model */
  286. foreach ($this->getOrderLinesCollector()->getCollectors($store) as $model) {
  287. if ($model->isIsTotalCollector() && $orderItemsOnly) {
  288. continue;
  289. }
  290. $model->fetch($this);
  291. }
  292. return $this->orderLines;
  293. }
  294. /**
  295. * Add an order line
  296. *
  297. * @param array $orderLine
  298. *
  299. * @return $this
  300. */
  301. public function addOrderLine(array $orderLine)
  302. {
  303. $this->orderLines[] = $orderLine;
  304. return $this;
  305. }
  306. /**
  307. * Remove all order lines
  308. *
  309. * @return $this
  310. */
  311. public function resetOrderLines()
  312. {
  313. $this->orderLines = [];
  314. return $this;
  315. }
  316. /**
  317. * Get merchant references
  318. *
  319. * @param $quote
  320. * @return DataObject
  321. */
  322. public function getMerchantReferences($quote)
  323. {
  324. $merchantReferences = $this->dataObjectFactory->create([
  325. 'data' => [
  326. 'merchant_reference_1' => $quote->getReservedOrderId(),
  327. 'merchant_reference_2' => ''
  328. ]
  329. ]);
  330. $this->eventManager->dispatch(
  331. $this->prefix . '_merchant_reference_update',
  332. [
  333. 'quote' => $quote,
  334. 'merchant_reference_object' => $merchantReferences
  335. ]
  336. );
  337. return $merchantReferences;
  338. }
  339. /**
  340. * Get Terms URL
  341. *
  342. * @param $store
  343. * @param $configPath
  344. * @return mixed|string
  345. */
  346. public function getTermsUrl($store, $configPath = 'terms_url')
  347. {
  348. $termsUrl = $this->configHelper->getCheckoutConfig($configPath, $store);
  349. if (!empty($termsUrl) && !parse_url($termsUrl, PHP_URL_SCHEME)) {
  350. $termsUrl = $this->url->getDirectUrl($termsUrl, ['_nosid' => true]);
  351. return $termsUrl;
  352. }
  353. return $termsUrl;
  354. }
  355. /**
  356. * Populate prefill values
  357. *
  358. * @param $create
  359. * @param $quote
  360. * @param $store
  361. * @return mixed
  362. */
  363. public function prefill($create, $quote, $store)
  364. {
  365. /**
  366. * Customer
  367. */
  368. $create['customer'] = $this->getCustomerData($quote);
  369. /**
  370. * Billing Address
  371. */
  372. $create['billing_address'] = $this->getAddressData($quote, Address::TYPE_BILLING);
  373. /**
  374. * Shipping Address
  375. */
  376. if (isset($create['billing_address'])
  377. && $this->configHelper->isCheckoutConfigFlag('separate_address', $store)
  378. ) {
  379. $create['shipping_address'] = $this->getAddressData($quote, Address::TYPE_SHIPPING);
  380. }
  381. return $create;
  382. }
  383. /**
  384. * Get customer details
  385. *
  386. * @param $quote
  387. * @return array|null
  388. */
  389. public function getCustomerData($quote)
  390. {
  391. if (!$quote->getCustomerIsGuest() && $quote->getCustomerDob()) {
  392. return [
  393. 'date_of_birth' => $this->coreDate->date('Y-m-d', $quote->getCustomerDob())
  394. ];
  395. }
  396. return null;
  397. }
  398. /**
  399. * Auto fill user address details
  400. *
  401. * @param \Magento\Quote\Api\Data\CartInterface $quote
  402. * @param string $type
  403. *
  404. * @return array
  405. */
  406. protected function getAddressData($quote, $type = null)
  407. {
  408. $result = [];
  409. if ($quote->getCustomerEmail()) {
  410. $result['email'] = $quote->getCustomerEmail();
  411. }
  412. $customer = $quote->getCustomer();
  413. if ($quote->isVirtual() || $type === Address::TYPE_BILLING) {
  414. $address = $quote->getBillingAddress();
  415. if ($customer->getId() && !$address->getPostcode()) {
  416. $address = $this->getCustomerAddress($customer->getDefaultBilling());
  417. }
  418. } else {
  419. $address = $quote->getShippingAddress();
  420. if ($customer->getId() && !$address->getPostcode()) {
  421. $address = $this->getCustomerAddress($customer->getDefaultShipping());
  422. }
  423. }
  424. return $this->processAddress($result, $address);
  425. }
  426. /**
  427. * Retrieve customer address
  428. *
  429. * @param AddressInterface|string $address_id
  430. * @return CustomerAddress|AddressInterface
  431. */
  432. private function getCustomerAddress($address_id)
  433. {
  434. if (!$address_id) {
  435. return null;
  436. }
  437. if ($address_id instanceof AddressInterface) {
  438. return $address_id;
  439. }
  440. try {
  441. return $this->addressRegistry->retrieve($address_id);
  442. } catch (\Exception $e) {
  443. return null;
  444. }
  445. }
  446. /**
  447. * @param $result
  448. * @param $address
  449. * @return array
  450. */
  451. private function processAddress($result, $address = null)
  452. {
  453. $resultObject = $this->dataObjectFactory->create(['data' => $result]);
  454. if ($address) {
  455. $address->explodeStreetAddress();
  456. $this->objCopyService->copyFieldsetToTarget(
  457. 'sales_convert_quote_address',
  458. 'to_klarna',
  459. $address,
  460. $resultObject
  461. );
  462. if ($address->getCountryId() === 'US') {
  463. $resultObject->setRegion($address->getRegionCode());
  464. }
  465. }
  466. $street_address = $this->prepareStreetAddressArray($resultObject);
  467. $resultObject->setStreetAddress($street_address[0]);
  468. $resultObject->setData('street_address2', $street_address[1]);
  469. if (isset($result['email'])) {
  470. $resultObject->setEmail($result['email']);
  471. }
  472. return array_filter($resultObject->toArray());
  473. }
  474. /**
  475. * @param $resultObject
  476. * @return array
  477. */
  478. private function prepareStreetAddressArray($resultObject)
  479. {
  480. $street_address = $resultObject->getStreetAddress();
  481. if (!is_array($street_address)) {
  482. $street_address = [$street_address];
  483. }
  484. if (count($street_address) === 1) {
  485. $street_address[] = '';
  486. }
  487. return $street_address;
  488. }
  489. /**
  490. * @param $items
  491. * @return $this
  492. */
  493. public function setItems($items)
  494. {
  495. $this->setData('items', $items);
  496. return $this;
  497. }
  498. /**
  499. * @return array
  500. */
  501. public function getItems()
  502. {
  503. return $this->getData('items');
  504. }
  505. }