PageRenderTime 27ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/app/code/Magento/Paypal/Model/Pro.php

https://gitlab.com/crazybutterfly815/magento2
PHP | 436 lines | 213 code | 41 blank | 182 comment | 19 complexity | 441475e7631c30ec31b548eacfbc2853 MD5 | raw file
  1. <?php
  2. /**
  3. * Copyright © 2016 Magento. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. // @codingStandardsIgnoreFile
  7. namespace Magento\Paypal\Model;
  8. use Magento\Paypal\Model\Api\AbstractApi;
  9. use Magento\Sales\Api\TransactionRepositoryInterface;
  10. use Magento\Paypal\Model\Info;
  11. /**
  12. * PayPal Website Payments Pro implementation for payment method instances
  13. * This model was created because right now PayPal Direct and PayPal Express payment methods cannot have same abstract
  14. */
  15. class Pro
  16. {
  17. /**
  18. * Possible payment review actions (for FMF only)
  19. */
  20. const PAYMENT_REVIEW_ACCEPT = 'accept';
  21. const PAYMENT_REVIEW_DENY = 'deny';
  22. /**
  23. * Config instance
  24. *
  25. * @var \Magento\Paypal\Model\Config
  26. */
  27. protected $_config;
  28. /**
  29. * API instance
  30. *
  31. * @var \Magento\Paypal\Model\Api\Nvp
  32. */
  33. protected $_api;
  34. /**
  35. * PayPal info object
  36. *
  37. * @var \Magento\Paypal\Model\Info
  38. */
  39. protected $_infoInstance;
  40. /**
  41. * API model type
  42. *
  43. * @var string
  44. */
  45. protected $_apiType = \Magento\Paypal\Model\Api\Nvp::class;
  46. /**
  47. * Config model type
  48. *
  49. * @var string
  50. */
  51. protected $_configType = \Magento\Paypal\Model\Config::class;
  52. /**
  53. * @var \Magento\Paypal\Model\Config\Factory
  54. */
  55. protected $_configFactory;
  56. /**
  57. * @var \Magento\Paypal\Model\Api\Type\Factory
  58. */
  59. protected $_apiFactory;
  60. /**
  61. * @var \Magento\Paypal\Model\InfoFactory
  62. */
  63. protected $_infoFactory;
  64. /**
  65. * @var TransactionRepositoryInterface
  66. */
  67. protected $transactionRepository;
  68. /**
  69. * @param \Magento\Paypal\Model\Config\Factory $configFactory
  70. * @param \Magento\Paypal\Model\Api\Type\Factory $apiFactory
  71. * @param \Magento\Paypal\Model\InfoFactory $infoFactory
  72. * @param TransactionRepositoryInterface $transactionRepository
  73. */
  74. public function __construct(
  75. \Magento\Paypal\Model\Config\Factory $configFactory,
  76. \Magento\Paypal\Model\Api\Type\Factory $apiFactory,
  77. \Magento\Paypal\Model\InfoFactory $infoFactory,
  78. TransactionRepositoryInterface $transactionRepository
  79. ) {
  80. $this->_configFactory = $configFactory;
  81. $this->_apiFactory = $apiFactory;
  82. $this->_infoFactory = $infoFactory;
  83. $this->transactionRepository = $transactionRepository;
  84. }
  85. /**
  86. * Payment method code setter. Also instantiates/updates config
  87. *
  88. * @param string $code
  89. * @param int|null $storeId
  90. * @return $this
  91. */
  92. public function setMethod($code, $storeId = null)
  93. {
  94. if (null === $this->_config) {
  95. $params = [$code];
  96. if (null !== $storeId) {
  97. $params[] = $storeId;
  98. }
  99. $this->_config = $this->_configFactory->create($this->_configType, ['params' => $params]);
  100. } else {
  101. $this->_config->setMethod($code);
  102. if (null !== $storeId) {
  103. $this->_config->setStoreId($storeId);
  104. }
  105. }
  106. return $this;
  107. }
  108. /**
  109. * Config instance setter
  110. *
  111. * @param \Magento\Paypal\Model\Config $instace
  112. * @param int|null $storeId
  113. * @return $this
  114. */
  115. public function setConfig(\Magento\Paypal\Model\Config $instance, $storeId = null)
  116. {
  117. $this->_config = $instance;
  118. if (null !== $storeId) {
  119. $this->_config->setStoreId($storeId);
  120. }
  121. return $this;
  122. }
  123. /**
  124. * Config instance getter
  125. *
  126. * @return \Magento\Paypal\Model\Config
  127. */
  128. public function getConfig()
  129. {
  130. return $this->_config;
  131. }
  132. /**
  133. * API instance getter
  134. * Sets current store id to current config instance and passes it to API
  135. *
  136. * @return \Magento\Paypal\Model\Api\Nvp
  137. */
  138. public function getApi()
  139. {
  140. if (null === $this->_api) {
  141. $this->_api = $this->_apiFactory->create($this->_apiType);
  142. }
  143. $this->_api->setConfigObject($this->_config);
  144. return $this->_api;
  145. }
  146. /**
  147. * Destroy existing NVP Api object
  148. *
  149. * @return $this
  150. */
  151. public function resetApi()
  152. {
  153. $this->_api = null;
  154. return $this;
  155. }
  156. /**
  157. * Instantiate and return info model
  158. *
  159. * @return \Magento\Paypal\Model\Info
  160. */
  161. public function getInfo()
  162. {
  163. if (null === $this->_infoInstance) {
  164. $this->_infoInstance = $this->_infoFactory->create();
  165. }
  166. return $this->_infoInstance;
  167. }
  168. /**
  169. * Transfer transaction/payment information from API instance to order payment
  170. *
  171. * @param \Magento\Framework\DataObject|AbstractApi $from
  172. * @param \Magento\Payment\Model\InfoInterface $to
  173. * @return $this
  174. */
  175. public function importPaymentInfo(\Magento\Framework\DataObject $from, \Magento\Payment\Model\InfoInterface $to)
  176. {
  177. // update PayPal-specific payment information in the payment object
  178. $this->getInfo()->importToPayment($from, $to);
  179. /**
  180. * Detect payment review and/or frauds
  181. * PayPal pro API returns fraud results only in the payment call response
  182. */
  183. if ($from->getDataUsingMethod(\Magento\Paypal\Model\Info::IS_FRAUD)) {
  184. $to->setIsTransactionPending(true);
  185. $to->setIsFraudDetected(true);
  186. } elseif (Info::isPaymentReviewRequired($to)) {
  187. $to->setIsTransactionPending(true);
  188. }
  189. // give generic info about transaction state
  190. if (Info::isPaymentSuccessful($to)) {
  191. $to->setIsTransactionApproved(true);
  192. } elseif (Info::isPaymentFailed($to)) {
  193. $to->setIsTransactionDenied(true);
  194. }
  195. return $this;
  196. }
  197. /**
  198. * Void transaction
  199. *
  200. * @param \Magento\Framework\DataObject $payment
  201. * @return void
  202. * @throws \Magento\Framework\Exception\LocalizedException
  203. */
  204. public function void(\Magento\Framework\DataObject $payment)
  205. {
  206. $authTransactionId = $this->_getParentTransactionId($payment);
  207. if ($authTransactionId) {
  208. $api = $this->getApi();
  209. $api->setPayment($payment)->setAuthorizationId($authTransactionId)->callDoVoid();
  210. $this->importPaymentInfo($api, $payment);
  211. } else {
  212. throw new \Magento\Framework\Exception\LocalizedException(
  213. __('You need an authorization transaction to void.')
  214. );
  215. }
  216. }
  217. /**
  218. * Attempt to capture payment
  219. * Will return false if the payment is not supposed to be captured
  220. *
  221. * @param \Magento\Framework\DataObject $payment
  222. * @param float $amount
  223. * @return false|null
  224. */
  225. public function capture(\Magento\Framework\DataObject $payment, $amount)
  226. {
  227. $authTransactionId = $this->_getParentTransactionId($payment);
  228. if (!$authTransactionId) {
  229. return false;
  230. }
  231. $api = $this->getApi()
  232. ->setAuthorizationId($authTransactionId)
  233. ->setIsCaptureComplete($payment->getShouldCloseParentTransaction())
  234. ->setAmount($amount);
  235. $order = $payment->getOrder();
  236. $orderIncrementId = $order->getIncrementId();
  237. $api->setCurrencyCode($order->getBaseCurrencyCode())
  238. ->setInvNum($orderIncrementId)
  239. ->setCustref($orderIncrementId)
  240. ->setPonum($order->getId());
  241. // TODO: pass 'NOTE' to API
  242. $api->callDoCapture();
  243. $this->_importCaptureResultToPayment($api, $payment);
  244. }
  245. /**
  246. * Refund a capture transaction
  247. *
  248. * @param \Magento\Framework\DataObject $payment
  249. * @param float $amount
  250. * @return void
  251. * @throws \Magento\Framework\Exception\LocalizedException
  252. */
  253. public function refund(\Magento\Framework\DataObject $payment, $amount)
  254. {
  255. $captureTxnId = $this->_getParentTransactionId($payment);
  256. if ($captureTxnId) {
  257. $api = $this->getApi();
  258. $order = $payment->getOrder();
  259. $api->setPayment(
  260. $payment
  261. )->setTransactionId(
  262. $captureTxnId
  263. )->setAmount(
  264. $amount
  265. )->setCurrencyCode(
  266. $order->getBaseCurrencyCode()
  267. );
  268. $canRefundMore = $payment->getCreditmemo()->getInvoice()->canRefund();
  269. $isFullRefund = !$canRefundMore &&
  270. 0 == (double)$order->getBaseTotalOnlineRefunded() + (double)$order->getBaseTotalOfflineRefunded();
  271. $api->setRefundType(
  272. $isFullRefund ? \Magento\Paypal\Model\Config::REFUND_TYPE_FULL : \Magento\Paypal\Model\Config::REFUND_TYPE_PARTIAL
  273. );
  274. $api->callRefundTransaction();
  275. $this->_importRefundResultToPayment($api, $payment, $canRefundMore);
  276. } else {
  277. throw new \Magento\Framework\Exception\LocalizedException(
  278. __('We can\'t issue a refund transaction because there is no capture transaction.')
  279. );
  280. }
  281. }
  282. /**
  283. * Cancel payment
  284. *
  285. * @param \Magento\Framework\DataObject $payment
  286. * @return void
  287. */
  288. public function cancel(\Magento\Framework\DataObject $payment)
  289. {
  290. if (!$payment->getOrder()->getInvoiceCollection()->count()) {
  291. $this->void($payment);
  292. }
  293. }
  294. /**
  295. * Check whether can do payment review
  296. *
  297. * @param \Magento\Payment\Model\InfoInterface $payment
  298. * @return bool
  299. */
  300. public function canReviewPayment(\Magento\Payment\Model\InfoInterface $payment)
  301. {
  302. $pendingReason = $payment->getAdditionalInformation(\Magento\Paypal\Model\Info::PENDING_REASON_GLOBAL);
  303. return $this->_isPaymentReviewRequired(
  304. $payment
  305. ) && $pendingReason != \Magento\Paypal\Model\Info::PAYMENTSTATUS_REVIEW;
  306. }
  307. /**
  308. * Check whether payment review is required
  309. *
  310. * @param \Magento\Payment\Model\InfoInterface $payment
  311. * @return bool
  312. */
  313. protected function _isPaymentReviewRequired(\Magento\Payment\Model\InfoInterface $payment)
  314. {
  315. return Info::isPaymentReviewRequired($payment);
  316. }
  317. /**
  318. * Perform the payment review
  319. *
  320. * @param \Magento\Payment\Model\InfoInterface $payment
  321. * @param string $action
  322. * @return bool
  323. */
  324. public function reviewPayment(\Magento\Payment\Model\InfoInterface $payment, $action)
  325. {
  326. $api = $this->getApi()->setTransactionId($payment->getLastTransId());
  327. // check whether the review is still needed
  328. $api->callGetTransactionDetails();
  329. $this->importPaymentInfo($api, $payment);
  330. if (!Info::isPaymentReviewRequired($payment)) {
  331. return false;
  332. }
  333. // perform the review action
  334. $api->setAction($action)->callManagePendingTransactionStatus();
  335. $api->callGetTransactionDetails();
  336. $this->importPaymentInfo($api, $payment);
  337. return true;
  338. }
  339. /**
  340. * Fetch transaction details info
  341. *
  342. * @param \Magento\Payment\Model\InfoInterface $payment
  343. * @param string $transactionId
  344. * @return array
  345. */
  346. public function fetchTransactionInfo(\Magento\Payment\Model\InfoInterface $payment, $transactionId)
  347. {
  348. $api = $this->getApi()->setTransactionId($transactionId)->setRawResponseNeeded(true);
  349. $api->callGetTransactionDetails();
  350. $this->importPaymentInfo($api, $payment);
  351. $data = $api->getRawSuccessResponseData();
  352. return $data ? $data : [];
  353. }
  354. /**
  355. * Import capture results to payment
  356. *
  357. * @param \Magento\Paypal\Model\Api\Nvp $api
  358. * @param \Magento\Sales\Model\Order\Payment $payment
  359. * @return void
  360. */
  361. protected function _importCaptureResultToPayment($api, $payment)
  362. {
  363. $payment->setTransactionId($api->getTransactionId())->setIsTransactionClosed(false);
  364. $this->importPaymentInfo($api, $payment);
  365. }
  366. /**
  367. * Import refund results to payment
  368. *
  369. * @param \Magento\Paypal\Model\Api\Nvp $api
  370. * @param \Magento\Sales\Model\Order\Payment $payment
  371. * @param bool $canRefundMore
  372. * @return void
  373. */
  374. protected function _importRefundResultToPayment($api, $payment, $canRefundMore)
  375. {
  376. $payment->setTransactionId(
  377. $api->getRefundTransactionId()
  378. )->setIsTransactionClosed(
  379. 1 // refund initiated by merchant
  380. )->setShouldCloseParentTransaction(
  381. !$canRefundMore
  382. );
  383. $this->importPaymentInfo($api, $payment);
  384. }
  385. /**
  386. * Parent transaction id getter
  387. *
  388. * @param \Magento\Framework\DataObject $payment
  389. * @return string
  390. */
  391. protected function _getParentTransactionId(\Magento\Framework\DataObject $payment)
  392. {
  393. return $payment->getParentTransactionId();
  394. }
  395. }