AbstractIpn.php 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Paypal\Model;
  7. use Magento\Framework\Exception\RemoteServiceUnavailableException;
  8. class AbstractIpn
  9. {
  10. /**
  11. * @var Config
  12. */
  13. protected $_config;
  14. /**
  15. * IPN request data
  16. *
  17. * @var array
  18. */
  19. protected $_ipnRequest;
  20. /**
  21. * Collected debug information
  22. *
  23. * @var array
  24. */
  25. protected $_debugData = [];
  26. /**
  27. * @var \Magento\Paypal\Model\ConfigFactory
  28. */
  29. protected $_configFactory;
  30. /**
  31. * @var \Magento\Framework\HTTP\Adapter\CurlFactory
  32. */
  33. protected $_curlFactory;
  34. /**
  35. * @param \Magento\Paypal\Model\ConfigFactory $configFactory
  36. * @param \Psr\Log\LoggerInterface $logger
  37. * @param \Magento\Framework\HTTP\Adapter\CurlFactory $curlFactory
  38. * @param array $data
  39. */
  40. public function __construct(
  41. \Magento\Paypal\Model\ConfigFactory $configFactory,
  42. \Psr\Log\LoggerInterface $logger,
  43. \Magento\Framework\HTTP\Adapter\CurlFactory $curlFactory,
  44. array $data = []
  45. ) {
  46. $this->_configFactory = $configFactory;
  47. $this->logger = $logger;
  48. $this->_curlFactory = $curlFactory;
  49. $this->_ipnRequest = $data;
  50. }
  51. /**
  52. * IPN request data getter
  53. *
  54. * @param string $key
  55. * @return array|string
  56. */
  57. public function getRequestData($key = null)
  58. {
  59. if (null === $key) {
  60. return $this->_ipnRequest;
  61. }
  62. return isset($this->_ipnRequest[$key]) ? $this->_ipnRequest[$key] : null;
  63. }
  64. /**
  65. * Post back to PayPal to check whether this request is a valid one
  66. *
  67. * @return void
  68. * @throws RemoteServiceUnavailableException
  69. * @throws \Exception
  70. */
  71. protected function _postBack()
  72. {
  73. $httpAdapter = $this->_curlFactory->create();
  74. $postbackQuery = http_build_query($this->getRequestData()) . '&cmd=_notify-validate';
  75. $postbackUrl = $this->_config->getPayPalIpnUrl();
  76. $this->_addDebugData('postback_to', $postbackUrl);
  77. $httpAdapter->setConfig(['verifypeer' => $this->_config->getValue('verifyPeer')]);
  78. $httpAdapter->write(\Zend_Http_Client::POST, $postbackUrl, '1.1', ['Connection: close'], $postbackQuery);
  79. try {
  80. $postbackResult = $httpAdapter->read();
  81. } catch (\Exception $e) {
  82. $this->_addDebugData('http_error', ['error' => $e->getMessage(), 'code' => $e->getCode()]);
  83. throw $e;
  84. }
  85. /*
  86. * Handle errors on PayPal side.
  87. */
  88. $responseCode = \Zend_Http_Response::extractCode($postbackResult);
  89. if (empty($postbackResult) || in_array($responseCode, ['500', '502', '503'])) {
  90. if (empty($postbackResult)) {
  91. $reason = 'Empty response.';
  92. } else {
  93. $reason = 'Response code: ' . $responseCode . '.';
  94. }
  95. $this->_debugData['exception'] = 'PayPal IPN postback failure. ' . $reason;
  96. throw new RemoteServiceUnavailableException(__($reason));
  97. }
  98. $response = preg_split('/^\r?$/m', $postbackResult, 2);
  99. $response = trim($response[1]);
  100. if ($response != 'VERIFIED') {
  101. $this->_addDebugData('postback', $postbackQuery);
  102. $this->_addDebugData('postback_result', $postbackResult);
  103. throw new \Exception('PayPal IPN postback failure. See system.log for details.');
  104. }
  105. }
  106. /**
  107. * Filter payment status from NVP into paypal/info format
  108. *
  109. * @param string $ipnPaymentStatus
  110. * @return string
  111. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  112. */
  113. protected function _filterPaymentStatus($ipnPaymentStatus)
  114. {
  115. switch ($ipnPaymentStatus) {
  116. case 'Created':
  117. // break is intentionally omitted
  118. case 'Completed':
  119. return Info::PAYMENTSTATUS_COMPLETED;
  120. case 'Denied':
  121. return Info::PAYMENTSTATUS_DENIED;
  122. case 'Expired':
  123. return Info::PAYMENTSTATUS_EXPIRED;
  124. case 'Failed':
  125. return Info::PAYMENTSTATUS_FAILED;
  126. case 'Pending':
  127. return Info::PAYMENTSTATUS_PENDING;
  128. case 'Refunded':
  129. return Info::PAYMENTSTATUS_REFUNDED;
  130. case 'Reversed':
  131. return Info::PAYMENTSTATUS_REVERSED;
  132. case 'Canceled_Reversal':
  133. return Info::PAYMENTSTATUS_UNREVERSED;
  134. case 'Processed':
  135. return Info::PAYMENTSTATUS_PROCESSED;
  136. case 'Voided':
  137. return Info::PAYMENTSTATUS_VOIDED;
  138. default:
  139. return '';
  140. }
  141. // documented in NVP, but not documented in IPN:
  142. //Info::PAYMENTSTATUS_NONE
  143. //Info::PAYMENTSTATUS_INPROGRESS
  144. //Info::PAYMENTSTATUS_REFUNDEDPART
  145. }
  146. /**
  147. * Log debug data to file
  148. *
  149. * @return void
  150. */
  151. protected function _debug()
  152. {
  153. if ($this->_config && $this->_config->getValue('debug')) {
  154. $this->logger->debug(var_export($this->_debugData, true));
  155. }
  156. }
  157. /**
  158. * @param string $key
  159. * @param array|string $value
  160. * @return $this
  161. */
  162. protected function _addDebugData($key, $value)
  163. {
  164. $this->_debugData[$key] = $value;
  165. return $this;
  166. }
  167. }