Skip to content

Commit c4a6a16

Browse files
authored
Merge pull request #4275 from magento-honey-badgers/MC-15967-paypal-express
[honey] MC-15967: Paypal Express Checkout Support
2 parents 19fab05 + 8da2c5b commit c4a6a16

23 files changed

+1873
-2
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\PaypalGraphQl\Model;
9+
10+
use Magento\Framework\Stdlib\ArrayManager;
11+
use Magento\QuoteGraphQl\Model\Cart\Payment\AdditionalDataProviderInterface;
12+
13+
/**
14+
* Get payment additional data for Paypal Express payment
15+
*/
16+
class PaypalExpressAdditionalDataProvider implements AdditionalDataProviderInterface
17+
{
18+
19+
private const PATH_ADDITIONAL_DATA = 'input/payment_method/additional_data';
20+
21+
/**
22+
* @var ArrayManager
23+
*/
24+
private $arrayManager;
25+
26+
/**
27+
* @param ArrayManager $arrayManager
28+
*/
29+
public function __construct(
30+
ArrayManager $arrayManager
31+
) {
32+
$this->arrayManager = $arrayManager;
33+
}
34+
35+
/**
36+
* Returns additional data
37+
*
38+
* @param array $args
39+
* @return array
40+
*/
41+
public function getData(array $args): array
42+
{
43+
$additionalData = $this->arrayManager->get(self::PATH_ADDITIONAL_DATA, $args) ?? [];
44+
45+
return $additionalData;
46+
}
47+
}
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\PaypalGraphQl\Model\Plugin\Resolver;
9+
10+
use Magento\Framework\Exception\LocalizedException;
11+
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
12+
use Magento\Framework\GraphQl\Query\Resolver\ContextInterface;
13+
use Magento\Framework\GraphQl\Query\ResolverInterface;
14+
use Magento\Framework\GraphQl\Config\Element\Field;
15+
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
16+
use Magento\Paypal\Model\Express\Checkout\Factory as CheckoutFactory;
17+
use Magento\PaypalGraphQl\Model\PaypalExpressAdditionalDataProvider;
18+
use Magento\Framework\Stdlib\ArrayManager;
19+
use Magento\PaypalGraphQl\Model\Provider\Checkout as CheckoutProvider;
20+
use Magento\PaypalGraphQl\Model\Provider\Config as ConfigProvider;
21+
22+
/**
23+
* Plugin to perform Paypal-specific logic when setting payment method on cart
24+
*/
25+
class SetPaymentMethodOnCart
26+
{
27+
private const PATH_CODE = 'input/payment_method/code';
28+
29+
private $allowedPaymentMethodCodes = [];
30+
31+
/**
32+
* @var CheckoutFactory
33+
*/
34+
private $checkoutFactory;
35+
36+
/**
37+
* @var PaypalExpressAdditionalDataProvider
38+
*/
39+
private $paypalExpressAdditionalDataProvider;
40+
41+
/**
42+
* @var ArrayManager
43+
*/
44+
private $arrayManager;
45+
46+
/**
47+
* @var CheckoutProvider
48+
*/
49+
private $checkoutProvider;
50+
51+
/**
52+
* @var ConfigProvider
53+
*/
54+
private $configProvider;
55+
56+
/**
57+
* @param CheckoutFactory $checkoutFactory
58+
* @param PaypalExpressAdditionalDataProvider $paypalExpressAdditionalDataProvider
59+
* @param ArrayManager $arrayManager
60+
* @param CheckoutProvider $checkoutProvider
61+
* @param ConfigProvider $configProvider
62+
* @param array $allowedPaymentMethodCodes
63+
*/
64+
public function __construct(
65+
CheckoutFactory $checkoutFactory,
66+
PaypalExpressAdditionalDataProvider $paypalExpressAdditionalDataProvider,
67+
ArrayManager $arrayManager,
68+
CheckoutProvider $checkoutProvider,
69+
ConfigProvider $configProvider,
70+
array $allowedPaymentMethodCodes = []
71+
) {
72+
$this->checkoutFactory = $checkoutFactory;
73+
$this->paypalExpressAdditionalDataProvider = $paypalExpressAdditionalDataProvider;
74+
$this->arrayManager = $arrayManager;
75+
$this->checkoutProvider = $checkoutProvider;
76+
$this->configProvider = $configProvider;
77+
$this->allowedPaymentMethodCodes = $allowedPaymentMethodCodes;
78+
}
79+
80+
/**
81+
* Update Paypal payment information on cart
82+
*
83+
* @param ResolverInterface $subject
84+
* @param array $resolvedValue
85+
* @param Field $field
86+
* @param ContextInterface $context
87+
* @param ResolveInfo $info
88+
* @param array|null $value
89+
* @param array|null $args
90+
* @return mixed
91+
* @throws GraphQlInputException
92+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
93+
*/
94+
public function afterResolve(
95+
ResolverInterface $subject,
96+
$resolvedValue,
97+
Field $field,
98+
$context,
99+
ResolveInfo $info,
100+
array $value = null,
101+
array $args = null
102+
) {
103+
$paymentCode = $this->arrayManager->get(self::PATH_CODE, $args) ?? '';
104+
if (!$this->isAllowedPaymentMethod($paymentCode)) {
105+
return $resolvedValue;
106+
}
107+
108+
$paypalAdditionalData = $this->paypalExpressAdditionalDataProvider->getData($args);
109+
$payerId = $paypalAdditionalData[$paymentCode]['payer_id'] ?? null;
110+
$token = $paypalAdditionalData[$paymentCode]['token'] ?? null;
111+
$cart = $resolvedValue['cart']['model'];
112+
113+
if ($payerId && $token) {
114+
$config = $this->configProvider->getConfig($paymentCode);
115+
$checkout = $this->checkoutProvider->getCheckout($config, $cart);
116+
117+
try {
118+
$checkout->returnFromPaypal($token, $payerId);
119+
} catch (LocalizedException $e) {
120+
throw new GraphQlInputException(__($e->getMessage()));
121+
}
122+
}
123+
124+
return $resolvedValue;
125+
}
126+
127+
/**
128+
* Check if payment method code is one that should be handled by this plugin
129+
*
130+
* @param string $paymentCode
131+
* @return bool
132+
*/
133+
private function isAllowedPaymentMethod(string $paymentCode): bool
134+
{
135+
return !empty($paymentCode) && in_array($paymentCode, $this->allowedPaymentMethodCodes);
136+
}
137+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\PaypalGraphQl\Model\Provider;
9+
10+
use Magento\Paypal\Model\AbstractConfig;
11+
use Magento\Paypal\Model\Express\Checkout as ExpressCheckout;
12+
use Magento\Paypal\Model\Express\Checkout\Factory as CheckoutFactory;
13+
use Magento\Quote\Api\Data\CartInterface;
14+
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
15+
16+
/**
17+
* Provides correct Checkout instance for payment method
18+
*/
19+
class Checkout
20+
{
21+
/**
22+
* @var array
23+
*/
24+
private $checkoutTypes;
25+
26+
/**
27+
* @var CheckoutFactory
28+
*/
29+
private $checkoutFactory;
30+
31+
/**
32+
* @param CheckoutFactory $checkoutFactory
33+
* @param array $checkoutTypes
34+
*/
35+
public function __construct(
36+
CheckoutFactory $checkoutFactory,
37+
array $checkoutTypes
38+
) {
39+
$this->checkoutFactory = $checkoutFactory;
40+
$this->checkoutTypes = $checkoutTypes;
41+
}
42+
43+
/**
44+
* Get Checkout model by payment method code
45+
*
46+
* @param AbstractConfig $config
47+
* @param CartInterface $cart
48+
* @return ExpressCheckout
49+
* @throws GraphQlInputException
50+
*/
51+
public function getCheckout(AbstractConfig $config, CartInterface $cart): ExpressCheckout
52+
{
53+
try {
54+
$checkout = $this->checkoutFactory->create(
55+
$this->checkoutTypes[$config->getMethodCode()],
56+
[
57+
'params' => [
58+
'quote' => $cart,
59+
'config' => $config,
60+
],
61+
]
62+
);
63+
} catch (\Exception $e) {
64+
throw new GraphQlInputException(__('The requested Payment Method is not available.'));
65+
}
66+
67+
return $checkout;
68+
}
69+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\PaypalGraphQl\Model\Provider;
9+
10+
use Magento\Framework\ObjectManagerInterface;
11+
use Magento\Paypal\Model\AbstractConfig;
12+
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
13+
14+
/**
15+
* Provides correct Config instance for payment method
16+
*/
17+
class Config
18+
{
19+
/**
20+
* @var array
21+
*/
22+
private $configTypes;
23+
24+
/**
25+
* @var ObjectManagerInterface
26+
*/
27+
private $objectManager;
28+
29+
/**
30+
* @param ObjectManagerInterface $objectManager
31+
* @param array $configTypes
32+
*/
33+
public function __construct(
34+
ObjectManagerInterface $objectManager,
35+
array $configTypes
36+
) {
37+
$this->objectManager = $objectManager;
38+
$this->configTypes = $configTypes;
39+
}
40+
41+
/**
42+
* Get Config model by payment method code
43+
*
44+
* @param string $paymentMethod
45+
* @return AbstractConfig
46+
* @throws GraphQlInputException
47+
*/
48+
public function getConfig(string $paymentMethod): AbstractConfig
49+
{
50+
//validate code string
51+
if (empty($this->configTypes[$paymentMethod]) || !class_exists($this->configTypes[$paymentMethod])) {
52+
throw new GraphQlInputException(__('The requested Payment Method is not available.'));
53+
}
54+
55+
/** @var AbstractConfig $config */
56+
$config = $this->objectManager->get($this->configTypes[$paymentMethod]);
57+
$config->setMethod($paymentMethod);
58+
59+
if (!$config->isMethodAvailable($paymentMethod)) {
60+
throw new GraphQlInputException(__('The requested Payment Method is not available.'));
61+
}
62+
63+
return $config;
64+
}
65+
}

0 commit comments

Comments
 (0)