Skip to content

Commit c4609cc

Browse files
committed
Generate nonce from public hash server-side
This simplifies the implementation requirements when setting the payment method to Braintree Vault. In the current frontend implementation the nonce is created server-side, but using an ajax request when the place order button is activated. The current storefront flow executes: 1. User selects vault method and places order 2. Ajax request with public hash is made to server returning the nonce 3. Nonce is submitted with order placement request in payment details The second step is uncessecary and is removed from the graphql flow with this commit.
1 parent a6e32e8 commit c4609cc

File tree

7 files changed

+103
-23
lines changed

7 files changed

+103
-23
lines changed
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
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\BraintreeGraphQl\Plugin;
9+
10+
use Magento\Braintree\Gateway\Command\GetPaymentNonceCommand;
11+
use Magento\Braintree\Model\Ui\ConfigProvider;
12+
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
13+
use Psr\Log\LoggerInterface;
14+
15+
/**
16+
* Plugin creating nonce from Magento Vault Braintree public hash
17+
*/
18+
class SetVaultPaymentNonce
19+
{
20+
/**
21+
* @var GetPaymentNonceCommand
22+
*/
23+
private $command;
24+
25+
/**
26+
* @var LoggerInterface
27+
*/
28+
private $logger;
29+
30+
/**
31+
* @param GetPaymentNonceCommand $command
32+
* @param LoggerInterface $logger
33+
*/
34+
public function __construct(
35+
GetPaymentNonceCommand $command,
36+
LoggerInterface $logger
37+
) {
38+
$this->command = $command;
39+
$this->logger = $logger;
40+
}
41+
42+
/**
43+
* Set Braintree nonce from public hash
44+
*
45+
* @param \Magento\QuoteGraphQl\Model\Cart\SetPaymentMethodOnCart $subject
46+
* @param \Magento\Quote\Model\Quote $quote
47+
* @param array $paymentData
48+
* @return array
49+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
50+
*/
51+
public function beforeExecute(
52+
\Magento\QuoteGraphQl\Model\Cart\SetPaymentMethodOnCart $subject,
53+
\Magento\Quote\Model\Quote $quote,
54+
array $paymentData
55+
): array {
56+
if ($paymentData['code'] !== ConfigProvider::CC_VAULT_CODE) {
57+
return [$quote, $paymentData];
58+
}
59+
60+
$subject = [
61+
'public_hash' => $paymentData[ConfigProvider::CC_VAULT_CODE]['public_hash'],
62+
'customer_id' => $quote->getCustomerId(),
63+
'store_id' => $quote->getStoreId(),
64+
];
65+
66+
try {
67+
$result = $this->command->execute($subject)->get();
68+
$paymentData[ConfigProvider::CC_VAULT_CODE]['payment_method_nonce'] = $result['paymentMethodNonce'];
69+
} catch (\Exception $e) {
70+
$this->logger->critical($e);
71+
throw new GraphQlInputException(__('Sorry, but something went wrong'));
72+
}
73+
74+
return [$quote, $paymentData];
75+
}
76+
}

app/code/Magento/BraintreeGraphQl/composer.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
"require": {
66
"php": "~7.1.3||~7.2.0",
77
"magento/framework": "*",
8+
"magento/module-braintree": "*",
9+
"magento/module-store": "*",
10+
"magento/module-quote": "*",
811
"magento/module-quote-graph-ql": "*"
912
},
1013
"suggest": {

app/code/Magento/BraintreeGraphQl/etc/graphql/di.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,7 @@
1414
</argument>
1515
</arguments>
1616
</type>
17+
<type name="Magento\QuoteGraphQl\Model\Cart\SetPaymentMethodOnCart">
18+
<plugin name="braintree_generate_vault_nonce" type="Magento\BraintreeGraphQl\Plugin\SetVaultPaymentNonce" />
19+
</type>
1720
</config>

app/code/Magento/BraintreeGraphQl/etc/schema.graphqls

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ input BraintreeInput {
1717
}
1818

1919
input BraintreeCcVaultInput {
20-
payment_method_nonce: String!
2120
public_hash: String!
22-
is_active_payment_token_enabler: Boolean!
2321
device_data: String
2422
}

dev/tests/api-functional/_files/Magento/TestModuleBraintree/Model/Adapter/BraintreeAdapter.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ public function sale(array $attributes)
6666
/**
6767
* @param array $params
6868
* @return string|\Braintree\Result\Error
69+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
6970
*/
7071
public function generate(array $params = [])
7172
{

dev/tests/api-functional/_files/Magento/TestModuleBraintree/Model/MockResponseDataProvider.php

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,26 @@ public function __construct(
3232
* Create mock sale response for testing
3333
*
3434
* @param array $attributes
35-
* @return \Braintree\Instance
35+
* @return \Braintree\Result\Error|\Braintree\Result\Successful
3636
*/
37-
public function generateMockSaleResponse(array $attributes): \Braintree\Instance
37+
public function generateMockSaleResponse(array $attributes)
3838
{
39+
if (empty($attributes['paymentMethodNonce'])) {
40+
return new \Braintree\Result\Error(
41+
[
42+
'errors' => [
43+
[
44+
'errorData' => [
45+
'code' => 2019,
46+
'message' => 'Your transaction has been declined.'
47+
]
48+
]
49+
],
50+
'transaction' => $this->createTransaction($attributes)->jsonSerialize(),
51+
]
52+
);
53+
}
54+
3955
$transaction = $this->createTransaction($attributes);
4056

4157
return new \Braintree\Result\Successful([$transaction]);

dev/tests/api-functional/testsuite/Magento/GraphQl/Braintree/Customer/SetPaymentMethodTest.php

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -169,19 +169,9 @@ public function testPlaceOrderWithVault()
169169
$reservedOrderId = 'test_quote';
170170
$maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute($reservedOrderId);
171171

172-
$nonceResult = $this->getNonceCommand->execute(
173-
[
174-
'customer_id' => 1,
175-
'public_hash' => 'braintree_public_hash',
176-
]
177-
);
178-
$nonce = $nonceResult->get()['paymentMethodNonce'];
179-
180172
$setPaymentQuery = $this->getSetPaymentBraintreeVaultQuery(
181173
$maskedQuoteId,
182-
'braintree_public_hash',
183-
$nonce,
184-
true
174+
'braintree_public_hash'
185175
);
186176
$setPaymentResponse = $this->graphQlMutation($setPaymentQuery, [], '', $this->getHeaderMap());
187177

@@ -243,26 +233,19 @@ private function getSetPaymentBraintreeQuery(string $maskedQuoteId, bool $saveIn
243233
/**
244234
* @param string $maskedQuoteId
245235
* @param string $publicHash
246-
* @param string $nonce
247-
* @param bool $saveInVault
248236
* @return string
249237
*/
250238
private function getSetPaymentBraintreeVaultQuery(
251239
string $maskedQuoteId,
252-
string $publicHash,
253-
string $nonce,
254-
bool $saveInVault = false
240+
string $publicHash
255241
): string {
256-
$saveInVault = json_encode($saveInVault);
257242
return <<<QUERY
258243
mutation {
259244
setPaymentMethodOnCart(input:{
260245
cart_id:"{$maskedQuoteId}"
261246
payment_method:{
262247
code:"braintree_cc_vault"
263248
braintree_cc_vault:{
264-
is_active_payment_token_enabler:{$saveInVault}
265-
payment_method_nonce:"{$nonce}"
266249
public_hash:"{$publicHash}"
267250
}
268251
}

0 commit comments

Comments
 (0)