Magento 2 Documentation  2.3
Documentation for Magento 2 CMS v2.3 (December 2018)
Payflowlink.php
Go to the documentation of this file.
1 <?php
7 namespace Magento\Paypal\Model;
8 
10 use Magento\Payment\Model\Method\ConfigInterfaceFactory;
15 
21 {
25  const LAYOUT_TEMPLATE = 'mobile';
26 
32  protected $_callbackController = 'payflow';
33 
40 
44  protected $_formBlockType = \Magento\Paypal\Block\Payflow\Link\Form::class;
45 
49  protected $_infoBlockType = \Magento\Paypal\Block\Payment\Info::class;
50 
56  protected $_canUseInternal = false;
57 
63  protected $_isInitializeNeeded = true;
64 
70  protected $_response;
71 
75  const TRANSACTION_PAYFLOW_URL = 'https://payflowlink.paypal.com/';
76 
80  const RESPONSE_ERROR_MSG = 'Payment error. %s was not found.';
81 
87  protected $_secureSilentPostHashKey = 'secure_silent_post_hash';
88 
92  protected $_requestFactory;
93 
97  protected $quoteRepository;
98 
102  protected $_orderFactory;
103 
107  protected $_websiteFactory;
108 
112  protected $orderSender;
113 
117  private $mathRandom;
118 
145  public function __construct(
146  \Magento\Framework\Model\Context $context,
147  \Magento\Framework\Registry $registry,
148  \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory,
149  \Magento\Framework\Api\AttributeValueFactory $customAttributeFactory,
150  \Magento\Payment\Helper\Data $paymentData,
151  \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
152  \Magento\Payment\Model\Method\Logger $logger,
153  \Magento\Framework\Module\ModuleListInterface $moduleList,
154  \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate,
156  ConfigInterfaceFactory $configFactory,
157  \Magento\Paypal\Model\Payflow\Service\Gateway $gateway,
158  HandlerInterface $errorHandler,
159  \Magento\Framework\Math\Random $mathRandom,
160  \Magento\Paypal\Model\Payflow\RequestFactory $requestFactory,
161  \Magento\Quote\Api\CartRepositoryInterface $quoteRepository,
162  \Magento\Sales\Model\OrderFactory $orderFactory,
163  \Magento\Framework\App\RequestInterface $requestHttp,
164  \Magento\Store\Model\WebsiteFactory $websiteFactory,
166  \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
167  \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
168  array $data = []
169  ) {
170  $this->_requestFactory = $requestFactory;
171  $this->quoteRepository = $quoteRepository;
172  $this->_orderFactory = $orderFactory;
173  $this->_requestHttp = $requestHttp;
174  $this->_websiteFactory = $websiteFactory;
175  $this->orderSender = $orderSender;
176  parent::__construct(
177  $context,
178  $registry,
179  $extensionFactory,
180  $customAttributeFactory,
181  $paymentData,
182  $scopeConfig,
183  $logger,
184  $moduleList,
185  $localeDate,
188  $gateway,
189  $errorHandler,
190  $resource,
191  $resourceCollection,
192  $data
193  );
194  $this->mathRandom = $mathRandom;
195  }
196 
202  public function validate()
203  {
204  return true;
205  }
206 
214  public function isAvailable(\Magento\Quote\Api\Data\CartInterface $quote = null)
215  {
216  return AbstractMethod::isAvailable($quote) && $this->getConfig()->isMethodAvailable($this->getCode());
217  }
218 
225  public function isActive($storeId = null)
226  {
227  return (bool)(int)$this->getConfigData('active', $storeId);
228  }
229 
237  public function initialize($paymentAction, $stateObject)
238  {
239  switch ($paymentAction) {
240  case \Magento\Paypal\Model\Config::PAYMENT_ACTION_AUTH:
241  case \Magento\Paypal\Model\Config::PAYMENT_ACTION_SALE:
242  $payment = $this->getInfoInstance();
244  $order = $payment->getOrder();
245  $order->setCanSendNewEmailFlag(false);
246  $payment->setAmountAuthorized($order->getTotalDue());
247  $payment->setBaseAmountAuthorized($order->getBaseTotalDue());
249  $this->setStore($order->getStoreId());
251  $response = $this->postRequest($request, $this->getConfig());
253 
254  $order = $payment->getOrder();
255  $order->setCanSendNewEmailFlag(false);
256 
257  $stateObject->setState(\Magento\Sales\Model\Order::STATE_PENDING_PAYMENT);
258  $stateObject->setStatus('pending_payment');
259  $stateObject->setIsNotified(false);
260  break;
261  default:
262  break;
263  }
264  }
265 
271  public function getResponse()
272  {
273  if (!$this->_response) {
274  $this->_response = $this->_requestFactory->create();
275  }
276 
277  return $this->_response;
278  }
279 
287  public function process($responseData)
288  {
289  $debugData = ['response' => $responseData];
290  $this->_debug($debugData);
291 
292  $this->mapGatewayResponse($responseData, $this->getResponse());
293  $order = $this->_getOrderFromResponse();
294  if ($order) {
295  $this->_processOrder($order);
296  }
297  }
298 
308  protected function _processOrder(\Magento\Sales\Model\Order $order)
309  {
310  $response = $this->getResponse();
311  $payment = $order->getPayment();
312  $payment->setTransactionId($response->getPnref())->setIsTransactionClosed(0);
313  $payment->setCcType($response->getData(OrderPaymentInterface::CC_TYPE));
314 
315  $canSendNewOrderEmail = true;
316  if ($response->getResult() == self::RESPONSE_CODE_FRAUDSERVICE_FILTER ||
318  ) {
319  $canSendNewOrderEmail = false;
320 
321  $payment->setIsTransactionPending(true)
322  ->setIsFraudDetected(true);
323 
324  $fraudMessage = $response->getData('respmsg');
325  if ($response->getData('fps_prexmldata')) {
326  $xml = new \SimpleXMLElement($response->getData('fps_prexmldata'));
327  $fraudMessage = (string)$xml->rule->triggeredMessage;
328  }
329  $payment->setAdditionalInformation(
331  $fraudMessage
332  );
333  }
334 
335  if ($response->getData('avsdata') && strstr(substr($response->getData('avsdata'), 0, 2), 'N')) {
336  $payment->setAdditionalInformation(Info::PAYPAL_AVS_CODE, substr($response->getData('avsdata'), 0, 2));
337  }
338 
339  if ($response->getData('cvv2match') && $response->getData('cvv2match') != 'Y') {
340  $payment->setAdditionalInformation(Info::PAYPAL_CVV_2_MATCH, $response->getData('cvv2match'));
341  }
342 
343  switch ($response->getType()) {
345  $payment->registerAuthorizationNotification($payment->getBaseAmountAuthorized());
346  break;
347  case self::TRXTYPE_SALE:
348  $payment->registerCaptureNotification($payment->getBaseAmountAuthorized());
349  break;
350  default:
351  break;
352  }
353  $order->save();
354 
355  try {
356  if ($canSendNewOrderEmail) {
357  $this->orderSender->send($order);
358  }
359  $quote = $this->quoteRepository->get($order->getQuoteId())->setIsActive(false);
360  $this->quoteRepository->save($quote);
361  } catch (\Exception $e) {
362  throw new \Magento\Framework\Exception\LocalizedException(__('We cannot send the new order email.'));
363  }
364  }
365 
374  protected function _getOrderFromResponse()
375  {
376  $response = $this->getResponse();
377  $order = $this->_orderFactory->create()->loadByIncrementId($response->getInvnum());
378 
379  if ($this->_getSecureSilentPostHash(
380  $order->getPayment()
381  ) != $response->getData('user2') || $this->_code != $order->getPayment()->getMethodInstance()->getCode()
382  ) {
383  return false;
384  }
385 
386  if ($response->getResult() != self::RESPONSE_CODE_FRAUDSERVICE_FILTER &&
387  $response->getResult() != self::RESPONSE_CODE_DECLINED_BY_FILTER &&
389  ) {
391  $order->registerCancellation($response->getRespmsg())->save();
392  }
393  throw new \Magento\Framework\Exception\LocalizedException(__($response->getRespmsg()));
394  }
395 
396  $amountCompared = $response->getAmt() == $order->getPayment()->getBaseAmountAuthorized() ? true : false;
397  if (!$order->getId() ||
399  !$amountCompared
400  ) {
401  throw new \Magento\Framework\Exception\LocalizedException(
402  __('Payment error. %value was not found.', ['value' => 'Order'])
403  );
404  }
405 
406  $fetchData = $this->fetchTransactionInfo($order->getPayment(), $response->getPnref());
407  if (!isset($fetchData['custref']) || $fetchData['custref'] != $order->getIncrementId()) {
408  throw new \Magento\Framework\Exception\LocalizedException(
409  __('Payment error. %value was not found.', ['value' => 'Transaction'])
410  );
411  }
412 
413  return $order;
414  }
415 
422  protected function _buildTokenRequest(\Magento\Sales\Model\Order\Payment $payment)
423  {
424  $request = $this->buildBasicRequest();
425  $request->setCreatesecuretoken('Y')
426  ->setSecuretokenid($this->mathRandom->getUniqueHash())
427  ->setTrxtype($this->_getTrxTokenType());
428 
429  $order = $payment->getOrder();
430  $request->setAmt(sprintf('%.2F', $order->getBaseTotalDue()))
431  ->setCurrency($order->getBaseCurrencyCode());
432  $this->addRequestOrderInfo($request, $order);
433 
434  $request = $this->fillCustomerContacts($order, $request);
435  //pass store Id to request
436  $request->setData('USER1', $order->getStoreId());
437  $request->setData('USER2', $this->_getSecureSilentPostHash($payment));
438 
439  return $request;
440  }
441 
448  protected function _getStoreId()
449  {
450  $response = $this->getResponse();
451  if ($response->getData('user1')) {
452  return (int)$response->getData('user1');
453  }
454  return $this->storeManager->getStore($this->getStore())->getId();
455  }
456 
462  public function buildBasicRequest()
463  {
465  $request = $this->_requestFactory->create();
466  $cscEditable = $this->getConfigData('csc_editable');
467 
468  $data = parent::buildBasicRequest();
469 
470  $request->setData($data->getData());
471 
472  $request->setCancelurl(
473  $this->_getCallbackUrl('cancelPayment')
474  )->setErrorurl(
475  $this->_getCallbackUrl('returnUrl')
476  )->setSilentpost(
477  'TRUE'
478  )->setSilentposturl(
479  $this->_getCallbackUrl('silentPost')
480  )->setReturnurl(
481  $this->_getCallbackUrl('returnUrl')
482  )->setTemplate(
483  self::LAYOUT_TEMPLATE
484  )->setDisablereceipt(
485  'TRUE'
486  )->setCscrequired(
487  $cscEditable && $this->getConfigData('csc_required') ? 'TRUE' : 'FALSE'
488  )->setCscedit(
489  $cscEditable ? 'TRUE' : 'FALSE'
490  )->setEmailcustomer(
491  $this->getConfigData('email_confirmation') ? 'TRUE' : 'FALSE'
492  )->setUrlmethod(
493  $this->getConfigData('url_method')
494  );
495  return $request;
496  }
497 
503  protected function _getTrxTokenType()
504  {
505  switch ($this->getConfigData('payment_action')) {
506  case \Magento\Paypal\Model\Config::PAYMENT_ACTION_AUTH:
508  case \Magento\Paypal\Model\Config::PAYMENT_ACTION_SALE:
509  return self::TRXTYPE_SALE;
510  default:
511  break;
512  }
513  }
514 
525  {
526  if (!$response->getSecuretoken() &&
527  $response->getResult() != self::RESPONSE_CODE_APPROVED &&
529  ) {
530  throw new \Magento\Framework\Exception\LocalizedException(__($response->getRespmsg()));
531  } else {
532  $payment->setAdditionalInformation(
533  'secure_token_id',
534  $response->getSecuretokenid()
535  )->setAdditionalInformation(
536  'secure_token',
537  $response->getSecuretoken()
538  );
539  }
540  }
541 
548  protected function _getSecureSilentPostHash($payment)
549  {
550  return $payment->getAdditionalInformation($this->_secureSilentPostHashKey);
551  }
552 
560  {
561  $secureHash = md5($this->mathRandom->getRandomString(10));
562  $payment->setAdditionalInformation($this->_secureSilentPostHashKey, $secureHash);
563  return $secureHash;
564  }
565 
572  protected function _getCallbackUrl($actionName)
573  {
574  if ($this->_requestHttp->getParam('website')) {
576  $website = $this->_websiteFactory->create()->load($this->_requestHttp->getParam('website'));
577  $secure = $this->_scopeConfig->isSetFlag(
580  $website->getDefaultStore()
581  );
582  $path = $secure
585  $websiteUrl = $this->_scopeConfig->getValue(
586  $path,
588  $website->getDefaultStore()
589  );
590  } else {
591  $secure = $this->_scopeConfig->isSetFlag(
594  );
595  $websiteUrl = $this->storeManager->getStore()->getBaseUrl(
597  $secure
598  );
599  }
600 
601  return $websiteUrl . 'paypal/' . $this->_callbackController . '/' . $actionName;
602  }
603 }
$response
Definition: 404.php:11
fetchTransactionInfo(InfoInterface $payment, $transactionId)
Definition: Payflowpro.php:533
mapGatewayResponse(array $postData, DataObject $response)
Definition: Payflowpro.php:781
$quote
$order
Definition: order.php:55
__()
Definition: __.php:13
$resource
Definition: bulk.php:12
$logger
fillCustomerContacts(DataObject $order, DataObject $request)
Definition: Payflowpro.php:855
$payment
Definition: order.php:17
const XML_PATH_SECURE_BASE_LINK_URL
Definition: Store.php:78
addRequestOrderInfo(DataObject $request, Order $order)
Definition: Payflowpro.php:876
const XML_PATH_UNSECURE_BASE_LINK_URL
Definition: Store.php:80
const XML_PATH_SECURE_IN_FRONTEND
Definition: Store.php:70
isAvailable(\Magento\Quote\Api\Data\CartInterface $quote=null)
postRequest(DataObject $request, ConfigInterface $config)
Definition: Payflowpro.php:589