PlaceOrder.php 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. <?php
  2. /**
  3. *
  4. * Copyright © Magento, Inc. All rights reserved.
  5. * See COPYING.txt for license details.
  6. */
  7. namespace Magento\Paypal\Controller\Express\AbstractExpress;
  8. use Magento\Framework\Exception\LocalizedException;
  9. use Magento\Paypal\Model\Api\ProcessableException as ApiProcessableException;
  10. /**
  11. * Class PlaceOrder
  12. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  13. */
  14. class PlaceOrder extends \Magento\Paypal\Controller\Express\AbstractExpress
  15. {
  16. /**
  17. * @var \Magento\Checkout\Api\AgreementsValidatorInterface
  18. */
  19. protected $agreementsValidator;
  20. /**
  21. * @var \Magento\Sales\Api\PaymentFailuresInterface
  22. */
  23. private $paymentFailures;
  24. /**
  25. * @param \Magento\Framework\App\Action\Context $context
  26. * @param \Magento\Customer\Model\Session $customerSession
  27. * @param \Magento\Checkout\Model\Session $checkoutSession
  28. * @param \Magento\Sales\Model\OrderFactory $orderFactory
  29. * @param \Magento\Paypal\Model\Express\Checkout\Factory $checkoutFactory
  30. * @param \Magento\Framework\Session\Generic $paypalSession
  31. * @param \Magento\Framework\Url\Helper\Data $urlHelper
  32. * @param \Magento\Customer\Model\Url $customerUrl
  33. * @param \Magento\Checkout\Api\AgreementsValidatorInterface $agreementValidator
  34. * @param \Magento\Sales\Api\PaymentFailuresInterface|null $paymentFailures
  35. * @SuppressWarnings(PHPMD.ExcessiveParameterList)
  36. */
  37. public function __construct(
  38. \Magento\Framework\App\Action\Context $context,
  39. \Magento\Customer\Model\Session $customerSession,
  40. \Magento\Checkout\Model\Session $checkoutSession,
  41. \Magento\Sales\Model\OrderFactory $orderFactory,
  42. \Magento\Paypal\Model\Express\Checkout\Factory $checkoutFactory,
  43. \Magento\Framework\Session\Generic $paypalSession,
  44. \Magento\Framework\Url\Helper\Data $urlHelper,
  45. \Magento\Customer\Model\Url $customerUrl,
  46. \Magento\Checkout\Api\AgreementsValidatorInterface $agreementValidator,
  47. \Magento\Sales\Api\PaymentFailuresInterface $paymentFailures = null
  48. ) {
  49. parent::__construct(
  50. $context,
  51. $customerSession,
  52. $checkoutSession,
  53. $orderFactory,
  54. $checkoutFactory,
  55. $paypalSession,
  56. $urlHelper,
  57. $customerUrl
  58. );
  59. $this->agreementsValidator = $agreementValidator;
  60. $this->paymentFailures = $paymentFailures ? : $this->_objectManager->get(
  61. \Magento\Sales\Api\PaymentFailuresInterface::class
  62. );
  63. }
  64. /**
  65. * Submit the order
  66. *
  67. * @return void
  68. * @throws \Magento\Framework\Exception\LocalizedException
  69. */
  70. public function execute()
  71. {
  72. if ($this->isValidationRequired() &&
  73. !$this->agreementsValidator->isValid(array_keys($this->getRequest()->getPost('agreement', [])))
  74. ) {
  75. $e = new \Magento\Framework\Exception\LocalizedException(
  76. __(
  77. "The order wasn't placed. "
  78. . "First, agree to the terms and conditions, then try placing your order again."
  79. )
  80. );
  81. $this->messageManager->addExceptionMessage(
  82. $e,
  83. $e->getMessage()
  84. );
  85. $this->_redirect('*/*/review');
  86. return;
  87. }
  88. try {
  89. $this->_initCheckout();
  90. $this->_checkout->place($this->_initToken());
  91. // prepare session to success or cancellation page
  92. $this->_getCheckoutSession()->clearHelperData();
  93. // "last successful quote"
  94. $quoteId = $this->_getQuote()->getId();
  95. $this->_getCheckoutSession()->setLastQuoteId($quoteId)->setLastSuccessQuoteId($quoteId);
  96. // an order may be created
  97. $order = $this->_checkout->getOrder();
  98. if ($order) {
  99. $this->_getCheckoutSession()->setLastOrderId($order->getId())
  100. ->setLastRealOrderId($order->getIncrementId())
  101. ->setLastOrderStatus($order->getStatus());
  102. }
  103. $this->_eventManager->dispatch(
  104. 'paypal_express_place_order_success',
  105. [
  106. 'order' => $order,
  107. 'quote' => $this->_getQuote()
  108. ]
  109. );
  110. // redirect if PayPal specified some URL (for example, to Giropay bank)
  111. $url = $this->_checkout->getRedirectUrl();
  112. if ($url) {
  113. $this->getResponse()->setRedirect($url);
  114. return;
  115. }
  116. $this->_initToken(false); // no need in token anymore
  117. $this->_redirect('checkout/onepage/success');
  118. return;
  119. } catch (ApiProcessableException $e) {
  120. $this->_processPaypalApiError($e);
  121. } catch (LocalizedException $e) {
  122. $this->processException($e, $e->getRawMessage());
  123. } catch (\Exception $e) {
  124. $this->processException($e, 'We can\'t place the order.');
  125. }
  126. }
  127. /**
  128. * Process exception.
  129. *
  130. * @param \Exception $exception
  131. * @param string $message
  132. *
  133. * @return void
  134. */
  135. private function processException(\Exception $exception, string $message): void
  136. {
  137. $this->messageManager->addExceptionMessage($exception, __($message));
  138. $this->_redirect('*/*/review');
  139. }
  140. /**
  141. * Process PayPal API's processable errors
  142. *
  143. * @param \Magento\Paypal\Model\Api\ProcessableException $exception
  144. * @return void
  145. */
  146. protected function _processPaypalApiError($exception)
  147. {
  148. $this->paymentFailures->handle((int)$this->_getCheckoutSession()->getQuoteId(), $exception->getMessage());
  149. switch ($exception->getCode()) {
  150. case ApiProcessableException::API_MAX_PAYMENT_ATTEMPTS_EXCEEDED:
  151. case ApiProcessableException::API_TRANSACTION_EXPIRED:
  152. $this->getResponse()->setRedirect(
  153. $this->_getQuote()->getPayment()->getCheckoutRedirectUrl()
  154. );
  155. break;
  156. case ApiProcessableException::API_DO_EXPRESS_CHECKOUT_FAIL:
  157. $this->_redirectSameToken();
  158. break;
  159. case ApiProcessableException::API_ADDRESS_MATCH_FAIL:
  160. $this->redirectToOrderReviewPageAndShowError($exception->getUserMessage());
  161. break;
  162. case ApiProcessableException::API_UNABLE_TRANSACTION_COMPLETE:
  163. if ($this->_config->getPaymentAction() == \Magento\Payment\Model\Method\AbstractMethod::ACTION_ORDER) {
  164. $paypalTransactionData = $this->_getCheckoutSession()->getPaypalTransactionData();
  165. $this->getResponse()->setRedirect(
  166. $this->_config->getExpressCheckoutOrderUrl($paypalTransactionData['transaction_id'])
  167. );
  168. } else {
  169. $this->_redirectSameToken();
  170. }
  171. break;
  172. default:
  173. $this->_redirectToCartAndShowError($exception->getUserMessage());
  174. break;
  175. }
  176. }
  177. /**
  178. * Redirect customer back to PayPal with the same token
  179. *
  180. * @return void
  181. */
  182. protected function _redirectSameToken()
  183. {
  184. $token = $this->_initToken();
  185. $this->getResponse()->setRedirect(
  186. $this->_config->getExpressCheckoutStartUrl($token)
  187. );
  188. }
  189. /**
  190. * Redirect customer to shopping cart and show error message
  191. *
  192. * @param string $errorMessage
  193. * @return void
  194. */
  195. protected function _redirectToCartAndShowError($errorMessage)
  196. {
  197. $this->messageManager->addErrorMessage($errorMessage);
  198. $this->_redirect('checkout/cart');
  199. }
  200. /**
  201. * Redirect customer to the paypal order review page and show error message
  202. *
  203. * @param string $errorMessage
  204. * @return void
  205. */
  206. private function redirectToOrderReviewPageAndShowError($errorMessage)
  207. {
  208. $this->messageManager->addErrorMessage($errorMessage);
  209. $this->_redirect('*/*/review');
  210. }
  211. /**
  212. * Return true if agreements validation required
  213. *
  214. * @return bool
  215. */
  216. protected function isValidationRequired()
  217. {
  218. return is_array($this->getRequest()->getBeforeForwardInfo())
  219. && empty($this->getRequest()->getBeforeForwardInfo());
  220. }
  221. }