Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
Express.php
Go to the documentation of this file.
1 <?php
6 namespace Magento\Paypal\Model;
7 
9 use Magento\Paypal\Model\Api\ProcessableException as ApiProcessableException;
18 
26 {
31 
35  protected $_formBlockType = \Magento\Paypal\Block\Express\Form::class;
36 
40  protected $_infoBlockType = \Magento\Paypal\Block\Payment\Info::class;
41 
47  protected $_isGateway = false;
48 
54  protected $_canOrder = true;
55 
61  protected $_canAuthorize = true;
62 
68  protected $_canCapture = true;
69 
75  protected $_canCapturePartial = true;
76 
82  protected $_canRefund = true;
83 
89  protected $_canRefundInvoicePartial = true;
90 
96  protected $_canVoid = true;
97 
103  protected $_canUseInternal = false;
104 
110  protected $_canUseCheckout = true;
111 
117  protected $_canFetchTransactionInfo = true;
118 
124  protected $_canReviewPayment = true;
125 
131  protected $_pro;
132 
138  protected $_isOrderPaymentActionKey = 'is_order_action';
139 
145  protected $_authorizationCountKey = 'authorization_count';
146 
150  protected $_storeManager;
151 
155  protected $_urlBuilder;
156 
160  protected $_cartFactory;
161 
165  protected $_checkoutSession;
166 
170  protected $_exception;
171 
176 
181 
203  public function __construct(
204  \Magento\Framework\Model\Context $context,
205  \Magento\Framework\Registry $registry,
206  \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory,
208  \Magento\Payment\Helper\Data $paymentData,
209  \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
210  \Magento\Payment\Model\Method\Logger $logger,
211  ProFactory $proFactory,
213  \Magento\Framework\UrlInterface $urlBuilder,
214  \Magento\Paypal\Model\CartFactory $cartFactory,
215  \Magento\Checkout\Model\Session $checkoutSession,
216  \Magento\Framework\Exception\LocalizedExceptionFactory $exception,
219  \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
220  \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
221  array $data = []
222  ) {
223  parent::__construct(
224  $context,
225  $registry,
226  $extensionFactory,
228  $paymentData,
229  $scopeConfig,
230  $logger,
231  $resource,
232  $resourceCollection,
233  $data
234  );
235  $this->_storeManager = $storeManager;
236  $this->_urlBuilder = $urlBuilder;
237  $this->_cartFactory = $cartFactory;
238  $this->_checkoutSession = $checkoutSession;
239  $this->_exception = $exception;
240  $this->transactionRepository = $transactionRepository;
241  $this->transactionBuilder = $transactionBuilder;
242 
243  $proInstance = array_shift($data);
244  if ($proInstance && $proInstance instanceof \Magento\Paypal\Model\Pro) {
245  $this->_pro = $proInstance;
246  } else {
247  $this->_pro = $proFactory->create();
248  }
249  $this->_pro->setMethod($this->_code);
250  $this->_setApiProcessableErrors();
251  }
252 
258  protected function _setApiProcessableErrors()
259  {
260  return $this->_pro->getApi()->setProcessableErrors(
261  [
262  ApiProcessableException::API_INTERNAL_ERROR,
263  ApiProcessableException::API_UNABLE_PROCESS_PAYMENT_ERROR_CODE,
264  ApiProcessableException::API_DO_EXPRESS_CHECKOUT_FAIL,
265  ApiProcessableException::API_UNABLE_TRANSACTION_COMPLETE,
266  ApiProcessableException::API_TRANSACTION_EXPIRED,
267  ApiProcessableException::API_MAX_PAYMENT_ATTEMPTS_EXCEEDED,
268  ApiProcessableException::API_COUNTRY_FILTER_DECLINE,
269  ApiProcessableException::API_MAXIMUM_AMOUNT_FILTER_DECLINE,
270  ApiProcessableException::API_OTHER_FILTER_DECLINE,
271  ApiProcessableException::API_ADDRESS_MATCH_FAIL,
272  ]
273  );
274  }
275 
283  public function setStore($store)
284  {
285  $this->setData('store', $store);
286  if (null === $store) {
287  $store = $this->_storeManager->getStore()->getId();
288  }
289  $this->_pro->getConfig()->setStoreId(is_object($store) ? $store->getId() : $store);
290  return $this;
291  }
292 
298  public function canUseCheckout()
299  {
300  if ($this->_scopeConfig->isSetFlag(
301  'payment/hosted_pro/active',
303  ) && !$this->_scopeConfig->isSetFlag(
304  'payment/hosted_pro/display_ec',
306  )
307  ) {
308  return false;
309  }
310  return parent::canUseCheckout();
311  }
312 
319  public function canUseForCurrency($currencyCode)
320  {
321  return $this->_pro->getConfig()->isCurrencyCodeSupported($currencyCode);
322  }
323 
330  public function getConfigPaymentAction()
331  {
332  return $this->_pro->getConfig()->getPaymentAction();
333  }
334 
340  public function isAvailable(\Magento\Quote\Api\Data\CartInterface $quote = null)
341  {
342  return parent::isAvailable($quote) && $this->_pro->getConfig()->isMethodAvailable();
343  }
344 
353  public function getConfigData($field, $storeId = null)
354  {
355  if ('order_place_redirect_url' === $field) {
356  return $this->getOrderPlaceRedirectUrl();
357  }
358  return $this->_pro->getConfig()->getValue($field);
359  }
360 
369  {
370  $paypalTransactionData = $this->_checkoutSession->getPaypalTransactionData();
371  if (!is_array($paypalTransactionData)) {
372  $this->_placeOrder($payment, $amount);
373  } else {
374  $this->_importToPayment($this->_pro->getApi()->setData($paypalTransactionData), $payment);
375  }
376 
377  $payment->setAdditionalInformation($this->_isOrderPaymentActionKey, true);
378  if ($payment->getIsFraudDetected()) {
379  return $this;
380  }
381 
382  $payment->getOrder()->setActionFlag(Order::ACTION_FLAG_INVOICE, false);
383 
384  return $this;
385  }
386 
395  {
396  return $this->_placeOrder($payment, $amount);
397  }
398 
406  public function void(\Magento\Payment\Model\InfoInterface $payment)
407  {
408  //Switching to order transaction if needed
409  if ($payment->getAdditionalInformation(
410  $this->_isOrderPaymentActionKey
411  ) && !$payment->getVoidOnlyAuthorization()
412  ) {
413  $orderTransaction = $this->getOrderTransaction($payment);
414  if ($orderTransaction) {
415  $payment->setParentTransactionId($orderTransaction->getTxnId());
416  $payment->setTransactionId($orderTransaction->getTxnId() . '-void');
417  }
418  }
419  $this->_pro->void($payment);
420  return $this;
421  }
422 
433  {
434  $authorizationTransaction = $payment->getAuthorizationTransaction();
435  $authorizationPeriod = abs(intval($this->getConfigData('authorization_honor_period')));
436  $maxAuthorizationNumber = abs(intval($this->getConfigData('child_authorization_number')));
437  $order = $payment->getOrder();
438  $isAuthorizationCreated = false;
439 
440  if ($payment->getAdditionalInformation($this->_isOrderPaymentActionKey)) {
441  $voided = false;
442  if (!$authorizationTransaction->getIsClosed() && $this->_isTransactionExpired(
443  $authorizationTransaction,
444  $authorizationPeriod
445  )
446  ) {
447  //Save payment state and configure payment object for voiding
448  $isCaptureFinal = $payment->getShouldCloseParentTransaction();
449  $payment->setShouldCloseParentTransaction(false);
450  $payment->setParentTransactionId($authorizationTransaction->getTxnId());
451  $payment->unsTransactionId();
452  $payment->setVoidOnlyAuthorization(true);
453  $payment->void(new \Magento\Framework\DataObject());
454 
455  //Revert payment state after voiding
456  $payment->unsAuthorizationTransaction();
457  $payment->unsTransactionId();
458  $payment->setShouldCloseParentTransaction($isCaptureFinal);
459  $voided = true;
460  }
461 
462  if ($authorizationTransaction->getIsClosed() || $voided) {
463  if ($payment->getAdditionalInformation($this->_authorizationCountKey) > $maxAuthorizationNumber - 1) {
464  $this->_exception->create(
465  ['phrase' => __('The maximum number of child authorizations is reached.')]
466  );
467  }
468  $api = $this->_callDoAuthorize($amount, $payment, $authorizationTransaction->getParentTxnId());
469 
470  //Adding authorization transaction
471  $this->_pro->importPaymentInfo($api, $payment);
472  $payment->setTransactionId($api->getTransactionId());
473  $payment->setParentTransactionId($authorizationTransaction->getParentTxnId());
474  $payment->setIsTransactionClosed(false);
475 
476  $formatedPrice = $order->getBaseCurrency()->formatTxt($amount);
477 
478  if ($payment->getIsTransactionPending()) {
479  $message = __(
480  'We\'ll authorize the amount of %1 as soon as the payment gateway approves it.',
481  $formatedPrice
482  );
483  } else {
484  $message = __('The authorized amount is %1.', $formatedPrice);
485  }
486 
487  $transaction = $this->transactionBuilder->setPayment($payment)
488  ->setOrder($order)
489  ->setTransactionId($payment->getTransactionId())
490  ->setFailSafe(true)
491  ->build(Transaction::TYPE_AUTH);
492  $payment->addTransactionCommentsToOrder($transaction, $message);
493 
494  $payment->setParentTransactionId($api->getTransactionId());
495  $isAuthorizationCreated = true;
496  }
497  //close order transaction if needed
498  if ($payment->getShouldCloseParentTransaction()) {
499  $orderTransaction = $this->getOrderTransaction($payment);
500 
501  if ($orderTransaction) {
502  $orderTransaction->setIsClosed(true);
503  $order->addRelatedObject($orderTransaction);
504  }
505  }
506  }
507 
508  if (false === $this->_pro->capture($payment, $amount)) {
509  $this->_placeOrder($payment, $amount);
510  }
511 
512  if ($isAuthorizationCreated && isset($transaction)) {
513  $transaction->setIsClosed(true);
514  }
515 
516  return $this;
517  }
518 
528  {
529  $this->_pro->refund($payment, $amount);
530  return $this;
531  }
532 
539  public function cancel(\Magento\Payment\Model\InfoInterface $payment)
540  {
541  $this->void($payment);
542 
543  return $this;
544  }
545 
550  public function canReviewPayment()
551  {
552  return parent::canReviewPayment() && $this->_pro->canReviewPayment($this->getInfoInstance());
553  }
554 
562  {
563  parent::acceptPayment($payment);
564  return $this->_pro->reviewPayment($payment, \Magento\Paypal\Model\Pro::PAYMENT_REVIEW_ACCEPT);
565  }
566 
574  {
575  parent::denyPayment($payment);
576  return $this->_pro->reviewPayment($payment, \Magento\Paypal\Model\Pro::PAYMENT_REVIEW_DENY);
577  }
578 
586  public function getCheckoutRedirectUrl()
587  {
588  return $this->_urlBuilder->getUrl('paypal/express/start');
589  }
590 
598  public function fetchTransactionInfo(\Magento\Payment\Model\InfoInterface $payment, $transactionId)
599  {
600  return $this->_pro->fetchTransactionInfo($payment, $transactionId);
601  }
602 
606  public function getApi()
607  {
608  return $this->_pro->getApi();
609  }
610 
618  public function assignData(\Magento\Framework\DataObject $data)
619  {
620  parent::assignData($data);
621 
622  $additionalData = $data->getData(PaymentInterface::KEY_ADDITIONAL_DATA);
623 
624  if (!is_array($additionalData)) {
625  return $this;
626  }
627 
628  foreach ($additionalData as $key => $value) {
629  // Skip extension attributes
630  if ($key === \Magento\Framework\Api\ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY) {
631  continue;
632  }
633 
634  $this->getInfoInstance()->setAdditionalInformation($key, $value);
635  }
636  return $this;
637  }
638 
646  protected function _placeOrder(Payment $payment, $amount)
647  {
648  $order = $payment->getOrder();
649 
650  // prepare api call
651  $token = $payment->getAdditionalInformation(ExpressCheckout::PAYMENT_INFO_TRANSPORT_TOKEN);
652 
653  $cart = $this->_cartFactory->create(['salesModel' => $order]);
654 
655  $api = $this->getApi()->setToken(
656  $token
657  )->setPayerId(
658  $payment->getAdditionalInformation(ExpressCheckout::PAYMENT_INFO_TRANSPORT_PAYER_ID)
659  )->setAmount(
660  $amount
661  )->setPaymentAction(
662  $this->_pro->getConfig()->getValue('paymentAction')
663  )->setNotifyUrl(
664  $this->_urlBuilder->getUrl('paypal/ipn/')
665  )->setInvNum(
666  $order->getIncrementId()
667  )->setCurrencyCode(
668  $order->getBaseCurrencyCode()
669  )->setPaypalCart(
670  $cart
671  )->setIsLineItemsEnabled(
672  $this->_pro->getConfig()->getValue('lineItemsEnabled')
673  );
674  if ($order->getIsVirtual()) {
675  $api->setAddress($order->getBillingAddress())->setSuppressShipping(true);
676  } else {
677  $api->setAddress($order->getShippingAddress());
678  $api->setBillingAddress($order->getBillingAddress());
679  }
680 
681  // call api and get details from it
682  $api->callDoExpressCheckoutPayment();
683 
684  $this->_importToPayment($api, $payment);
685  return $this;
686  }
687 
695  protected function _importToPayment($api, $payment)
696  {
697  $payment->setTransactionId(
698  $api->getTransactionId()
699  )->setIsTransactionClosed(
700  0
701  )->setAdditionalInformation(
702  ExpressCheckout::PAYMENT_INFO_TRANSPORT_REDIRECT,
703  $api->getRedirectRequired()
704  );
705 
706  if ($api->getBillingAgreementId()) {
707  $payment->setBillingAgreementData(
708  [
709  'billing_agreement_id' => $api->getBillingAgreementId(),
711  ]
712  );
713  }
714 
715  $this->_pro->importPaymentInfo($api, $payment);
716  }
717 
724  public function canVoid()
725  {
726  $info = $this->getInfoInstance();
727  if ($info->getAdditionalInformation($this->_isOrderPaymentActionKey)) {
728  $orderTransaction = $this->getOrderTransaction($info);
729  if ($orderTransaction) {
730  $info->setParentTransactionId($orderTransaction->getTxnId());
731  }
732  }
733 
734  return $this->_canVoid;
735  }
736 
742  public function canCapture()
743  {
744  $payment = $this->getInfoInstance();
745  $this->_pro->getConfig()->setStoreId($payment->getOrder()->getStore()->getId());
746 
747  if ($payment->getAdditionalInformation($this->_isOrderPaymentActionKey)) {
748  $orderTransaction = $this->getOrderTransaction($payment);
749  if ($orderTransaction->getIsClosed()) {
750  return false;
751  }
752 
753  $orderValidPeriod = abs(intval($this->getConfigData('order_valid_period')));
754 
755  $dateCompass = new \DateTime($orderTransaction->getCreatedAt());
756  $dateCompass->modify('+' . $orderValidPeriod . ' days');
757  $currentDate = new \DateTime();
758 
759  if ($currentDate > $dateCompass || $orderValidPeriod == 0) {
760  return false;
761  }
762  }
763 
764  return $this->_canCapture;
765  }
766 
775  protected function _callDoAuthorize($amount, $payment, $parentTransactionId)
776  {
777  $apiData = $this->_pro->getApi()->getData();
778  foreach ($apiData as $k => $v) {
779  if (is_object($v)) {
780  unset($apiData[$k]);
781  }
782  }
783  $this->_checkoutSession->setPaypalTransactionData($apiData);
784  $this->_pro->resetApi();
785  $api = $this->_setApiProcessableErrors()
786  ->setAmount($amount)
787  ->setCurrencyCode($payment->getOrder()->getBaseCurrencyCode())
788  ->setTransactionId($parentTransactionId)
789  ->callDoAuthorization();
790 
791  $payment->setAdditionalInformation(
792  $this->_authorizationCountKey,
793  $payment->getAdditionalInformation($this->_authorizationCountKey) + 1
794  );
795 
796  return $api;
797  }
798 
806  protected function _isTransactionExpired(Transaction $transaction, $period)
807  {
808  $period = intval($period);
809  if (0 == $period) {
810  return true;
811  }
812 
813  $transactionClosingDate = new \DateTime($transaction->getCreatedAt(), new \DateTimeZone('GMT'));
814  $transactionClosingDate->setTimezone(new \DateTimeZone('US/Pacific'));
818  $transactionClosingDate->setTime(11, 49, 00);
819  $transactionClosingDate->modify('+' . $period . ' days');
820 
821  $currentTime = new \DateTime(null, new \DateTimeZone('US/Pacific'));
822 
823  if ($currentTime > $transactionClosingDate) {
824  return true;
825  }
826 
827  return false;
828  }
829 
836  public function isActive($storeId = null)
837  {
838  $pathStandardExpress = 'payment/' . Config::METHOD_WPS_EXPRESS . '/active';
839  $pathPaypalExpress = 'payment/' . Config::METHOD_WPP_EXPRESS . '/active';
840 
841  return parent::isActive($storeId)
842  || (bool)(int)$this->_scopeConfig->getValue($pathStandardExpress, ScopeInterface::SCOPE_STORE, $storeId)
843  || (bool)(int)$this->_scopeConfig->getValue($pathPaypalExpress, ScopeInterface::SCOPE_STORE, $storeId);
844  }
845 
852  protected function getOrderTransaction($payment)
853  {
854  return $this->transactionRepository->getByTransactionType(
856  $payment->getId(),
857  $payment->getOrder()->getId()
858  );
859  }
860 }
$transaction
cancel(\Magento\Payment\Model\InfoInterface $payment)
Definition: Express.php:539
acceptPayment(\Magento\Payment\Model\InfoInterface $payment)
Definition: Express.php:561
denyPayment(\Magento\Payment\Model\InfoInterface $payment)
Definition: Express.php:573
order(\Magento\Payment\Model\InfoInterface $payment, $amount)
Definition: Express.php:368
const PAYMENT_REVIEW_DENY
Definition: Pro.php:24
$quote
$order
Definition: order.php:55
$storeManager
__()
Definition: __.php:13
$resource
Definition: bulk.php:12
$message
capture(\Magento\Payment\Model\InfoInterface $payment, $amount)
Definition: Express.php:432
fetchTransactionInfo(\Magento\Payment\Model\InfoInterface $payment, $transactionId)
Definition: Express.php:598
$amount
Definition: order.php:14
const PAYMENT_REVIEW_ACCEPT
Definition: Pro.php:22
$payment
Definition: order.php:17
_callDoAuthorize($amount, $payment, $parentTransactionId)
Definition: Express.php:775
refund(\Magento\Payment\Model\InfoInterface $payment, $amount)
Definition: Express.php:527
$value
Definition: gender.phtml:16
assignData(\Magento\Framework\DataObject $data)
Definition: Express.php:618
authorize(\Magento\Payment\Model\InfoInterface $payment, $amount)
Definition: Express.php:394
isActive($storeId=null)
Definition: Express.php:836
_placeOrder(Payment $payment, $amount)
Definition: Express.php:646
isAvailable(\Magento\Quote\Api\Data\CartInterface $quote=null)
Definition: Express.php:340
void(\Magento\Payment\Model\InfoInterface $payment)
Definition: Express.php:406
_importToPayment($api, $payment)
Definition: Express.php:695
canUseForCurrency($currencyCode)
Definition: Express.php:319
foreach( $_productCollection as $_product)() ?>" class $info
Definition: listing.phtml:52
__construct(\Magento\Framework\Model\Context $context, \Magento\Framework\Registry $registry, \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory, \Magento\Framework\Api\AttributeValueFactory $customAttributeFactory, \Magento\Payment\Helper\Data $paymentData, \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, \Magento\Payment\Model\Method\Logger $logger, ProFactory $proFactory, \Magento\Store\Model\StoreManagerInterface $storeManager, \Magento\Framework\UrlInterface $urlBuilder, \Magento\Paypal\Model\CartFactory $cartFactory, \Magento\Checkout\Model\Session $checkoutSession, \Magento\Framework\Exception\LocalizedExceptionFactory $exception, \Magento\Sales\Api\TransactionRepositoryInterface $transactionRepository, \Magento\Sales\Model\Order\Payment\Transaction\BuilderInterface $transactionBuilder, \Magento\Framework\Model\ResourceModel\AbstractResource $resource=null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection=null, array $data=[])
Definition: Express.php:203
_isTransactionExpired(Transaction $transaction, $period)
Definition: Express.php:806
getAdditionalInformation($key=null)
Definition: Info.php:174
getConfigData($field, $storeId=null)
Definition: Express.php:353