From c4609cc0267049c412e686525d40d30bc90d050d Mon Sep 17 00:00:00 2001 From: Patrick McLain Date: Tue, 9 Jul 2019 17:26:32 -0400 Subject: [PATCH] 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. --- .../Plugin/SetVaultPaymentNonce.php | 76 +++++++++++++++++++ .../Magento/BraintreeGraphQl/composer.json | 3 + .../BraintreeGraphQl/etc/graphql/di.xml | 3 + .../BraintreeGraphQl/etc/schema.graphqls | 2 - .../Model/Adapter/BraintreeAdapter.php | 1 + .../Model/MockResponseDataProvider.php | 20 ++++- .../Customer/SetPaymentMethodTest.php | 21 +---- 7 files changed, 103 insertions(+), 23 deletions(-) create mode 100644 app/code/Magento/BraintreeGraphQl/Plugin/SetVaultPaymentNonce.php diff --git a/app/code/Magento/BraintreeGraphQl/Plugin/SetVaultPaymentNonce.php b/app/code/Magento/BraintreeGraphQl/Plugin/SetVaultPaymentNonce.php new file mode 100644 index 000000000000..b8294f1a1352 --- /dev/null +++ b/app/code/Magento/BraintreeGraphQl/Plugin/SetVaultPaymentNonce.php @@ -0,0 +1,76 @@ +command = $command; + $this->logger = $logger; + } + + /** + * Set Braintree nonce from public hash + * + * @param \Magento\QuoteGraphQl\Model\Cart\SetPaymentMethodOnCart $subject + * @param \Magento\Quote\Model\Quote $quote + * @param array $paymentData + * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function beforeExecute( + \Magento\QuoteGraphQl\Model\Cart\SetPaymentMethodOnCart $subject, + \Magento\Quote\Model\Quote $quote, + array $paymentData + ): array { + if ($paymentData['code'] !== ConfigProvider::CC_VAULT_CODE) { + return [$quote, $paymentData]; + } + + $subject = [ + 'public_hash' => $paymentData[ConfigProvider::CC_VAULT_CODE]['public_hash'], + 'customer_id' => $quote->getCustomerId(), + 'store_id' => $quote->getStoreId(), + ]; + + try { + $result = $this->command->execute($subject)->get(); + $paymentData[ConfigProvider::CC_VAULT_CODE]['payment_method_nonce'] = $result['paymentMethodNonce']; + } catch (\Exception $e) { + $this->logger->critical($e); + throw new GraphQlInputException(__('Sorry, but something went wrong')); + } + + return [$quote, $paymentData]; + } +} diff --git a/app/code/Magento/BraintreeGraphQl/composer.json b/app/code/Magento/BraintreeGraphQl/composer.json index 27537598052c..7f06b34353be 100644 --- a/app/code/Magento/BraintreeGraphQl/composer.json +++ b/app/code/Magento/BraintreeGraphQl/composer.json @@ -5,6 +5,9 @@ "require": { "php": "~7.1.3||~7.2.0", "magento/framework": "*", + "magento/module-braintree": "*", + "magento/module-store": "*", + "magento/module-quote": "*", "magento/module-quote-graph-ql": "*" }, "suggest": { diff --git a/app/code/Magento/BraintreeGraphQl/etc/graphql/di.xml b/app/code/Magento/BraintreeGraphQl/etc/graphql/di.xml index 1f3ee1a1eeda..a31066316377 100644 --- a/app/code/Magento/BraintreeGraphQl/etc/graphql/di.xml +++ b/app/code/Magento/BraintreeGraphQl/etc/graphql/di.xml @@ -14,4 +14,7 @@ + + + diff --git a/app/code/Magento/BraintreeGraphQl/etc/schema.graphqls b/app/code/Magento/BraintreeGraphQl/etc/schema.graphqls index fc24211ec0bc..0492f8aaf989 100644 --- a/app/code/Magento/BraintreeGraphQl/etc/schema.graphqls +++ b/app/code/Magento/BraintreeGraphQl/etc/schema.graphqls @@ -17,8 +17,6 @@ input BraintreeInput { } input BraintreeCcVaultInput { - payment_method_nonce: String! public_hash: String! - is_active_payment_token_enabler: Boolean! device_data: String } diff --git a/dev/tests/api-functional/_files/Magento/TestModuleBraintree/Model/Adapter/BraintreeAdapter.php b/dev/tests/api-functional/_files/Magento/TestModuleBraintree/Model/Adapter/BraintreeAdapter.php index d8f5c8627960..a4bc29270cc1 100644 --- a/dev/tests/api-functional/_files/Magento/TestModuleBraintree/Model/Adapter/BraintreeAdapter.php +++ b/dev/tests/api-functional/_files/Magento/TestModuleBraintree/Model/Adapter/BraintreeAdapter.php @@ -66,6 +66,7 @@ public function sale(array $attributes) /** * @param array $params * @return string|\Braintree\Result\Error + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function generate(array $params = []) { diff --git a/dev/tests/api-functional/_files/Magento/TestModuleBraintree/Model/MockResponseDataProvider.php b/dev/tests/api-functional/_files/Magento/TestModuleBraintree/Model/MockResponseDataProvider.php index 9149d9ac637d..ab86109c6f91 100644 --- a/dev/tests/api-functional/_files/Magento/TestModuleBraintree/Model/MockResponseDataProvider.php +++ b/dev/tests/api-functional/_files/Magento/TestModuleBraintree/Model/MockResponseDataProvider.php @@ -32,10 +32,26 @@ public function __construct( * Create mock sale response for testing * * @param array $attributes - * @return \Braintree\Instance + * @return \Braintree\Result\Error|\Braintree\Result\Successful */ - public function generateMockSaleResponse(array $attributes): \Braintree\Instance + public function generateMockSaleResponse(array $attributes) { + if (empty($attributes['paymentMethodNonce'])) { + return new \Braintree\Result\Error( + [ + 'errors' => [ + [ + 'errorData' => [ + 'code' => 2019, + 'message' => 'Your transaction has been declined.' + ] + ] + ], + 'transaction' => $this->createTransaction($attributes)->jsonSerialize(), + ] + ); + } + $transaction = $this->createTransaction($attributes); return new \Braintree\Result\Successful([$transaction]); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Braintree/Customer/SetPaymentMethodTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Braintree/Customer/SetPaymentMethodTest.php index e9cca64c9650..8baf7c6386b3 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Braintree/Customer/SetPaymentMethodTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Braintree/Customer/SetPaymentMethodTest.php @@ -169,19 +169,9 @@ public function testPlaceOrderWithVault() $reservedOrderId = 'test_quote'; $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute($reservedOrderId); - $nonceResult = $this->getNonceCommand->execute( - [ - 'customer_id' => 1, - 'public_hash' => 'braintree_public_hash', - ] - ); - $nonce = $nonceResult->get()['paymentMethodNonce']; - $setPaymentQuery = $this->getSetPaymentBraintreeVaultQuery( $maskedQuoteId, - 'braintree_public_hash', - $nonce, - true + 'braintree_public_hash' ); $setPaymentResponse = $this->graphQlMutation($setPaymentQuery, [], '', $this->getHeaderMap()); @@ -243,17 +233,12 @@ private function getSetPaymentBraintreeQuery(string $maskedQuoteId, bool $saveIn /** * @param string $maskedQuoteId * @param string $publicHash - * @param string $nonce - * @param bool $saveInVault * @return string */ private function getSetPaymentBraintreeVaultQuery( string $maskedQuoteId, - string $publicHash, - string $nonce, - bool $saveInVault = false + string $publicHash ): string { - $saveInVault = json_encode($saveInVault); return <<