AmazonPaymentAdapter.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. <?php
  2. /**
  3. * Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License").
  6. * You may not use this file except in compliance with the License.
  7. * A copy of the License is located at
  8. *
  9. * http://aws.amazon.com/apache2.0
  10. *
  11. * or in the "license" file accompanying this file. This file is distributed
  12. * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
  13. * express or implied. See the License for the specific language governing
  14. * permissions and limitations under the License.
  15. */
  16. namespace Amazon\Payment\Model\Adapter;
  17. use Amazon\Core\Client\ClientFactoryInterface;
  18. use Amazon\Payment\Domain\AmazonSetOrderDetailsResponseFactory;
  19. use Magento\Payment\Model\Method\Logger;
  20. use Amazon\Payment\Domain\AmazonAuthorizationResponseFactory;
  21. use Amazon\Payment\Domain\AmazonCaptureResponseFactory;
  22. use Amazon\Payment\Gateway\Helper\SubjectReader;
  23. use Amazon\Core\Helper\Data;
  24. use Amazon\Payment\Api\Data\PendingAuthorizationInterfaceFactory;
  25. use Amazon\Payment\Api\Data\PendingCaptureInterfaceFactory;
  26. /**
  27. * Class AmazonPaymentAdapter
  28. * Use \Amazon\Payment\Model\Adapter\AmazonPaymentAdapterFactory to create new instance of adapter.
  29. * @codeCoverageIgnore
  30. */
  31. class AmazonPaymentAdapter
  32. {
  33. const SUCCESS_CODES = ['Open', 'Closed', 'Completed'];
  34. /**
  35. * @var Logger
  36. */
  37. private $logger;
  38. /**
  39. * @var ClientFactoryInterface
  40. */
  41. private $clientFactory;
  42. /**
  43. * @var AmazonSetOrderDetailsResponseFactory
  44. */
  45. private $amazonSetOrderDetailsResponseFactory;
  46. /**
  47. * @var AmazonCaptureResponseFactory
  48. */
  49. private $amazonCaptureResponseFactory;
  50. /**
  51. * @var AmazonAuthorizationResponseFactory
  52. */
  53. private $amazonAuthorizationResponseFactory;
  54. /**
  55. * @var SubjectReader
  56. */
  57. private $subjectReader;
  58. /**
  59. * @var Data
  60. */
  61. private $coreHelper;
  62. /**
  63. * @var PendingCaptureInterfaceFactory
  64. */
  65. private $pendingCaptureFactory;
  66. /**
  67. * @var PendingAuthorizationInterfaceFactory
  68. */
  69. private $pendingAuthorizationFactory;
  70. /**
  71. * AmazonPaymentAdapter constructor.
  72. * @param ClientFactoryInterface $clientFactory
  73. * @param AmazonCaptureResponseFactory $amazonCaptureResponseFactory
  74. * @param AmazonSetOrderDetailsResponseFactory $amazonSetOrderDetailsResponseFactory
  75. * @param AmazonAuthorizationResponseFactory $amazonAuthorizationResponseFactory
  76. * @param PendingCaptureInterfaceFactory $pendingCaptureFactory
  77. * @param PendingAuthorizationInterfaceFactory $pendingAuthorizationFactory
  78. * @param SubjectReader $subjectReader
  79. * @param Data $coreHelper
  80. * @param Logger $logger
  81. */
  82. public function __construct(
  83. ClientFactoryInterface $clientFactory,
  84. AmazonCaptureResponseFactory $amazonCaptureResponseFactory,
  85. AmazonSetOrderDetailsResponseFactory $amazonSetOrderDetailsResponseFactory,
  86. AmazonAuthorizationResponseFactory $amazonAuthorizationResponseFactory,
  87. PendingCaptureInterfaceFactory $pendingCaptureFactory,
  88. PendingAuthorizationInterfaceFactory $pendingAuthorizationFactory,
  89. SubjectReader $subjectReader,
  90. Data $coreHelper,
  91. Logger $logger
  92. ) {
  93. $this->clientFactory = $clientFactory;
  94. $this->amazonSetOrderDetailsResponseFactory = $amazonSetOrderDetailsResponseFactory;
  95. $this->logger = $logger;
  96. $this->amazonCaptureResponseFactory = $amazonCaptureResponseFactory;
  97. $this->amazonAuthorizationResponseFactory = $amazonAuthorizationResponseFactory;
  98. $this->subjectReader = $subjectReader;
  99. $this->coreHelper = $coreHelper;
  100. $this->pendingCaptureFactory = $pendingCaptureFactory;
  101. $this->pendingAuthorizationFactory = $pendingAuthorizationFactory;
  102. }
  103. /**
  104. * Sets Amazon Pay order data
  105. *
  106. * @param $storeId
  107. * @param $data
  108. * @return array
  109. */
  110. public function setOrderReferenceDetails($storeId, $data)
  111. {
  112. $response = [];
  113. try {
  114. $responseParser = $this->clientFactory->create($storeId)->setOrderReferenceDetails($data);
  115. $constraints = $this->amazonSetOrderDetailsResponseFactory->create(
  116. [
  117. 'response' => $responseParser
  118. ]
  119. );
  120. $response = [
  121. 'status' => $responseParser->response['Status'],
  122. 'constraints' => $constraints->getConstraints()
  123. ];
  124. } catch (\Exception $e) {
  125. $log['error'] = $e->getMessage();
  126. $this->logger->debug($log);
  127. }
  128. return $response;
  129. }
  130. /**
  131. * Confirms that payment has been created for Amazon Pay
  132. *
  133. * @param $storeId
  134. * @param $amazonOrderReferenceId
  135. * @return array
  136. */
  137. private function confirmOrderReference($storeId, $amazonOrderReferenceId)
  138. {
  139. $response = [];
  140. $response = $this->clientFactory->create($storeId)->confirmOrderReference(
  141. [
  142. 'amazon_order_reference_id' => $amazonOrderReferenceId
  143. ]
  144. );
  145. if (!$response) {
  146. $log['error'] = __('Unable to confirm order reference.');
  147. $this->logger->debug($log);
  148. }
  149. return $response;
  150. }
  151. /**
  152. * @param $storeId
  153. * @param $data
  154. * @return \Amazon\Payment\Domain\AmazonAuthorizationResponse|\Amazon\Payment\Domain\Details\AmazonAuthorizationDetails
  155. */
  156. private function getAuthorization($storeId, $data)
  157. {
  158. $response = null;
  159. $client = $this->clientFactory->create($storeId);
  160. $responseParser = $client->authorize($data);
  161. $response = $this->amazonAuthorizationResponseFactory->create(['response' => $responseParser]);
  162. return $response ? $response->getDetails() : $response;
  163. }
  164. /**
  165. * @param $data
  166. * @param bool $captureNow
  167. * @return array
  168. */
  169. public function authorize($data, $captureNow = false, $attempts = 0)
  170. {
  171. $response = [];
  172. $confirmResponse = null;
  173. $storeId = $this->subjectReader->getStoreId();
  174. $authMode = $this->coreHelper->getAuthorizationMode('store', $storeId);
  175. (isset($data['additional_information']) && $data['additional_information'] != 'default')
  176. ? $additionalInformation = $data['additional_information'] : $additionalInformation = '';
  177. if ($additionalInformation) {
  178. if ($additionalInformation == 'TransactionTimedOut') {
  179. $response['response_code'] = 'TransactionTimedOut';
  180. }
  181. unset($data['additional_information']);
  182. }
  183. $authorizeData = [
  184. 'amazon_order_reference_id' => $data['amazon_order_reference_id'],
  185. 'authorization_amount' => $data['amount'],
  186. 'currency_code' => $data['currency_code'],
  187. 'authorization_reference_id' => $data['amazon_order_reference_id'] . '-A' . time().$attempts,
  188. 'capture_now' => $captureNow,
  189. 'transaction_timeout' => 0
  190. ];
  191. /** if first synchronous attempt failed, on second attempt try an asynchronous attempt. */
  192. if ($authMode != 'synchronous' && $attempts) {
  193. $authorizeData['transaction_timeout'] = 1440;
  194. }
  195. $response['status'] = false;
  196. $response['attempts'] = $attempts;
  197. $response['auth_mode'] = $authMode;
  198. $response['constraints'] = [];
  199. $response['amazon_order_reference_id'] = $data['amazon_order_reference_id'];
  200. if (!$attempts) {
  201. $detailResponse = $this->setOrderReferenceDetails($storeId, $data);
  202. if (isset($detailResponse['constraints']) && !empty($detailResponse['constraints'])) {
  203. $response['constraints'] = $detailResponse['constraints'];
  204. return $response;
  205. }
  206. }
  207. $confirmResponse = $this->confirmOrderReference($storeId, $data['amazon_order_reference_id']);
  208. if ($confirmResponse->response['Status'] == 200) {
  209. $authorizeResponse = $this->getAuthorization($storeId, $authorizeData);
  210. if ($authorizeResponse) {
  211. if ($authorizeResponse->getCaptureTransactionId() || $authorizeResponse->getAuthorizeTransactionId()) {
  212. $response['authorize_transaction_id'] = $authorizeResponse->getAuthorizeTransactionId();
  213. if ($authorizeResponse->getStatus()->getState() == 'Pending' && $authMode == 'synchronous_possible') {
  214. if ($captureNow) {
  215. $response['capture_transaction_id'] = $authorizeResponse->getCaptureTransactionId();
  216. }
  217. $response['response_code'] = 'TransactionTimedOut';
  218. } elseif (!in_array($authorizeResponse->getStatus()->getState(), self::SUCCESS_CODES)) {
  219. $response['response_code'] = $authorizeResponse->getStatus()->getReasonCode();
  220. if ($authMode == 'synchronous' && $authorizeResponse->getStatus()->getReasonCode() == 'TransactionTimedOut') {
  221. $cancelData = [
  222. 'store_id' => $storeId,
  223. 'amazon_order_reference_id' => $data['amazon_order_reference_id']
  224. ];
  225. $this->clientFactory->create($storeId)->cancelOrderReference($cancelData);
  226. }
  227. } else {
  228. $response['status'] = true;
  229. if ($captureNow) {
  230. $response['capture_transaction_id'] = $authorizeResponse->getCaptureTransactionId();
  231. }
  232. }
  233. } else {
  234. $response['status'] = false;
  235. $response['response_status'] = $authorizeResponse->getStatus()->getState();
  236. $response['response_code'] = $authorizeResponse->getStatus()->getReasonCode();
  237. $log['error'] = $authorizeResponse->getStatus()->getState() . ': ' . $authorizeResponse->getStatus()->getReasonCode();
  238. $this->logger->debug($log);
  239. }
  240. }
  241. } else {
  242. /** something went wrong, parse response body for use by authorization validator */
  243. $response['response_status'] = $confirmResponse->response['Status'];
  244. $xml = simplexml_load_string($confirmResponse->response['ResponseBody']);
  245. $code = $xml->Error->Code[0];
  246. if ($code) {
  247. $response['response_code'] = (string)$code;
  248. } else {
  249. $log['error'] = __('AmazonPaymentAdapter: Improperly formatted XML response, no response code available.');
  250. $this->logger->debug($log);
  251. }
  252. }
  253. if ($additionalInformation) {
  254. $response['sandbox'] = $additionalInformation;
  255. }
  256. return $response;
  257. }
  258. /**
  259. * @param $data
  260. * @param $storeId
  261. * @return array
  262. */
  263. public function completeCapture($data, $storeId)
  264. {
  265. $response = [
  266. 'status' => false
  267. ];
  268. $responseParser = $this->clientFactory->create($storeId)->capture($data);
  269. if ($responseParser->response['Status'] == 200) {
  270. $captureResponse = $this->amazonCaptureResponseFactory->create(['response' => $responseParser]);
  271. $capture = $captureResponse->getDetails();
  272. if (in_array($capture->getStatus()->getState(), self::SUCCESS_CODES)) {
  273. $response = [
  274. 'status' => true,
  275. 'transaction_id' => $capture->getTransactionId(),
  276. 'reauthorized' => false
  277. ];
  278. } elseif ($capture->getStatus()->getState() == 'Pending') {
  279. $order = $this->subjectReader->getOrder();
  280. try {
  281. $this->pendingCaptureFactory->create()
  282. ->setCaptureId($capture->getTransactionId())
  283. ->setOrderId($order->getId())
  284. ->setPaymentId($order->getPayment()->getEntityId())
  285. ->save();
  286. } catch (\Exception $e) {
  287. $log['error'] = __('AmazonPaymentAdapter: Unable to capture pending information for capture.');
  288. $this->logger->debug($log);
  289. }
  290. } else {
  291. $response['response_code'] = $capture->getReasonCode();
  292. }
  293. } else {
  294. $log['error'] = __('AmazonPaymentAdapter: Bad status - no capture details available.');
  295. $this->logger->debug($log);
  296. }
  297. return $response;
  298. }
  299. /**
  300. * @param $storeId
  301. * @param $amazonId
  302. * @param $orderId
  303. */
  304. public function setOrderAttributes($storeId, $amazonId, $orderId)
  305. {
  306. $orderAttributes = [
  307. 'amazon_order_reference_id' => $amazonId,
  308. 'seller_order_id' => $orderId
  309. ];
  310. $this->clientFactory->create($storeId)->setOrderAttributes($orderAttributes);
  311. }
  312. /**
  313. * @param $data
  314. * @return bool
  315. */
  316. public function checkAuthorizationStatus($data)
  317. {
  318. $authorizeData = [
  319. 'amazon_authorization_id' => $data['amazon_authorization_id']
  320. ];
  321. $storeId = $data['store_id'];
  322. $responseParser = $this->clientFactory->create($storeId)->getAuthorizationDetails($authorizeData);
  323. if ($responseParser->response['Status'] != 200) {
  324. $log['error'] = 'AmazonPaymentAdapter: Called getAuthorizationDetails and received bad status response: '
  325. . $responseParser->response['Status'];
  326. $this->logger->debug($log);
  327. return false;
  328. }
  329. return true;
  330. }
  331. }