From 7c90031c4f6f667c0a24793a470a5cbc3791aea8 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 16 Apr 2024 12:05:06 +0200 Subject: [PATCH 01/20] Add boilerplate for advanced card block integration --- .../js/advanced-card-checkout-block.js | 12 +++ modules/ppcp-blocks/services.php | 7 ++ .../src/AdvancedCardPaymentMethod.php | 82 +++++++++++++++++++ modules/ppcp-blocks/src/BlocksModule.php | 1 + modules/ppcp-blocks/webpack.config.js | 1 + 5 files changed, 103 insertions(+) create mode 100644 modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js create mode 100644 modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php diff --git a/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js b/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js new file mode 100644 index 000000000..1775c1182 --- /dev/null +++ b/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js @@ -0,0 +1,12 @@ +import { registerPaymentMethod } from '@woocommerce/blocks-registry'; + +const config = wc.wcSettings.getSetting('ppcp-credit-card-gateway_data'); + +registerPaymentMethod({ + name: config.id, + label:
, + content:

content

, + edit:

edit...

, + ariaLabel: config.title, + canMakePayment: () => {return true}, +}) diff --git a/modules/ppcp-blocks/services.php b/modules/ppcp-blocks/services.php index a841e97b9..c5a9dddc8 100644 --- a/modules/ppcp-blocks/services.php +++ b/modules/ppcp-blocks/services.php @@ -45,6 +45,13 @@ return array( $container->get( 'wcgateway.all-funding-sources' ) ); }, + 'blocks.advanced-card-method' => static function( ContainerInterface $container ): AdvancedCardPaymentMethod { + return new AdvancedCardPaymentMethod( + $container->get( 'blocks.url' ), + $container->get( 'ppcp.asset-version' ), + $container->get( 'wcgateway.credit-card-gateway' ) + ); + }, 'blocks.settings.final_review_enabled' => static function ( ContainerInterface $container ): bool { $settings = $container->get( 'wcgateway.settings' ); assert( $settings instanceof ContainerInterface ); diff --git a/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php b/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php new file mode 100644 index 000000000..933af1954 --- /dev/null +++ b/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php @@ -0,0 +1,82 @@ +name = CreditCardGateway::ID; + $this->module_url = $module_url; + $this->version = $version; + $this->gateway = $gateway; + } + + public function initialize() {} + + /** + * {@inheritDoc} + */ + public function is_active() { + return true; + } + + /** + * {@inheritDoc} + */ + public function get_payment_method_script_handles() { + wp_register_script( + 'ppcp-advanced-card-checkout-block', + trailingslashit( $this->module_url ) . 'assets/js/advanced-card-checkout-block.js', + array(), + $this->version, + true + ); + + return array( 'ppcp-advanced-card-checkout-block' ); + } + + /** + * {@inheritDoc} + */ + public function get_payment_method_data() { + return array( + 'id' => $this->name, + 'title' => $this->gateway->title, + 'description' => $this->gateway->description, + ); + } +} diff --git a/modules/ppcp-blocks/src/BlocksModule.php b/modules/ppcp-blocks/src/BlocksModule.php index 9b00d61bc..c0f2174d4 100644 --- a/modules/ppcp-blocks/src/BlocksModule.php +++ b/modules/ppcp-blocks/src/BlocksModule.php @@ -61,6 +61,7 @@ class BlocksModule implements ModuleInterface { 'woocommerce_blocks_payment_method_type_registration', function( PaymentMethodRegistry $payment_method_registry ) use ( $c ): void { $payment_method_registry->register( $c->get( 'blocks.method' ) ); + $payment_method_registry->register( $c->get( 'blocks.advanced-card-method' ) ); } ); diff --git a/modules/ppcp-blocks/webpack.config.js b/modules/ppcp-blocks/webpack.config.js index bdf508fba..44ab3e68f 100644 --- a/modules/ppcp-blocks/webpack.config.js +++ b/modules/ppcp-blocks/webpack.config.js @@ -10,6 +10,7 @@ module.exports = { plugins: [ new DependencyExtractionWebpackPlugin() ], entry: { 'checkout-block': path.resolve('./resources/js/checkout-block.js'), + 'advanced-card-checkout-block': path.resolve('./resources/js/advanced-card-checkout-block.js'), "gateway": path.resolve('./resources/css/gateway.scss') }, output: { From 061d30f57506c28643f535be782f63c7210d553c Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 17 Apr 2024 16:20:42 +0200 Subject: [PATCH 02/20] Add card fields component (WIP) --- .../resources/js/Components/CardFields.js | 32 +++++++++++++++ .../js/advanced-card-checkout-block.js | 3 +- modules/ppcp-blocks/services.php | 5 ++- .../src/AdvancedCardPaymentMethod.php | 40 ++++++++++++++++--- 4 files changed, 73 insertions(+), 7 deletions(-) create mode 100644 modules/ppcp-blocks/resources/js/Components/CardFields.js diff --git a/modules/ppcp-blocks/resources/js/Components/CardFields.js b/modules/ppcp-blocks/resources/js/Components/CardFields.js new file mode 100644 index 000000000..ca3798855 --- /dev/null +++ b/modules/ppcp-blocks/resources/js/Components/CardFields.js @@ -0,0 +1,32 @@ +import {useEffect} from '@wordpress/element'; + +export function CardFields({config}) { + const cardField = paypal.CardFields({ + createOrder: () => {}, + onApprove: () => {}, + onError: function (error) { + console.error(error) + } + }); + + useEffect(() => { + if (cardField.isEligible()) { + const numberField = cardField.NumberField(); + numberField.render("#card-number-field-container"); + + const cvvField = cardField.CVVField(); + cvvField.render("#card-cvv-field-container"); + + const expiryField = cardField.ExpiryField(); + expiryField.render("#card-expiry-field-container"); + } + }, []); + + return ( + <> +
+
+
+ + ) +} diff --git a/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js b/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js index 1775c1182..cdfa2a5e8 100644 --- a/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js +++ b/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js @@ -1,11 +1,12 @@ import { registerPaymentMethod } from '@woocommerce/blocks-registry'; +import {CardFields} from "./Components/CardFields"; const config = wc.wcSettings.getSetting('ppcp-credit-card-gateway_data'); registerPaymentMethod({ name: config.id, label:
, - content:

content

, + content: , edit:

edit...

, ariaLabel: config.title, canMakePayment: () => {return true}, diff --git a/modules/ppcp-blocks/services.php b/modules/ppcp-blocks/services.php index c5a9dddc8..051f6323d 100644 --- a/modules/ppcp-blocks/services.php +++ b/modules/ppcp-blocks/services.php @@ -49,7 +49,10 @@ return array( return new AdvancedCardPaymentMethod( $container->get( 'blocks.url' ), $container->get( 'ppcp.asset-version' ), - $container->get( 'wcgateway.credit-card-gateway' ) + $container->get( 'wcgateway.credit-card-gateway' ), + function () use ( $container ): SmartButtonInterface { + return $container->get( 'button.smart-button' ); + } ); }, 'blocks.settings.final_review_enabled' => static function ( ContainerInterface $container ): bool { diff --git a/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php b/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php index 933af1954..57b9d5b9a 100644 --- a/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php +++ b/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php @@ -10,6 +10,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Blocks; use Automattic\WooCommerce\Blocks\Payments\Integrations\AbstractPaymentMethodType; +use WooCommerce\PayPalCommerce\Button\Assets\SmartButtonInterface; use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway; class AdvancedCardPaymentMethod extends AbstractPaymentMethodType { @@ -34,15 +35,24 @@ class AdvancedCardPaymentMethod extends AbstractPaymentMethodType { */ private $gateway; + /** + * The smart button script loading handler. + * + * @var SmartButtonInterface|callable + */ + private $smart_button; + public function __construct( string $module_url, string $version, - CreditCardGateway $gateway + CreditCardGateway $gateway, + $smart_button ) { - $this->name = CreditCardGateway::ID; - $this->module_url = $module_url; - $this->version = $version; - $this->gateway = $gateway; + $this->name = CreditCardGateway::ID; + $this->module_url = $module_url; + $this->version = $version; + $this->gateway = $gateway; + $this->smart_button = $smart_button; } public function initialize() {} @@ -73,10 +83,30 @@ class AdvancedCardPaymentMethod extends AbstractPaymentMethodType { * {@inheritDoc} */ public function get_payment_method_data() { + $script_data = $this->smart_button()->script_data(); + return array( 'id' => $this->name, 'title' => $this->gateway->title, 'description' => $this->gateway->description, + 'scriptData' => $script_data, ); } + + /** + * The smart button. + * + * @return SmartButtonInterface + */ + private function smart_button(): SmartButtonInterface { + if ( $this->smart_button instanceof SmartButtonInterface ) { + return $this->smart_button; + } + + if ( is_callable( $this->smart_button ) ) { + $this->smart_button = ( $this->smart_button )(); + } + + return $this->smart_button; + } } From 94379e4feb02b2ea5365ca28aa226a0cab6f9aac Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 17 Apr 2024 16:22:36 +0200 Subject: [PATCH 03/20] Add card fields component (WIP) --- modules/ppcp-button/src/Assets/SmartButton.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-button/src/Assets/SmartButton.php b/modules/ppcp-button/src/Assets/SmartButton.php index d795d4950..fcd519368 100644 --- a/modules/ppcp-button/src/Assets/SmartButton.php +++ b/modules/ppcp-button/src/Assets/SmartButton.php @@ -1365,7 +1365,7 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages $disable_funding, array_diff( array_keys( $this->all_funding_sources ), - array( 'venmo', 'paylater', 'paypal' ) + array( 'venmo', 'paylater', 'paypal', 'card' ) ) ); } From 3f619608809f0ec1fcc196c52f3fd88ba02ac5c9 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 17 Apr 2024 17:35:30 +0200 Subject: [PATCH 04/20] Add card fields component (WIP) --- .../resources/js/Components/CardFields.js | 75 ++++++++++++++++++- 1 file changed, 72 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-blocks/resources/js/Components/CardFields.js b/modules/ppcp-blocks/resources/js/Components/CardFields.js index ca3798855..074788da5 100644 --- a/modules/ppcp-blocks/resources/js/Components/CardFields.js +++ b/modules/ppcp-blocks/resources/js/Components/CardFields.js @@ -1,14 +1,83 @@ import {useEffect} from '@wordpress/element'; -export function CardFields({config}) { +export function CardFields({config, eventRegistration, emitResponse}) { + const {onPaymentSetup, onCheckoutFail, onCheckoutValidation} = eventRegistration; + const {responseTypes} = emitResponse; + + const createOrder = async () => { + try { + const res = await fetch(config.scriptData.ajax.create_order.endpoint, { + method: 'POST', + credentials: 'same-origin', + body: JSON.stringify({ + nonce: config.scriptData.ajax.create_order.nonce, + context: config.scriptData.context, + payment_method: 'ppcp-credit-card-gateway', + createaccount: false + }), + }); + + const json = await res.json(); + if (!json.success) { + console.error(json) + } + + console.log(json.data.id) + + return json.data.id; + } catch (err) { + console.error(err); + } + }; + + const handleApprove = async (data, actions) => { + try { + const res = await fetch(config.scriptData.ajax.approve_order.endpoint, { + method: 'POST', + credentials: 'same-origin', + body: JSON.stringify({ + nonce: config.scriptData.ajax.approve_order.nonce, + order_id: data.orderID, + }) + }); + + const json = await res.json(); + if (!json.success) { + console.error(json) + } + + console.log(json) + } catch (err) { + console.error(err); + } + }; + const cardField = paypal.CardFields({ - createOrder: () => {}, - onApprove: () => {}, + createOrder: () => { + return createOrder(); + }, + onApprove: (data, actions) => { + return handleApprove(data, actions); + }, onError: function (error) { console.error(error) } }); + useEffect(() => { + const unsubscribe = onPaymentSetup(() => { + + cardField.submit() + .catch((error) => { + console.error(error) + return {type: responseTypes.ERROR}; + }); + + return true; + }); + return unsubscribe; + }, [onPaymentSetup]); + useEffect(() => { if (cardField.isEligible()) { const numberField = cardField.NumberField(); From e3ac1908499dda290d4edc3f82b4b910f36bb165 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 15 May 2024 17:37:02 +0200 Subject: [PATCH 05/20] Add card fields component (WIP) --- modules/ppcp-blocks/package.json | 2 +- .../resources/js/Components/CardFields.js | 71 +++++++------------ .../js/Components/checkout-handler.js | 33 +++++++++ modules/ppcp-blocks/yarn.lock | 18 ++--- 4 files changed, 70 insertions(+), 54 deletions(-) create mode 100644 modules/ppcp-blocks/resources/js/Components/checkout-handler.js diff --git a/modules/ppcp-blocks/package.json b/modules/ppcp-blocks/package.json index 4958d3648..993b115e3 100644 --- a/modules/ppcp-blocks/package.json +++ b/modules/ppcp-blocks/package.json @@ -10,7 +10,7 @@ "Edge >= 14" ], "dependencies": { - "@paypal/react-paypal-js": "^8.2.0", + "@paypal/react-paypal-js": "^8.3.0", "core-js": "^3.25.0", "react": "^17.0.0", "react-dom": "^17.0.0" diff --git a/modules/ppcp-blocks/resources/js/Components/CardFields.js b/modules/ppcp-blocks/resources/js/Components/CardFields.js index 074788da5..c73e32d2f 100644 --- a/modules/ppcp-blocks/resources/js/Components/CardFields.js +++ b/modules/ppcp-blocks/resources/js/Components/CardFields.js @@ -1,4 +1,10 @@ -import {useEffect} from '@wordpress/element'; +import { + PayPalScriptProvider, + PayPalCardFieldsProvider, + PayPalCardFieldsForm, +} from "@paypal/react-paypal-js"; + +import {CheckoutHandler} from "./checkout-handler"; export function CardFields({config, eventRegistration, emitResponse}) { const {onPaymentSetup, onCheckoutFail, onCheckoutValidation} = eventRegistration; @@ -30,7 +36,7 @@ export function CardFields({config, eventRegistration, emitResponse}) { } }; - const handleApprove = async (data, actions) => { + const onApprove = async (data, actions) => { try { const res = await fetch(config.scriptData.ajax.approve_order.endpoint, { method: 'POST', @@ -52,50 +58,27 @@ export function CardFields({config, eventRegistration, emitResponse}) { } }; - const cardField = paypal.CardFields({ - createOrder: () => { - return createOrder(); - }, - onApprove: (data, actions) => { - return handleApprove(data, actions); - }, - onError: function (error) { - console.error(error) - } - }); - - useEffect(() => { - const unsubscribe = onPaymentSetup(() => { - - cardField.submit() - .catch((error) => { - console.error(error) - return {type: responseTypes.ERROR}; - }); - - return true; - }); - return unsubscribe; - }, [onPaymentSetup]); - - useEffect(() => { - if (cardField.isEligible()) { - const numberField = cardField.NumberField(); - numberField.render("#card-number-field-container"); - - const cvvField = cardField.CVVField(); - cvvField.render("#card-cvv-field-container"); - - const expiryField = cardField.ExpiryField(); - expiryField.render("#card-expiry-field-container"); - } - }, []); - return ( <> -
-
-
+ + { + console.log(err); + }} + > + + + + ) } diff --git a/modules/ppcp-blocks/resources/js/Components/checkout-handler.js b/modules/ppcp-blocks/resources/js/Components/checkout-handler.js new file mode 100644 index 000000000..035c10609 --- /dev/null +++ b/modules/ppcp-blocks/resources/js/Components/checkout-handler.js @@ -0,0 +1,33 @@ +import {useEffect} from '@wordpress/element'; +import {usePayPalCardFields} from "@paypal/react-paypal-js"; + +export const CheckoutHandler = ({onPaymentSetup, responseTypes}) => { + const {cardFieldsForm} = usePayPalCardFields(); + + useEffect(() => { + const unsubscribe = onPaymentSetup(async () => { + + cardFieldsForm.submit() + .then(() => { + return { + type: responseTypes.SUCCESS, + meta: { + paymentMethodData: { + foo: 'bar', + } + } + }; + }) + .catch((err) => { + return { + type: responseTypes.ERROR, + message: err + } + }); + }) + + return unsubscribe + }, [onPaymentSetup, cardFieldsForm]); + + return null +} diff --git a/modules/ppcp-blocks/yarn.lock b/modules/ppcp-blocks/yarn.lock index ff731f5b7..1e812a4ae 100644 --- a/modules/ppcp-blocks/yarn.lock +++ b/modules/ppcp-blocks/yarn.lock @@ -1005,19 +1005,19 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@paypal/paypal-js@^8.0.4": - version "8.0.4" - resolved "https://registry.yarnpkg.com/@paypal/paypal-js/-/paypal-js-8.0.4.tgz#abe9f40f519b1d2c306adddfbe733be03eb26ce5" - integrity sha512-91g5fhRBHGEBoikDzQT6uBn3PzlJQ75g0c3MvqVJqN0XRm5kHa9wz+6+Uaq8QQuxRzz5C2x55Zg057CW6EuwpQ== +"@paypal/paypal-js@^8.0.5": + version "8.0.5" + resolved "https://registry.yarnpkg.com/@paypal/paypal-js/-/paypal-js-8.0.5.tgz#77bc461b4d1e5a2c6f081269e3ef0b2e3331a68c" + integrity sha512-yQNV7rOILeaVCNU4aVDRPqEnbIlzfxgQfFsxzsBuZW1ouqRD/4kYBWJDzczCiscSr2xOeA/Pkm7e3a9fRfnuMQ== dependencies: promise-polyfill "^8.3.0" -"@paypal/react-paypal-js@^8.2.0": - version "8.2.0" - resolved "https://registry.yarnpkg.com/@paypal/react-paypal-js/-/react-paypal-js-8.2.0.tgz#4b1a142bbb68e62dca4a92da4a6b5568f54901f0" - integrity sha512-SworUfu0BNNcqoh0O53Ke4MFpx2m3qJRu3hayXvlluEEXJpKqGSV5aaSGFhbsZqi8hnbsx/hZR7BQbmqsggiGQ== +"@paypal/react-paypal-js@^8.3.0": + version "8.3.0" + resolved "https://registry.yarnpkg.com/@paypal/react-paypal-js/-/react-paypal-js-8.3.0.tgz#a103080b752766b8ff59b8620887abf802e1a01b" + integrity sha512-SX17d2h1CMNFGI+wtjb329AEDaBR8Ziy2LCV076eDcY1Q0MFKRkfQ/v0HOAvZtk3sJoydRmYez2pq47BRblwqQ== dependencies: - "@paypal/paypal-js" "^8.0.4" + "@paypal/paypal-js" "^8.0.5" "@paypal/sdk-constants" "^1.0.122" "@paypal/sdk-constants@^1.0.122": From aec32512cedbaf3aafb5f8de5ee63270f1a8c496 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Thu, 16 May 2024 12:29:05 +0200 Subject: [PATCH 06/20] Add basic card payment flow --- .../resources/js/Components/CardFields.js | 92 +++++++++---------- .../js/Components/checkout-handler.js | 16 +--- 2 files changed, 47 insertions(+), 61 deletions(-) diff --git a/modules/ppcp-blocks/resources/js/Components/CardFields.js b/modules/ppcp-blocks/resources/js/Components/CardFields.js index c73e32d2f..952896396 100644 --- a/modules/ppcp-blocks/resources/js/Components/CardFields.js +++ b/modules/ppcp-blocks/resources/js/Components/CardFields.js @@ -10,69 +10,65 @@ export function CardFields({config, eventRegistration, emitResponse}) { const {onPaymentSetup, onCheckoutFail, onCheckoutValidation} = eventRegistration; const {responseTypes} = emitResponse; - const createOrder = async () => { - try { - const res = await fetch(config.scriptData.ajax.create_order.endpoint, { - method: 'POST', - credentials: 'same-origin', - body: JSON.stringify({ - nonce: config.scriptData.ajax.create_order.nonce, - context: config.scriptData.context, - payment_method: 'ppcp-credit-card-gateway', - createaccount: false - }), + async function createOrder() { + return fetch(config.scriptData.ajax.create_order.endpoint, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + nonce: config.scriptData.ajax.create_order.nonce, + context: config.scriptData.context, + payment_method: 'ppcp-credit-card-gateway', + createaccount: false + }), + }) + .then((response) => response.json()) + .then((order) => { + return order.data.id; + }) + .catch((err) => { + console.error(err); }); + } - const json = await res.json(); - if (!json.success) { - console.error(json) - } - - console.log(json.data.id) - - return json.data.id; - } catch (err) { - console.error(err); - } - }; - - const onApprove = async (data, actions) => { - try { - const res = await fetch(config.scriptData.ajax.approve_order.endpoint, { - method: 'POST', - credentials: 'same-origin', - body: JSON.stringify({ - nonce: config.scriptData.ajax.approve_order.nonce, - order_id: data.orderID, - }) + function onApprove(data) { + fetch(config.scriptData.ajax.approve_order.endpoint, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + order_id: data.orderID, + nonce: config.scriptData.ajax.approve_order.nonce, + }), + }) + .then((response) => response.json()) + .then((data) => { + console.log(data) + return { + type: responseTypes.SUCCESS, + }; + }) + .catch((err) => { + console.error(err); }); - - const json = await res.json(); - if (!json.success) { - console.error(json) - } - - console.log(json) - } catch (err) { - console.error(err); - } - }; + } return ( <> { - console.log(err); + console.error(err); }} > diff --git a/modules/ppcp-blocks/resources/js/Components/checkout-handler.js b/modules/ppcp-blocks/resources/js/Components/checkout-handler.js index 035c10609..e4b2c9f8b 100644 --- a/modules/ppcp-blocks/resources/js/Components/checkout-handler.js +++ b/modules/ppcp-blocks/resources/js/Components/checkout-handler.js @@ -7,21 +7,11 @@ export const CheckoutHandler = ({onPaymentSetup, responseTypes}) => { useEffect(() => { const unsubscribe = onPaymentSetup(async () => { - cardFieldsForm.submit() - .then(() => { - return { - type: responseTypes.SUCCESS, - meta: { - paymentMethodData: { - foo: 'bar', - } - } - }; - }) - .catch((err) => { + await cardFieldsForm.submit() + .catch((error) => { + console.error(error) return { type: responseTypes.ERROR, - message: err } }); }) From e93e9db3be8b42d2a205c20011aa20a09fefd662 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 20 May 2024 16:37:11 +0200 Subject: [PATCH 07/20] Add place order logic (WIP) --- .../resources/js/Components/CardFields.js | 53 ++++++++++++++++--- .../js/Components/checkout-handler.js | 17 ++---- modules/ppcp-blocks/services.php | 6 +++ modules/ppcp-blocks/src/BlocksModule.php | 11 ++++ .../Endpoint/GetPayPalOrderFromSession.php | 43 +++++++++++++++ .../ppcp-button/src/Assets/SmartButton.php | 5 ++ 6 files changed, 115 insertions(+), 20 deletions(-) create mode 100644 modules/ppcp-blocks/src/Endpoint/GetPayPalOrderFromSession.php diff --git a/modules/ppcp-blocks/resources/js/Components/CardFields.js b/modules/ppcp-blocks/resources/js/Components/CardFields.js index 952896396..ad724eb42 100644 --- a/modules/ppcp-blocks/resources/js/Components/CardFields.js +++ b/modules/ppcp-blocks/resources/js/Components/CardFields.js @@ -1,3 +1,5 @@ +import {useEffect, useState} from '@wordpress/element'; + import { PayPalScriptProvider, PayPalCardFieldsProvider, @@ -7,9 +9,11 @@ import { import {CheckoutHandler} from "./checkout-handler"; export function CardFields({config, eventRegistration, emitResponse}) { - const {onPaymentSetup, onCheckoutFail, onCheckoutValidation} = eventRegistration; + const {onPaymentSetup} = eventRegistration; const {responseTypes} = emitResponse; + const [cardFieldsForm, setCardFieldsForm] = useState(); + async function createOrder() { return fetch(config.scriptData.ajax.create_order.endpoint, { method: "POST", @@ -20,7 +24,6 @@ export function CardFields({config, eventRegistration, emitResponse}) { nonce: config.scriptData.ajax.create_order.nonce, context: config.scriptData.context, payment_method: 'ppcp-credit-card-gateway', - createaccount: false }), }) .then((response) => response.json()) @@ -46,15 +49,53 @@ export function CardFields({config, eventRegistration, emitResponse}) { .then((response) => response.json()) .then((data) => { console.log(data) - return { - type: responseTypes.SUCCESS, - }; }) .catch((err) => { console.error(err); }); } + const getCardFieldsForm = (cardFieldsForm) => { + setCardFieldsForm(cardFieldsForm) + } + + const wait = (milliseconds) => { + return new Promise((resolve) => { + console.log('start...') + setTimeout(() => { + console.log('resolve') + resolve() + }, milliseconds) + }) + } + + useEffect( + () => + onPaymentSetup(() => { + async function handlePaymentProcessing() { + await cardFieldsForm.submit(); + + // TODO temporary workaround to wait for PayPal order in the session + await wait(3000) + + const response = await fetch(config.scriptData.ajax.get_paypal_order_from_session.endpoint) + const result = await response.json() + + return { + type: responseTypes.SUCCESS, + meta: { + paymentMethodData: { + 'paypal_order_id': result.data, + }, + }, + }; + } + + return handlePaymentProcessing(); + }), + [onPaymentSetup, cardFieldsForm] + ); + return ( <> - + diff --git a/modules/ppcp-blocks/resources/js/Components/checkout-handler.js b/modules/ppcp-blocks/resources/js/Components/checkout-handler.js index e4b2c9f8b..08f4bef5a 100644 --- a/modules/ppcp-blocks/resources/js/Components/checkout-handler.js +++ b/modules/ppcp-blocks/resources/js/Components/checkout-handler.js @@ -1,23 +1,12 @@ import {useEffect} from '@wordpress/element'; import {usePayPalCardFields} from "@paypal/react-paypal-js"; -export const CheckoutHandler = ({onPaymentSetup, responseTypes}) => { +export const CheckoutHandler = ({getCardFieldsForm}) => { const {cardFieldsForm} = usePayPalCardFields(); useEffect(() => { - const unsubscribe = onPaymentSetup(async () => { - - await cardFieldsForm.submit() - .catch((error) => { - console.error(error) - return { - type: responseTypes.ERROR, - } - }); - }) - - return unsubscribe - }, [onPaymentSetup, cardFieldsForm]); + getCardFieldsForm(cardFieldsForm) + }, []); return null } diff --git a/modules/ppcp-blocks/services.php b/modules/ppcp-blocks/services.php index 051f6323d..7c26baf9c 100644 --- a/modules/ppcp-blocks/services.php +++ b/modules/ppcp-blocks/services.php @@ -9,6 +9,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Blocks; +use WooCommerce\PayPalCommerce\Blocks\Endpoint\GetPayPalOrderFromSession; use WooCommerce\PayPalCommerce\Blocks\Endpoint\UpdateShippingEndpoint; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; use WooCommerce\PayPalCommerce\Button\Assets\SmartButtonInterface; @@ -72,6 +73,11 @@ return array( $container->get( 'woocommerce.logger.woocommerce' ) ); }, + 'blocks.endpoint.get-paypal-order-from-session' => static function ( ContainerInterface $container ): GetPayPalOrderFromSession { + return new GetPayPalOrderFromSession( + $container->get( 'session.handler' ) + ); + }, 'blocks.add-place-order-method' => function ( ContainerInterface $container ) : bool { /** diff --git a/modules/ppcp-blocks/src/BlocksModule.php b/modules/ppcp-blocks/src/BlocksModule.php index 1dd1560dd..765af5698 100644 --- a/modules/ppcp-blocks/src/BlocksModule.php +++ b/modules/ppcp-blocks/src/BlocksModule.php @@ -10,6 +10,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Blocks; use Automattic\WooCommerce\Blocks\Payments\PaymentMethodRegistry; +use WooCommerce\PayPalCommerce\Blocks\Endpoint\GetPayPalOrderFromSession; use WooCommerce\PayPalCommerce\Blocks\Endpoint\UpdateShippingEndpoint; use WooCommerce\PayPalCommerce\Button\Assets\SmartButtonInterface; use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider; @@ -90,6 +91,16 @@ class BlocksModule implements ModuleInterface { } ); + add_action( + 'wc_ajax_' . GetPayPalOrderFromSession::ENDPOINT, + static function () use ( $c ) { + $endpoint = $c->get( 'blocks.endpoint.get-paypal-order-from-session' ); + assert( $endpoint instanceof GetPayPalOrderFromSession ); + + $endpoint->handle_request(); + } + ); + // Enqueue frontend scripts. add_action( 'wp_enqueue_scripts', diff --git a/modules/ppcp-blocks/src/Endpoint/GetPayPalOrderFromSession.php b/modules/ppcp-blocks/src/Endpoint/GetPayPalOrderFromSession.php new file mode 100644 index 000000000..d343a846d --- /dev/null +++ b/modules/ppcp-blocks/src/Endpoint/GetPayPalOrderFromSession.php @@ -0,0 +1,43 @@ +session_handler = $session_handler; + } + + public static function nonce(): string + { + return self::ENDPOINT; + } + + public function handle_request(): bool + { + $order = $this->session_handler->order(); + + wp_send_json_success($order->id()); + return true; + } +} diff --git a/modules/ppcp-button/src/Assets/SmartButton.php b/modules/ppcp-button/src/Assets/SmartButton.php index e3fa8c4ba..39b6f61cb 100644 --- a/modules/ppcp-button/src/Assets/SmartButton.php +++ b/modules/ppcp-button/src/Assets/SmartButton.php @@ -19,6 +19,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Entity\Money; use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentToken; use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory; use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies; +use WooCommerce\PayPalCommerce\Blocks\Endpoint\GetPayPalOrderFromSession; use WooCommerce\PayPalCommerce\Blocks\Endpoint\UpdateShippingEndpoint; use WooCommerce\PayPalCommerce\Button\Endpoint\ApproveOrderEndpoint; use WooCommerce\PayPalCommerce\Button\Endpoint\ApproveSubscriptionEndpoint; @@ -1159,6 +1160,10 @@ document.querySelector("#payment").before(document.querySelector(".ppcp-messages 'wp_rest_nonce' => wp_create_nonce( 'wc_store_api' ), 'update_shipping_method' => \WC_AJAX::get_endpoint( 'update_shipping_method' ), ), + 'get_paypal_order_from_session' => array( + 'endpoint' => \WC_AJAX::get_endpoint( GetPayPalOrderFromSession::ENDPOINT ), + 'nonce' => wp_create_nonce( GetPayPalOrderFromSession::nonce() ), + ), ), 'cart_contains_subscription' => $this->subscription_helper->cart_contains_subscription(), 'subscription_plan_id' => $this->subscription_helper->paypal_subscription_id(), From a42488b41d2db82ca7209b068f1929660e4f048b Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 20 May 2024 18:03:31 +0200 Subject: [PATCH 08/20] Do not use custom ajax endpoint for get order from session --- .../resources/js/Components/CardFields.js | 10 +---- modules/ppcp-blocks/src/BlocksModule.php | 11 ----- .../Endpoint/GetPayPalOrderFromSession.php | 43 ------------------- .../ppcp-button/src/Assets/SmartButton.php | 5 --- 4 files changed, 1 insertion(+), 68 deletions(-) delete mode 100644 modules/ppcp-blocks/src/Endpoint/GetPayPalOrderFromSession.php diff --git a/modules/ppcp-blocks/resources/js/Components/CardFields.js b/modules/ppcp-blocks/resources/js/Components/CardFields.js index ad724eb42..85c8ce139 100644 --- a/modules/ppcp-blocks/resources/js/Components/CardFields.js +++ b/modules/ppcp-blocks/resources/js/Components/CardFields.js @@ -78,17 +78,9 @@ export function CardFields({config, eventRegistration, emitResponse}) { // TODO temporary workaround to wait for PayPal order in the session await wait(3000) - const response = await fetch(config.scriptData.ajax.get_paypal_order_from_session.endpoint) - const result = await response.json() - return { type: responseTypes.SUCCESS, - meta: { - paymentMethodData: { - 'paypal_order_id': result.data, - }, - }, - }; + } } return handlePaymentProcessing(); diff --git a/modules/ppcp-blocks/src/BlocksModule.php b/modules/ppcp-blocks/src/BlocksModule.php index 765af5698..1dd1560dd 100644 --- a/modules/ppcp-blocks/src/BlocksModule.php +++ b/modules/ppcp-blocks/src/BlocksModule.php @@ -10,7 +10,6 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Blocks; use Automattic\WooCommerce\Blocks\Payments\PaymentMethodRegistry; -use WooCommerce\PayPalCommerce\Blocks\Endpoint\GetPayPalOrderFromSession; use WooCommerce\PayPalCommerce\Blocks\Endpoint\UpdateShippingEndpoint; use WooCommerce\PayPalCommerce\Button\Assets\SmartButtonInterface; use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider; @@ -91,16 +90,6 @@ class BlocksModule implements ModuleInterface { } ); - add_action( - 'wc_ajax_' . GetPayPalOrderFromSession::ENDPOINT, - static function () use ( $c ) { - $endpoint = $c->get( 'blocks.endpoint.get-paypal-order-from-session' ); - assert( $endpoint instanceof GetPayPalOrderFromSession ); - - $endpoint->handle_request(); - } - ); - // Enqueue frontend scripts. add_action( 'wp_enqueue_scripts', diff --git a/modules/ppcp-blocks/src/Endpoint/GetPayPalOrderFromSession.php b/modules/ppcp-blocks/src/Endpoint/GetPayPalOrderFromSession.php deleted file mode 100644 index d343a846d..000000000 --- a/modules/ppcp-blocks/src/Endpoint/GetPayPalOrderFromSession.php +++ /dev/null @@ -1,43 +0,0 @@ -session_handler = $session_handler; - } - - public static function nonce(): string - { - return self::ENDPOINT; - } - - public function handle_request(): bool - { - $order = $this->session_handler->order(); - - wp_send_json_success($order->id()); - return true; - } -} diff --git a/modules/ppcp-button/src/Assets/SmartButton.php b/modules/ppcp-button/src/Assets/SmartButton.php index 39b6f61cb..e3fa8c4ba 100644 --- a/modules/ppcp-button/src/Assets/SmartButton.php +++ b/modules/ppcp-button/src/Assets/SmartButton.php @@ -19,7 +19,6 @@ use WooCommerce\PayPalCommerce\ApiClient\Entity\Money; use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentToken; use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory; use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies; -use WooCommerce\PayPalCommerce\Blocks\Endpoint\GetPayPalOrderFromSession; use WooCommerce\PayPalCommerce\Blocks\Endpoint\UpdateShippingEndpoint; use WooCommerce\PayPalCommerce\Button\Endpoint\ApproveOrderEndpoint; use WooCommerce\PayPalCommerce\Button\Endpoint\ApproveSubscriptionEndpoint; @@ -1160,10 +1159,6 @@ document.querySelector("#payment").before(document.querySelector(".ppcp-messages 'wp_rest_nonce' => wp_create_nonce( 'wc_store_api' ), 'update_shipping_method' => \WC_AJAX::get_endpoint( 'update_shipping_method' ), ), - 'get_paypal_order_from_session' => array( - 'endpoint' => \WC_AJAX::get_endpoint( GetPayPalOrderFromSession::ENDPOINT ), - 'nonce' => wp_create_nonce( GetPayPalOrderFromSession::nonce() ), - ), ), 'cart_contains_subscription' => $this->subscription_helper->cart_contains_subscription(), 'subscription_plan_id' => $this->subscription_helper->paypal_subscription_id(), From de3fd75f691be97ac8f25e9b2afc49b1d5f02b5f Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 20 May 2024 18:46:44 +0200 Subject: [PATCH 09/20] Rename component filename --- .../{CardFields.js => card-fields.js} | 46 ++----------------- .../js/advanced-card-checkout-block.js | 4 +- .../resources/js/card-fields-config.js | 42 +++++++++++++++++ 3 files changed, 47 insertions(+), 45 deletions(-) rename modules/ppcp-blocks/resources/js/Components/{CardFields.js => card-fields.js} (59%) create mode 100644 modules/ppcp-blocks/resources/js/card-fields-config.js diff --git a/modules/ppcp-blocks/resources/js/Components/CardFields.js b/modules/ppcp-blocks/resources/js/Components/card-fields.js similarity index 59% rename from modules/ppcp-blocks/resources/js/Components/CardFields.js rename to modules/ppcp-blocks/resources/js/Components/card-fields.js index 85c8ce139..97cf83ab5 100644 --- a/modules/ppcp-blocks/resources/js/Components/CardFields.js +++ b/modules/ppcp-blocks/resources/js/Components/card-fields.js @@ -7,6 +7,7 @@ import { } from "@paypal/react-paypal-js"; import {CheckoutHandler} from "./checkout-handler"; +import {createOrder, onApprove} from "../card-fields-config"; export function CardFields({config, eventRegistration, emitResponse}) { const {onPaymentSetup} = eventRegistration; @@ -14,47 +15,6 @@ export function CardFields({config, eventRegistration, emitResponse}) { const [cardFieldsForm, setCardFieldsForm] = useState(); - async function createOrder() { - return fetch(config.scriptData.ajax.create_order.endpoint, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - nonce: config.scriptData.ajax.create_order.nonce, - context: config.scriptData.context, - payment_method: 'ppcp-credit-card-gateway', - }), - }) - .then((response) => response.json()) - .then((order) => { - return order.data.id; - }) - .catch((err) => { - console.error(err); - }); - } - - function onApprove(data) { - fetch(config.scriptData.ajax.approve_order.endpoint, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - order_id: data.orderID, - nonce: config.scriptData.ajax.approve_order.nonce, - }), - }) - .then((response) => response.json()) - .then((data) => { - console.log(data) - }) - .catch((err) => { - console.error(err); - }); - } - const getCardFieldsForm = (cardFieldsForm) => { setCardFieldsForm(cardFieldsForm) } @@ -67,7 +27,7 @@ export function CardFields({config, eventRegistration, emitResponse}) { resolve() }, milliseconds) }) - } + }; useEffect( () => @@ -75,7 +35,7 @@ export function CardFields({config, eventRegistration, emitResponse}) { async function handlePaymentProcessing() { await cardFieldsForm.submit(); - // TODO temporary workaround to wait for PayPal order in the session + // TODO temporary workaround to wait for onApprove to resolve await wait(3000) return { diff --git a/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js b/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js index cdfa2a5e8..f8fb6f6e3 100644 --- a/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js +++ b/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js @@ -1,5 +1,5 @@ import { registerPaymentMethod } from '@woocommerce/blocks-registry'; -import {CardFields} from "./Components/CardFields"; +import {CardFields} from "./Components/card-fields"; const config = wc.wcSettings.getSetting('ppcp-credit-card-gateway_data'); @@ -7,7 +7,7 @@ registerPaymentMethod({ name: config.id, label:
, content: , - edit:

edit...

, + edit:
, ariaLabel: config.title, canMakePayment: () => {return true}, }) diff --git a/modules/ppcp-blocks/resources/js/card-fields-config.js b/modules/ppcp-blocks/resources/js/card-fields-config.js new file mode 100644 index 000000000..92700ac8a --- /dev/null +++ b/modules/ppcp-blocks/resources/js/card-fields-config.js @@ -0,0 +1,42 @@ +const config = wc.wcSettings.getSetting('ppcp-credit-card-gateway_data'); + +export async function createOrder() { + return fetch(config.scriptData.ajax.create_order.endpoint, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + nonce: config.scriptData.ajax.create_order.nonce, + context: config.scriptData.context, + payment_method: 'ppcp-credit-card-gateway', + }), + }) + .then((response) => response.json()) + .then((order) => { + return order.data.id; + }) + .catch((err) => { + console.error(err); + }); +} + +export function onApprove(data) { + fetch(config.scriptData.ajax.approve_order.endpoint, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + order_id: data.orderID, + nonce: config.scriptData.ajax.approve_order.nonce, + }), + }) + .then((response) => response.json()) + .then((data) => { + console.log(data) + }) + .catch((err) => { + console.error(err); + }); +} From b7b19cfbc78404202306204cb11bb2b5ca908da0 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 20 May 2024 20:46:53 +0200 Subject: [PATCH 10/20] Add save payment checkbox --- .../resources/js/Components/checkout-handler.js | 7 ++++++- modules/ppcp-blocks/resources/js/card-fields-config.js | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-blocks/resources/js/Components/checkout-handler.js b/modules/ppcp-blocks/resources/js/Components/checkout-handler.js index 08f4bef5a..6ccb0eea4 100644 --- a/modules/ppcp-blocks/resources/js/Components/checkout-handler.js +++ b/modules/ppcp-blocks/resources/js/Components/checkout-handler.js @@ -8,5 +8,10 @@ export const CheckoutHandler = ({getCardFieldsForm}) => { getCardFieldsForm(cardFieldsForm) }, []); - return null + return ( + <> + + + + ) } diff --git a/modules/ppcp-blocks/resources/js/card-fields-config.js b/modules/ppcp-blocks/resources/js/card-fields-config.js index 92700ac8a..5dcd2a447 100644 --- a/modules/ppcp-blocks/resources/js/card-fields-config.js +++ b/modules/ppcp-blocks/resources/js/card-fields-config.js @@ -10,6 +10,7 @@ export async function createOrder() { nonce: config.scriptData.ajax.create_order.nonce, context: config.scriptData.context, payment_method: 'ppcp-credit-card-gateway', + save_payment_method: true, }), }) .then((response) => response.json()) From cf9f323e9b1c73237b0e820c4a2ef8ec01d3e1ac Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 21 May 2024 10:39:40 +0200 Subject: [PATCH 11/20] Use `showSaveOption` for save card checkbox --- .../resources/js/Components/checkout-handler.js | 7 +------ .../resources/js/advanced-card-checkout-block.js | 4 ++++ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/modules/ppcp-blocks/resources/js/Components/checkout-handler.js b/modules/ppcp-blocks/resources/js/Components/checkout-handler.js index 6ccb0eea4..08f4bef5a 100644 --- a/modules/ppcp-blocks/resources/js/Components/checkout-handler.js +++ b/modules/ppcp-blocks/resources/js/Components/checkout-handler.js @@ -8,10 +8,5 @@ export const CheckoutHandler = ({getCardFieldsForm}) => { getCardFieldsForm(cardFieldsForm) }, []); - return ( - <> - - - - ) + return null } diff --git a/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js b/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js index f8fb6f6e3..923c88da8 100644 --- a/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js +++ b/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js @@ -10,4 +10,8 @@ registerPaymentMethod({ edit:
, ariaLabel: config.title, canMakePayment: () => {return true}, + supports: { + showSavedCards: true, + showSaveOption: true + } }) From d840296a25737853574c1277ffabeb64a89f12a3 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 21 May 2024 10:45:25 +0200 Subject: [PATCH 12/20] Remove unused service --- modules/ppcp-blocks/services.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/modules/ppcp-blocks/services.php b/modules/ppcp-blocks/services.php index 7c26baf9c..7f7fdf048 100644 --- a/modules/ppcp-blocks/services.php +++ b/modules/ppcp-blocks/services.php @@ -73,11 +73,6 @@ return array( $container->get( 'woocommerce.logger.woocommerce' ) ); }, - 'blocks.endpoint.get-paypal-order-from-session' => static function ( ContainerInterface $container ): GetPayPalOrderFromSession { - return new GetPayPalOrderFromSession( - $container->get( 'session.handler' ) - ); - }, 'blocks.add-place-order-method' => function ( ContainerInterface $container ) : bool { /** From 17fd6886ffc9ec95ea4f89a40a8ccec1103ba02d Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 21 May 2024 15:52:41 +0200 Subject: [PATCH 13/20] Add gateway features support --- .../resources/js/advanced-card-checkout-block.js | 3 ++- .../ppcp-blocks/src/AdvancedCardPaymentMethod.php | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js b/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js index 923c88da8..9ddf3a6b1 100644 --- a/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js +++ b/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js @@ -12,6 +12,7 @@ registerPaymentMethod({ canMakePayment: () => {return true}, supports: { showSavedCards: true, - showSaveOption: true + showSaveOption: true, + features: config.supports } }) diff --git a/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php b/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php index 57b9d5b9a..d85555040 100644 --- a/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php +++ b/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php @@ -13,6 +13,9 @@ use Automattic\WooCommerce\Blocks\Payments\Integrations\AbstractPaymentMethodTyp use WooCommerce\PayPalCommerce\Button\Assets\SmartButtonInterface; use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway; +/** + * Class AdvancedCardPaymentMethod + */ class AdvancedCardPaymentMethod extends AbstractPaymentMethodType { /** * The URL of this module. @@ -42,6 +45,14 @@ class AdvancedCardPaymentMethod extends AbstractPaymentMethodType { */ private $smart_button; + /** + * AdvancedCardPaymentMethod constructor. + * + * @param string $module_url The URL of this module. + * @param string $version The assets version. + * @param CreditCardGateway $gateway Credit card gateway. + * @param SmartButtonInterface|callable $smart_button The smart button script loading handler. + */ public function __construct( string $module_url, string $version, @@ -55,6 +66,9 @@ class AdvancedCardPaymentMethod extends AbstractPaymentMethodType { $this->smart_button = $smart_button; } + /** + * {@inheritDoc} + */ public function initialize() {} /** @@ -90,6 +104,7 @@ class AdvancedCardPaymentMethod extends AbstractPaymentMethodType { 'title' => $this->gateway->title, 'description' => $this->gateway->description, 'scriptData' => $script_data, + 'supports' => $this->gateway->supports, ); } From 748fe6dfcf21e28013b9dc9a5c50518582b6140c Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 22 May 2024 12:06:15 +0200 Subject: [PATCH 14/20] Get payment method value from filter instead of request --- modules/ppcp-card-fields/src/CardFieldsModule.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-card-fields/src/CardFieldsModule.php b/modules/ppcp-card-fields/src/CardFieldsModule.php index 6835108c3..b499891a0 100644 --- a/modules/ppcp-card-fields/src/CardFieldsModule.php +++ b/modules/ppcp-card-fields/src/CardFieldsModule.php @@ -105,9 +105,8 @@ class CardFieldsModule implements ModuleInterface { add_filter( 'ppcp_create_order_request_body_data', - function( array $data ) use ( $c ): array { + function( array $data, string $payment_method ) use ( $c ): array { // phpcs:ignore WordPress.Security.NonceVerification.Missing - $payment_method = wc_clean( wp_unslash( $_POST['payment_method'] ?? '' ) ); if ( $payment_method !== CreditCardGateway::ID ) { return $data; } @@ -134,7 +133,9 @@ class CardFieldsModule implements ModuleInterface { } return $data; - } + }, + 10, + 2 ); } } From 880bd48271cd03873ca06797e9f9d39053134f0d Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 22 May 2024 15:52:45 +0200 Subject: [PATCH 15/20] Use custom save card payment checkbox and show/hide it based on vaulting setting --- .../resources/js/Components/card-fields.js | 12 +++++++-- .../js/Components/checkout-handler.js | 13 ++++++++-- .../js/advanced-card-checkout-block.js | 1 - .../resources/js/card-fields-config.js | 3 ++- modules/ppcp-blocks/services.php | 3 ++- .../src/AdvancedCardPaymentMethod.php | 26 ++++++++++++++----- 6 files changed, 45 insertions(+), 13 deletions(-) diff --git a/modules/ppcp-blocks/resources/js/Components/card-fields.js b/modules/ppcp-blocks/resources/js/Components/card-fields.js index 97cf83ab5..dcd3ba2dd 100644 --- a/modules/ppcp-blocks/resources/js/Components/card-fields.js +++ b/modules/ppcp-blocks/resources/js/Components/card-fields.js @@ -14,11 +14,14 @@ export function CardFields({config, eventRegistration, emitResponse}) { const {responseTypes} = emitResponse; const [cardFieldsForm, setCardFieldsForm] = useState(); - const getCardFieldsForm = (cardFieldsForm) => { setCardFieldsForm(cardFieldsForm) } + const getSavePayment = (savePayment) => { + localStorage.setItem('ppcp-save-card-payment', savePayment); + } + const wait = (milliseconds) => { return new Promise((resolve) => { console.log('start...') @@ -65,7 +68,12 @@ export function CardFields({config, eventRegistration, emitResponse}) { }} > - + diff --git a/modules/ppcp-blocks/resources/js/Components/checkout-handler.js b/modules/ppcp-blocks/resources/js/Components/checkout-handler.js index 08f4bef5a..8e9658fc2 100644 --- a/modules/ppcp-blocks/resources/js/Components/checkout-handler.js +++ b/modules/ppcp-blocks/resources/js/Components/checkout-handler.js @@ -1,12 +1,21 @@ import {useEffect} from '@wordpress/element'; import {usePayPalCardFields} from "@paypal/react-paypal-js"; -export const CheckoutHandler = ({getCardFieldsForm}) => { +export const CheckoutHandler = ({getCardFieldsForm, getSavePayment, saveCardText, is_vaulting_enabled}) => { const {cardFieldsForm} = usePayPalCardFields(); useEffect(() => { getCardFieldsForm(cardFieldsForm) }, []); - return null + if (!is_vaulting_enabled) { + return null; + } + + return ( + <> + getSavePayment(e.target.checked)}/> + + + ) } diff --git a/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js b/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js index 9ddf3a6b1..6d268c122 100644 --- a/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js +++ b/modules/ppcp-blocks/resources/js/advanced-card-checkout-block.js @@ -12,7 +12,6 @@ registerPaymentMethod({ canMakePayment: () => {return true}, supports: { showSavedCards: true, - showSaveOption: true, features: config.supports } }) diff --git a/modules/ppcp-blocks/resources/js/card-fields-config.js b/modules/ppcp-blocks/resources/js/card-fields-config.js index 5dcd2a447..23b8859fb 100644 --- a/modules/ppcp-blocks/resources/js/card-fields-config.js +++ b/modules/ppcp-blocks/resources/js/card-fields-config.js @@ -10,7 +10,7 @@ export async function createOrder() { nonce: config.scriptData.ajax.create_order.nonce, context: config.scriptData.context, payment_method: 'ppcp-credit-card-gateway', - save_payment_method: true, + save_payment_method: localStorage.getItem('ppcp-save-card-payment') === 'true', }), }) .then((response) => response.json()) @@ -36,6 +36,7 @@ export function onApprove(data) { .then((response) => response.json()) .then((data) => { console.log(data) + localStorage.removeItem('ppcp-save-card-payment'); }) .catch((err) => { console.error(err); diff --git a/modules/ppcp-blocks/services.php b/modules/ppcp-blocks/services.php index 7f7fdf048..21a97f984 100644 --- a/modules/ppcp-blocks/services.php +++ b/modules/ppcp-blocks/services.php @@ -53,7 +53,8 @@ return array( $container->get( 'wcgateway.credit-card-gateway' ), function () use ( $container ): SmartButtonInterface { return $container->get( 'button.smart-button' ); - } + }, + $container->get( 'wcgateway.settings' ) ); }, 'blocks.settings.final_review_enabled' => static function ( ContainerInterface $container ): bool { diff --git a/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php b/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php index d85555040..55b5cfdc9 100644 --- a/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php +++ b/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php @@ -12,11 +12,13 @@ namespace WooCommerce\PayPalCommerce\Blocks; use Automattic\WooCommerce\Blocks\Payments\Integrations\AbstractPaymentMethodType; use WooCommerce\PayPalCommerce\Button\Assets\SmartButtonInterface; use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway; +use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; /** * Class AdvancedCardPaymentMethod */ class AdvancedCardPaymentMethod extends AbstractPaymentMethodType { + /** * The URL of this module. * @@ -45,6 +47,13 @@ class AdvancedCardPaymentMethod extends AbstractPaymentMethodType { */ private $smart_button; + /** + * The settings. + * + * @var Settings + */ + protected $settings; + /** * AdvancedCardPaymentMethod constructor. * @@ -52,18 +61,21 @@ class AdvancedCardPaymentMethod extends AbstractPaymentMethodType { * @param string $version The assets version. * @param CreditCardGateway $gateway Credit card gateway. * @param SmartButtonInterface|callable $smart_button The smart button script loading handler. + * @param Settings $settings The settings. */ public function __construct( string $module_url, string $version, CreditCardGateway $gateway, - $smart_button + $smart_button, + Settings $settings ) { $this->name = CreditCardGateway::ID; $this->module_url = $module_url; $this->version = $version; $this->gateway = $gateway; $this->smart_button = $smart_button; + $this->settings = $settings; } /** @@ -100,11 +112,13 @@ class AdvancedCardPaymentMethod extends AbstractPaymentMethodType { $script_data = $this->smart_button()->script_data(); return array( - 'id' => $this->name, - 'title' => $this->gateway->title, - 'description' => $this->gateway->description, - 'scriptData' => $script_data, - 'supports' => $this->gateway->supports, + 'id' => $this->name, + 'title' => $this->gateway->title, + 'description' => $this->gateway->description, + 'scriptData' => $script_data, + 'supports' => $this->gateway->supports, + 'save_card_text' => esc_html__( 'Save your card', 'woocommerce-paypal-payments' ), + 'is_vaulting_enabled' => $this->settings->has( 'vault_enabled_dcc' ) && $this->settings->get( 'vault_enabled_dcc' ), ); } From eaca9f6a41b38c18e6b0fc1063a86e211879ab5e Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 3 Jun 2024 11:29:04 +0200 Subject: [PATCH 16/20] Do not use timeout but return a promise on onApprove --- .../resources/js/Components/card-fields.js | 13 ------------- .../ppcp-blocks/resources/js/card-fields-config.js | 4 ++-- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/modules/ppcp-blocks/resources/js/Components/card-fields.js b/modules/ppcp-blocks/resources/js/Components/card-fields.js index dcd3ba2dd..2d24bfce1 100644 --- a/modules/ppcp-blocks/resources/js/Components/card-fields.js +++ b/modules/ppcp-blocks/resources/js/Components/card-fields.js @@ -22,25 +22,12 @@ export function CardFields({config, eventRegistration, emitResponse}) { localStorage.setItem('ppcp-save-card-payment', savePayment); } - const wait = (milliseconds) => { - return new Promise((resolve) => { - console.log('start...') - setTimeout(() => { - console.log('resolve') - resolve() - }, milliseconds) - }) - }; - useEffect( () => onPaymentSetup(() => { async function handlePaymentProcessing() { await cardFieldsForm.submit(); - // TODO temporary workaround to wait for onApprove to resolve - await wait(3000) - return { type: responseTypes.SUCCESS, } diff --git a/modules/ppcp-blocks/resources/js/card-fields-config.js b/modules/ppcp-blocks/resources/js/card-fields-config.js index 23b8859fb..37d939a5f 100644 --- a/modules/ppcp-blocks/resources/js/card-fields-config.js +++ b/modules/ppcp-blocks/resources/js/card-fields-config.js @@ -22,8 +22,8 @@ export async function createOrder() { }); } -export function onApprove(data) { - fetch(config.scriptData.ajax.approve_order.endpoint, { +export async function onApprove(data) { + return fetch(config.scriptData.ajax.approve_order.endpoint, { method: "POST", headers: { "Content-Type": "application/json", From 210d70a551bbf2a207763d6bee37799a83031805 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 3 Jun 2024 14:32:51 +0200 Subject: [PATCH 17/20] Return error type when catching submit eror --- modules/ppcp-blocks/resources/js/Components/card-fields.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-blocks/resources/js/Components/card-fields.js b/modules/ppcp-blocks/resources/js/Components/card-fields.js index 2d24bfce1..c74359be0 100644 --- a/modules/ppcp-blocks/resources/js/Components/card-fields.js +++ b/modules/ppcp-blocks/resources/js/Components/card-fields.js @@ -26,7 +26,12 @@ export function CardFields({config, eventRegistration, emitResponse}) { () => onPaymentSetup(() => { async function handlePaymentProcessing() { - await cardFieldsForm.submit(); + await cardFieldsForm.submit() + .catch((error) => { + return { + type: responseTypes.ERROR, + } + }); return { type: responseTypes.SUCCESS, From cb96f888a0e804d028c08f57d5aa85915658f928 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 4 Jun 2024 17:33:28 +0200 Subject: [PATCH 18/20] Add card icons component --- modules/ppcp-blocks/resources/js/Components/card-fields.js | 4 +++- modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-blocks/resources/js/Components/card-fields.js b/modules/ppcp-blocks/resources/js/Components/card-fields.js index c74359be0..00517dfdb 100644 --- a/modules/ppcp-blocks/resources/js/Components/card-fields.js +++ b/modules/ppcp-blocks/resources/js/Components/card-fields.js @@ -9,9 +9,10 @@ import { import {CheckoutHandler} from "./checkout-handler"; import {createOrder, onApprove} from "../card-fields-config"; -export function CardFields({config, eventRegistration, emitResponse}) { +export function CardFields({config, eventRegistration, emitResponse, components}) { const {onPaymentSetup} = eventRegistration; const {responseTypes} = emitResponse; + const { PaymentMethodIcons } = components; const [cardFieldsForm, setCardFieldsForm] = useState(); const getCardFieldsForm = (cardFieldsForm) => { @@ -60,6 +61,7 @@ export function CardFields({config, eventRegistration, emitResponse}) { }} > + $this->gateway->supports, 'save_card_text' => esc_html__( 'Save your card', 'woocommerce-paypal-payments' ), 'is_vaulting_enabled' => $this->settings->has( 'vault_enabled_dcc' ) && $this->settings->get( 'vault_enabled_dcc' ), + 'card_icons' => $this->settings->has( 'card_icons' ) ? (array) $this->settings->get( 'card_icons' ) : array(), ); } From 559d0654a20963adf9d0dfe16b4bde76498c2b65 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 4 Jun 2024 18:17:33 +0200 Subject: [PATCH 19/20] Show save card checkbox checked and disabled if subscription in cart --- .../resources/js/Components/card-fields.js | 4 ++++ .../resources/js/Components/checkout-handler.js | 11 +++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-blocks/resources/js/Components/card-fields.js b/modules/ppcp-blocks/resources/js/Components/card-fields.js index 00517dfdb..f7ff6c7e0 100644 --- a/modules/ppcp-blocks/resources/js/Components/card-fields.js +++ b/modules/ppcp-blocks/resources/js/Components/card-fields.js @@ -8,6 +8,7 @@ import { import {CheckoutHandler} from "./checkout-handler"; import {createOrder, onApprove} from "../card-fields-config"; +import {cartHasSubscriptionProducts} from "../Helper/Subscription"; export function CardFields({config, eventRegistration, emitResponse, components}) { const {onPaymentSetup} = eventRegistration; @@ -23,6 +24,8 @@ export function CardFields({config, eventRegistration, emitResponse, components} localStorage.setItem('ppcp-save-card-payment', savePayment); } + const hasSubscriptionProducts = cartHasSubscriptionProducts(config.scriptData); + useEffect( () => onPaymentSetup(() => { @@ -65,6 +68,7 @@ export function CardFields({config, eventRegistration, emitResponse, components} diff --git a/modules/ppcp-blocks/resources/js/Components/checkout-handler.js b/modules/ppcp-blocks/resources/js/Components/checkout-handler.js index 8e9658fc2..48dd0602d 100644 --- a/modules/ppcp-blocks/resources/js/Components/checkout-handler.js +++ b/modules/ppcp-blocks/resources/js/Components/checkout-handler.js @@ -1,7 +1,7 @@ import {useEffect} from '@wordpress/element'; import {usePayPalCardFields} from "@paypal/react-paypal-js"; -export const CheckoutHandler = ({getCardFieldsForm, getSavePayment, saveCardText, is_vaulting_enabled}) => { +export const CheckoutHandler = ({getCardFieldsForm, getSavePayment, hasSubscriptionProducts, saveCardText, is_vaulting_enabled}) => { const {cardFieldsForm} = usePayPalCardFields(); useEffect(() => { @@ -14,7 +14,14 @@ export const CheckoutHandler = ({getCardFieldsForm, getSavePayment, saveCardText return ( <> - getSavePayment(e.target.checked)}/> + getSavePayment(e.target.checked)} + defaultChecked={hasSubscriptionProducts} + disabled={hasSubscriptionProducts} + /> ) From 76bcc99bc5222339b4140deaf7630be0302c4f6e Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 5 Jun 2024 15:35:47 +0200 Subject: [PATCH 20/20] Rename method --- modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php b/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php index 51b45b495..2c6fd60f4 100644 --- a/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php +++ b/modules/ppcp-blocks/src/AdvancedCardPaymentMethod.php @@ -109,7 +109,7 @@ class AdvancedCardPaymentMethod extends AbstractPaymentMethodType { * {@inheritDoc} */ public function get_payment_method_data() { - $script_data = $this->smart_button()->script_data(); + $script_data = $this->smart_button_instance()->script_data(); return array( 'id' => $this->name, @@ -128,7 +128,7 @@ class AdvancedCardPaymentMethod extends AbstractPaymentMethodType { * * @return SmartButtonInterface */ - private function smart_button(): SmartButtonInterface { + private function smart_button_instance(): SmartButtonInterface { if ( $this->smart_button instanceof SmartButtonInterface ) { return $this->smart_button; }