Skip to content

Commit 5b6fa62

Browse files
committed
Merge remote-tracking branch 'l3/ACP2E-2533' into Tier4-PR-Delivery-12-1-23
2 parents b99f911 + 7ba70f2 commit 5b6fa62

File tree

3 files changed

+105
-37
lines changed

3 files changed

+105
-37
lines changed

app/code/Magento/Paypal/Model/Api/Nvp.php

Lines changed: 83 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ class Nvp extends \Magento\Paypal\Model\Api\AbstractApi
4444

4545
public const DO_EXPRESS_CHECKOUT_PAYMENT = 'DoExpressCheckoutPayment';
4646

47+
public const DO_EXPRESS_CHECKOUT = 'DoExpressCheckout';
48+
4749
public const CALLBACK_RESPONSE = 'CallbackResponse';
4850

4951
/**
@@ -687,7 +689,11 @@ class Nvp extends \Magento\Paypal\Model\Api\AbstractApi
687689
*
688690
* @var array
689691
*/
690-
protected $_requiredResponseParams = [self::DO_DIRECT_PAYMENT => ['ACK', 'CORRELATIONID', 'AMT']];
692+
protected $_requiredResponseParams = [
693+
self::DO_DIRECT_PAYMENT => ['ACK', 'CORRELATIONID', 'AMT'],
694+
self::DO_EXPRESS_CHECKOUT => ['ACK'],
695+
self::DO_EXPRESS_CHECKOUT_PAYMENT => ['ACK']
696+
];
691697

692698
/**
693699
* Warning codes recollected after each API call
@@ -737,6 +743,11 @@ class Nvp extends \Magento\Paypal\Model\Api\AbstractApi
737743
*/
738744
protected $_headers = [];
739745

746+
/**
747+
* @var Curl
748+
*/
749+
private $curl;
750+
740751
/**
741752
* @param \Magento\Customer\Helper\Address $customerAddress
742753
* @param \Psr\Log\LoggerInterface $logger
@@ -1154,15 +1165,13 @@ protected function _postProcessResponse($response)
11541165
}
11551166

11561167
/**
1157-
* Do the API call
1168+
* Prepare request for the API call
11581169
*
11591170
* @param string $methodName
11601171
* @param array $request
11611172
* @return array
1162-
* @throws ClientException|\Magento\Framework\Exception\LocalizedException|\Exception
1163-
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
11641173
*/
1165-
public function call($methodName, array $request)
1174+
private function prepareRequest(string $methodName, array $request): array
11661175
{
11671176
$request = $this->_addMethodToRequest($methodName, $request);
11681177
$eachCallRequest = $this->_prepareEachCallRequest($methodName);
@@ -1172,28 +1181,83 @@ public function call($methodName, array $request)
11721181
unset($eachCallRequest[$key]);
11731182
}
11741183
}
1175-
$request = $this->_exportToRequest($eachCallRequest, $request);
1176-
$debugData = ['url' => $this->getApiEndpoint(), $methodName => $request];
1184+
return $this->_exportToRequest($eachCallRequest, $request);
1185+
}
11771186

1178-
try {
1179-
/** @var Curl $http */
1180-
$http = $this->_curlFactory->create();
1187+
/**
1188+
* Creates a Curl object and sets parameters for the call
1189+
*
1190+
* @param array $request
1191+
* @return Curl
1192+
*/
1193+
private function getCurl(array $request = null): Curl
1194+
{
1195+
if (!$this->curl) {
1196+
$this->curl = $this->_curlFactory->create();
11811197
$config = ['timeout' => 60, 'verifypeer' => $this->_config->getValue('verifyPeer')];
11821198
if ($this->getUseProxy()) {
11831199
$config['proxy'] = $this->getProxyHost() . ':' . $this->getProxyPort();
11841200
}
11851201
if ($this->getUseCertAuthentication()) {
11861202
$config['ssl_cert'] = $this->getApiCertificate();
11871203
}
1188-
$http->setOptions($config);
1189-
$http->write(
1190-
Request::METHOD_POST,
1191-
$this->getApiEndpoint(),
1192-
'1.1',
1193-
$this->_headers,
1194-
$this->_buildQuery($request)
1204+
$this->curl->setOptions($config);
1205+
if ($request) {
1206+
$this->curl->write(
1207+
Request::METHOD_POST,
1208+
$this->getApiEndpoint(),
1209+
'1.1',
1210+
$this->_headers,
1211+
$this->_buildQuery($request)
1212+
);
1213+
}
1214+
}
1215+
return $this->curl;
1216+
}
1217+
1218+
/**
1219+
* Checks if transport errors occurred and throws exception if needed
1220+
*
1221+
* @return void
1222+
* @throws ClientException
1223+
*/
1224+
private function handleConnectionErrors(): void
1225+
{
1226+
if ($this->getCurl()->getErrno()) {
1227+
$this->_logger->critical(
1228+
new \Exception(
1229+
sprintf(
1230+
'PayPal NVP CURL connection error #%s: %s',
1231+
$this->getCurl()->getErrno(),
1232+
$this->getCurl()->getError()
1233+
)
1234+
)
1235+
);
1236+
$this->getCurl()->close();
1237+
1238+
throw new ClientException(
1239+
__('Payment Gateway is unreachable at the moment. Please use another payment option.')
11951240
);
1196-
$response = $http->read();
1241+
}
1242+
// cUrl resource must be closed after checking it for errors
1243+
$this->getCurl()->close();
1244+
}
1245+
1246+
/**
1247+
* Do the API call
1248+
*
1249+
* @param string $methodName
1250+
* @param array $request
1251+
* @return array
1252+
* @throws ClientException|\Magento\Framework\Exception\LocalizedException|\Exception
1253+
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
1254+
*/
1255+
public function call($methodName, array $request)
1256+
{
1257+
$request = $this->prepareRequest($methodName, $request);
1258+
$debugData = ['url' => $this->getApiEndpoint(), $methodName => $request];
1259+
try {
1260+
$response = $this->getCurl($request)->read();
11971261
} catch (\Exception $e) {
11981262
$debugData['http_error'] = ['error' => $e->getMessage(), 'code' => $e->getCode()];
11991263
$this->_debug($debugData);
@@ -1203,29 +1267,11 @@ public function call($methodName, array $request)
12031267
$response = preg_split('/^\r?$/m', $response, 2);
12041268
$response = trim($response[1] ?? '');
12051269
$response = $this->_deformatNVP($response);
1206-
12071270
$debugData['response'] = $response;
12081271
$this->_debug($debugData);
1209-
12101272
$response = $this->_postProcessResponse($response);
12111273

1212-
// handle transport error
1213-
if ($http->getErrno()) {
1214-
$this->_logger->critical(
1215-
new \Exception(
1216-
sprintf('PayPal NVP CURL connection error #%s: %s', $http->getErrno(), $http->getError())
1217-
)
1218-
);
1219-
$http->close();
1220-
1221-
throw new ClientException(
1222-
__('Payment Gateway is unreachable at the moment. Please use another payment option.')
1223-
);
1224-
}
1225-
1226-
// cUrl resource must be closed after checking it for errors
1227-
$http->close();
1228-
1274+
$this->handleConnectionErrors();
12291275
if (!$this->_validateResponse($methodName, $response)) {
12301276
$this->_logger->critical(new \Exception(__('PayPal response hasn\'t required fields.')));
12311277
throw new \Magento\Framework\Exception\LocalizedException(

app/code/Magento/Paypal/Test/Unit/Model/Api/NvpTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ public function testCall($response, $processableErrors, $exception, $exceptionMe
161161
$this->curl->expects($this->once())
162162
->method('read')
163163
->willReturn($response);
164+
$this->curl->method('getInfo')->with(CURLINFO_HTTP_CODE)->willReturn(200);
164165
$this->model->setProcessableErrors($processableErrors);
165166
$this->customLoggerMock->expects($this->once())
166167
->method('debug');
@@ -218,6 +219,7 @@ public function testCallGetExpressCheckoutDetails($input, $expected)
218219
$this->curl->expects($this->once())
219220
->method('read')
220221
->willReturn($input);
222+
$this->curl->method('getInfo')->with(CURLINFO_HTTP_CODE)->willReturn(200);
221223
$this->model->callGetExpressCheckoutDetails();
222224
$address = $this->model->getExportedShippingAddress();
223225
$this->assertEquals($expected['firstName'], $address->getData('firstname'));
@@ -281,6 +283,7 @@ public function testCallDoReauthorization()
281283
. '&PROTECTIONELIGIBILITYTYPE=' . $protectionEligibilityType
282284
);
283285

286+
$this->curl->method('getInfo')->with(CURLINFO_HTTP_CODE)->willReturn(200);
284287
$this->model->callDoReauthorization();
285288

286289
$expectedImportedData = [
@@ -315,11 +318,26 @@ public function testCallTransactionHasBeenCompleted()
315318
$this->curl->expects($this->once())
316319
->method('read')
317320
->willReturn($response);
321+
$this->curl->method('getInfo')->with(CURLINFO_HTTP_CODE)->willReturn(200);
318322
$this->model->setProcessableErrors($processableErrors);
319323

320324
$this->expectExceptionMessageMatches('/PayPal gateway has rejected request/');
321325
$this->expectException(ProcessableException::class);
322326

323327
$this->model->call('DoExpressCheckout', ['data' => 'some data']);
324328
}
329+
330+
/**
331+
* Test handling error response
332+
*/
333+
public function testCallTransactionOnError()
334+
{
335+
$response = 'HTTP/1.1 502 Bad Gateway';
336+
$this->curl->expects($this->once())
337+
->method('read')
338+
->willReturn($response);
339+
$this->expectExceptionMessageMatches('/Something went wrong while processing your order/');
340+
341+
$this->model->call('DoExpressCheckout', ['data' => 'some data']);
342+
}
325343
}

app/code/Magento/Paypal/view/frontend/web/js/in-context/express-checkout-smart-buttons.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ define([
5656
clientConfig.rendererComponent.beforeOnAuthorize(deferred.resolve, deferred.reject, actions)
5757
.then(function () {
5858
$.post(clientConfig.onAuthorizeUrl, params).done(function (res) {
59+
if (res.success === false) {
60+
clientConfig.rendererComponent.catchOnAuthorize(res, deferred.resolve, deferred.reject);
61+
return;
62+
}
5963
clientConfig.rendererComponent
6064
.afterOnAuthorize(res, deferred.resolve, deferred.reject, actions);
6165
customerData.set('paypal-funding-source', '');

0 commit comments

Comments
 (0)