From 57620bb0ee53c0c538f4e431babb195bbeeedfb7 Mon Sep 17 00:00:00 2001 From: carmenmaymo Date: Thu, 4 May 2023 15:33:25 +0200 Subject: [PATCH 01/70] Improve error message on certain exceptions PCP-696 --- .../src/Endpoint/OrderEndpoint.php | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php b/modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php index 249f5a106..b80e1ac06 100644 --- a/modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php +++ b/modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php @@ -324,6 +324,7 @@ class OrderEndpoint { $json = json_decode( $response['body'] ); $status_code = (int) wp_remote_retrieve_response_code( $response ); if ( 201 !== $status_code ) { + $json = $this->addImprovedErrorMessage($json); $error = new PayPalApiException( $json, $status_code @@ -619,4 +620,35 @@ class OrderEndpoint { return $json; } + + /** + * @param $json + * @return Object + */ + public function addImprovedErrorMessage($json): Object + { + $improved_keys_messages = array( + 'PAYMENT_DENIED' => __('PayPal rejected the payment. Please reach out to the PayPal support for more information.', 'woocommerce-paypal-payments'), + 'TRANSACTION_REFUSED' => __('The transaction has been refused by the payment processor. Please reach out to the PayPal support for more information.', 'woocommerce-paypal-payments'), + 'DUPLICATE_INVOICE_ID' => __('The transaction has been refused because the Invoice ID already exists. Please create a new order or reach out to the store owner.', 'woocommerce-paypal-payments'), + 'PAYER_CANNOT_PAY' => __('There was a problem processing this transaction. Please reach out to the store owner.', 'woocommerce-paypal-payments'), + 'PAYEE_ACCOUNT_RESTRICTED' => __('There was a problem processing this transaction. Please reach out to the store owner.', 'woocommerce-paypal-payments'), + ); + $improved_errors = array_filter( + array_keys($improved_keys_messages), + function ($key) use ($json): bool { + foreach ($json->details as $detail) { + if (isset($detail->issue) && $detail->issue === $key) { + return true; + } + } + return false; + } + ); + if ($improved_errors) { + $improved_errors = array_values($improved_errors); + $json->message = $improved_keys_messages[$improved_errors[0]]; + } + return $json; + } } From cec69d64da917a73eb0b68841801a3ecb5bed5a5 Mon Sep 17 00:00:00 2001 From: carmenmaymo Date: Fri, 5 May 2023 11:41:07 +0200 Subject: [PATCH 02/70] Bail if json property not set & fix ci PCP-696 --- .../src/Endpoint/OrderEndpoint.php | 39 +++++++++++-------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php b/modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php index b80e1ac06..437c98714 100644 --- a/modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php +++ b/modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php @@ -324,7 +324,7 @@ class OrderEndpoint { $json = json_decode( $response['body'] ); $status_code = (int) wp_remote_retrieve_response_code( $response ); if ( 201 !== $status_code ) { - $json = $this->addImprovedErrorMessage($json); + $json = $this->add_improved_error_message( $json ); $error = new PayPalApiException( $json, $status_code @@ -404,6 +404,7 @@ class OrderEndpoint { if ( false !== strpos( $response['body'], ErrorResponse::ORDER_ALREADY_AUTHORIZED ) ) { return $this->order( $order->id() ); } + $json = $this->add_improved_error_message( $json ); $error = new PayPalApiException( $json, $status_code @@ -622,32 +623,36 @@ class OrderEndpoint { } /** - * @param $json + * Adds an improved error message to the response if the error detail is known. + * + * @param Object $json The response. * @return Object */ - public function addImprovedErrorMessage($json): Object - { + public function add_improved_error_message( $json ): object { + if ( ! isset( $json->details ) ) { + return $json; + } $improved_keys_messages = array( - 'PAYMENT_DENIED' => __('PayPal rejected the payment. Please reach out to the PayPal support for more information.', 'woocommerce-paypal-payments'), - 'TRANSACTION_REFUSED' => __('The transaction has been refused by the payment processor. Please reach out to the PayPal support for more information.', 'woocommerce-paypal-payments'), - 'DUPLICATE_INVOICE_ID' => __('The transaction has been refused because the Invoice ID already exists. Please create a new order or reach out to the store owner.', 'woocommerce-paypal-payments'), - 'PAYER_CANNOT_PAY' => __('There was a problem processing this transaction. Please reach out to the store owner.', 'woocommerce-paypal-payments'), - 'PAYEE_ACCOUNT_RESTRICTED' => __('There was a problem processing this transaction. Please reach out to the store owner.', 'woocommerce-paypal-payments'), + 'PAYMENT_DENIED' => __( 'PayPal rejected the payment. Please reach out to the PayPal support for more information.', 'woocommerce-paypal-payments' ), + 'TRANSACTION_REFUSED' => __( 'The transaction has been refused by the payment processor. Please reach out to the PayPal support for more information.', 'woocommerce-paypal-payments' ), + 'DUPLICATE_INVOICE_ID' => __( 'The transaction has been refused because the Invoice ID already exists. Please create a new order or reach out to the store owner.', 'woocommerce-paypal-payments' ), + 'PAYER_CANNOT_PAY' => __( 'There was a problem processing this transaction. Please reach out to the store owner.', 'woocommerce-paypal-payments' ), + 'PAYEE_ACCOUNT_RESTRICTED' => __( 'There was a problem processing this transaction. Please reach out to the store owner.', 'woocommerce-paypal-payments' ), ); - $improved_errors = array_filter( - array_keys($improved_keys_messages), - function ($key) use ($json): bool { - foreach ($json->details as $detail) { - if (isset($detail->issue) && $detail->issue === $key) { + $improved_errors = array_filter( + array_keys( $improved_keys_messages ), + function ( $key ) use ( $json ): bool { + foreach ( $json->details as $detail ) { + if ( isset( $detail->issue ) && $detail->issue === $key ) { return true; } } return false; } ); - if ($improved_errors) { - $improved_errors = array_values($improved_errors); - $json->message = $improved_keys_messages[$improved_errors[0]]; + if ( $improved_errors ) { + $improved_errors = array_values( $improved_errors ); + $json->message = $improved_keys_messages[ $improved_errors[0] ]; } return $json; } From 6f463cbce5bb529521c1d2d4b5433c5b91c70664 Mon Sep 17 00:00:00 2001 From: carmenmaymo Date: Fri, 5 May 2023 11:47:01 +0200 Subject: [PATCH 03/70] Fix psalm PCP-696 --- modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php b/modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php index 437c98714..1170e03ea 100644 --- a/modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php +++ b/modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php @@ -625,10 +625,10 @@ class OrderEndpoint { /** * Adds an improved error message to the response if the error detail is known. * - * @param Object $json The response. - * @return Object + * @param stdClass $json The response. + * @return stdClass */ - public function add_improved_error_message( $json ): object { + public function add_improved_error_message( stdClass $json ): stdClass { if ( ! isset( $json->details ) ) { return $json; } From fa0508d57244f4e1906dcd6d60ee76302d30fa17 Mon Sep 17 00:00:00 2001 From: carmenmaymo Date: Mon, 12 Jun 2023 08:46:45 +0200 Subject: [PATCH 04/70] Move improved error to ApiException --- .../src/Endpoint/OrderEndpoint.php | 36 --------------- .../src/Exception/PayPalApiException.php | 44 +++++++++++++++++-- 2 files changed, 41 insertions(+), 39 deletions(-) diff --git a/modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php b/modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php index 05e8ae7db..72163f35c 100644 --- a/modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php +++ b/modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php @@ -416,7 +416,6 @@ class OrderEndpoint { if ( false !== strpos( $response['body'], ErrorResponse::ORDER_ALREADY_AUTHORIZED ) ) { return $this->order( $order->id() ); } - $json = $this->add_improved_error_message( $json ); $error = new PayPalApiException( $json, $status_code @@ -635,39 +634,4 @@ class OrderEndpoint { return $json; } - - /** - * Adds an improved error message to the response if the error detail is known. - * - * @param stdClass $json The response. - * @return stdClass - */ - public function add_improved_error_message( stdClass $json ): stdClass { - if ( ! isset( $json->details ) ) { - return $json; - } - $improved_keys_messages = array( - 'PAYMENT_DENIED' => __( 'PayPal rejected the payment. Please reach out to the PayPal support for more information.', 'woocommerce-paypal-payments' ), - 'TRANSACTION_REFUSED' => __( 'The transaction has been refused by the payment processor. Please reach out to the PayPal support for more information.', 'woocommerce-paypal-payments' ), - 'DUPLICATE_INVOICE_ID' => __( 'The transaction has been refused because the Invoice ID already exists. Please create a new order or reach out to the store owner.', 'woocommerce-paypal-payments' ), - 'PAYER_CANNOT_PAY' => __( 'There was a problem processing this transaction. Please reach out to the store owner.', 'woocommerce-paypal-payments' ), - 'PAYEE_ACCOUNT_RESTRICTED' => __( 'There was a problem processing this transaction. Please reach out to the store owner.', 'woocommerce-paypal-payments' ), - ); - $improved_errors = array_filter( - array_keys( $improved_keys_messages ), - function ( $key ) use ( $json ): bool { - foreach ( $json->details as $detail ) { - if ( isset( $detail->issue ) && $detail->issue === $key ) { - return true; - } - } - return false; - } - ); - if ( $improved_errors ) { - $improved_errors = array_values( $improved_errors ); - $json->message = $improved_keys_messages[ $improved_errors[0] ]; - } - return $json; - } } diff --git a/modules/ppcp-api-client/src/Exception/PayPalApiException.php b/modules/ppcp-api-client/src/Exception/PayPalApiException.php index 36e6c1e57..4dfe3494d 100644 --- a/modules/ppcp-api-client/src/Exception/PayPalApiException.php +++ b/modules/ppcp-api-client/src/Exception/PayPalApiException.php @@ -9,6 +9,8 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\ApiClient\Exception; +use stdClass; + /** * Class PayPalApiException */ @@ -17,7 +19,7 @@ class PayPalApiException extends RuntimeException { /** * The JSON response object of PayPal. * - * @var \stdClass + * @var stdClass */ private $response; @@ -31,10 +33,10 @@ class PayPalApiException extends RuntimeException { /** * PayPalApiException constructor. * - * @param \stdClass|null $response The JSON object. + * @param stdClass|null $response The JSON object. * @param int $status_code The HTTP status code. */ - public function __construct( \stdClass $response = null, int $status_code = 0 ) { + public function __construct( stdClass $response = null, int $status_code = 0 ) { if ( is_null( $response ) ) { $response = new \stdClass(); } @@ -57,6 +59,7 @@ class PayPalApiException extends RuntimeException { if ( ! isset( $response->links ) || ! is_array( $response->links ) ) { $response->links = array(); } + $response = $this->add_improved_error_message( $response ); /** * The JSON response object. @@ -141,4 +144,39 @@ class PayPalApiException extends RuntimeException { return $details; } + + /** + * Adds an improved error message to the response if the error detail is known. + * + * @param stdClass $json The response. + * @return stdClass + */ + public function add_improved_error_message( stdClass $json ): stdClass { + if ( ! isset( $json->details ) ) { + return $json; + } + $improved_keys_messages = array( + 'PAYMENT_DENIED' => __( 'PayPal rejected the payment. Please reach out to the PayPal support for more information.', 'woocommerce-paypal-payments' ), + 'TRANSACTION_REFUSED' => __( 'The transaction has been refused by the payment processor. Please reach out to the PayPal support for more information.', 'woocommerce-paypal-payments' ), + 'DUPLICATE_INVOICE_ID' => __( 'The transaction has been refused because the Invoice ID already exists. Please create a new order or reach out to the store owner.', 'woocommerce-paypal-payments' ), + 'PAYER_CANNOT_PAY' => __( 'There was a problem processing this transaction. Please reach out to the store owner.', 'woocommerce-paypal-payments' ), + 'PAYEE_ACCOUNT_RESTRICTED' => __( 'There was a problem processing this transaction. Please reach out to the store owner.', 'woocommerce-paypal-payments' ), + ); + $improved_errors = array_filter( + array_keys( $improved_keys_messages ), + function ( $key ) use ( $json ): bool { + foreach ( $json->details as $detail ) { + if ( isset( $detail->issue ) && $detail->issue === $key ) { + return true; + } + } + return false; + } + ); + if ( $improved_errors ) { + $improved_errors = array_values( $improved_errors ); + $json->message = $improved_keys_messages[ $improved_errors[0] ]; + } + return $json; + } } From f6384efdf18610e08815c6ff5079a88bf6bfe7c6 Mon Sep 17 00:00:00 2001 From: carmenmaymo Date: Mon, 12 Jun 2023 09:17:20 +0200 Subject: [PATCH 05/70] Remove old call to method --- modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php b/modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php index 72163f35c..cf22e8106 100644 --- a/modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php +++ b/modules/ppcp-api-client/src/Endpoint/OrderEndpoint.php @@ -336,7 +336,6 @@ class OrderEndpoint { $json = json_decode( $response['body'] ); $status_code = (int) wp_remote_retrieve_response_code( $response ); if ( 201 !== $status_code ) { - $json = $this->add_improved_error_message( $json ); $error = new PayPalApiException( $json, $status_code From 54fdc6b3ec7ef31a504bf3d893ca71fb960f6b7e Mon Sep 17 00:00:00 2001 From: carmenmaymo Date: Mon, 12 Jun 2023 09:17:31 +0200 Subject: [PATCH 06/70] Fix CS --- modules/ppcp-api-client/src/Exception/PayPalApiException.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-api-client/src/Exception/PayPalApiException.php b/modules/ppcp-api-client/src/Exception/PayPalApiException.php index 4dfe3494d..5bb1009dd 100644 --- a/modules/ppcp-api-client/src/Exception/PayPalApiException.php +++ b/modules/ppcp-api-client/src/Exception/PayPalApiException.php @@ -34,7 +34,7 @@ class PayPalApiException extends RuntimeException { * PayPalApiException constructor. * * @param stdClass|null $response The JSON object. - * @param int $status_code The HTTP status code. + * @param int $status_code The HTTP status code. */ public function __construct( stdClass $response = null, int $status_code = 0 ) { if ( is_null( $response ) ) { @@ -59,7 +59,7 @@ class PayPalApiException extends RuntimeException { if ( ! isset( $response->links ) || ! is_array( $response->links ) ) { $response->links = array(); } - $response = $this->add_improved_error_message( $response ); + $response = $this->add_improved_error_message( $response ); /** * The JSON response object. From d22cdba4a4f3898ba462f4d651f8bac515b5dafc Mon Sep 17 00:00:00 2001 From: carmenmaymo Date: Mon, 19 Jun 2023 08:44:11 +0200 Subject: [PATCH 07/70] Return message without changing the response --- .../src/Exception/PayPalApiException.php | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/modules/ppcp-api-client/src/Exception/PayPalApiException.php b/modules/ppcp-api-client/src/Exception/PayPalApiException.php index 5bb1009dd..e11e71bb6 100644 --- a/modules/ppcp-api-client/src/Exception/PayPalApiException.php +++ b/modules/ppcp-api-client/src/Exception/PayPalApiException.php @@ -59,7 +59,6 @@ class PayPalApiException extends RuntimeException { if ( ! isset( $response->links ) || ! is_array( $response->links ) ) { $response->links = array(); } - $response = $this->add_improved_error_message( $response ); /** * The JSON response object. @@ -68,7 +67,7 @@ class PayPalApiException extends RuntimeException { */ $this->response = $response; $this->status_code = $status_code; - $message = $response->message; + $message = $this->get_customer_friendly_message($response); if ( $response->name ) { $message = '[' . $response->name . '] ' . $message; } @@ -146,14 +145,14 @@ class PayPalApiException extends RuntimeException { } /** - * Adds an improved error message to the response if the error detail is known. + * Returns a friendly message if the error detail is known. * * @param stdClass $json The response. - * @return stdClass + * @return string */ - public function add_improved_error_message( stdClass $json ): stdClass { - if ( ! isset( $json->details ) ) { - return $json; + public function get_customer_friendly_message( stdClass $json ): string { + if ( empty( $json->details ) ) { + return $json->message; } $improved_keys_messages = array( 'PAYMENT_DENIED' => __( 'PayPal rejected the payment. Please reach out to the PayPal support for more information.', 'woocommerce-paypal-payments' ), @@ -175,8 +174,8 @@ class PayPalApiException extends RuntimeException { ); if ( $improved_errors ) { $improved_errors = array_values( $improved_errors ); - $json->message = $improved_keys_messages[ $improved_errors[0] ]; + return $improved_keys_messages[ $improved_errors[0] ]; } - return $json; + return $json->message; } } From 1540c591ab5eed60fefaad36aa4a3a1c374fd381 Mon Sep 17 00:00:00 2001 From: carmenmaymo Date: Mon, 19 Jun 2023 08:53:24 +0200 Subject: [PATCH 08/70] Fix cs --- modules/ppcp-api-client/src/Exception/PayPalApiException.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-api-client/src/Exception/PayPalApiException.php b/modules/ppcp-api-client/src/Exception/PayPalApiException.php index e11e71bb6..97ac91c5c 100644 --- a/modules/ppcp-api-client/src/Exception/PayPalApiException.php +++ b/modules/ppcp-api-client/src/Exception/PayPalApiException.php @@ -67,7 +67,7 @@ class PayPalApiException extends RuntimeException { */ $this->response = $response; $this->status_code = $status_code; - $message = $this->get_customer_friendly_message($response); + $message = $this->get_customer_friendly_message( $response ); if ( $response->name ) { $message = '[' . $response->name . '] ' . $message; } From 5352e9e38a535ca74b78df5e4bcb6e8d4472dcac Mon Sep 17 00:00:00 2001 From: carmenmaymo Date: Mon, 19 Jun 2023 09:19:33 +0200 Subject: [PATCH 09/70] Add AGREEMENT_ALREADY_CANCELLED error message --- modules/ppcp-api-client/src/Exception/PayPalApiException.php | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/ppcp-api-client/src/Exception/PayPalApiException.php b/modules/ppcp-api-client/src/Exception/PayPalApiException.php index 97ac91c5c..9db633729 100644 --- a/modules/ppcp-api-client/src/Exception/PayPalApiException.php +++ b/modules/ppcp-api-client/src/Exception/PayPalApiException.php @@ -160,6 +160,7 @@ class PayPalApiException extends RuntimeException { 'DUPLICATE_INVOICE_ID' => __( 'The transaction has been refused because the Invoice ID already exists. Please create a new order or reach out to the store owner.', 'woocommerce-paypal-payments' ), 'PAYER_CANNOT_PAY' => __( 'There was a problem processing this transaction. Please reach out to the store owner.', 'woocommerce-paypal-payments' ), 'PAYEE_ACCOUNT_RESTRICTED' => __( 'There was a problem processing this transaction. Please reach out to the store owner.', 'woocommerce-paypal-payments' ), + 'AGREEMENT_ALREADY_CANCELLED' => __( 'The requested agreement is already canceled. Please reach out to the PayPal support for more information.', 'woocommerce-paypal-payments' ), ); $improved_errors = array_filter( array_keys( $improved_keys_messages ), From fc7b8022e8bf31d8f65caadb638531c6b3f2b127 Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Tue, 6 Feb 2024 12:01:38 +0000 Subject: [PATCH 10/70] Add AXO module --- modules/ppcp-axo/.babelrc | 14 ++++++++++ modules/ppcp-axo/.gitignore | 3 ++ modules/ppcp-axo/composer.json | 17 +++++++++++ modules/ppcp-axo/extensions.php | 14 ++++++++++ modules/ppcp-axo/module.php | 16 +++++++++++ modules/ppcp-axo/package.json | 34 ++++++++++++++++++++++ modules/ppcp-axo/services.php | 14 ++++++++++ modules/ppcp-axo/src/AxoModule.php | 45 ++++++++++++++++++++++++++++++ modules/ppcp-axo/webpack.config.js | 37 ++++++++++++++++++++++++ 9 files changed, 194 insertions(+) create mode 100644 modules/ppcp-axo/.babelrc create mode 100644 modules/ppcp-axo/.gitignore create mode 100644 modules/ppcp-axo/composer.json create mode 100644 modules/ppcp-axo/extensions.php create mode 100644 modules/ppcp-axo/module.php create mode 100644 modules/ppcp-axo/package.json create mode 100644 modules/ppcp-axo/services.php create mode 100644 modules/ppcp-axo/src/AxoModule.php create mode 100644 modules/ppcp-axo/webpack.config.js diff --git a/modules/ppcp-axo/.babelrc b/modules/ppcp-axo/.babelrc new file mode 100644 index 000000000..822778e6c --- /dev/null +++ b/modules/ppcp-axo/.babelrc @@ -0,0 +1,14 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "useBuiltIns": "usage", + "corejs": "3.25.0" + } + ], + [ + "@babel/preset-react" + ] + ] +} diff --git a/modules/ppcp-axo/.gitignore b/modules/ppcp-axo/.gitignore new file mode 100644 index 000000000..0bd2b9f58 --- /dev/null +++ b/modules/ppcp-axo/.gitignore @@ -0,0 +1,3 @@ +node_modules +assets/js +assets/css diff --git a/modules/ppcp-axo/composer.json b/modules/ppcp-axo/composer.json new file mode 100644 index 000000000..352c30523 --- /dev/null +++ b/modules/ppcp-axo/composer.json @@ -0,0 +1,17 @@ +{ + "name": "woocommerce/ppcp-axo", + "type": "dhii-mod", + "description": "Axo module for PPCP", + "license": "GPL-2.0", + "require": { + "php": "^7.2 | ^8.0", + "dhii/module-interface": "^0.3.0-alpha1" + }, + "autoload": { + "psr-4": { + "WooCommerce\\PayPalCommerce\\Axo\\": "src" + } + }, + "minimum-stability": "dev", + "prefer-stable": true +} diff --git a/modules/ppcp-axo/extensions.php b/modules/ppcp-axo/extensions.php new file mode 100644 index 000000000..2240e1f38 --- /dev/null +++ b/modules/ppcp-axo/extensions.php @@ -0,0 +1,14 @@ + 0.5%", + "Safari >= 8", + "Chrome >= 41", + "Firefox >= 43", + "Edge >= 14" + ], + "dependencies": { + "@paypal/paypal-js": "^6.0.0", + "core-js": "^3.25.0" + }, + "devDependencies": { + "@babel/core": "^7.19", + "@babel/preset-env": "^7.19", + "@babel/preset-react": "^7.18.6", + "@woocommerce/dependency-extraction-webpack-plugin": "^2.2.0", + "babel-loader": "^8.2", + "cross-env": "^7.0.3", + "file-loader": "^6.2.0", + "sass": "^1.42.1", + "sass-loader": "^12.1.0", + "webpack": "^5.76", + "webpack-cli": "^4.10" + }, + "scripts": { + "build": "cross-env BABEL_ENV=default NODE_ENV=production webpack", + "watch": "cross-env BABEL_ENV=default NODE_ENV=production webpack --watch", + "dev": "cross-env BABEL_ENV=default webpack --watch" + } +} diff --git a/modules/ppcp-axo/services.php b/modules/ppcp-axo/services.php new file mode 100644 index 000000000..d8e0b6fd3 --- /dev/null +++ b/modules/ppcp-axo/services.php @@ -0,0 +1,14 @@ + Date: Thu, 8 Feb 2024 14:37:56 +0000 Subject: [PATCH 11/70] Axo gateway skeleton. --- modules.php | 7 + .../src/Authentication/PayPalBearer.php | 2 +- .../src/Authentication/UserIdToken.php | 24 +- modules/ppcp-axo/extensions.php | 112 + modules/ppcp-axo/resources/css/styles.scss | 1 + modules/ppcp-axo/resources/js/boot.js | 207 ++ modules/ppcp-axo/services.php | 13 + modules/ppcp-axo/src/AxoModule.php | 69 + modules/ppcp-axo/src/Gateway/AxoGateway.php | 136 + .../src/Helper/PropertiesDictionary.php | 41 + modules/ppcp-axo/webpack.config.js | 2 + modules/ppcp-axo/yarn.lock | 2262 +++++++++++++++++ .../js/modules/Helper/ScriptLoading.js | 2 + package.json | 3 + 14 files changed, 2872 insertions(+), 9 deletions(-) create mode 100644 modules/ppcp-axo/resources/css/styles.scss create mode 100644 modules/ppcp-axo/resources/js/boot.js create mode 100644 modules/ppcp-axo/src/Gateway/AxoGateway.php create mode 100644 modules/ppcp-axo/src/Helper/PropertiesDictionary.php create mode 100644 modules/ppcp-axo/yarn.lock diff --git a/modules.php b/modules.php index 75b9593b0..7626018b2 100644 --- a/modules.php +++ b/modules.php @@ -71,5 +71,12 @@ return function ( string $root_dir ): iterable { $modules[] = ( require "$modules_dir/ppcp-paylater-block/module.php" )(); } + if ( apply_filters( + 'woocommerce.feature-flags.woocommerce_paypal_payments.axo_enabled', + getenv( 'PCP_AXO_ENABLED' ) === '1' + ) ) { + $modules[] = ( require "$modules_dir/ppcp-axo/module.php" )(); + } + return $modules; }; diff --git a/modules/ppcp-api-client/src/Authentication/PayPalBearer.php b/modules/ppcp-api-client/src/Authentication/PayPalBearer.php index 25af18575..0ac4f60ad 100644 --- a/modules/ppcp-api-client/src/Authentication/PayPalBearer.php +++ b/modules/ppcp-api-client/src/Authentication/PayPalBearer.php @@ -118,7 +118,7 @@ class PayPalBearer implements Bearer { private function newBearer(): Token { $key = $this->settings->has( 'client_id' ) && $this->settings->get( 'client_id' ) ? $this->settings->get( 'client_id' ) : $this->key; $secret = $this->settings->has( 'client_secret' ) && $this->settings->get( 'client_secret' ) ? $this->settings->get( 'client_secret' ) : $this->secret; - $url = trailingslashit( $this->host ) . 'v1/oauth2/token?grant_type=client_credentials'; + $url = trailingslashit( $this->host ) . 'v1/oauth2/token?grant_type=client_credentials&intent=sdk_init'; $args = array( 'method' => 'POST', diff --git a/modules/ppcp-api-client/src/Authentication/UserIdToken.php b/modules/ppcp-api-client/src/Authentication/UserIdToken.php index cae8cb58a..887440208 100644 --- a/modules/ppcp-api-client/src/Authentication/UserIdToken.php +++ b/modules/ppcp-api-client/src/Authentication/UserIdToken.php @@ -11,6 +11,7 @@ use Psr\Log\LoggerInterface; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\RequestTrait; use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException; use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; +use WooCommerce\PayPalCommerce\PPCP; use WP_Error; /** @@ -71,20 +72,27 @@ class UserIdToken { public function id_token( string $target_customer_id = '' ): string { $bearer = $this->bearer->bearer(); - $url = trailingslashit( $this->host ) . 'v1/oauth2/token?grant_type=client_credentials&response_type=id_token'; + $url = trailingslashit( $this->host ) . 'v1/oauth2/token?grant_type=client_credentials&response_type=id_token&intent=sdk_init'; if ( $target_customer_id ) { - $url = add_query_arg( - array( - 'target_customer_id' => $target_customer_id, - ), - $url - ); +// $url = add_query_arg( +// array( +// 'target_customer_id' => $target_customer_id, +// ), +// $url +// ); } + // TODO fix this to use Bearer instead of Basic auth: + $settings = PPCP::container()->get( 'wcgateway.settings' ); + $key = $settings->has( 'client_id' ) && $settings->get( 'client_id' ) ? $settings->get( 'client_id' ) : null; + $secret = $settings->has( 'client_secret' ) && $settings->get( 'client_secret' ) ? $settings->get( 'client_secret' ) : null; + $args = array( 'method' => 'POST', 'headers' => array( - 'Authorization' => 'Bearer ' . $bearer->token(), +// 'Authorization' => 'Bearer ' . $bearer->token(), + 'Authorization' => 'Basic ' . base64_encode( "$key:$secret" ), + 'Content-Type' => 'application/x-www-form-urlencoded', ), ); diff --git a/modules/ppcp-axo/extensions.php b/modules/ppcp-axo/extensions.php index 2240e1f38..0d0b7665c 100644 --- a/modules/ppcp-axo/extensions.php +++ b/modules/ppcp-axo/extensions.php @@ -9,6 +9,118 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Axo; +use WooCommerce\PayPalCommerce\Axo\Helper\PropertiesDictionary; +use WooCommerce\PayPalCommerce\Onboarding\State; +use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; +use WooCommerce\PayPalCommerce\WcGateway\Helper\DisplayManager; +use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; + return array( + 'wcgateway.settings.fields' => function ( ContainerInterface $container, array $fields ): array { + + $insert_after = function( array $array, string $key, array $new ): array { + $keys = array_keys( $array ); + $index = array_search( $key, $keys, true ); + $pos = false === $index ? count( $array ) : $index + 1; + + return array_merge( array_slice( $array, 0, $pos ), $new, array_slice( $array, $pos ) ); + }; + + $display_manager = $container->get( 'wcgateway.display-manager' ); + assert( $display_manager instanceof DisplayManager ); + + // Standard Payments tab fields. + return $insert_after( + $fields, + 'vault_enabled', + array( + 'axo_enabled' => array( + 'title' => __( 'Fastlane', 'woocommerce-paypal-payments' ), + 'type' => 'checkbox', + 'label' => __( 'Enable Fastlane Checkout', 'woocommerce-paypal-payments' ) + . '

' + . sprintf( + // translators: %1$s and %2$s are the opening and closing of HTML tag. + __( 'Buyers can use %1$sFastlane%2$s to make payments.', 'woocommerce-paypal-payments' ), + '', + '' + ) + . '

', + 'default' => 'yes', + 'screens' => array( State::STATE_ONBOARDED ), + 'gateway' => 'paypal', + 'requirements' => array(), + 'custom_attributes' => array( + 'data-ppcp-display' => wp_json_encode( + array( + $display_manager + ->rule() + ->condition_element( 'axo_enabled', '1' ) + ->action_visible( 'axo_gateway_title' ) + ->action_visible( 'axo_address_widget' ) + ->action_visible( 'axo_payment_widget' ) + ->to_array(), + ) + ), + ), + ), + 'axo_gateway_title' => array( + 'title' => __( 'Gateway Title', 'woocommerce-paypal-payments' ), + 'type' => 'text', + 'classes' => array( 'ppcp-field-indent' ), + 'desc_tip' => true, + 'description' => __( + 'This controls the title of the Fastlane gateway the user sees on checkout.', + 'woocommerce-paypal-payments' + ), + 'default' => __( + 'Fastlane Debit & Credit Cards', + 'woocommerce-paypal-payments' + ), + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array(), + 'gateway' => 'paypal', + ), + 'axo_address_widget' => array( + 'title' => __( 'Address Widget', 'woocommerce-paypal-payments' ), + 'type' => 'select', + 'desc_tip' => true, + 'description' => __( + 'This controls if the Hosted Address Widget should be used.', + 'woocommerce-paypal-payments' + ), + 'classes' => array( 'ppcp-field-indent' ), + 'class' => array(), + 'input_class' => array( 'wc-enhanced-select' ), + 'default' => 'pay', + 'options' => PropertiesDictionary::address_widget_options(), + 'screens' => array( State::STATE_ONBOARDED ), + 'gateway' => 'paypal', + 'requirements' => array(), + ), + 'axo_payment_widget' => array( + 'title' => __( 'Payment Widget', 'woocommerce-paypal-payments' ), + 'type' => 'select', + 'desc_tip' => true, + 'description' => __( + 'This controls if the Hosted Payment Widget should be used.', + 'woocommerce-paypal-payments' + ), + 'label' => '', + 'input_class' => array( 'wc-enhanced-select' ), + 'classes' => array( 'ppcp-field-indent' ), + 'class' => array(), + 'default' => 'black', + 'options' => PropertiesDictionary::payment_widget_options(), + 'screens' => array( State::STATE_ONBOARDED ), + 'gateway' => 'paypal', + 'requirements' => array(), + ), + ) + ); + }, + ); diff --git a/modules/ppcp-axo/resources/css/styles.scss b/modules/ppcp-axo/resources/css/styles.scss new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/modules/ppcp-axo/resources/css/styles.scss @@ -0,0 +1 @@ + diff --git a/modules/ppcp-axo/resources/js/boot.js b/modules/ppcp-axo/resources/js/boot.js new file mode 100644 index 000000000..2dc6735e5 --- /dev/null +++ b/modules/ppcp-axo/resources/js/boot.js @@ -0,0 +1,207 @@ + +let initialized = false; + +function log(message) { + console.log('Log: ', message); + + // let div = document.createElement('div'); + // div.style.cssText = 'margin-bottom: 4px;'; + // div.textContent = message; + // document.getElementById('log-container').appendChild(div); +} + +const init = () => { + if (initialized) { + return; + } + initialized = true; + + (async function () { + // sets the SDK to run in sandbox mode (Remove for production) + window.localStorage.setItem('axoEnv', 'sandbox'); + + //specify global styles here + const styles = { + root: { + backgroundColorPrimary: "#ffffff" + } + } + + const locale = 'en_us'; + + // instantiates the Connect module + const connect = await window.paypal.Connect({ + locale, + styles + }); + + console.log('connect', connect); + + // Specifying the locale if necessary + connect.setLocale('en_us'); + + const { + identity, + profile, + ConnectCardComponent, + ConnectWatermarkComponent + } = connect; + + console.log( + '[identity, profile, ConnectCardComponent]', + identity, + profile, + ConnectCardComponent + ); + + //-------- + const emailInput = document.querySelector('#billing_email_field').querySelector('input'); + + jQuery(emailInput).after(` +
+ `); + + jQuery('.payment_method_ppcp-axo-gateway').append(` +
+ `); + + const connectWatermarkComponent = ConnectWatermarkComponent({ + includeAdditionalInfo: true + }).render('#watermark-container'); + + //-------- + + const emailUpdated = async () => { + log('Email changed: ' + emailInput.value); + + if (!emailInput.checkValidity()) { + log('The email address is not valid.'); + return; + } + + const { customerContextId } = await identity.lookupCustomerByEmail(emailInput.value); + + if (customerContextId) { + // Email is associated with a Connect profile or aPayPal member + // Authenticate the customer to get access to their profile + log('Email is associated with a Connect profile or a PayPal member'); + } else { + // No profile found with this email address. + // This is a guest customer. + log('No profile found with this email address.'); + + const fields = { + phoneNumber: { + prefill: "1234567890" + }, + + cardholderName: {} // optionally pass this to show the card holder name + }; + + jQuery("#payment-container").css({ + 'padding': '1rem 0', + 'background-color': '#ffffff', + }); + + const connectCardComponent = await connect + .ConnectCardComponent({fields}) + .render("#payment-container"); + + const submitButton = document.getElementById('submit-button'); + + // event listener when the customer clicks to place the order + submitButton.addEventListener("click", async ()=> { + const { nonce } = await connectCardComponent.tokenize({ + name: { + fullName: "John Doe" + }, + billingAddress: { + addressLine1: "2211 North 1st St", + adminArea1: "San Jose", + adminArea2: "CA", + postalCode: "95131", + countryCode: "US" + } + }); + + // Send the nonce and previously captured device data to server to complete checkout + log('nonce: ' + nonce); + + // fetch('submit.php', { + // method: 'POST', + // headers: { + // 'Content-Type': 'application/json', + // }, + // body: JSON.stringify({ + // nonce: nonce + // }), + // }) + // + // .then(response => { + // if (!response.ok) { + // throw new Error('Network response was not ok'); + // } + // return response.json(); + // }) + // .then(data => { + // console.log('Submit response', data); + // log(JSON.stringify(data)); + // }) + // .catch(error => { + // console.error('There has been a problem with your fetch operation:', error); + // }); + + }); + } + } + + emailInput.addEventListener("change", async ()=> { + emailUpdated(); + }); + + if (emailInput.value) { + emailUpdated(); + } + + })(); + +} + +const bootstrap = () => { + + + jQuery(document).on('change', '#payment_method_ppcp-axo-gateway', (ev) => { + if(ev.target.checked) { + let emailRow = document.querySelector('#billing_email_field'); + jQuery(emailRow.parentNode).prepend(emailRow); + emailRow.querySelector('input').focus(); + + init(); + } else { + console.log('Checkbox is not checked.'); + // Additional actions when the checkbox is unchecked + } + }); + + let gatewaysRenderedInterval = setInterval(() => { + console.log('not rendered'); + if (document.querySelector('.wc_payment_methods')) { + console.log('YES rendered'); + clearInterval(gatewaysRenderedInterval); + jQuery('#payment_method_ppcp-axo-gateway').trigger('change'); + } + }, 100); + +} + +document.addEventListener( + 'DOMContentLoaded', + () => { + if (!typeof (PayPalCommerceGateway)) { + console.error('PayPal button could not be configured.'); + return; + } + + bootstrap(); + }, +); diff --git a/modules/ppcp-axo/services.php b/modules/ppcp-axo/services.php index d8e0b6fd3..76c87944c 100644 --- a/modules/ppcp-axo/services.php +++ b/modules/ppcp-axo/services.php @@ -9,6 +9,19 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Axo; +use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; + return array( + 'axo.url' => static function ( ContainerInterface $container ): string { + $path = realpath( __FILE__ ); + if ( false === $path ) { + return ''; + } + return plugins_url( + '/modules/ppcp-axo/', + dirname( $path, 3 ) . '/woocommerce-paypal-payments.php' + ); + }, + ); diff --git a/modules/ppcp-axo/src/AxoModule.php b/modules/ppcp-axo/src/AxoModule.php index 8576f5fde..5c45e12a4 100644 --- a/modules/ppcp-axo/src/AxoModule.php +++ b/modules/ppcp-axo/src/AxoModule.php @@ -9,6 +9,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Axo; +use WooCommerce\PayPalCommerce\Axo\Gateway\AxoGateway; use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider; use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface; use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface; @@ -33,6 +34,74 @@ class AxoModule implements ModuleInterface { */ public function run( ContainerInterface $c ): void { + add_filter( + 'woocommerce_payment_gateways', + function ( $methods ): array { + $methods[] = new AxoGateway(); + return $methods; + }, + 1, + 9 + ); + + /** + * Param types removed to avoid third-party issues. + * + * @psalm-suppress MissingClosureParamType + */ + add_filter( + 'woocommerce_paypal_payments_sdk_components_hook', + function( $components ) { + $components[] = 'connect'; + return $components; + } + ); + + add_action( + 'init', + static function () use ( $c ) { + + // Enqueue frontend scripts. + add_action( + 'wp_enqueue_scripts', + static function () use ( $c ) { + $module_url = $c->get( 'axo.url' ); + $version = '1'; + + // Register styles. + wp_register_style( + 'wc-ppcp-axo', + untrailingslashit( $module_url ) . '/assets/css/styles.css', + array(), + $version + ); + wp_enqueue_style( 'wc-ppcp-axo' ); + + // Register scripts. + wp_register_script( + 'wc-ppcp-axo', + untrailingslashit( $module_url ) . '/assets/js/boot.js', + array(), + $version, + true + ); + wp_enqueue_script( 'wc-ppcp-axo' ); + + wp_localize_script( + 'wc-ppcp-axo', + 'wc_ppcp_axo', + array( + // TODO + ) + ); + + } + ); + + }, + 1 + ); + } /** diff --git a/modules/ppcp-axo/src/Gateway/AxoGateway.php b/modules/ppcp-axo/src/Gateway/AxoGateway.php new file mode 100644 index 000000000..0aa0d2cc9 --- /dev/null +++ b/modules/ppcp-axo/src/Gateway/AxoGateway.php @@ -0,0 +1,136 @@ +id = self::ID; + + $this->method_title = __( 'Fastlane Debit & Credit Cards', 'woocommerce-paypal-payments' ); + $this->method_description = __( 'Fastlane Debit & Credit Cards', 'woocommerce-paypal-payments' ); + + $this->title = $this->get_option( 'title', $this->method_title ); + $this->description = $this->get_option( 'description', __( '', 'woocommerce-paypal-payments' ) ); + + $this->init_form_fields(); + $this->init_settings(); + + add_action( + 'woocommerce_update_options_payment_gateways_' . $this->id, + array( + $this, + 'process_admin_options', + ) + ); + +// $this->order_endpoint = $order_endpoint; +// $this->purchase_unit_factory = $purchase_unit_factory; +// $this->shipping_preference_factory = $shipping_preference_factory; +// $this->module_url = $module_url; +// $this->logger = $logger; + +// $this->icon = esc_url( $this->module_url ) . 'assets/images/axo.svg'; // TODO +// $this->environment = $environment; + + $this->update_option( 'enabled', 'yes' ); // TODO : depend on settings + } + + /** + * Initialize the form fields. + */ + public function init_form_fields() { + $this->form_fields = array( + 'enabled' => array( + 'title' => __( 'Enable/Disable', 'woocommerce-paypal-payments' ), + 'type' => 'checkbox', + 'label' => __( 'AXO', 'woocommerce-paypal-payments' ), + 'default' => 'no', + 'desc_tip' => true, + 'description' => __( 'Enable/Disable AXO payment gateway.', 'woocommerce-paypal-payments' ), + ), + ); + } + + /** + * Processes the order. + * + * @param int $order_id The WC order ID. + * @return array + */ + public function process_payment( $order_id ) { + $wc_order = wc_get_order( $order_id ); + + // TODO ... + + WC()->cart->empty_cart(); + + $result = array( + 'result' => 'success', + 'redirect' => $this->get_return_url( $wc_order ), + ); + + return $result; + } + +// public function is_available() +// { +// return $this->is_enabled(); // parent::is_available(); +// } +// +// /** +// * Returns if the gateway is enabled. +// * +// * @return bool +// */ +// private function is_enabled(): bool { +// return true; +// //return $this->config->has( 'axo_enabled' ) && $this->config->get( 'axo_enabled' ); // TODO +// } + + /** + * Returns the icons of the gateway. + * + * @return string + */ + public function get_icon() { + $images = array(); + + $cards = array( + array('title' => 'Visa', 'file' => 'visa-dark.svg'), + array('title' => 'MasterCard', 'file' => 'mastercard-dark.svg'), + array('title' => 'American Express', 'file' => 'amex.svg'), + array('title' => 'Discover', 'file' => 'discover.svg'), + ); + + foreach ($cards as $card) { + $images[] = ' '; + } + + return '
' . implode( '', $images ) . '
'; + } + +} diff --git a/modules/ppcp-axo/src/Helper/PropertiesDictionary.php b/modules/ppcp-axo/src/Helper/PropertiesDictionary.php new file mode 100644 index 000000000..e1fc70ece --- /dev/null +++ b/modules/ppcp-axo/src/Helper/PropertiesDictionary.php @@ -0,0 +1,41 @@ + __( 'Render address options.', 'woocommerce-paypal-payments' ), + 'use_widget' => __( 'Use address widget.', 'woocommerce-paypal-payments' ), + ); + } + + /** + * Returns the possible list of possible address widget options. + * + * @return array + */ + public static function payment_widget_options(): array { + return array( + 'render' => __( 'Render payment options.', 'woocommerce-paypal-payments' ), + 'use_widget' => __( 'Use payment widget.', 'woocommerce-paypal-payments' ), + ); + } + +} diff --git a/modules/ppcp-axo/webpack.config.js b/modules/ppcp-axo/webpack.config.js index f6190a27e..d54838da6 100644 --- a/modules/ppcp-axo/webpack.config.js +++ b/modules/ppcp-axo/webpack.config.js @@ -9,6 +9,8 @@ module.exports = { target: 'web', plugins: [ new DependencyExtractionWebpackPlugin() ], entry: { + 'boot': path.resolve('./resources/js/boot.js'), + "styles": path.resolve('./resources/css/styles.scss') }, output: { path: path.resolve(__dirname, 'assets/'), diff --git a/modules/ppcp-axo/yarn.lock b/modules/ppcp-axo/yarn.lock new file mode 100644 index 000000000..7353faf0c --- /dev/null +++ b/modules/ppcp-axo/yarn.lock @@ -0,0 +1,2262 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@ampproject/remapping@^2.2.0": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" + integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@babel/code-frame@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.23.5.tgz#9009b69a8c602293476ad598ff53e4562e15c244" + integrity sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA== + dependencies: + "@babel/highlight" "^7.23.4" + chalk "^2.4.2" + +"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.23.3", "@babel/compat-data@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.23.5.tgz#ffb878728bb6bdcb6f4510aa51b1be9afb8cfd98" + integrity sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw== + +"@babel/core@^7.19": + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.9.tgz#b028820718000f267870822fec434820e9b1e4d1" + integrity sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.23.5" + "@babel/generator" "^7.23.6" + "@babel/helper-compilation-targets" "^7.23.6" + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helpers" "^7.23.9" + "@babel/parser" "^7.23.9" + "@babel/template" "^7.23.9" + "@babel/traverse" "^7.23.9" + "@babel/types" "^7.23.9" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + +"@babel/generator@^7.23.6": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.6.tgz#9e1fca4811c77a10580d17d26b57b036133f3c2e" + integrity sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw== + dependencies: + "@babel/types" "^7.23.6" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + +"@babel/helper-annotate-as-pure@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" + integrity sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-builder-binary-assignment-operator-visitor@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz#5426b109cf3ad47b91120f8328d8ab1be8b0b956" + integrity sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw== + dependencies: + "@babel/types" "^7.22.15" + +"@babel/helper-compilation-targets@^7.22.15", "@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.23.6": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz#4d79069b16cbcf1461289eccfbbd81501ae39991" + integrity sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ== + dependencies: + "@babel/compat-data" "^7.23.5" + "@babel/helper-validator-option" "^7.23.5" + browserslist "^4.22.2" + lru-cache "^5.1.1" + semver "^6.3.1" + +"@babel/helper-create-class-features-plugin@^7.22.15": + version "7.23.10" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.10.tgz#25d55fafbaea31fd0e723820bb6cc3df72edf7ea" + integrity sha512-2XpP2XhkXzgxecPNEEK8Vz8Asj9aRxt08oKOqtiZoqV2UGZ5T+EkyP9sXQ9nwMxBIG34a7jmasVqoMop7VdPUw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-member-expression-to-functions" "^7.23.0" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.20" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + semver "^6.3.1" + +"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.22.15", "@babel/helper-create-regexp-features-plugin@^7.22.5": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz#5ee90093914ea09639b01c711db0d6775e558be1" + integrity sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + regexpu-core "^5.3.1" + semver "^6.3.1" + +"@babel/helper-define-polyfill-provider@^0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.5.0.tgz#465805b7361f461e86c680f1de21eaf88c25901b" + integrity sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q== + dependencies: + "@babel/helper-compilation-targets" "^7.22.6" + "@babel/helper-plugin-utils" "^7.22.5" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + +"@babel/helper-environment-visitor@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== + +"@babel/helper-function-name@^7.22.5", "@babel/helper-function-name@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" + integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== + dependencies: + "@babel/template" "^7.22.15" + "@babel/types" "^7.23.0" + +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-member-expression-to-functions@^7.22.15", "@babel/helper-member-expression-to-functions@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz#9263e88cc5e41d39ec18c9a3e0eced59a3e7d366" + integrity sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA== + dependencies: + "@babel/types" "^7.23.0" + +"@babel/helper-module-imports@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0" + integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w== + dependencies: + "@babel/types" "^7.22.15" + +"@babel/helper-module-transforms@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz#d7d12c3c5d30af5b3c0fcab2a6d5217773e2d0f1" + integrity sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-module-imports" "^7.22.15" + "@babel/helper-simple-access" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/helper-validator-identifier" "^7.22.20" + +"@babel/helper-optimise-call-expression@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz#f21531a9ccbff644fdd156b4077c16ff0c3f609e" + integrity sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" + integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== + +"@babel/helper-remap-async-to-generator@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz#7b68e1cb4fa964d2996fd063723fb48eca8498e0" + integrity sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-wrap-function" "^7.22.20" + +"@babel/helper-replace-supers@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz#e37d367123ca98fe455a9887734ed2e16eb7a793" + integrity sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-member-expression-to-functions" "^7.22.15" + "@babel/helper-optimise-call-expression" "^7.22.5" + +"@babel/helper-simple-access@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" + integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-skip-transparent-expression-wrappers@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz#007f15240b5751c537c40e77abb4e89eeaaa8847" + integrity sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-string-parser@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83" + integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ== + +"@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== + +"@babel/helper-validator-option@^7.22.15", "@babel/helper-validator-option@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307" + integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw== + +"@babel/helper-wrap-function@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz#15352b0b9bfb10fc9c76f79f6342c00e3411a569" + integrity sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw== + dependencies: + "@babel/helper-function-name" "^7.22.5" + "@babel/template" "^7.22.15" + "@babel/types" "^7.22.19" + +"@babel/helpers@^7.23.9": + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.9.tgz#c3e20bbe7f7a7e10cb9b178384b4affdf5995c7d" + integrity sha512-87ICKgU5t5SzOT7sBMfCOZQ2rHjRU+Pcb9BoILMYz600W6DkVRLFBPwQ18gwUVvggqXivaUakpnxWQGbpywbBQ== + dependencies: + "@babel/template" "^7.23.9" + "@babel/traverse" "^7.23.9" + "@babel/types" "^7.23.9" + +"@babel/highlight@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b" + integrity sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" + js-tokens "^4.0.0" + +"@babel/parser@^7.23.9": + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.9.tgz#7b903b6149b0f8fa7ad564af646c4c38a77fc44b" + integrity sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA== + +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.23.3.tgz#5cd1c87ba9380d0afb78469292c954fee5d2411a" + integrity sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.23.3.tgz#f6652bb16b94f8f9c20c50941e16e9756898dc5d" + integrity sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/plugin-transform-optional-chaining" "^7.23.3" + +"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.23.7": + version "7.23.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.7.tgz#516462a95d10a9618f197d39ad291a9b47ae1d7b" + integrity sha512-LlRT7HgaifEpQA1ZgLVOIJZZFVPWN5iReq/7/JixwBtwcoeVGDBD53ZV28rrsLYOZs1Y/EHhA8N/Z6aazHR8cw== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2": + version "7.21.0-placeholder-for-preset-env.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703" + integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w== + +"@babel/plugin-syntax-async-generators@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-class-properties@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-syntax-class-static-block@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" + integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-dynamic-import@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" + integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-export-namespace-from@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" + integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-import-assertions@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.23.3.tgz#9c05a7f592982aff1a2768260ad84bcd3f0c77fc" + integrity sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-syntax-import-attributes@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.23.3.tgz#992aee922cf04512461d7dae3ff6951b90a2dc06" + integrity sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-syntax-import-meta@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-json-strings@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-jsx@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz#8f2e4f8a9b5f9aa16067e142c1ac9cd9f810f473" + integrity sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-numeric-separator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-object-rest-spread@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-private-property-in-object@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" + integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-top-level-await@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" + integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-unicode-sets-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz#d49a3b3e6b52e5be6740022317580234a6a47357" + integrity sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-arrow-functions@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.23.3.tgz#94c6dcfd731af90f27a79509f9ab7fb2120fc38b" + integrity sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-async-generator-functions@^7.23.9": + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.9.tgz#9adaeb66fc9634a586c5df139c6240d41ed801ce" + integrity sha512-8Q3veQEDGe14dTYuwagbRtwxQDnytyg1JFu4/HwEMETeofocrB0U0ejBJIXoeG/t2oXZ8kzCyI0ZZfbT80VFNQ== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-remap-async-to-generator" "^7.22.20" + "@babel/plugin-syntax-async-generators" "^7.8.4" + +"@babel/plugin-transform-async-to-generator@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz#d1f513c7a8a506d43f47df2bf25f9254b0b051fa" + integrity sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw== + dependencies: + "@babel/helper-module-imports" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-remap-async-to-generator" "^7.22.20" + +"@babel/plugin-transform-block-scoped-functions@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.23.3.tgz#fe1177d715fb569663095e04f3598525d98e8c77" + integrity sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-block-scoping@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.4.tgz#b2d38589531c6c80fbe25e6b58e763622d2d3cf5" + integrity sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-class-properties@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.23.3.tgz#35c377db11ca92a785a718b6aa4e3ed1eb65dc48" + integrity sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-class-static-block@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.23.4.tgz#2a202c8787a8964dd11dfcedf994d36bfc844ab5" + integrity sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + +"@babel/plugin-transform-classes@^7.23.8": + version "7.23.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.8.tgz#d08ae096c240347badd68cdf1b6d1624a6435d92" + integrity sha512-yAYslGsY1bX6Knmg46RjiCiNSwJKv2IUC8qOdYKqMMr0491SXFhcHqOdRDeCRohOOIzwN/90C6mQ9qAKgrP7dg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-compilation-targets" "^7.23.6" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.20" + "@babel/helper-split-export-declaration" "^7.22.6" + globals "^11.1.0" + +"@babel/plugin-transform-computed-properties@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.23.3.tgz#652e69561fcc9d2b50ba4f7ac7f60dcf65e86474" + integrity sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/template" "^7.22.15" + +"@babel/plugin-transform-destructuring@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.3.tgz#8c9ee68228b12ae3dff986e56ed1ba4f3c446311" + integrity sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-dotall-regex@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.23.3.tgz#3f7af6054882ede89c378d0cf889b854a993da50" + integrity sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-duplicate-keys@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.23.3.tgz#664706ca0a5dfe8d066537f99032fc1dc8b720ce" + integrity sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-dynamic-import@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.23.4.tgz#c7629e7254011ac3630d47d7f34ddd40ca535143" + integrity sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + +"@babel/plugin-transform-exponentiation-operator@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.23.3.tgz#ea0d978f6b9232ba4722f3dbecdd18f450babd18" + integrity sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-export-namespace-from@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.23.4.tgz#084c7b25e9a5c8271e987a08cf85807b80283191" + integrity sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + +"@babel/plugin-transform-for-of@^7.23.6": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.6.tgz#81c37e24171b37b370ba6aaffa7ac86bcb46f94e" + integrity sha512-aYH4ytZ0qSuBbpfhuofbg/e96oQ7U2w1Aw/UQmKT+1l39uEhUPoFS3fHevDc1G0OvewyDudfMKY1OulczHzWIw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + +"@babel/plugin-transform-function-name@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.23.3.tgz#8f424fcd862bf84cb9a1a6b42bc2f47ed630f8dc" + integrity sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw== + dependencies: + "@babel/helper-compilation-targets" "^7.22.15" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-json-strings@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.23.4.tgz#a871d9b6bd171976efad2e43e694c961ffa3714d" + integrity sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-json-strings" "^7.8.3" + +"@babel/plugin-transform-literals@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.23.3.tgz#8214665f00506ead73de157eba233e7381f3beb4" + integrity sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-logical-assignment-operators@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.23.4.tgz#e599f82c51d55fac725f62ce55d3a0886279ecb5" + integrity sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + +"@babel/plugin-transform-member-expression-literals@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.23.3.tgz#e37b3f0502289f477ac0e776b05a833d853cabcc" + integrity sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-modules-amd@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.3.tgz#e19b55436a1416829df0a1afc495deedfae17f7d" + integrity sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw== + dependencies: + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-modules-commonjs@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz#661ae831b9577e52be57dd8356b734f9700b53b4" + integrity sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA== + dependencies: + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-simple-access" "^7.22.5" + +"@babel/plugin-transform-modules-systemjs@^7.23.9": + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.9.tgz#105d3ed46e4a21d257f83a2f9e2ee4203ceda6be" + integrity sha512-KDlPRM6sLo4o1FkiSlXoAa8edLXFsKKIda779fbLrvmeuc3itnjCtaO6RrtoaANsIJANj+Vk1zqbZIMhkCAHVw== + dependencies: + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.20" + +"@babel/plugin-transform-modules-umd@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.23.3.tgz#5d4395fccd071dfefe6585a4411aa7d6b7d769e9" + integrity sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg== + dependencies: + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-named-capturing-groups-regex@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz#67fe18ee8ce02d57c855185e27e3dc959b2e991f" + integrity sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-new-target@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.23.3.tgz#5491bb78ed6ac87e990957cea367eab781c4d980" + integrity sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-nullish-coalescing-operator@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.23.4.tgz#45556aad123fc6e52189ea749e33ce090637346e" + integrity sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + +"@babel/plugin-transform-numeric-separator@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.23.4.tgz#03d08e3691e405804ecdd19dd278a40cca531f29" + integrity sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + +"@babel/plugin-transform-object-rest-spread@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.23.4.tgz#2b9c2d26bf62710460bdc0d1730d4f1048361b83" + integrity sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g== + dependencies: + "@babel/compat-data" "^7.23.3" + "@babel/helper-compilation-targets" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.23.3" + +"@babel/plugin-transform-object-super@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.23.3.tgz#81fdb636dcb306dd2e4e8fd80db5b2362ed2ebcd" + integrity sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.20" + +"@babel/plugin-transform-optional-catch-binding@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.23.4.tgz#318066de6dacce7d92fa244ae475aa8d91778017" + integrity sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + +"@babel/plugin-transform-optional-chaining@^7.23.3", "@babel/plugin-transform-optional-chaining@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.4.tgz#6acf61203bdfc4de9d4e52e64490aeb3e52bd017" + integrity sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + +"@babel/plugin-transform-parameters@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz#83ef5d1baf4b1072fa6e54b2b0999a7b2527e2af" + integrity sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-private-methods@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.23.3.tgz#b2d7a3c97e278bfe59137a978d53b2c2e038c0e4" + integrity sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-private-property-in-object@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.23.4.tgz#3ec711d05d6608fd173d9b8de39872d8dbf68bf5" + integrity sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + +"@babel/plugin-transform-property-literals@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.23.3.tgz#54518f14ac4755d22b92162e4a852d308a560875" + integrity sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-react-display-name@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.23.3.tgz#70529f034dd1e561045ad3c8152a267f0d7b6200" + integrity sha512-GnvhtVfA2OAtzdX58FJxU19rhoGeQzyVndw3GgtdECQvQFXPEZIOVULHVZGAYmOgmqjXpVpfocAbSjh99V/Fqw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-react-jsx-development@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz#e716b6edbef972a92165cd69d92f1255f7e73e87" + integrity sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A== + dependencies: + "@babel/plugin-transform-react-jsx" "^7.22.5" + +"@babel/plugin-transform-react-jsx@^7.22.15", "@babel/plugin-transform-react-jsx@^7.22.5": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz#393f99185110cea87184ea47bcb4a7b0c2e39312" + integrity sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-module-imports" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-jsx" "^7.23.3" + "@babel/types" "^7.23.4" + +"@babel/plugin-transform-react-pure-annotations@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.23.3.tgz#fabedbdb8ee40edf5da96f3ecfc6958e3783b93c" + integrity sha512-qMFdSS+TUhB7Q/3HVPnEdYJDQIk57jkntAwSuz9xfSE4n+3I+vHYCli3HoHawN1Z3RfCz/y1zXA/JXjG6cVImQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-regenerator@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.23.3.tgz#141afd4a2057298602069fce7f2dc5173e6c561c" + integrity sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + regenerator-transform "^0.15.2" + +"@babel/plugin-transform-reserved-words@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.23.3.tgz#4130dcee12bd3dd5705c587947eb715da12efac8" + integrity sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-shorthand-properties@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.23.3.tgz#97d82a39b0e0c24f8a981568a8ed851745f59210" + integrity sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-spread@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.23.3.tgz#41d17aacb12bde55168403c6f2d6bdca563d362c" + integrity sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + +"@babel/plugin-transform-sticky-regex@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.23.3.tgz#dec45588ab4a723cb579c609b294a3d1bd22ff04" + integrity sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-template-literals@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz#5f0f028eb14e50b5d0f76be57f90045757539d07" + integrity sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-typeof-symbol@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.23.3.tgz#9dfab97acc87495c0c449014eb9c547d8966bca4" + integrity sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-unicode-escapes@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.23.3.tgz#1f66d16cab01fab98d784867d24f70c1ca65b925" + integrity sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-unicode-property-regex@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.23.3.tgz#19e234129e5ffa7205010feec0d94c251083d7ad" + integrity sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-unicode-regex@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.23.3.tgz#26897708d8f42654ca4ce1b73e96140fbad879dc" + integrity sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-unicode-sets-regex@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.23.3.tgz#4fb6f0a719c2c5859d11f6b55a050cc987f3799e" + integrity sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/preset-env@^7.19": + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.23.9.tgz#beace3b7994560ed6bf78e4ae2073dff45387669" + integrity sha512-3kBGTNBBk9DQiPoXYS0g0BYlwTQYUTifqgKTjxUwEUkduRT2QOa0FPGBJ+NROQhGyYO5BuTJwGvBnqKDykac6A== + dependencies: + "@babel/compat-data" "^7.23.5" + "@babel/helper-compilation-targets" "^7.23.6" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-validator-option" "^7.23.5" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.23.3" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.23.3" + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly" "^7.23.7" + "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2" + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-class-properties" "^7.12.13" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-import-assertions" "^7.23.3" + "@babel/plugin-syntax-import-attributes" "^7.23.3" + "@babel/plugin-syntax-import-meta" "^7.10.4" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/plugin-syntax-top-level-await" "^7.14.5" + "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" + "@babel/plugin-transform-arrow-functions" "^7.23.3" + "@babel/plugin-transform-async-generator-functions" "^7.23.9" + "@babel/plugin-transform-async-to-generator" "^7.23.3" + "@babel/plugin-transform-block-scoped-functions" "^7.23.3" + "@babel/plugin-transform-block-scoping" "^7.23.4" + "@babel/plugin-transform-class-properties" "^7.23.3" + "@babel/plugin-transform-class-static-block" "^7.23.4" + "@babel/plugin-transform-classes" "^7.23.8" + "@babel/plugin-transform-computed-properties" "^7.23.3" + "@babel/plugin-transform-destructuring" "^7.23.3" + "@babel/plugin-transform-dotall-regex" "^7.23.3" + "@babel/plugin-transform-duplicate-keys" "^7.23.3" + "@babel/plugin-transform-dynamic-import" "^7.23.4" + "@babel/plugin-transform-exponentiation-operator" "^7.23.3" + "@babel/plugin-transform-export-namespace-from" "^7.23.4" + "@babel/plugin-transform-for-of" "^7.23.6" + "@babel/plugin-transform-function-name" "^7.23.3" + "@babel/plugin-transform-json-strings" "^7.23.4" + "@babel/plugin-transform-literals" "^7.23.3" + "@babel/plugin-transform-logical-assignment-operators" "^7.23.4" + "@babel/plugin-transform-member-expression-literals" "^7.23.3" + "@babel/plugin-transform-modules-amd" "^7.23.3" + "@babel/plugin-transform-modules-commonjs" "^7.23.3" + "@babel/plugin-transform-modules-systemjs" "^7.23.9" + "@babel/plugin-transform-modules-umd" "^7.23.3" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.22.5" + "@babel/plugin-transform-new-target" "^7.23.3" + "@babel/plugin-transform-nullish-coalescing-operator" "^7.23.4" + "@babel/plugin-transform-numeric-separator" "^7.23.4" + "@babel/plugin-transform-object-rest-spread" "^7.23.4" + "@babel/plugin-transform-object-super" "^7.23.3" + "@babel/plugin-transform-optional-catch-binding" "^7.23.4" + "@babel/plugin-transform-optional-chaining" "^7.23.4" + "@babel/plugin-transform-parameters" "^7.23.3" + "@babel/plugin-transform-private-methods" "^7.23.3" + "@babel/plugin-transform-private-property-in-object" "^7.23.4" + "@babel/plugin-transform-property-literals" "^7.23.3" + "@babel/plugin-transform-regenerator" "^7.23.3" + "@babel/plugin-transform-reserved-words" "^7.23.3" + "@babel/plugin-transform-shorthand-properties" "^7.23.3" + "@babel/plugin-transform-spread" "^7.23.3" + "@babel/plugin-transform-sticky-regex" "^7.23.3" + "@babel/plugin-transform-template-literals" "^7.23.3" + "@babel/plugin-transform-typeof-symbol" "^7.23.3" + "@babel/plugin-transform-unicode-escapes" "^7.23.3" + "@babel/plugin-transform-unicode-property-regex" "^7.23.3" + "@babel/plugin-transform-unicode-regex" "^7.23.3" + "@babel/plugin-transform-unicode-sets-regex" "^7.23.3" + "@babel/preset-modules" "0.1.6-no-external-plugins" + babel-plugin-polyfill-corejs2 "^0.4.8" + babel-plugin-polyfill-corejs3 "^0.9.0" + babel-plugin-polyfill-regenerator "^0.5.5" + core-js-compat "^3.31.0" + semver "^6.3.1" + +"@babel/preset-modules@0.1.6-no-external-plugins": + version "0.1.6-no-external-plugins" + resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz#ccb88a2c49c817236861fee7826080573b8a923a" + integrity sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/types" "^7.4.4" + esutils "^2.0.2" + +"@babel/preset-react@^7.18.6": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.23.3.tgz#f73ca07e7590f977db07eb54dbe46538cc015709" + integrity sha512-tbkHOS9axH6Ysf2OUEqoSZ6T3Fa2SrNH6WTWSPBboxKzdxNc9qOICeLXkNG0ZEwbQ1HY8liwOce4aN/Ceyuq6w== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-validator-option" "^7.22.15" + "@babel/plugin-transform-react-display-name" "^7.23.3" + "@babel/plugin-transform-react-jsx" "^7.22.15" + "@babel/plugin-transform-react-jsx-development" "^7.22.5" + "@babel/plugin-transform-react-pure-annotations" "^7.23.3" + +"@babel/regjsgen@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" + integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== + +"@babel/runtime@^7.8.4": + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.9.tgz#47791a15e4603bb5f905bc0753801cf21d6345f7" + integrity sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw== + dependencies: + regenerator-runtime "^0.14.0" + +"@babel/template@^7.22.15", "@babel/template@^7.23.9": + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.23.9.tgz#f881d0487cba2828d3259dcb9ef5005a9731011a" + integrity sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA== + dependencies: + "@babel/code-frame" "^7.23.5" + "@babel/parser" "^7.23.9" + "@babel/types" "^7.23.9" + +"@babel/traverse@^7.23.9": + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.9.tgz#2f9d6aead6b564669394c5ce0f9302bb65b9d950" + integrity sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg== + dependencies: + "@babel/code-frame" "^7.23.5" + "@babel/generator" "^7.23.6" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.23.9" + "@babel/types" "^7.23.9" + debug "^4.3.1" + globals "^11.1.0" + +"@babel/types@^7.22.15", "@babel/types@^7.22.19", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.4", "@babel/types@^7.23.6", "@babel/types@^7.23.9", "@babel/types@^7.4.4": + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.9.tgz#1dd7b59a9a2b5c87f8b41e52770b5ecbf492e002" + integrity sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q== + dependencies: + "@babel/helper-string-parser" "^7.23.4" + "@babel/helper-validator-identifier" "^7.22.20" + to-fast-properties "^2.0.0" + +"@discoveryjs/json-ext@^0.5.0": + version "0.5.7" + resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" + integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== + +"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/source-map@^0.3.3": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.5.tgz#a3bb4d5c6825aab0d281268f47f6ad5853431e91" + integrity sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.22" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz#72a621e5de59f5f1ef792d0793a82ee20f645e4c" + integrity sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@paypal/paypal-js@^6.0.0": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@paypal/paypal-js/-/paypal-js-6.0.1.tgz#5d68d5863a5176383fee9424bc944231668fcffd" + integrity sha512-bvYetmkg2GEC6onsUJQx1E9hdAJWff2bS3IPeiZ9Sh9U7h26/fIgMKm240cq/908sbSoDjHys75XXd8at9OpQA== + dependencies: + promise-polyfill "^8.3.0" + +"@types/eslint-scope@^3.7.3": + version "3.7.7" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" + integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*": + version "8.56.2" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.56.2.tgz#1c72a9b794aa26a8b94ad26d5b9aa51c8a6384bb" + integrity sha512-uQDwm1wFHmbBbCZCqAlq6Do9LYwByNZHWzXppSnay9SuwJ+VRbjkbLABer54kcPnMSlG6Fdiy2yaFXm/z9Z5gw== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*", "@types/estree@^1.0.5": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" + integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== + +"@types/json-schema@*", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +"@types/node@*": + version "20.11.16" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.16.tgz#4411f79411514eb8e2926f036c86c9f0e4ec6708" + integrity sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ== + dependencies: + undici-types "~5.26.4" + +"@webassemblyjs/ast@1.11.6", "@webassemblyjs/ast@^1.11.5": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.6.tgz#db046555d3c413f8966ca50a95176a0e2c642e24" + integrity sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q== + dependencies: + "@webassemblyjs/helper-numbers" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + +"@webassemblyjs/floating-point-hex-parser@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz#dacbcb95aff135c8260f77fa3b4c5fea600a6431" + integrity sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw== + +"@webassemblyjs/helper-api-error@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz#6132f68c4acd59dcd141c44b18cbebbd9f2fa768" + integrity sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q== + +"@webassemblyjs/helper-buffer@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz#b66d73c43e296fd5e88006f18524feb0f2c7c093" + integrity sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA== + +"@webassemblyjs/helper-numbers@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz#cbce5e7e0c1bd32cf4905ae444ef64cea919f1b5" + integrity sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.6" + "@webassemblyjs/helper-api-error" "1.11.6" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz#bb2ebdb3b83aa26d9baad4c46d4315283acd51e9" + integrity sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA== + +"@webassemblyjs/helper-wasm-section@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz#ff97f3863c55ee7f580fd5c41a381e9def4aa577" + integrity sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" + +"@webassemblyjs/ieee754@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz#bb665c91d0b14fffceb0e38298c329af043c6e3a" + integrity sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.6.tgz#70e60e5e82f9ac81118bc25381a0b283893240d7" + integrity sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.6.tgz#90f8bc34c561595fe156603be7253cdbcd0fab5a" + integrity sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA== + +"@webassemblyjs/wasm-edit@^1.11.5": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz#c72fa8220524c9b416249f3d94c2958dfe70ceab" + integrity sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/helper-wasm-section" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" + "@webassemblyjs/wasm-opt" "1.11.6" + "@webassemblyjs/wasm-parser" "1.11.6" + "@webassemblyjs/wast-printer" "1.11.6" + +"@webassemblyjs/wasm-gen@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz#fb5283e0e8b4551cc4e9c3c0d7184a65faf7c268" + integrity sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wasm-opt@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz#d9a22d651248422ca498b09aa3232a81041487c2" + integrity sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" + "@webassemblyjs/wasm-parser" "1.11.6" + +"@webassemblyjs/wasm-parser@1.11.6", "@webassemblyjs/wasm-parser@^1.11.5": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz#bb85378c527df824004812bbdb784eea539174a1" + integrity sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-api-error" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wast-printer@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz#a7bf8dd7e362aeb1668ff43f35cb849f188eff20" + integrity sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@xtuc/long" "4.2.2" + +"@webpack-cli/configtest@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.2.0.tgz#7b20ce1c12533912c3b217ea68262365fa29a6f5" + integrity sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg== + +"@webpack-cli/info@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.5.0.tgz#6c78c13c5874852d6e2dd17f08a41f3fe4c261b1" + integrity sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ== + dependencies: + envinfo "^7.7.3" + +"@webpack-cli/serve@^1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.7.0.tgz#e1993689ac42d2b16e9194376cfb6753f6254db1" + integrity sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q== + +"@woocommerce/dependency-extraction-webpack-plugin@^2.2.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@woocommerce/dependency-extraction-webpack-plugin/-/dependency-extraction-webpack-plugin-2.3.0.tgz#9d242e538072fa5cc2d5f6241af1272317c757bb" + integrity sha512-dfa5bBZFRKcjFpqTEXyuUKC16n85RlKQ4EamdAAqUVQedv4DMRCj2jWUcId7pN2mOFGgAp+heGgb9i6fwiZk2Q== + dependencies: + "@wordpress/dependency-extraction-webpack-plugin" "^3.3.0" + +"@wordpress/dependency-extraction-webpack-plugin@^3.3.0": + version "3.7.0" + resolved "https://registry.yarnpkg.com/@wordpress/dependency-extraction-webpack-plugin/-/dependency-extraction-webpack-plugin-3.7.0.tgz#e52ef31f66b8c4add3d773a87e11007375127b04" + integrity sha512-SHyp88D1ICSaRVMfs/kKEicjKXWf1y2wecUeZIiMtkfAi8Bnk3JsnUo11LH7drJIXfjmDoer2B2rrBMZmRm8VA== + dependencies: + json2php "^0.0.4" + webpack-sources "^3.2.2" + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + +acorn-import-assertions@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" + integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== + +acorn@^8.7.1, acorn@^8.8.2: + version "8.11.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" + integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== + +ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + +ajv@^6.12.4, ajv@^6.12.5: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +babel-loader@^8.2: + version "8.3.0" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.3.0.tgz#124936e841ba4fe8176786d6ff28add1f134d6a8" + integrity sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q== + dependencies: + find-cache-dir "^3.3.1" + loader-utils "^2.0.0" + make-dir "^3.1.0" + schema-utils "^2.6.5" + +babel-plugin-polyfill-corejs2@^0.4.8: + version "0.4.8" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.8.tgz#dbcc3c8ca758a290d47c3c6a490d59429b0d2269" + integrity sha512-OtIuQfafSzpo/LhnJaykc0R/MMnuLSSVjVYy9mHArIZ9qTCSZ6TpWCuEKZYVoN//t8HqBNScHrOtCrIK5IaGLg== + dependencies: + "@babel/compat-data" "^7.22.6" + "@babel/helper-define-polyfill-provider" "^0.5.0" + semver "^6.3.1" + +babel-plugin-polyfill-corejs3@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.9.0.tgz#9eea32349d94556c2ad3ab9b82ebb27d4bf04a81" + integrity sha512-7nZPG1uzK2Ymhy/NbaOWTg3uibM2BmGASS4vHS4szRZAIR8R6GwA/xAujpdrXU5iyklrimWnLWU+BLF9suPTqg== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.5.0" + core-js-compat "^3.34.0" + +babel-plugin-polyfill-regenerator@^0.5.5: + version "0.5.5" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.5.tgz#8b0c8fc6434239e5d7b8a9d1f832bb2b0310f06a" + integrity sha512-OJGYZlhLqBh2DDHeqAxWB1XIvr49CxiJ2gIt61/PU55CQK4Z58OzMqjDe1zwQdQk+rBYsRc+1rJmdajM3gimHg== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.5.0" + +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +browserslist@^4.21.10, browserslist@^4.22.2: + version "4.22.3" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.3.tgz#299d11b7e947a6b843981392721169e27d60c5a6" + integrity sha512-UAp55yfwNv0klWNapjs/ktHoguxuQNGnOzxYmfnXIS+8AsRDZkSDxg7R1AX3GKzn078SBI5dzwzj/Yx0Or0e3A== + dependencies: + caniuse-lite "^1.0.30001580" + electron-to-chromium "^1.4.648" + node-releases "^2.0.14" + update-browserslist-db "^1.0.13" + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +caniuse-lite@^1.0.30001580: + version "1.0.30001585" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001585.tgz#0b4e848d84919c783b2a41c13f7de8ce96744401" + integrity sha512-yr2BWR1yLXQ8fMpdS/4ZZXpseBgE7o4g41x3a6AJOqZuOi+iE/WdJYAuZ6Y95i4Ohd2Y+9MzIWRR+uGABH4s3Q== + +chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +"chokidar@>=3.0.0 <4.0.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chrome-trace-event@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" + integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== + +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +colorette@^2.0.14: + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commander@^7.0.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== + +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + +core-js-compat@^3.31.0, core-js-compat@^3.34.0: + version "3.35.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.35.1.tgz#215247d7edb9e830efa4218ff719beb2803555e2" + integrity sha512-sftHa5qUJY3rs9Zht1WEnmkvXputCyDBczPnr7QDgL8n3qrF3CMXY4VPSYtOLLiOUJcah2WNXREd48iOl6mQIw== + dependencies: + browserslist "^4.22.2" + +core-js@^3.25.0: + version "3.35.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.35.1.tgz#9c28f8b7ccee482796f8590cc8d15739eaaf980c" + integrity sha512-IgdsbxNyMskrTFxa9lWHyMwAJU5gXOPP+1yO+K59d50VLVAIDAbs7gIv705KzALModfK3ZrSZTPNpC0PQgIZuw== + +cross-env@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf" + integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw== + dependencies: + cross-spawn "^7.0.1" + +cross-spawn@^7.0.1, cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +electron-to-chromium@^1.4.648: + version "1.4.659" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.659.tgz#e93af8119b6610cb4d2614a47508a74543b96ce5" + integrity sha512-sRJ3nV3HowrYpBtPF9bASQV7OW49IgZC01Xiq43WfSE3RTCkK0/JidoCmR73Hyc1mN+l/H4Yqx0eNiomvExFZg== + +emojis-list@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== + +enhanced-resolve@^5.15.0: + version "5.15.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" + integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +envinfo@^7.7.3: + version "7.11.1" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.11.1.tgz#2ffef77591057081b0129a8fd8cf6118da1b94e1" + integrity sha512-8PiZgZNIB4q/Lw4AhOvAfB/ityHAd2bli3lESSWmWSzSsl5dKpy5N1d1Rfkd2teq/g9xN90lc6o98DOjMeYHpg== + +es-module-lexer@^1.2.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.4.1.tgz#41ea21b43908fe6a287ffcbe4300f790555331f5" + integrity sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w== + +escalade@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" + integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +eslint-scope@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +events@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fastest-levenshtein@^1.0.12: + version "1.0.16" + resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" + integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== + +file-loader@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.2.0.tgz#baef7cf8e1840df325e4390b4484879480eebe4d" + integrity sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw== + dependencies: + loader-utils "^2.0.0" + schema-utils "^3.0.0" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +find-cache-dir@^3.3.1: + version "3.3.2" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" + integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== + dependencies: + commondir "^1.0.1" + make-dir "^3.0.2" + pkg-dir "^4.1.0" + +find-up@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +graceful-fs@^4.1.2, graceful-fs@^4.2.4, graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +hasown@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" + integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== + dependencies: + function-bind "^1.1.2" + +immutable@^4.0.0: + version "4.3.5" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.5.tgz#f8b436e66d59f99760dc577f5c99a4fd2a5cc5a0" + integrity sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw== + +import-local@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" + integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + +interpret@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" + integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-core-module@^2.13.0: + version "2.13.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== + dependencies: + hasown "^2.0.0" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== + +jest-worker@^27.4.5: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== + +json-parse-even-better-errors@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json2php@^0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/json2php/-/json2php-0.0.4.tgz#6bd85a1dda6a5dd7e91022bb24403cc1b7c2ee34" + integrity sha512-hFzejhs28f70sGnutcsRS459MnAsjRVI85RgPAL1KQIZEpjiDitc27CZv4IgOtaR86vrqOVlu9vJNew2XyTH4g== + +json5@^2.1.2, json5@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +klona@^2.0.4: + version "2.0.6" + resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.6.tgz#85bffbf819c03b2f53270412420a4555ef882e22" + integrity sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA== + +loader-runner@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" + integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== + +loader-utils@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" + integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^2.1.2" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +make-dir@^3.0.2, make-dir@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.27: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +node-releases@^2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" + integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.0.4, picomatch@^2.2.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pkg-dir@^4.1.0, pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +promise-polyfill@^8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/promise-polyfill/-/promise-polyfill-8.3.0.tgz#9284810268138d103807b11f4e23d5e945a4db63" + integrity sha512-H5oELycFml5yto/atYqmjyigJoAo3+OXwolYiH7OfQuYlAqhxNvTfiNMbV9hsC6Yp83yE5r2KTVmtrG6R9i6Pg== + +punycode@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +rechoir@^0.7.0: + version "0.7.1" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.1.tgz#9478a96a1ca135b5e88fc027f03ee92d6c645686" + integrity sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg== + dependencies: + resolve "^1.9.0" + +regenerate-unicode-properties@^10.1.0: + version "10.1.1" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz#6b0e05489d9076b04c436f318d9b067bba459480" + integrity sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q== + dependencies: + regenerate "^1.4.2" + +regenerate@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" + integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== + +regenerator-runtime@^0.14.0: + version "0.14.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" + integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== + +regenerator-transform@^0.15.2: + version "0.15.2" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.2.tgz#5bbae58b522098ebdf09bca2f83838929001c7a4" + integrity sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg== + dependencies: + "@babel/runtime" "^7.8.4" + +regexpu-core@^5.3.1: + version "5.3.2" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.2.tgz#11a2b06884f3527aec3e93dbbf4a3b958a95546b" + integrity sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ== + dependencies: + "@babel/regjsgen" "^0.8.0" + regenerate "^1.4.2" + regenerate-unicode-properties "^10.1.0" + regjsparser "^0.9.1" + unicode-match-property-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript "^2.1.0" + +regjsparser@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.9.1.tgz#272d05aa10c7c1f67095b1ff0addae8442fc5709" + integrity sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ== + dependencies: + jsesc "~0.5.0" + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve@^1.14.2, resolve@^1.9.0: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +safe-buffer@^5.1.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +sass-loader@^12.1.0: + version "12.6.0" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-12.6.0.tgz#5148362c8e2cdd4b950f3c63ac5d16dbfed37bcb" + integrity sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA== + dependencies: + klona "^2.0.4" + neo-async "^2.6.2" + +sass@^1.42.1: + version "1.70.0" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.70.0.tgz#761197419d97b5358cb25f9dd38c176a8a270a75" + integrity sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ== + dependencies: + chokidar ">=3.0.0 <4.0.0" + immutable "^4.0.0" + source-map-js ">=0.6.2 <2.0.0" + +schema-utils@^2.6.5: + version "2.7.1" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" + integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg== + dependencies: + "@types/json-schema" "^7.0.5" + ajv "^6.12.4" + ajv-keywords "^3.5.2" + +schema-utils@^3.0.0, schema-utils@^3.1.1, schema-utils@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" + integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +semver@^6.0.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +serialize-javascript@^6.0.1: + version "6.0.2" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" + integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== + dependencies: + randombytes "^2.1.0" + +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +"source-map-js@>=0.6.2 <2.0.0": + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + +source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +tapable@^2.1.1, tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +terser-webpack-plugin@^5.3.10: + version "5.3.10" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz#904f4c9193c6fd2a03f693a2150c62a92f40d199" + integrity sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w== + dependencies: + "@jridgewell/trace-mapping" "^0.3.20" + jest-worker "^27.4.5" + schema-utils "^3.1.1" + serialize-javascript "^6.0.1" + terser "^5.26.0" + +terser@^5.26.0: + version "5.27.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.27.0.tgz#70108689d9ab25fef61c4e93e808e9fd092bf20c" + integrity sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A== + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.8.2" + commander "^2.20.0" + source-map-support "~0.5.20" + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +unicode-canonical-property-names-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" + integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== + +unicode-match-property-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" + integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== + dependencies: + unicode-canonical-property-names-ecmascript "^2.0.0" + unicode-property-aliases-ecmascript "^2.0.0" + +unicode-match-property-value-ecmascript@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz#cb5fffdcd16a05124f5a4b0bf7c3770208acbbe0" + integrity sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA== + +unicode-property-aliases-ecmascript@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" + integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== + +update-browserslist-db@^1.0.13: + version "1.0.13" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" + integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +watchpack@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" + integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + +webpack-cli@^4.10: + version "4.10.0" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.10.0.tgz#37c1d69c8d85214c5a65e589378f53aec64dab31" + integrity sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w== + dependencies: + "@discoveryjs/json-ext" "^0.5.0" + "@webpack-cli/configtest" "^1.2.0" + "@webpack-cli/info" "^1.5.0" + "@webpack-cli/serve" "^1.7.0" + colorette "^2.0.14" + commander "^7.0.0" + cross-spawn "^7.0.3" + fastest-levenshtein "^1.0.12" + import-local "^3.0.2" + interpret "^2.2.0" + rechoir "^0.7.0" + webpack-merge "^5.7.3" + +webpack-merge@^5.7.3: + version "5.10.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.10.0.tgz#a3ad5d773241e9c682803abf628d4cd62b8a4177" + integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA== + dependencies: + clone-deep "^4.0.1" + flat "^5.0.2" + wildcard "^2.0.0" + +webpack-sources@^3.2.2, webpack-sources@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" + integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== + +webpack@^5.76: + version "5.90.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.90.1.tgz#62ab0c097d7cbe83d32523dbfbb645cdb7c3c01c" + integrity sha512-SstPdlAC5IvgFnhiRok8hqJo/+ArAbNv7rhU4fnWGHNVfN59HSQFaxZDSAL3IFG2YmqxuRs+IU33milSxbPlog== + dependencies: + "@types/eslint-scope" "^3.7.3" + "@types/estree" "^1.0.5" + "@webassemblyjs/ast" "^1.11.5" + "@webassemblyjs/wasm-edit" "^1.11.5" + "@webassemblyjs/wasm-parser" "^1.11.5" + acorn "^8.7.1" + acorn-import-assertions "^1.9.0" + browserslist "^4.21.10" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.15.0" + es-module-lexer "^1.2.1" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.9" + json-parse-even-better-errors "^2.3.1" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.2.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.3.10" + watchpack "^2.4.0" + webpack-sources "^3.2.3" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wildcard@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" + integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== diff --git a/modules/ppcp-button/resources/js/modules/Helper/ScriptLoading.js b/modules/ppcp-button/resources/js/modules/Helper/ScriptLoading.js index cbe867ae6..2c6b6341d 100644 --- a/modules/ppcp-button/resources/js/modules/Helper/ScriptLoading.js +++ b/modules/ppcp-button/resources/js/modules/Helper/ScriptLoading.js @@ -72,6 +72,8 @@ export const loadPaypalScript = (config, onLoaded, onError = null) => { scriptOptions['data-user-id-token'] = userIdToken; } + scriptOptions['data-client-metadata-id'] = 'ppcp-cm-id'; + // Load PayPal script loadScript(scriptOptions) .then(callback) diff --git a/package.json b/package.json index 0535eb41e..af57aefbd 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "install:modules:ppcp-order-tracking": "cd modules/ppcp-order-tracking && yarn install", "install:modules:ppcp-paypal-subscriptions": "cd modules/ppcp-paypal-subscriptions && yarn install", "install:modules:ppcp-save-payment-methods": "cd modules/ppcp-save-payment-methods && yarn install", + "install:modules:ppcp-axo": "cd modules/ppcp-axo && yarn install", "install:modules:ppcp-onboarding": "cd modules/ppcp-onboarding && yarn install", "install:modules:ppcp-compat": "cd modules/ppcp-compat && yarn install", "install:modules:ppcp-uninstall": "cd modules/ppcp-uninstall && yarn install", @@ -29,6 +30,7 @@ "build:modules:ppcp-webhooks": "cd modules/ppcp-webhooks && yarn run build", "build:modules:ppcp-order-tracking": "cd modules/ppcp-order-tracking && yarn run build", "build:modules:ppcp-save-payment-methods": "cd modules/ppcp-save-payment-methods && yarn run build", + "build:modules:ppcp-axo": "cd modules/ppcp-axo && yarn run build", "build:modules:ppcp-paypal-subscriptions": "cd modules/ppcp-paypal-subscriptions && yarn run build", "build:modules:ppcp-onboarding": "cd modules/ppcp-onboarding && yarn run build", "build:modules:ppcp-compat": "cd modules/ppcp-compat && yarn run build", @@ -44,6 +46,7 @@ "watch:modules:ppcp-order-tracking": "cd modules/ppcp-order-tracking && yarn run watch", "watch:modules:ppcp-paypal-subscriptions": "cd modules/ppcp-paypal-subscriptions && yarn run watch", "watch:modules:ppcp-save-payment-methods": "cd modules/ppcp-save-payment-methods && yarn run watch", + "watch:modules:ppcp-axo": "cd modules/ppcp-axo && yarn run watch", "watch:modules:ppcp-onboarding": "cd modules/ppcp-onboarding && yarn run watch", "watch:modules:ppcp-compat": "cd modules/ppcp-compat && yarn run watch", "watch:modules:ppcp-uninstall": "cd modules/ppcp-uninstall && yarn run watch", From 7f93e5762decfa635f0f29e35d753b3092cc737f Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Fri, 9 Feb 2024 17:49:45 +0000 Subject: [PATCH 12/70] Refactoring AXO module --- modules/ppcp-axo/resources/js/AxoManager.js | 194 ++++++++++++++++++ .../ppcp-axo/resources/js/Entity/Fastlane.js | 47 +++++ modules/ppcp-axo/resources/js/boot.js | 194 +----------------- modules/ppcp-axo/src/AxoModule.php | 42 ++++ modules/ppcp-axo/webpack.config.js | 2 +- 5 files changed, 286 insertions(+), 193 deletions(-) create mode 100644 modules/ppcp-axo/resources/js/AxoManager.js create mode 100644 modules/ppcp-axo/resources/js/Entity/Fastlane.js diff --git a/modules/ppcp-axo/resources/js/AxoManager.js b/modules/ppcp-axo/resources/js/AxoManager.js new file mode 100644 index 000000000..b560108f4 --- /dev/null +++ b/modules/ppcp-axo/resources/js/AxoManager.js @@ -0,0 +1,194 @@ +import Fastlane from "./Entity/Fastlane"; + +class AxoManager { + + constructor() { + this.initialized = false; + this.fastlane = new Fastlane(); + + + jQuery(document).on('change', '#payment_method_ppcp-axo-gateway', (ev) => { + if(ev.target.checked) { + let emailRow = document.querySelector('#billing_email_field'); + jQuery(emailRow.parentNode).prepend(emailRow); + emailRow.querySelector('input').focus(); + + this.init(); + + // show stuff + jQuery('#place_order').hide(); + jQuery('#axo-submit-button-container').show(); + jQuery('#payment-container').show(); + } else { + console.log('Checkbox is not checked.'); + + // hide stuff + jQuery('#place_order').show(); + jQuery('#axo-submit-button-container').hide(); + jQuery('#payment-container').hide(); + } + }); + + let gatewaysRenderedInterval = setInterval(() => { + console.log('not rendered'); + if (document.querySelector('.wc_payment_methods')) { + console.log('YES rendered'); + clearInterval(gatewaysRenderedInterval); + jQuery('#payment_method_ppcp-axo-gateway').trigger('change'); + } + }, 100); + + jQuery(document.body).on('updated_checkout payment_method_selected', () => { + jQuery('#payment_method_ppcp-axo-gateway').trigger('change'); + }); + + jQuery(document).on('click', '.ppcp-axo-order-button', () => { + try { + + this.connectCardComponent.tokenize({ + name: { + fullName: "John Doe" + }, + billingAddress: { + addressLine1: "2211 North 1st St", + adminArea1: "San Jose", + adminArea2: "CA", + postalCode: "95131", + countryCode: "US" + } + }).then((response) => { + + // Send the nonce and previously captured device data to server to complete checkout + this.log('nonce: ' + response.nonce); + alert('nonce: ' + response.nonce); + + // fetch('submit.php', { + // method: 'POST', + // headers: { + // 'Content-Type': 'application/json', + // }, + // body: JSON.stringify({ + // nonce: nonce + // }), + // }) + // + // .then(response => { + // if (!response.ok) { + // throw new Error('Network response was not ok'); + // } + // return response.json(); + // }) + // .then(data => { + // console.log('Submit response', data); + // this.log(JSON.stringify(data)); + // }) + // .catch(error => { + // console.error('There has been a problem with your fetch operation:', error); + // }); + + }); + + } catch (e) { + console.log('Error tokenizing.'); + } + + return false; + }); + + } + + init() { + if (this.initialized) { + return; + } + this.initialized = true; + + this.initManager(); + } + + async initManager() { + window.localStorage.setItem('axoEnv', 'sandbox'); + + const styles = { + root: { + backgroundColorPrimary: "#ffffff" + } + } + + const locale = 'en_us'; + + await this.fastlane.connect({ + locale, + styles + }); + + this.fastlane.setLocale('en_us'); + + this.emailInput = document.querySelector('#billing_email_field input'); + + jQuery(this.emailInput).after(` +
+ `); + + jQuery('.payment_method_ppcp-axo-gateway').append(` +
+ `); + + this.fastlane.FastlaneWatermarkComponent({ + includeAdditionalInfo: true + }).render('#watermark-container'); + + this.emailInput.addEventListener("change", async ()=> { + this.emailUpdated(); + }); + + if (this.emailInput.value) { + this.emailUpdated(); + } + } + + async emailUpdated () { + this.log('Email changed: ' + this.emailInput.value); + + if (!this.emailInput.checkValidity()) { + this.log('The email address is not valid.'); + return; + } + + const { customerContextId } = await this.fastlane.identity.lookupCustomerByEmail(this.emailInput.value); + + if (customerContextId) { + // Email is associated with a Connect profile or aPayPal member + // Authenticate the customer to get access to their profile + this.log('Email is associated with a Connect profile or a PayPal member'); + } else { + // No profile found with this email address. + // This is a guest customer. + this.log('No profile found with this email address.'); + + const fields = { + phoneNumber: { + prefill: "1234567890" + }, + + cardholderName: {} // optionally pass this to show the card holder name + }; + + jQuery("#payment-container").css({ + 'padding': '1rem 0', + 'background-color': '#ffffff', + }); + + this.connectCardComponent = await this.fastlane + .FastlaneCardComponent({fields}) + .render("#payment-container"); + } + } + + log(message) { + console.log('[AXO] ', message); + } + +} + +export default AxoManager; diff --git a/modules/ppcp-axo/resources/js/Entity/Fastlane.js b/modules/ppcp-axo/resources/js/Entity/Fastlane.js new file mode 100644 index 000000000..5f3c16e7f --- /dev/null +++ b/modules/ppcp-axo/resources/js/Entity/Fastlane.js @@ -0,0 +1,47 @@ + +class Fastlane { + + construct() { + this.connection = null; + this.identity = null; + this.profile = null; + this.FastlaneCardComponent = null; + this.FastlaneEmailComponent = null; + this.FastlaneAddressComponent = null; + this.FastlaneWatermarkComponent = null; + } + + connect(config) { + return new Promise((resolve, reject) => { + window.paypal.Connect(config) // TODO: migrate from 0.6 to 0.7 + .then((result) => { + this.init(result); + console.log('[AXO] Connected', result); + resolve(); + }) + .catch((error) => { + console.error('[AXO] Failed to connect', error); + reject(); + }); + }); + } + + init(connection) { + this.connection = connection; + this.identity = this.connection.identity; + this.profile = this.connection.profile; + this.FastlaneCardComponent = this.connection.ConnectCardComponent; // TODO: migrate from 0.6 to 0.7 + this.FastlaneEmailComponent = null; // TODO: migrate from 0.6 to 0.7 + this.FastlaneAddressComponent = null; // TODO: migrate from 0.6 to 0.7 + this.FastlaneWatermarkComponent = this.connection.ConnectWatermarkComponent // TODO: migrate from 0.6 to 0.7 + + console.log('[AXO] Fastlane initialized', this); + } + + setLocale(locale) { + this.connection.setLocale(locale); + } + +} + +export default Fastlane; diff --git a/modules/ppcp-axo/resources/js/boot.js b/modules/ppcp-axo/resources/js/boot.js index 2dc6735e5..45a6b6e70 100644 --- a/modules/ppcp-axo/resources/js/boot.js +++ b/modules/ppcp-axo/resources/js/boot.js @@ -1,197 +1,7 @@ - -let initialized = false; - -function log(message) { - console.log('Log: ', message); - - // let div = document.createElement('div'); - // div.style.cssText = 'margin-bottom: 4px;'; - // div.textContent = message; - // document.getElementById('log-container').appendChild(div); -} - -const init = () => { - if (initialized) { - return; - } - initialized = true; - - (async function () { - // sets the SDK to run in sandbox mode (Remove for production) - window.localStorage.setItem('axoEnv', 'sandbox'); - - //specify global styles here - const styles = { - root: { - backgroundColorPrimary: "#ffffff" - } - } - - const locale = 'en_us'; - - // instantiates the Connect module - const connect = await window.paypal.Connect({ - locale, - styles - }); - - console.log('connect', connect); - - // Specifying the locale if necessary - connect.setLocale('en_us'); - - const { - identity, - profile, - ConnectCardComponent, - ConnectWatermarkComponent - } = connect; - - console.log( - '[identity, profile, ConnectCardComponent]', - identity, - profile, - ConnectCardComponent - ); - - //-------- - const emailInput = document.querySelector('#billing_email_field').querySelector('input'); - - jQuery(emailInput).after(` -
- `); - - jQuery('.payment_method_ppcp-axo-gateway').append(` -
- `); - - const connectWatermarkComponent = ConnectWatermarkComponent({ - includeAdditionalInfo: true - }).render('#watermark-container'); - - //-------- - - const emailUpdated = async () => { - log('Email changed: ' + emailInput.value); - - if (!emailInput.checkValidity()) { - log('The email address is not valid.'); - return; - } - - const { customerContextId } = await identity.lookupCustomerByEmail(emailInput.value); - - if (customerContextId) { - // Email is associated with a Connect profile or aPayPal member - // Authenticate the customer to get access to their profile - log('Email is associated with a Connect profile or a PayPal member'); - } else { - // No profile found with this email address. - // This is a guest customer. - log('No profile found with this email address.'); - - const fields = { - phoneNumber: { - prefill: "1234567890" - }, - - cardholderName: {} // optionally pass this to show the card holder name - }; - - jQuery("#payment-container").css({ - 'padding': '1rem 0', - 'background-color': '#ffffff', - }); - - const connectCardComponent = await connect - .ConnectCardComponent({fields}) - .render("#payment-container"); - - const submitButton = document.getElementById('submit-button'); - - // event listener when the customer clicks to place the order - submitButton.addEventListener("click", async ()=> { - const { nonce } = await connectCardComponent.tokenize({ - name: { - fullName: "John Doe" - }, - billingAddress: { - addressLine1: "2211 North 1st St", - adminArea1: "San Jose", - adminArea2: "CA", - postalCode: "95131", - countryCode: "US" - } - }); - - // Send the nonce and previously captured device data to server to complete checkout - log('nonce: ' + nonce); - - // fetch('submit.php', { - // method: 'POST', - // headers: { - // 'Content-Type': 'application/json', - // }, - // body: JSON.stringify({ - // nonce: nonce - // }), - // }) - // - // .then(response => { - // if (!response.ok) { - // throw new Error('Network response was not ok'); - // } - // return response.json(); - // }) - // .then(data => { - // console.log('Submit response', data); - // log(JSON.stringify(data)); - // }) - // .catch(error => { - // console.error('There has been a problem with your fetch operation:', error); - // }); - - }); - } - } - - emailInput.addEventListener("change", async ()=> { - emailUpdated(); - }); - - if (emailInput.value) { - emailUpdated(); - } - - })(); - -} +import AxoManager from "./AxoManager"; const bootstrap = () => { - - - jQuery(document).on('change', '#payment_method_ppcp-axo-gateway', (ev) => { - if(ev.target.checked) { - let emailRow = document.querySelector('#billing_email_field'); - jQuery(emailRow.parentNode).prepend(emailRow); - emailRow.querySelector('input').focus(); - - init(); - } else { - console.log('Checkbox is not checked.'); - // Additional actions when the checkbox is unchecked - } - }); - - let gatewaysRenderedInterval = setInterval(() => { - console.log('not rendered'); - if (document.querySelector('.wc_payment_methods')) { - console.log('YES rendered'); - clearInterval(gatewaysRenderedInterval); - jQuery('#payment_method_ppcp-axo-gateway').trigger('change'); - } - }, 100); - + const axo = new AxoManager(); } document.addEventListener( diff --git a/modules/ppcp-axo/src/AxoModule.php b/modules/ppcp-axo/src/AxoModule.php index 5c45e12a4..b9c952f18 100644 --- a/modules/ppcp-axo/src/AxoModule.php +++ b/modules/ppcp-axo/src/AxoModule.php @@ -102,6 +102,48 @@ class AxoModule implements ModuleInterface { 1 ); + add_action( + $this->checkout_button_renderer_hook(), + array( + $this, + 'axo_button_renderer', + ), + 11 + ); + + } + + /** + * Returns the action name that PayPal AXO button will use for rendering on the checkout page. + * + * @return string + */ + private function checkout_button_renderer_hook(): string { + /** + * The filter returning the action name that PayPal AXO button will use for rendering on the checkout page. + */ + return (string) apply_filters( 'woocommerce_paypal_payments_checkout_axo_renderer_hook', 'woocommerce_review_order_after_submit' ); + } + + /** + * Renders the HTML for the AXO submit button. + */ + public function axo_button_renderer() { + $id = 'axo-submit-button-container'; + + /** + * The WC filter returning the WC order button text. + * phpcs:disable WordPress.WP.I18n.TextDomainMismatch + */ + $label = apply_filters( 'woocommerce_order_button_text', __( 'Place order', 'woocommerce' ) ); + + printf( + '', + esc_attr( $id ), + esc_html( $label ) + ); } /** diff --git a/modules/ppcp-axo/webpack.config.js b/modules/ppcp-axo/webpack.config.js index d54838da6..95c7f0fc6 100644 --- a/modules/ppcp-axo/webpack.config.js +++ b/modules/ppcp-axo/webpack.config.js @@ -10,7 +10,7 @@ module.exports = { plugins: [ new DependencyExtractionWebpackPlugin() ], entry: { 'boot': path.resolve('./resources/js/boot.js'), - "styles": path.resolve('./resources/css/styles.scss') + 'styles': path.resolve('./resources/css/styles.scss') }, output: { path: path.resolve(__dirname, 'assets/'), From 91b2df246ffca78808a22868c2f65c65f1086c00 Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Mon, 12 Feb 2024 18:06:48 +0000 Subject: [PATCH 13/70] Refactor AXO module frontend --- modules/ppcp-axo/resources/css/styles.scss | 17 ++ modules/ppcp-axo/resources/js/AxoManager.js | 270 ++++++++++-------- modules/ppcp-axo/resources/js/Helper/Debug.js | 4 + .../ppcp-axo/resources/js/Helper/MockData.js | 32 +++ modules/ppcp-axo/resources/js/boot.js | 38 ++- modules/ppcp-axo/src/Gateway/AxoGateway.php | 7 +- 6 files changed, 226 insertions(+), 142 deletions(-) create mode 100644 modules/ppcp-axo/resources/js/Helper/Debug.js create mode 100644 modules/ppcp-axo/resources/js/Helper/MockData.js diff --git a/modules/ppcp-axo/resources/css/styles.scss b/modules/ppcp-axo/resources/css/styles.scss index 8b1378917..6fc9afb42 100644 --- a/modules/ppcp-axo/resources/css/styles.scss +++ b/modules/ppcp-axo/resources/css/styles.scss @@ -1 +1,18 @@ +.axo-card-icons { + padding: 4px 0 16px 25px; + + .ppcp-card-icon { + float: left !important; + } +} + +.axo-watermark-container { + max-width: 200px; + margin-top: 10px; +} + +.axo-payment-container { + padding: 1rem 0; + background-color: #ffffff; +} diff --git a/modules/ppcp-axo/resources/js/AxoManager.js b/modules/ppcp-axo/resources/js/AxoManager.js index b560108f4..5011ba877 100644 --- a/modules/ppcp-axo/resources/js/AxoManager.js +++ b/modules/ppcp-axo/resources/js/AxoManager.js @@ -1,192 +1,210 @@ import Fastlane from "./Entity/Fastlane"; +import MockData from "./Helper/MockData"; +import {log} from "./Helper/Debug"; +import {hide, show} from '../../../ppcp-button/resources/js/modules/Helper/Hiding'; class AxoManager { - constructor() { + constructor(jQuery) { this.initialized = false; this.fastlane = new Fastlane(); + this.$ = jQuery; + this.elements = { + gatewayRadioButton: { + selector: '#payment_method_ppcp-axo-gateway', + }, + defaultSubmitButton: { + selector: '#place_order', + }, + paymentContainer: { + id: 'axo-payment-container', + selector: '#axo-payment-container', + className: 'axo-payment-container' + }, + watermarkContainer: { + id: 'axo-watermark-container', + selector: '#axo-watermark-container', + className: 'axo-watermark-container' + }, + submitButtonContainer: { + selector: '#axo-submit-button-container' + }, + fieldBillingEmail: { + selector: '#billing_email_field' + }, + } - jQuery(document).on('change', '#payment_method_ppcp-axo-gateway', (ev) => { - if(ev.target.checked) { - let emailRow = document.querySelector('#billing_email_field'); - jQuery(emailRow.parentNode).prepend(emailRow); - emailRow.querySelector('input').focus(); + this.styles = { + root: { + backgroundColorPrimary: '#ffffff' + } + } - this.init(); + this.locale = 'en_us'; - // show stuff - jQuery('#place_order').hide(); - jQuery('#axo-submit-button-container').show(); - jQuery('#payment-container').show(); + // Listen to Gateway Radio button changes. + this.$(document).on('change', this.elements.gatewayRadioButton.selector, (ev) => { + if (ev.target.checked) { + this.showAxo(); } else { - console.log('Checkbox is not checked.'); - - // hide stuff - jQuery('#place_order').show(); - jQuery('#axo-submit-button-container').hide(); - jQuery('#payment-container').hide(); + this.hideAxo(); } }); - let gatewaysRenderedInterval = setInterval(() => { - console.log('not rendered'); - if (document.querySelector('.wc_payment_methods')) { - console.log('YES rendered'); - clearInterval(gatewaysRenderedInterval); - jQuery('#payment_method_ppcp-axo-gateway').trigger('change'); - } - }, 100); - - jQuery(document.body).on('updated_checkout payment_method_selected', () => { - jQuery('#payment_method_ppcp-axo-gateway').trigger('change'); + this.$(document).on('updated_checkout payment_method_selected', () => { + this.triggerGatewayChange(); }); - jQuery(document).on('click', '.ppcp-axo-order-button', () => { - try { - - this.connectCardComponent.tokenize({ - name: { - fullName: "John Doe" - }, - billingAddress: { - addressLine1: "2211 North 1st St", - adminArea1: "San Jose", - adminArea2: "CA", - postalCode: "95131", - countryCode: "US" - } - }).then((response) => { - - // Send the nonce and previously captured device data to server to complete checkout - this.log('nonce: ' + response.nonce); - alert('nonce: ' + response.nonce); - - // fetch('submit.php', { - // method: 'POST', - // headers: { - // 'Content-Type': 'application/json', - // }, - // body: JSON.stringify({ - // nonce: nonce - // }), - // }) - // - // .then(response => { - // if (!response.ok) { - // throw new Error('Network response was not ok'); - // } - // return response.json(); - // }) - // .then(data => { - // console.log('Submit response', data); - // this.log(JSON.stringify(data)); - // }) - // .catch(error => { - // console.error('There has been a problem with your fetch operation:', error); - // }); - - }); - - } catch (e) { - console.log('Error tokenizing.'); - } - + this.$(document).on('click', this.elements.submitButtonContainer.selector + ' button', () => { + this.onClickSubmitButton(); return false; }); } - init() { + showAxo() { + this.moveEmail(); + this.init(); + + show(this.elements.watermarkContainer.selector); + show(this.elements.paymentContainer.selector); + show(this.elements.submitButtonContainer.selector); + hide(this.elements.defaultSubmitButton.selector); + } + + hideAxo() { + hide(this.elements.watermarkContainer.selector); + hide(this.elements.paymentContainer.selector); + hide(this.elements.submitButtonContainer.selector); + show(this.elements.defaultSubmitButton.selector); + } + + moveEmail() { + // Move email row to first place. + let emailRow = document.querySelector(this.elements.fieldBillingEmail.selector); + emailRow.parentNode.prepend(emailRow); + emailRow.querySelector('input').focus(); + } + + async init() { if (this.initialized) { return; } this.initialized = true; - this.initManager(); + await this.connect(); + this.insertDomElements(); + this.renderWatermark(); + this.watchEmail(); } - async initManager() { - window.localStorage.setItem('axoEnv', 'sandbox'); - - const styles = { - root: { - backgroundColorPrimary: "#ffffff" - } - } - - const locale = 'en_us'; + async connect() { + window.localStorage.setItem('axoEnv', 'sandbox'); // TODO: check sandbox await this.fastlane.connect({ - locale, - styles + locale: this.locale, + styles: this.styles }); this.fastlane.setLocale('en_us'); + } - this.emailInput = document.querySelector('#billing_email_field input'); - - jQuery(this.emailInput).after(` -
+ insertDomElements() { + this.emailInput = document.querySelector(this.elements.fieldBillingEmail.selector + ' input'); + this.emailInput.insertAdjacentHTML('afterend', ` +
`); - jQuery('.payment_method_ppcp-axo-gateway').append(` -
+ const gatewayPaymentContainer = document.querySelector('.payment_method_ppcp-axo-gateway'); + gatewayPaymentContainer.insertAdjacentHTML('beforeend', ` +
`); + } + triggerGatewayChange() { + this.$(this.elements.gatewayRadioButton.selector).trigger('change'); + } + + renderWatermark() { this.fastlane.FastlaneWatermarkComponent({ includeAdditionalInfo: true - }).render('#watermark-container'); + }).render(this.elements.watermarkContainer.selector); + } - this.emailInput.addEventListener("change", async ()=> { - this.emailUpdated(); + watchEmail() { + this.emailInput = document.querySelector(this.elements.fieldBillingEmail.selector + ' input'); + this.emailInput.addEventListener('change', async ()=> { + this.onChangeEmail(); }); if (this.emailInput.value) { - this.emailUpdated(); + this.onChangeEmail(); } } - async emailUpdated () { - this.log('Email changed: ' + this.emailInput.value); + async onChangeEmail () { + log('Email changed: ' + this.emailInput.value); if (!this.emailInput.checkValidity()) { - this.log('The email address is not valid.'); + log('The email address is not valid.'); return; } const { customerContextId } = await this.fastlane.identity.lookupCustomerByEmail(this.emailInput.value); if (customerContextId) { - // Email is associated with a Connect profile or aPayPal member - // Authenticate the customer to get access to their profile - this.log('Email is associated with a Connect profile or a PayPal member'); + // Email is associated with a Connect profile or a PayPal member. + // Authenticate the customer to get access to their profile. + log('Email is associated with a Connect profile or a PayPal member'); } else { // No profile found with this email address. // This is a guest customer. - this.log('No profile found with this email address.'); - - const fields = { - phoneNumber: { - prefill: "1234567890" - }, - - cardholderName: {} // optionally pass this to show the card holder name - }; - - jQuery("#payment-container").css({ - 'padding': '1rem 0', - 'background-color': '#ffffff', - }); + log('No profile found with this email address.'); this.connectCardComponent = await this.fastlane - .FastlaneCardComponent({fields}) - .render("#payment-container"); + .FastlaneCardComponent(MockData.cardComponent()) + .render(this.elements.paymentContainer.selector); } } - log(message) { - console.log('[AXO] ', message); + onClickSubmitButton() { + try { + this.connectCardComponent.tokenize(MockData.cardComponentTokenize()).then((response) => { + this.submit(response.nonce); + }); + } catch (e) { + log('Error tokenizing.'); + } + } + + submit(nonce) { + // Send the nonce and previously captured device data to server to complete checkout + log('nonce: ' + nonce); + alert('nonce: ' + nonce); + + // fetch('submit.php', { + // method: 'POST', + // headers: { + // 'Content-Type': 'application/json', + // }, + // body: JSON.stringify({ + // nonce: nonce + // }), + // }) + // .then(response => { + // if (!response.ok) { + // throw new Error('Network response was not ok'); + // } + // return response.json(); + // }) + // .then(data => { + // log('Submit response', data); + // log(JSON.stringify(data)); + // }) + // .catch(error => { + // console.error('There has been a problem with your fetch operation:', error); + // }); } } diff --git a/modules/ppcp-axo/resources/js/Helper/Debug.js b/modules/ppcp-axo/resources/js/Helper/Debug.js new file mode 100644 index 000000000..560bb5c6a --- /dev/null +++ b/modules/ppcp-axo/resources/js/Helper/Debug.js @@ -0,0 +1,4 @@ + +export function log(...args) { + console.log('[AXO] ', ...args); +} diff --git a/modules/ppcp-axo/resources/js/Helper/MockData.js b/modules/ppcp-axo/resources/js/Helper/MockData.js new file mode 100644 index 000000000..195a38de8 --- /dev/null +++ b/modules/ppcp-axo/resources/js/Helper/MockData.js @@ -0,0 +1,32 @@ + +class MockData { + + static cardComponent() { + return { + fields: { + phoneNumber: { + prefill: "1234567890" + }, + cardholderName: {} // optionally pass this to show the card holder name + } + } + } + + static cardComponentTokenize() { + return { + name: { + fullName: "John Doe" + }, + billingAddress: { + addressLine1: "2211 North 1st St", + adminArea1: "San Jose", + adminArea2: "CA", + postalCode: "95131", + countryCode: "US" + } + } + } + +} + +export default MockData; diff --git a/modules/ppcp-axo/resources/js/boot.js b/modules/ppcp-axo/resources/js/boot.js index 45a6b6e70..830b4b15c 100644 --- a/modules/ppcp-axo/resources/js/boot.js +++ b/modules/ppcp-axo/resources/js/boot.js @@ -1,17 +1,31 @@ import AxoManager from "./AxoManager"; +import {loadPaypalScript} from "../../../ppcp-button/resources/js/modules/Helper/ScriptLoading"; -const bootstrap = () => { - const axo = new AxoManager(); +const bootstrap = (jQuery) => { + const axo = new AxoManager(jQuery); } -document.addEventListener( - 'DOMContentLoaded', - () => { - if (!typeof (PayPalCommerceGateway)) { - console.error('PayPal button could not be configured.'); - return; - } +(function ({ + ppcpConfig, + jQuery +}) { - bootstrap(); - }, -); + document.addEventListener( + 'DOMContentLoaded', + () => { + if (!typeof (PayPalCommerceGateway)) { + console.error('AXO could not be configured.'); + return; + } + + // Load PayPal + loadPaypalScript(ppcpConfig, () => { + bootstrap(jQuery); + }); + }, + ); + +})({ + ppcpConfig: window.PayPalCommerceGateway, + jQuery: window.jQuery +}); diff --git a/modules/ppcp-axo/src/Gateway/AxoGateway.php b/modules/ppcp-axo/src/Gateway/AxoGateway.php index 0aa0d2cc9..f40508672 100644 --- a/modules/ppcp-axo/src/Gateway/AxoGateway.php +++ b/modules/ppcp-axo/src/Gateway/AxoGateway.php @@ -123,14 +123,13 @@ class AxoGateway extends WC_Payment_Gateway { foreach ($cards as $card) { $images[] = ' '; } - return '
' . implode( '', $images ) . '
'; + return '
' . implode( '', $images ) . '
'; } } From e71c34913f2dab05fa6e614bd67645dfea8cfa72 Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Tue, 13 Feb 2024 14:37:50 +0000 Subject: [PATCH 14/70] Implement AXO settings --- modules/ppcp-axo/src/AxoModule.php | 8 ++++++-- modules/ppcp-axo/src/Gateway/AxoGateway.php | 16 +++++++++++++++- .../ppcp-wc-gateway/resources/css/common.scss | 9 +++++++++ 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/modules/ppcp-axo/src/AxoModule.php b/modules/ppcp-axo/src/AxoModule.php index b9c952f18..3561c707b 100644 --- a/modules/ppcp-axo/src/AxoModule.php +++ b/modules/ppcp-axo/src/AxoModule.php @@ -36,8 +36,12 @@ class AxoModule implements ModuleInterface { add_filter( 'woocommerce_payment_gateways', - function ( $methods ): array { - $methods[] = new AxoGateway(); + function ( $methods ) use ( $c ): array { + $settings = $c->get( 'wcgateway.settings' ); + + $methods[] = new AxoGateway( + $settings + ); return $methods; }, 1, diff --git a/modules/ppcp-axo/src/Gateway/AxoGateway.php b/modules/ppcp-axo/src/Gateway/AxoGateway.php index f40508672..9e8bd5817 100644 --- a/modules/ppcp-axo/src/Gateway/AxoGateway.php +++ b/modules/ppcp-axo/src/Gateway/AxoGateway.php @@ -10,6 +10,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Axo\Gateway; use WC_Payment_Gateway; +use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; /** * Class AXOGateway. @@ -18,11 +19,23 @@ class AxoGateway extends WC_Payment_Gateway { const ID = 'ppcp-axo-gateway'; + /** + * The settings. + * + * @var ContainerInterface + */ + protected $config; + /** * AXOGateway constructor. + * + * @param ContainerInterface $config The settings. */ public function __construct( + ContainerInterface $config ) { + $this->config = $config; + $this->id = self::ID; $this->method_title = __( 'Fastlane Debit & Credit Cards', 'woocommerce-paypal-payments' ); @@ -51,7 +64,8 @@ class AxoGateway extends WC_Payment_Gateway { // $this->icon = esc_url( $this->module_url ) . 'assets/images/axo.svg'; // TODO // $this->environment = $environment; - $this->update_option( 'enabled', 'yes' ); // TODO : depend on settings + $is_axo_enabled = $this->config->has( 'axo_enabled' ) && $this->config->get( 'axo_enabled' ); + $this->update_option( 'enabled', $is_axo_enabled ? 'yes' : 'no' ); } /** diff --git a/modules/ppcp-wc-gateway/resources/css/common.scss b/modules/ppcp-wc-gateway/resources/css/common.scss index 6ab2f4a56..8217bd3b3 100644 --- a/modules/ppcp-wc-gateway/resources/css/common.scss +++ b/modules/ppcp-wc-gateway/resources/css/common.scss @@ -39,3 +39,12 @@ font-weight: bold; } } + +.ppcp-field-indent { + background-color: #f9f9f9; + border: 1px solid #c3c4c7; + + & + .ppcp-field-indent { + border-top: 2px solid transparent; + } +} From ffb2de496daff371f4353aabecb6f05c6cbc0272 Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Wed, 14 Feb 2024 18:17:03 +0000 Subject: [PATCH 15/70] Refactor AXO module --- modules/ppcp-axo/extensions.php | 19 +- modules/ppcp-axo/services.php | 21 +++ modules/ppcp-axo/src/Assets/AxoManager.php | 166 ++++++++++++++++++ modules/ppcp-axo/src/AxoModule.php | 44 ++--- modules/ppcp-axo/src/Gateway/AxoGateway.php | 21 ++- .../src/Helper/PropertiesDictionary.php | 20 ++- .../ppcp-wc-gateway/resources/css/common.scss | 27 +-- 7 files changed, 258 insertions(+), 60 deletions(-) create mode 100644 modules/ppcp-axo/src/Assets/AxoManager.php diff --git a/modules/ppcp-axo/extensions.php b/modules/ppcp-axo/extensions.php index 0d0b7665c..942c856f1 100644 --- a/modules/ppcp-axo/extensions.php +++ b/modules/ppcp-axo/extensions.php @@ -84,6 +84,23 @@ return array( 'requirements' => array(), 'gateway' => 'paypal', ), + 'axo_email_widget' => array( + 'title' => __( 'Email Widget', 'woocommerce-paypal-payments' ), + 'type' => 'select', + 'desc_tip' => true, + 'description' => __( + 'This controls if the Hosted Email Widget should be used.', + 'woocommerce-paypal-payments' + ), + 'classes' => array( 'ppcp-field-indent' ), + 'class' => array(), + 'input_class' => array( 'wc-enhanced-select' ), + 'default' => 'pay', + 'options' => PropertiesDictionary::email_widget_options(), + 'screens' => array( State::STATE_ONBOARDED ), + 'gateway' => 'paypal', + 'requirements' => array(), + ), 'axo_address_widget' => array( 'title' => __( 'Address Widget', 'woocommerce-paypal-payments' ), 'type' => 'select', @@ -101,7 +118,7 @@ return array( 'gateway' => 'paypal', 'requirements' => array(), ), - 'axo_payment_widget' => array( + 'axo_payment_widget' => array( 'title' => __( 'Payment Widget', 'woocommerce-paypal-payments' ), 'type' => 'select', 'desc_tip' => true, diff --git a/modules/ppcp-axo/services.php b/modules/ppcp-axo/services.php index 76c87944c..0e11e2742 100644 --- a/modules/ppcp-axo/services.php +++ b/modules/ppcp-axo/services.php @@ -9,6 +9,8 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Axo; +use WooCommerce\PayPalCommerce\Axo\Assets\AxoManager; +use WooCommerce\PayPalCommerce\Axo\Gateway\AxoGateway; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; return array( @@ -24,4 +26,23 @@ return array( ); }, + 'axo.manager' => static function ( ContainerInterface $container ): AxoManager { + return new AxoManager( + $container->get( 'axo.url' ), + $container->get( 'ppcp.asset-version' ), + $container->get( 'session.handler' ), + $container->get( 'wcgateway.settings' ), + $container->get( 'onboarding.environment' ), + $container->get( 'wcgateway.settings.status' ), + $container->get( 'api.shop.currency' ), + $container->get( 'woocommerce.logger.woocommerce' ) + ); + }, + + 'axo.gateway' => static function ( ContainerInterface $container ): AxoGateway { + return new AxoGateway( + $container->get( 'wcgateway.settings' ) + ); + }, + ); diff --git a/modules/ppcp-axo/src/Assets/AxoManager.php b/modules/ppcp-axo/src/Assets/AxoManager.php new file mode 100644 index 000000000..af4c8fd14 --- /dev/null +++ b/modules/ppcp-axo/src/Assets/AxoManager.php @@ -0,0 +1,166 @@ +module_url = $module_url; + $this->version = $version; + $this->session_handler = $session_handler; + $this->settings = $settings; + $this->environment = $environment; + $this->settings_status = $settings_status; + $this->currency = $currency; + $this->logger = $logger; + } + + /** + * Enqueues scripts/styles. + * + * @return void + */ + public function enqueue() { + + // Register styles. + wp_register_style( + 'wc-ppcp-axo', + untrailingslashit( $this->module_url ) . '/assets/css/styles.css', + array(), + $this->version + ); + wp_enqueue_style( 'wc-ppcp-axo' ); + + // Register scripts. + wp_register_script( + 'wc-ppcp-axo', + untrailingslashit( $this->module_url ) . '/assets/js/boot.js', + array(), + $this->version, + true + ); + wp_enqueue_script( 'wc-ppcp-axo' ); + + wp_localize_script( + 'wc-ppcp-axo', + 'wc_ppcp_axo', + $this->script_data() + ); + } + + /** + * The configuration for AXO. + * + * @return array + */ + private function script_data() { + $email_widget = $this->settings->has( 'axo_email_widget' ) ? $this->settings->get( 'axo_email_widget' ) : null; + $address_widget = $this->settings->has( 'axo_address_widget' ) ? $this->settings->get( 'axo_address_widget' ) : null; + $payment_widget = $this->settings->has( 'axo_payment_widget' ) ? $this->settings->get( 'axo_payment_widget' ) : null; + + return array( + 'widgets' => array( + 'email' => $email_widget ?: 'render', + 'address' => $address_widget ?: 'render', + 'payment' => $payment_widget ?: 'render', + ) + ); + } + +} diff --git a/modules/ppcp-axo/src/AxoModule.php b/modules/ppcp-axo/src/AxoModule.php index 3561c707b..42e55282f 100644 --- a/modules/ppcp-axo/src/AxoModule.php +++ b/modules/ppcp-axo/src/AxoModule.php @@ -9,7 +9,8 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Axo; -use WooCommerce\PayPalCommerce\Axo\Gateway\AxoGateway; +use WooCommerce\PayPalCommerce\Axo\Assets\AxoManager; +use WooCommerce\PayPalCommerce\Button\Assets\SmartButtonInterface; use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider; use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface; use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface; @@ -37,11 +38,7 @@ class AxoModule implements ModuleInterface { add_filter( 'woocommerce_payment_gateways', function ( $methods ) use ( $c ): array { - $settings = $c->get( 'wcgateway.settings' ); - - $methods[] = new AxoGateway( - $settings - ); + $methods[] = $c->get('axo.gateway'); return $methods; }, 1, @@ -69,36 +66,15 @@ class AxoModule implements ModuleInterface { add_action( 'wp_enqueue_scripts', static function () use ( $c ) { - $module_url = $c->get( 'axo.url' ); - $version = '1'; + $manager = $c->get( 'axo.manager' ); + assert( $manager instanceof AxoManager ); - // Register styles. - wp_register_style( - 'wc-ppcp-axo', - untrailingslashit( $module_url ) . '/assets/css/styles.css', - array(), - $version - ); - wp_enqueue_style( 'wc-ppcp-axo' ); - - // Register scripts. - wp_register_script( - 'wc-ppcp-axo', - untrailingslashit( $module_url ) . '/assets/js/boot.js', - array(), - $version, - true - ); - wp_enqueue_script( 'wc-ppcp-axo' ); - - wp_localize_script( - 'wc-ppcp-axo', - 'wc_ppcp_axo', - array( - // TODO - ) - ); + $smart_button = $c->get( 'button.smart-button' ); + assert( $smart_button instanceof SmartButtonInterface ); + if ( $smart_button->should_load_ppcp_script() ) { + $manager->enqueue(); + } } ); diff --git a/modules/ppcp-axo/src/Gateway/AxoGateway.php b/modules/ppcp-axo/src/Gateway/AxoGateway.php index 9e8bd5817..f35825a01 100644 --- a/modules/ppcp-axo/src/Gateway/AxoGateway.php +++ b/modules/ppcp-axo/src/Gateway/AxoGateway.php @@ -24,24 +24,30 @@ class AxoGateway extends WC_Payment_Gateway { * * @var ContainerInterface */ - protected $config; + protected $ppcp_settings; /** * AXOGateway constructor. * - * @param ContainerInterface $config The settings. + * @param ContainerInterface $ppcp_settings The settings. */ public function __construct( - ContainerInterface $config + ContainerInterface $ppcp_settings ) { - $this->config = $config; + $this->ppcp_settings = $ppcp_settings; $this->id = self::ID; $this->method_title = __( 'Fastlane Debit & Credit Cards', 'woocommerce-paypal-payments' ); $this->method_description = __( 'Fastlane Debit & Credit Cards', 'woocommerce-paypal-payments' ); - $this->title = $this->get_option( 'title', $this->method_title ); + $is_axo_enabled = $this->ppcp_settings->has( 'axo_enabled' ) && $this->ppcp_settings->get( 'axo_enabled' ); + $this->update_option( 'enabled', $is_axo_enabled ? 'yes' : 'no' ); + + $this->title = $this->ppcp_settings->has( 'axo_gateway_title' ) + ? $this->ppcp_settings->get( 'axo_gateway_title' ) + : $this->get_option( 'title', $this->method_title ); + $this->description = $this->get_option( 'description', __( '', 'woocommerce-paypal-payments' ) ); $this->init_form_fields(); @@ -63,9 +69,6 @@ class AxoGateway extends WC_Payment_Gateway { // $this->icon = esc_url( $this->module_url ) . 'assets/images/axo.svg'; // TODO // $this->environment = $environment; - - $is_axo_enabled = $this->config->has( 'axo_enabled' ) && $this->config->get( 'axo_enabled' ); - $this->update_option( 'enabled', $is_axo_enabled ? 'yes' : 'no' ); } /** @@ -117,7 +120,7 @@ class AxoGateway extends WC_Payment_Gateway { // */ // private function is_enabled(): bool { // return true; -// //return $this->config->has( 'axo_enabled' ) && $this->config->get( 'axo_enabled' ); // TODO +// //return $this->ppcp_settings->has( 'axo_enabled' ) && $this->ppcp_settings->get( 'axo_enabled' ); // TODO // } /** diff --git a/modules/ppcp-axo/src/Helper/PropertiesDictionary.php b/modules/ppcp-axo/src/Helper/PropertiesDictionary.php index e1fc70ece..714e635f5 100644 --- a/modules/ppcp-axo/src/Helper/PropertiesDictionary.php +++ b/modules/ppcp-axo/src/Helper/PropertiesDictionary.php @@ -14,6 +14,18 @@ namespace WooCommerce\PayPalCommerce\Axo\Helper; */ class PropertiesDictionary { + /** + * Returns the possible list of possible email widget options. + * + * @return array + */ + public static function email_widget_options(): array { + return array( + 'render' => __( 'Render email input', 'woocommerce-paypal-payments' ), + 'use_widget' => __( 'Use email widget', 'woocommerce-paypal-payments' ), + ); + } + /** * Returns the possible list of possible address widget options. * @@ -21,8 +33,8 @@ class PropertiesDictionary { */ public static function address_widget_options(): array { return array( - 'render' => __( 'Render address options.', 'woocommerce-paypal-payments' ), - 'use_widget' => __( 'Use address widget.', 'woocommerce-paypal-payments' ), + 'render' => __( 'Render address options list', 'woocommerce-paypal-payments' ), + 'use_widget' => __( 'Use address widget', 'woocommerce-paypal-payments' ), ); } @@ -33,8 +45,8 @@ class PropertiesDictionary { */ public static function payment_widget_options(): array { return array( - 'render' => __( 'Render payment options.', 'woocommerce-paypal-payments' ), - 'use_widget' => __( 'Use payment widget.', 'woocommerce-paypal-payments' ), + 'render' => __( 'Render payment options list', 'woocommerce-paypal-payments' ), + 'use_widget' => __( 'Use payment widget', 'woocommerce-paypal-payments' ), ); } diff --git a/modules/ppcp-wc-gateway/resources/css/common.scss b/modules/ppcp-wc-gateway/resources/css/common.scss index 8217bd3b3..3cc34ae01 100644 --- a/modules/ppcp-wc-gateway/resources/css/common.scss +++ b/modules/ppcp-wc-gateway/resources/css/common.scss @@ -11,9 +11,21 @@ opacity: 0.5; } -.ppcp-field-indent { - th { - padding-left: 20px; +.ppcp-settings-field { + border-left: 1px solid transparent; + border-right: 1px solid transparent; + + &.ppcp-field-indent { + background-color: #f9f9f9; + border: 1px solid #c3c4c7; + + th { + padding-left: 20px; + } + + & + .ppcp-field-indent { + border-top: 2px solid transparent; + } } } @@ -39,12 +51,3 @@ font-weight: bold; } } - -.ppcp-field-indent { - background-color: #f9f9f9; - border: 1px solid #c3c4c7; - - & + .ppcp-field-indent { - border-top: 2px solid transparent; - } -} From 9fd69e7f55338d2c9eb0ebdae01ea0f8bda5cd50 Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Thu, 15 Feb 2024 17:58:56 +0000 Subject: [PATCH 16/70] Fix styles and behaviours on AXO module --- modules/ppcp-axo/extensions.php | 1 + modules/ppcp-axo/resources/css/styles.scss | 20 ++++- modules/ppcp-axo/resources/js/AxoManager.js | 90 ++++++++++++++----- .../ppcp-axo/resources/js/Helper/MockData.js | 8 +- modules/ppcp-axo/resources/js/boot.js | 12 +-- modules/ppcp-axo/src/AxoModule.php | 2 +- modules/ppcp-axo/src/Gateway/AxoGateway.php | 2 +- .../ppcp-wc-gateway/resources/css/common.scss | 15 +++- 8 files changed, 112 insertions(+), 38 deletions(-) diff --git a/modules/ppcp-axo/extensions.php b/modules/ppcp-axo/extensions.php index 942c856f1..3d30aa183 100644 --- a/modules/ppcp-axo/extensions.php +++ b/modules/ppcp-axo/extensions.php @@ -58,6 +58,7 @@ return array( ->rule() ->condition_element( 'axo_enabled', '1' ) ->action_visible( 'axo_gateway_title' ) + ->action_visible( 'axo_email_widget' ) ->action_visible( 'axo_address_widget' ) ->action_visible( 'axo_payment_widget' ) ->to_array(), diff --git a/modules/ppcp-axo/resources/css/styles.scss b/modules/ppcp-axo/resources/css/styles.scss index 6fc9afb42..1c41b9ef9 100644 --- a/modules/ppcp-axo/resources/css/styles.scss +++ b/modules/ppcp-axo/resources/css/styles.scss @@ -1,5 +1,5 @@ -.axo-card-icons { +.ppcp-axo-card-icons { padding: 4px 0 16px 25px; .ppcp-card-icon { @@ -7,12 +7,26 @@ } } -.axo-watermark-container { +.ppcp-axo-watermark-container { max-width: 200px; margin-top: 10px; } -.axo-payment-container { +.ppcp-axo-payment-container { padding: 1rem 0; background-color: #ffffff; + + &.hidden { + display: none; + } +} + +.ppcp-axo-email-widget { + border: 1px solid #cccccc; + background-color: #eeeeee; + padding: 0.5rem 1rem; + margin-bottom: 1rem; + text-align: center; + font-weight: bold; + color: #000000; } diff --git a/modules/ppcp-axo/resources/js/AxoManager.js b/modules/ppcp-axo/resources/js/AxoManager.js index 5011ba877..ad42096bd 100644 --- a/modules/ppcp-axo/resources/js/AxoManager.js +++ b/modules/ppcp-axo/resources/js/AxoManager.js @@ -5,7 +5,10 @@ import {hide, show} from '../../../ppcp-button/resources/js/modules/Helper/Hidin class AxoManager { - constructor(jQuery) { + constructor(axoConfig, ppcpConfig) { + this.axoConfig = axoConfig; + this.ppcpConfig = ppcpConfig; + this.initialized = false; this.fastlane = new Fastlane(); this.$ = jQuery; @@ -18,21 +21,26 @@ class AxoManager { selector: '#place_order', }, paymentContainer: { - id: 'axo-payment-container', - selector: '#axo-payment-container', - className: 'axo-payment-container' + id: 'ppcp-axo-payment-container', + selector: '#ppcp-axo-payment-container', + className: 'ppcp-axo-payment-container' }, watermarkContainer: { - id: 'axo-watermark-container', - selector: '#axo-watermark-container', - className: 'axo-watermark-container' + id: 'ppcp-axo-watermark-container', + selector: '#ppcp-axo-watermark-container', + className: 'ppcp-axo-watermark-container' }, - submitButtonContainer: { - selector: '#axo-submit-button-container' + emailWidgetContainer: { + id: 'ppcp-axo-email-widget', + selector: '#ppcp-axo-email-widget', + className: 'ppcp-axo-email-widget' }, fieldBillingEmail: { selector: '#billing_email_field' }, + submitButtonContainer: { + selector: '#ppcp-axo-submit-button-container' + }, } this.styles = { @@ -64,27 +72,52 @@ class AxoManager { } showAxo() { - this.moveEmail(); + this.initEmail(); this.init(); + show(this.elements.emailWidgetContainer.selector); show(this.elements.watermarkContainer.selector); show(this.elements.paymentContainer.selector); show(this.elements.submitButtonContainer.selector); hide(this.elements.defaultSubmitButton.selector); + + if (this.useEmailWidget()) { + hide(this.elements.fieldBillingEmail.selector); + } } hideAxo() { + hide(this.elements.emailWidgetContainer.selector); hide(this.elements.watermarkContainer.selector); hide(this.elements.paymentContainer.selector); hide(this.elements.submitButtonContainer.selector); show(this.elements.defaultSubmitButton.selector); + + if (this.useEmailWidget()) { + show(this.elements.fieldBillingEmail.selector); + } } - moveEmail() { - // Move email row to first place. + initEmail() { let emailRow = document.querySelector(this.elements.fieldBillingEmail.selector); - emailRow.parentNode.prepend(emailRow); - emailRow.querySelector('input').focus(); + + if (this.useEmailWidget()) { + + // Display email widget. + if (!document.querySelector(this.elements.emailWidgetContainer.selector)) { + emailRow.parentNode.insertAdjacentHTML('afterbegin', ` +
+ --- EMAIL WIDGET PLACEHOLDER --- +
+ `); + } + + } else { + + // Move email row to first place. + emailRow.parentNode.prepend(emailRow); + emailRow.querySelector('input').focus(); + } } async init() { @@ -118,7 +151,7 @@ class AxoManager { const gatewayPaymentContainer = document.querySelector('.payment_method_ppcp-axo-gateway'); gatewayPaymentContainer.insertAdjacentHTML('beforeend', ` -
+ `); } @@ -133,13 +166,22 @@ class AxoManager { } watchEmail() { - this.emailInput = document.querySelector(this.elements.fieldBillingEmail.selector + ' input'); - this.emailInput.addEventListener('change', async ()=> { - this.onChangeEmail(); - }); - if (this.emailInput.value) { - this.onChangeEmail(); + if (this.useEmailWidget()) { + + // TODO + + } else { + + this.emailInput = document.querySelector(this.elements.fieldBillingEmail.selector + ' input'); + this.emailInput.addEventListener('change', async ()=> { + this.onChangeEmail(); + }); + + if (this.emailInput.value) { + this.onChangeEmail(); + } + } } @@ -162,6 +204,8 @@ class AxoManager { // This is a guest customer. log('No profile found with this email address.'); + document.querySelector(this.elements.paymentContainer.selector)?.classList.remove('hidden'); + this.connectCardComponent = await this.fastlane .FastlaneCardComponent(MockData.cardComponent()) .render(this.elements.paymentContainer.selector); @@ -207,6 +251,10 @@ class AxoManager { // }); } + useEmailWidget() { + return this.axoConfig?.widgets?.email === 'use_widget'; + } + } export default AxoManager; diff --git a/modules/ppcp-axo/resources/js/Helper/MockData.js b/modules/ppcp-axo/resources/js/Helper/MockData.js index 195a38de8..48d89ba11 100644 --- a/modules/ppcp-axo/resources/js/Helper/MockData.js +++ b/modules/ppcp-axo/resources/js/Helper/MockData.js @@ -19,10 +19,10 @@ class MockData { }, billingAddress: { addressLine1: "2211 North 1st St", - adminArea1: "San Jose", - adminArea2: "CA", - postalCode: "95131", - countryCode: "US" + adminArea1: "San Jose", + adminArea2: "CA", + postalCode: "95131", + countryCode: "US" } } } diff --git a/modules/ppcp-axo/resources/js/boot.js b/modules/ppcp-axo/resources/js/boot.js index 830b4b15c..a8fb85a50 100644 --- a/modules/ppcp-axo/resources/js/boot.js +++ b/modules/ppcp-axo/resources/js/boot.js @@ -1,15 +1,16 @@ import AxoManager from "./AxoManager"; import {loadPaypalScript} from "../../../ppcp-button/resources/js/modules/Helper/ScriptLoading"; -const bootstrap = (jQuery) => { - const axo = new AxoManager(jQuery); -} - (function ({ + axoConfig, ppcpConfig, jQuery }) { + const bootstrap = () => { + const axo = new AxoManager(axoConfig, ppcpConfig); + } + document.addEventListener( 'DOMContentLoaded', () => { @@ -20,12 +21,13 @@ const bootstrap = (jQuery) => { // Load PayPal loadPaypalScript(ppcpConfig, () => { - bootstrap(jQuery); + bootstrap(); }); }, ); })({ + axoConfig: window.wc_ppcp_axo, ppcpConfig: window.PayPalCommerceGateway, jQuery: window.jQuery }); diff --git a/modules/ppcp-axo/src/AxoModule.php b/modules/ppcp-axo/src/AxoModule.php index 42e55282f..a960d474d 100644 --- a/modules/ppcp-axo/src/AxoModule.php +++ b/modules/ppcp-axo/src/AxoModule.php @@ -109,7 +109,7 @@ class AxoModule implements ModuleInterface { * Renders the HTML for the AXO submit button. */ public function axo_button_renderer() { - $id = 'axo-submit-button-container'; + $id = 'ppcp-axo-submit-button-container'; /** * The WC filter returning the WC order button text. diff --git a/modules/ppcp-axo/src/Gateway/AxoGateway.php b/modules/ppcp-axo/src/Gateway/AxoGateway.php index f35825a01..d7cadcbd2 100644 --- a/modules/ppcp-axo/src/Gateway/AxoGateway.php +++ b/modules/ppcp-axo/src/Gateway/AxoGateway.php @@ -146,7 +146,7 @@ class AxoGateway extends WC_Payment_Gateway { > '; } - return '
' . implode( '', $images ) . '
'; + return '
' . implode( '', $images ) . '
'; } } diff --git a/modules/ppcp-wc-gateway/resources/css/common.scss b/modules/ppcp-wc-gateway/resources/css/common.scss index 3cc34ae01..c452eb724 100644 --- a/modules/ppcp-wc-gateway/resources/css/common.scss +++ b/modules/ppcp-wc-gateway/resources/css/common.scss @@ -1,5 +1,8 @@ @use "../../../ppcp-button/resources/css/mixins/apm-button" as apm-button; +$border-color: #c3c3c3; +$background-ident-color: #f9f9f9; + .ppcp-field-hidden { display: none !important; } @@ -16,15 +19,21 @@ border-right: 1px solid transparent; &.ppcp-field-indent { - background-color: #f9f9f9; - border: 1px solid #c3c4c7; + background-color: $background-ident-color; + border: 1px solid $border-color; th { padding-left: 20px; } + th, td { + border-top: 1px solid $border-color; + } + & + .ppcp-field-indent { - border-top: 2px solid transparent; + th, td { + border-top: 1px solid $background-ident-color; + } } } } From 9d2525ffcb5a1489111427c27ab592000f7a250e Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Fri, 16 Feb 2024 15:04:36 +0000 Subject: [PATCH 17/70] Fix admin ident styles --- modules/ppcp-applepay/extensions.php | 11 ++++++++ modules/ppcp-axo/extensions.php | 1 + modules/ppcp-googlepay/extensions.php | 1 + .../ppcp-wc-gateway/resources/css/common.scss | 20 ++++++++++++-- .../common/display-manager/ActionFactory.js | 9 ++++--- .../display-manager/action/AttributeAction.js | 17 ++++++++++++ .../{ElementAction.js => VisibilityAction.js} | 4 +-- .../src/Helper/DisplayRule.php | 26 ++++++++++++++++--- .../Settings/Fields/connection-tab-fields.php | 2 ++ 9 files changed, 81 insertions(+), 10 deletions(-) create mode 100644 modules/ppcp-wc-gateway/resources/js/common/display-manager/action/AttributeAction.js rename modules/ppcp-wc-gateway/resources/js/common/display-manager/action/{ElementAction.js => VisibilityAction.js} (93%) diff --git a/modules/ppcp-applepay/extensions.php b/modules/ppcp-applepay/extensions.php index 302095e3a..df5007e8e 100644 --- a/modules/ppcp-applepay/extensions.php +++ b/modules/ppcp-applepay/extensions.php @@ -131,6 +131,16 @@ return array( $fields, 'allow_card_button_gateway', array( + 'spacer' => array( + 'title' => __( '', 'woocommerce-paypal-payments' ), + 'type' => 'ppcp-text', + 'text' => '', + 'class' => array(), + 'classes' => array( 'ppcp-active-spacer' ), + 'screens' => array( State::STATE_ONBOARDED ), + 'gateway' => 'paypal', + 'requirements' => array(), + ), 'applepay_button_enabled' => array( 'title' => __( 'Apple Pay Button', 'woocommerce-paypal-payments' ), 'type' => 'checkbox', @@ -160,6 +170,7 @@ return array( ->action_visible( 'applepay_button_type' ) ->action_visible( 'applepay_button_language' ) ->action_visible( 'applepay_checkout_data_mode' ) + ->action_class( 'applepay_button_enabled', 'active' ) ->to_array(), ) ), diff --git a/modules/ppcp-axo/extensions.php b/modules/ppcp-axo/extensions.php index 3d30aa183..be05e604e 100644 --- a/modules/ppcp-axo/extensions.php +++ b/modules/ppcp-axo/extensions.php @@ -61,6 +61,7 @@ return array( ->action_visible( 'axo_email_widget' ) ->action_visible( 'axo_address_widget' ) ->action_visible( 'axo_payment_widget' ) + ->action_class( 'axo_enabled', 'active' ) ->to_array(), ) ), diff --git a/modules/ppcp-googlepay/extensions.php b/modules/ppcp-googlepay/extensions.php index fe915aba4..5b4ce034b 100644 --- a/modules/ppcp-googlepay/extensions.php +++ b/modules/ppcp-googlepay/extensions.php @@ -129,6 +129,7 @@ return array( ->action_visible( 'googlepay_button_color' ) ->action_visible( 'googlepay_button_language' ) ->action_visible( 'googlepay_button_shipping_enabled' ) + ->action_class( 'googlepay_button_enabled', 'active' ) ->to_array(), ) ), diff --git a/modules/ppcp-wc-gateway/resources/css/common.scss b/modules/ppcp-wc-gateway/resources/css/common.scss index c452eb724..c672aa660 100644 --- a/modules/ppcp-wc-gateway/resources/css/common.scss +++ b/modules/ppcp-wc-gateway/resources/css/common.scss @@ -1,7 +1,7 @@ @use "../../../ppcp-button/resources/css/mixins/apm-button" as apm-button; $border-color: #c3c3c3; -$background-ident-color: #f9f9f9; +$background-ident-color: #fbfbfb; .ppcp-field-hidden { display: none !important; @@ -18,13 +18,22 @@ $background-ident-color: #f9f9f9; border-left: 1px solid transparent; border-right: 1px solid transparent; - &.ppcp-field-indent { + &.active { background-color: $background-ident-color; border: 1px solid $border-color; th { padding-left: 20px; } + } + + &.ppcp-field-indent { + background-color: $background-ident-color; + border: 1px solid $border-color; + + th { + padding-left: 40px; + } th, td { border-top: 1px solid $border-color; @@ -38,6 +47,13 @@ $background-ident-color: #f9f9f9; } } +.ppcp-active-spacer { + th, td { + padding: 0; + height: 1rem; + } +} + // Prevents spacing after button group. .ppcp-button-preview-inner { line-height: 0; diff --git a/modules/ppcp-wc-gateway/resources/js/common/display-manager/ActionFactory.js b/modules/ppcp-wc-gateway/resources/js/common/display-manager/ActionFactory.js index 8531e17e1..caeb4ba3c 100644 --- a/modules/ppcp-wc-gateway/resources/js/common/display-manager/ActionFactory.js +++ b/modules/ppcp-wc-gateway/resources/js/common/display-manager/ActionFactory.js @@ -1,10 +1,13 @@ -import ElementAction from "./action/ElementAction"; +import VisibilityAction from "./action/VisibilityAction"; +import AttributeAction from "./action/AttributeAction"; class ActionFactory { static make(actionConfig) { switch (actionConfig.type) { - case 'element': - return new ElementAction(actionConfig); + case 'visibility': + return new VisibilityAction(actionConfig); + case 'attribute': + return new AttributeAction(actionConfig); } throw new Error('[ActionFactory] Unknown action: ' + actionConfig.type); diff --git a/modules/ppcp-wc-gateway/resources/js/common/display-manager/action/AttributeAction.js b/modules/ppcp-wc-gateway/resources/js/common/display-manager/action/AttributeAction.js new file mode 100644 index 000000000..82a6a114a --- /dev/null +++ b/modules/ppcp-wc-gateway/resources/js/common/display-manager/action/AttributeAction.js @@ -0,0 +1,17 @@ +import BaseAction from "./BaseAction"; + +class AttributeAction extends BaseAction { + + run(status) { + + if (status) { + jQuery(this.config.selector).addClass(this.config.html_class); + } else { + jQuery(this.config.selector).removeClass(this.config.html_class); + } + + } + +} + +export default AttributeAction; diff --git a/modules/ppcp-wc-gateway/resources/js/common/display-manager/action/ElementAction.js b/modules/ppcp-wc-gateway/resources/js/common/display-manager/action/VisibilityAction.js similarity index 93% rename from modules/ppcp-wc-gateway/resources/js/common/display-manager/action/ElementAction.js rename to modules/ppcp-wc-gateway/resources/js/common/display-manager/action/VisibilityAction.js index f32c036a3..32bc1fb70 100644 --- a/modules/ppcp-wc-gateway/resources/js/common/display-manager/action/ElementAction.js +++ b/modules/ppcp-wc-gateway/resources/js/common/display-manager/action/VisibilityAction.js @@ -1,6 +1,6 @@ import BaseAction from "./BaseAction"; -class ElementAction extends BaseAction { +class VisibilityAction extends BaseAction { run(status) { @@ -32,4 +32,4 @@ class ElementAction extends BaseAction { } -export default ElementAction; +export default VisibilityAction; diff --git a/modules/ppcp-wc-gateway/src/Helper/DisplayRule.php b/modules/ppcp-wc-gateway/src/Helper/DisplayRule.php index 807ab4b36..ac65a8fcb 100644 --- a/modules/ppcp-wc-gateway/src/Helper/DisplayRule.php +++ b/modules/ppcp-wc-gateway/src/Helper/DisplayRule.php @@ -26,10 +26,12 @@ class DisplayRule { const CONDITION_OPERATION_EMPTY = 'empty'; const CONDITION_OPERATION_NOT_EMPTY = 'not_empty'; - const ACTION_TYPE_ELEMENT = 'element'; + const ACTION_TYPE_VISIBILITY = 'visibility'; + const ACTION_TYPE_ATTRIBUTE = 'attribute'; const ACTION_VISIBLE = 'visible'; const ACTION_ENABLE = 'enable'; + const ACTION_CLASS = 'class'; /** * The element selector. @@ -140,7 +142,7 @@ class DisplayRule { public function action_visible( string $selector ): self { $this->add_action( array( - 'type' => self::ACTION_TYPE_ELEMENT, + 'type' => self::ACTION_TYPE_VISIBILITY, 'selector' => $selector, 'action' => self::ACTION_VISIBLE, ) @@ -148,6 +150,24 @@ class DisplayRule { return $this; } + /** + * Adds a condition to add/remove a html class. + * + * @param string $selector The condition selector. + * @param string $class The class. + */ + public function action_class( string $selector, string $class ): self { + $this->add_action( + array( + 'type' => self::ACTION_TYPE_ATTRIBUTE, + 'selector' => $selector, + 'html_class' => $class, + 'action' => self::ACTION_CLASS, + ) + ); + return $this; + } + /** * Adds a condition to enable/disable the element. * @@ -156,7 +176,7 @@ class DisplayRule { public function action_enable( string $selector ): self { $this->add_action( array( - 'type' => self::ACTION_TYPE_ELEMENT, + 'type' => self::ACTION_TYPE_VISIBILITY, 'selector' => $selector, 'action' => self::ACTION_ENABLE, ) diff --git a/modules/ppcp-wc-gateway/src/Settings/Fields/connection-tab-fields.php b/modules/ppcp-wc-gateway/src/Settings/Fields/connection-tab-fields.php index 59bb440f1..a692e29a8 100644 --- a/modules/ppcp-wc-gateway/src/Settings/Fields/connection-tab-fields.php +++ b/modules/ppcp-wc-gateway/src/Settings/Fields/connection-tab-fields.php @@ -538,6 +538,7 @@ return function ( ContainerInterface $container, array $fields ): array { ->rule() ->condition_element( 'subtotal_mismatch_behavior', PurchaseUnitSanitizer::MODE_EXTRA_LINE ) ->action_visible( 'subtotal_mismatch_line_name' ) + ->action_class( 'subtotal_mismatch_behavior', 'active' ) ->to_array(), ) ), @@ -548,6 +549,7 @@ return function ( ContainerInterface $container, array $fields ): array { 'type' => 'text', 'desc_tip' => true, 'description' => __( 'The name of the extra line that will be sent to PayPal to correct the subtotal mismatch.', 'woocommerce-paypal-payments' ), + 'classes' => array( 'ppcp-field-indent' ), 'maxlength' => 22, 'default' => '', 'screens' => array( From 288abde7522822feb6a28e7aed836496363f20f0 Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Fri, 16 Feb 2024 17:31:59 +0000 Subject: [PATCH 18/70] Refactoring AXO module --- modules/ppcp-axo/services.php | 13 ++++- modules/ppcp-axo/src/Assets/AxoManager.php | 33 ++++++++++++ modules/ppcp-axo/src/AxoModule.php | 57 ++++----------------- modules/ppcp-axo/src/Gateway/AxoGateway.php | 57 +++++++++++---------- 4 files changed, 85 insertions(+), 75 deletions(-) diff --git a/modules/ppcp-axo/services.php b/modules/ppcp-axo/services.php index 0e11e2742..21daccdd1 100644 --- a/modules/ppcp-axo/services.php +++ b/modules/ppcp-axo/services.php @@ -41,7 +41,18 @@ return array( 'axo.gateway' => static function ( ContainerInterface $container ): AxoGateway { return new AxoGateway( - $container->get( 'wcgateway.settings' ) + $container->get( 'wcgateway.settings' ), + $container->get( 'wcgateway.url' ), + $container->get( 'axo.card_icons' ) + ); + }, + + 'axo.card_icons' => static function ( ContainerInterface $container ): array { + return array( + array('title' => 'Visa', 'file' => 'visa-dark.svg'), + array('title' => 'MasterCard', 'file' => 'mastercard-dark.svg'), + array('title' => 'American Express', 'file' => 'amex.svg'), + array('title' => 'Discover', 'file' => 'discover.svg'), ); }, diff --git a/modules/ppcp-axo/src/Assets/AxoManager.php b/modules/ppcp-axo/src/Assets/AxoManager.php index af4c8fd14..26e920461 100644 --- a/modules/ppcp-axo/src/Assets/AxoManager.php +++ b/modules/ppcp-axo/src/Assets/AxoManager.php @@ -163,4 +163,37 @@ class AxoManager { ); } + /** + * Returns the action name that PayPal AXO button will use for rendering on the checkout page. + * + * @return string + */ + public function checkout_button_renderer_hook(): string { + /** + * The filter returning the action name that PayPal AXO button will use for rendering on the checkout page. + */ + return (string) apply_filters( 'woocommerce_paypal_payments_checkout_axo_renderer_hook', 'woocommerce_review_order_after_submit' ); + } + + /** + * Renders the HTML for the AXO submit button. + */ + public function render_checkout_button() { + $id = 'ppcp-axo-submit-button-container'; + + /** + * The WC filter returning the WC order button text. + * phpcs:disable WordPress.WP.I18n.TextDomainMismatch + */ + $label = apply_filters( 'woocommerce_order_button_text', __( 'Place order', 'woocommerce' ) ); + + printf( + '', + esc_attr( $id ), + esc_html( $label ) + ); + } + } diff --git a/modules/ppcp-axo/src/AxoModule.php b/modules/ppcp-axo/src/AxoModule.php index a960d474d..3656d8b98 100644 --- a/modules/ppcp-axo/src/AxoModule.php +++ b/modules/ppcp-axo/src/AxoModule.php @@ -61,14 +61,13 @@ class AxoModule implements ModuleInterface { add_action( 'init', static function () use ( $c ) { + $manager = $c->get( 'axo.manager' ); + assert( $manager instanceof AxoManager ); // Enqueue frontend scripts. add_action( 'wp_enqueue_scripts', - static function () use ( $c ) { - $manager = $c->get( 'axo.manager' ); - assert( $manager instanceof AxoManager ); - + static function () use ( $c, $manager ) { $smart_button = $c->get( 'button.smart-button' ); assert( $smart_button instanceof SmartButtonInterface ); @@ -78,52 +77,18 @@ class AxoModule implements ModuleInterface { } ); + // Render submit button. + add_action( + $manager->checkout_button_renderer_hook(), + static function () use ( $c, $manager ) { + $manager->render_checkout_button(); + } + ); + }, 1 ); - add_action( - $this->checkout_button_renderer_hook(), - array( - $this, - 'axo_button_renderer', - ), - 11 - ); - - } - - /** - * Returns the action name that PayPal AXO button will use for rendering on the checkout page. - * - * @return string - */ - private function checkout_button_renderer_hook(): string { - /** - * The filter returning the action name that PayPal AXO button will use for rendering on the checkout page. - */ - return (string) apply_filters( 'woocommerce_paypal_payments_checkout_axo_renderer_hook', 'woocommerce_review_order_after_submit' ); - } - - /** - * Renders the HTML for the AXO submit button. - */ - public function axo_button_renderer() { - $id = 'ppcp-axo-submit-button-container'; - - /** - * The WC filter returning the WC order button text. - * phpcs:disable WordPress.WP.I18n.TextDomainMismatch - */ - $label = apply_filters( 'woocommerce_order_button_text', __( 'Place order', 'woocommerce' ) ); - - printf( - '', - esc_attr( $id ), - esc_html( $label ) - ); } /** diff --git a/modules/ppcp-axo/src/Gateway/AxoGateway.php b/modules/ppcp-axo/src/Gateway/AxoGateway.php index d7cadcbd2..53d438103 100644 --- a/modules/ppcp-axo/src/Gateway/AxoGateway.php +++ b/modules/ppcp-axo/src/Gateway/AxoGateway.php @@ -26,18 +26,36 @@ class AxoGateway extends WC_Payment_Gateway { */ protected $ppcp_settings; + /** + * The WcGateway module URL. + * + * @var string + */ + protected $wcgateway_module_url; + + /** + * The card icons. + * + * @var array + */ + protected $card_icons; + /** * AXOGateway constructor. * * @param ContainerInterface $ppcp_settings The settings. */ public function __construct( - ContainerInterface $ppcp_settings + ContainerInterface $ppcp_settings, + string $wcgateway_module_url, + array $card_icons ) { - $this->ppcp_settings = $ppcp_settings; - $this->id = self::ID; + $this->ppcp_settings = $ppcp_settings; + $this->wcgateway_module_url = $wcgateway_module_url; + $this->card_icons = $card_icons; + $this->method_title = __( 'Fastlane Debit & Credit Cards', 'woocommerce-paypal-payments' ); $this->method_description = __( 'Fastlane Debit & Credit Cards', 'woocommerce-paypal-payments' ); @@ -108,41 +126,24 @@ class AxoGateway extends WC_Payment_Gateway { return $result; } -// public function is_available() -// { -// return $this->is_enabled(); // parent::is_available(); -// } -// -// /** -// * Returns if the gateway is enabled. -// * -// * @return bool -// */ -// private function is_enabled(): bool { -// return true; -// //return $this->ppcp_settings->has( 'axo_enabled' ) && $this->ppcp_settings->get( 'axo_enabled' ); // TODO -// } - /** * Returns the icons of the gateway. * * @return string */ public function get_icon() { + $icon = parent::get_icon(); + + if ( empty( $this->card_icons ) ) { + return $icon; + } + $images = array(); - - $cards = array( - array('title' => 'Visa', 'file' => 'visa-dark.svg'), - array('title' => 'MasterCard', 'file' => 'mastercard-dark.svg'), - array('title' => 'American Express', 'file' => 'amex.svg'), - array('title' => 'Discover', 'file' => 'discover.svg'), - ); - - foreach ($cards as $card) { + foreach ($this->card_icons as $card) { $images[] = ' '; } From e0dd215d92d9b51fa9fc043d148d1ea3c56d87ce Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Mon, 19 Feb 2024 14:48:02 +0000 Subject: [PATCH 19/70] Fix lint Revert authentication --- .../src/Authentication/UserIdToken.php | 21 +++++---------- modules/ppcp-applepay/extensions.php | 4 +-- modules/ppcp-axo/extensions.php | 10 +++---- modules/ppcp-axo/services.php | 26 ++++++++++++++----- modules/ppcp-axo/src/Assets/AxoManager.php | 2 +- modules/ppcp-axo/src/AxoModule.php | 2 +- modules/ppcp-axo/src/Gateway/AxoGateway.php | 19 +++++--------- 7 files changed, 41 insertions(+), 43 deletions(-) diff --git a/modules/ppcp-api-client/src/Authentication/UserIdToken.php b/modules/ppcp-api-client/src/Authentication/UserIdToken.php index 887440208..d848228d7 100644 --- a/modules/ppcp-api-client/src/Authentication/UserIdToken.php +++ b/modules/ppcp-api-client/src/Authentication/UserIdToken.php @@ -74,25 +74,18 @@ class UserIdToken { $url = trailingslashit( $this->host ) . 'v1/oauth2/token?grant_type=client_credentials&response_type=id_token&intent=sdk_init'; if ( $target_customer_id ) { -// $url = add_query_arg( -// array( -// 'target_customer_id' => $target_customer_id, -// ), -// $url -// ); + $url = add_query_arg( + array( + 'target_customer_id' => $target_customer_id, + ), + $url + ); } - // TODO fix this to use Bearer instead of Basic auth: - $settings = PPCP::container()->get( 'wcgateway.settings' ); - $key = $settings->has( 'client_id' ) && $settings->get( 'client_id' ) ? $settings->get( 'client_id' ) : null; - $secret = $settings->has( 'client_secret' ) && $settings->get( 'client_secret' ) ? $settings->get( 'client_secret' ) : null; - $args = array( 'method' => 'POST', 'headers' => array( -// 'Authorization' => 'Bearer ' . $bearer->token(), - 'Authorization' => 'Basic ' . base64_encode( "$key:$secret" ), - + 'Authorization' => 'Bearer ' . $bearer->token(), 'Content-Type' => 'application/x-www-form-urlencoded', ), ); diff --git a/modules/ppcp-applepay/extensions.php b/modules/ppcp-applepay/extensions.php index df5007e8e..857fd7575 100644 --- a/modules/ppcp-applepay/extensions.php +++ b/modules/ppcp-applepay/extensions.php @@ -131,8 +131,8 @@ return array( $fields, 'allow_card_button_gateway', array( - 'spacer' => array( - 'title' => __( '', 'woocommerce-paypal-payments' ), + 'spacer' => array( + 'title' => '', 'type' => 'ppcp-text', 'text' => '', 'class' => array(), diff --git a/modules/ppcp-axo/extensions.php b/modules/ppcp-axo/extensions.php index be05e604e..ae2e4580e 100644 --- a/modules/ppcp-axo/extensions.php +++ b/modules/ppcp-axo/extensions.php @@ -35,7 +35,7 @@ return array( $fields, 'vault_enabled', array( - 'axo_enabled' => array( + 'axo_enabled' => array( 'title' => __( 'Fastlane', 'woocommerce-paypal-payments' ), 'type' => 'checkbox', 'label' => __( 'Enable Fastlane Checkout', 'woocommerce-paypal-payments' ) @@ -67,7 +67,7 @@ return array( ), ), ), - 'axo_gateway_title' => array( + 'axo_gateway_title' => array( 'title' => __( 'Gateway Title', 'woocommerce-paypal-payments' ), 'type' => 'text', 'classes' => array( 'ppcp-field-indent' ), @@ -86,7 +86,7 @@ return array( 'requirements' => array(), 'gateway' => 'paypal', ), - 'axo_email_widget' => array( + 'axo_email_widget' => array( 'title' => __( 'Email Widget', 'woocommerce-paypal-payments' ), 'type' => 'select', 'desc_tip' => true, @@ -103,7 +103,7 @@ return array( 'gateway' => 'paypal', 'requirements' => array(), ), - 'axo_address_widget' => array( + 'axo_address_widget' => array( 'title' => __( 'Address Widget', 'woocommerce-paypal-payments' ), 'type' => 'select', 'desc_tip' => true, @@ -120,7 +120,7 @@ return array( 'gateway' => 'paypal', 'requirements' => array(), ), - 'axo_payment_widget' => array( + 'axo_payment_widget' => array( 'title' => __( 'Payment Widget', 'woocommerce-paypal-payments' ), 'type' => 'select', 'desc_tip' => true, diff --git a/modules/ppcp-axo/services.php b/modules/ppcp-axo/services.php index 21daccdd1..c2338128d 100644 --- a/modules/ppcp-axo/services.php +++ b/modules/ppcp-axo/services.php @@ -15,7 +15,7 @@ use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; return array( - 'axo.url' => static function ( ContainerInterface $container ): string { + 'axo.url' => static function ( ContainerInterface $container ): string { $path = realpath( __FILE__ ); if ( false === $path ) { return ''; @@ -26,7 +26,7 @@ return array( ); }, - 'axo.manager' => static function ( ContainerInterface $container ): AxoManager { + 'axo.manager' => static function ( ContainerInterface $container ): AxoManager { return new AxoManager( $container->get( 'axo.url' ), $container->get( 'ppcp.asset-version' ), @@ -39,7 +39,7 @@ return array( ); }, - 'axo.gateway' => static function ( ContainerInterface $container ): AxoGateway { + 'axo.gateway' => static function ( ContainerInterface $container ): AxoGateway { return new AxoGateway( $container->get( 'wcgateway.settings' ), $container->get( 'wcgateway.url' ), @@ -49,10 +49,22 @@ return array( 'axo.card_icons' => static function ( ContainerInterface $container ): array { return array( - array('title' => 'Visa', 'file' => 'visa-dark.svg'), - array('title' => 'MasterCard', 'file' => 'mastercard-dark.svg'), - array('title' => 'American Express', 'file' => 'amex.svg'), - array('title' => 'Discover', 'file' => 'discover.svg'), + array( + 'title' => 'Visa', + 'file' => 'visa-dark.svg', + ), + array( + 'title' => 'MasterCard', + 'file' => 'mastercard-dark.svg', + ), + array( + 'title' => 'American Express', + 'file' => 'amex.svg', + ), + array( + 'title' => 'Discover', + 'file' => 'discover.svg', + ), ); }, diff --git a/modules/ppcp-axo/src/Assets/AxoManager.php b/modules/ppcp-axo/src/Assets/AxoManager.php index 26e920461..4a92208c6 100644 --- a/modules/ppcp-axo/src/Assets/AxoManager.php +++ b/modules/ppcp-axo/src/Assets/AxoManager.php @@ -159,7 +159,7 @@ class AxoManager { 'email' => $email_widget ?: 'render', 'address' => $address_widget ?: 'render', 'payment' => $payment_widget ?: 'render', - ) + ), ); } diff --git a/modules/ppcp-axo/src/AxoModule.php b/modules/ppcp-axo/src/AxoModule.php index 3656d8b98..3b07dcf62 100644 --- a/modules/ppcp-axo/src/AxoModule.php +++ b/modules/ppcp-axo/src/AxoModule.php @@ -38,7 +38,7 @@ class AxoModule implements ModuleInterface { add_filter( 'woocommerce_payment_gateways', function ( $methods ) use ( $c ): array { - $methods[] = $c->get('axo.gateway'); + $methods[] = $c->get( 'axo.gateway' ); return $methods; }, 1, diff --git a/modules/ppcp-axo/src/Gateway/AxoGateway.php b/modules/ppcp-axo/src/Gateway/AxoGateway.php index 53d438103..143cc12f2 100644 --- a/modules/ppcp-axo/src/Gateway/AxoGateway.php +++ b/modules/ppcp-axo/src/Gateway/AxoGateway.php @@ -44,6 +44,8 @@ class AxoGateway extends WC_Payment_Gateway { * AXOGateway constructor. * * @param ContainerInterface $ppcp_settings The settings. + * @param string $wcgateway_module_url The WcGateway module URL. + * @param array $card_icons The card icons. */ public function __construct( ContainerInterface $ppcp_settings, @@ -66,7 +68,7 @@ class AxoGateway extends WC_Payment_Gateway { ? $this->ppcp_settings->get( 'axo_gateway_title' ) : $this->get_option( 'title', $this->method_title ); - $this->description = $this->get_option( 'description', __( '', 'woocommerce-paypal-payments' ) ); + $this->description = $this->get_option( 'description', '' ); $this->init_form_fields(); $this->init_settings(); @@ -78,15 +80,6 @@ class AxoGateway extends WC_Payment_Gateway { 'process_admin_options', ) ); - -// $this->order_endpoint = $order_endpoint; -// $this->purchase_unit_factory = $purchase_unit_factory; -// $this->shipping_preference_factory = $shipping_preference_factory; -// $this->module_url = $module_url; -// $this->logger = $logger; - -// $this->icon = esc_url( $this->module_url ) . 'assets/images/axo.svg'; // TODO -// $this->environment = $environment; } /** @@ -94,7 +87,7 @@ class AxoGateway extends WC_Payment_Gateway { */ public function init_form_fields() { $this->form_fields = array( - 'enabled' => array( + 'enabled' => array( 'title' => __( 'Enable/Disable', 'woocommerce-paypal-payments' ), 'type' => 'checkbox', 'label' => __( 'AXO', 'woocommerce-paypal-payments' ), @@ -112,7 +105,7 @@ class AxoGateway extends WC_Payment_Gateway { * @return array */ public function process_payment( $order_id ) { - $wc_order = wc_get_order( $order_id ); + $wc_order = wc_get_order( $order_id ); // TODO ... @@ -139,7 +132,7 @@ class AxoGateway extends WC_Payment_Gateway { } $images = array(); - foreach ($this->card_icons as $card) { + foreach ( $this->card_icons as $card ) { $images[] = ' Date: Mon, 19 Feb 2024 18:17:47 +0000 Subject: [PATCH 20/70] Add AXO order processing --- .../src/Authentication/UserIdToken.php | 21 ++- modules/ppcp-axo/resources/js/AxoManager.js | 7 +- modules/ppcp-axo/services.php | 8 +- modules/ppcp-axo/src/Gateway/AxoGateway.php | 168 +++++++++++++++++- modules/ppcp-wc-gateway/services.php | 2 + 5 files changed, 191 insertions(+), 15 deletions(-) diff --git a/modules/ppcp-api-client/src/Authentication/UserIdToken.php b/modules/ppcp-api-client/src/Authentication/UserIdToken.php index d848228d7..887440208 100644 --- a/modules/ppcp-api-client/src/Authentication/UserIdToken.php +++ b/modules/ppcp-api-client/src/Authentication/UserIdToken.php @@ -74,18 +74,25 @@ class UserIdToken { $url = trailingslashit( $this->host ) . 'v1/oauth2/token?grant_type=client_credentials&response_type=id_token&intent=sdk_init'; if ( $target_customer_id ) { - $url = add_query_arg( - array( - 'target_customer_id' => $target_customer_id, - ), - $url - ); +// $url = add_query_arg( +// array( +// 'target_customer_id' => $target_customer_id, +// ), +// $url +// ); } + // TODO fix this to use Bearer instead of Basic auth: + $settings = PPCP::container()->get( 'wcgateway.settings' ); + $key = $settings->has( 'client_id' ) && $settings->get( 'client_id' ) ? $settings->get( 'client_id' ) : null; + $secret = $settings->has( 'client_secret' ) && $settings->get( 'client_secret' ) ? $settings->get( 'client_secret' ) : null; + $args = array( 'method' => 'POST', 'headers' => array( - 'Authorization' => 'Bearer ' . $bearer->token(), +// 'Authorization' => 'Bearer ' . $bearer->token(), + 'Authorization' => 'Basic ' . base64_encode( "$key:$secret" ), + 'Content-Type' => 'application/x-www-form-urlencoded', ), ); diff --git a/modules/ppcp-axo/resources/js/AxoManager.js b/modules/ppcp-axo/resources/js/AxoManager.js index ad42096bd..c1b1fc839 100644 --- a/modules/ppcp-axo/resources/js/AxoManager.js +++ b/modules/ppcp-axo/resources/js/AxoManager.js @@ -206,7 +206,7 @@ class AxoManager { document.querySelector(this.elements.paymentContainer.selector)?.classList.remove('hidden'); - this.connectCardComponent = await this.fastlane + this.cardComponent = await this.fastlane .FastlaneCardComponent(MockData.cardComponent()) .render(this.elements.paymentContainer.selector); } @@ -214,7 +214,7 @@ class AxoManager { onClickSubmitButton() { try { - this.connectCardComponent.tokenize(MockData.cardComponentTokenize()).then((response) => { + this.cardComponent.tokenize(MockData.cardComponentTokenize()).then((response) => { this.submit(response.nonce); }); } catch (e) { @@ -249,6 +249,9 @@ class AxoManager { // .catch(error => { // console.error('There has been a problem with your fetch operation:', error); // }); + + // Submit form. + document.querySelector(this.elements.defaultSubmitButton.selector).click(); } useEmailWidget() { diff --git a/modules/ppcp-axo/services.php b/modules/ppcp-axo/services.php index c2338128d..c1044f0d0 100644 --- a/modules/ppcp-axo/services.php +++ b/modules/ppcp-axo/services.php @@ -43,7 +43,13 @@ return array( return new AxoGateway( $container->get( 'wcgateway.settings' ), $container->get( 'wcgateway.url' ), - $container->get( 'axo.card_icons' ) + $container->get( 'axo.card_icons' ), + $container->get( 'api.endpoint.order' ), + $container->get( 'api.factory.purchase-unit' ), + $container->get( 'api.factory.shipping-preference' ), + $container->get( 'wcgateway.transaction-url-provider' ), + $container->get( 'onboarding.environment' ), + $container->get( 'woocommerce.logger.woocommerce' ) ); }, diff --git a/modules/ppcp-axo/src/Gateway/AxoGateway.php b/modules/ppcp-axo/src/Gateway/AxoGateway.php index 143cc12f2..8b3523e88 100644 --- a/modules/ppcp-axo/src/Gateway/AxoGateway.php +++ b/modules/ppcp-axo/src/Gateway/AxoGateway.php @@ -9,13 +9,27 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Axo\Gateway; +use Psr\Log\LoggerInterface; +use WC_Order; use WC_Payment_Gateway; +use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint; +use WooCommerce\PayPalCommerce\ApiClient\Entity\ApplicationContext; +use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentSource; +use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException; +use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; +use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory; +use WooCommerce\PayPalCommerce\ApiClient\Factory\ShippingPreferenceFactory; +use WooCommerce\PayPalCommerce\Onboarding\Environment; +use WooCommerce\PayPalCommerce\PPCP; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; +use WooCommerce\PayPalCommerce\WcGateway\Gateway\TransactionUrlProvider; +use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderMetaTrait; /** * Class AXOGateway. */ class AxoGateway extends WC_Payment_Gateway { + use OrderMetaTrait; const ID = 'ppcp-axo-gateway'; @@ -40,17 +54,71 @@ class AxoGateway extends WC_Payment_Gateway { */ protected $card_icons; + /** + * The order endpoint. + * + * @var OrderEndpoint + */ + protected $order_endpoint; + + /** + * The purchase unit factory. + * + * @var PurchaseUnitFactory + */ + protected $purchase_unit_factory; + + /** + * The shipping preference factory. + * + * @var ShippingPreferenceFactory + */ + protected $shipping_preference_factory; + + /** + * The transaction url provider. + * + * @var TransactionUrlProvider + */ + protected $transaction_url_provider; + + /** + * The environment. + * + * @var Environment + */ + protected $environment; + + /** + * The logger. + * + * @var LoggerInterface + */ + protected $logger; + /** * AXOGateway constructor. * - * @param ContainerInterface $ppcp_settings The settings. - * @param string $wcgateway_module_url The WcGateway module URL. - * @param array $card_icons The card icons. + * @param ContainerInterface $ppcp_settings The settings. + * @param string $wcgateway_module_url The WcGateway module URL. + * @param array $card_icons The card icons. + * @param OrderEndpoint $order_endpoint The order endpoint. + * @param PurchaseUnitFactory $purchase_unit_factory The purchase unit factory. + * @param ShippingPreferenceFactory $shipping_preference_factory The shipping preference factory. + * @param TransactionUrlProvider $transaction_url_provider The transaction url provider. + * @param Environment $environment The environment. + * @param LoggerInterface $logger The logger. */ public function __construct( ContainerInterface $ppcp_settings, string $wcgateway_module_url, - array $card_icons + array $card_icons, + OrderEndpoint $order_endpoint, + PurchaseUnitFactory $purchase_unit_factory, + ShippingPreferenceFactory $shipping_preference_factory, + TransactionUrlProvider $transaction_url_provider, + Environment $environment, + LoggerInterface $logger ) { $this->id = self::ID; @@ -80,6 +148,14 @@ class AxoGateway extends WC_Payment_Gateway { 'process_admin_options', ) ); + + $this->order_endpoint = $order_endpoint; + $this->purchase_unit_factory = $purchase_unit_factory; + $this->shipping_preference_factory = $shipping_preference_factory; + $this->logger = $logger; + + $this->transaction_url_provider = $transaction_url_provider; + $this->environment = $environment; } /** @@ -106,8 +182,78 @@ class AxoGateway extends WC_Payment_Gateway { */ public function process_payment( $order_id ) { $wc_order = wc_get_order( $order_id ); + $purchase_unit = $this->purchase_unit_factory->from_wc_order( $wc_order ); - // TODO ... + $nonce = 'tokencc_bh_vkjzxd_tgpd3n_qmjs7k_3vhxzg_p82'; + + try { + $shipping_preference = $this->shipping_preference_factory->from_state( + $purchase_unit, + 'checkout' + ); + + $payment_source_properties = new \stdClass(); + $payment_source_properties->single_use_token = $nonce; + + $payment_source = new PaymentSource( + 'card', + $payment_source_properties + ); + + $order = $this->order_endpoint->create( + array( $purchase_unit ), + $shipping_preference, + null, + null, + '', + ApplicationContext::USER_ACTION_CONTINUE, + '', + array(), + $payment_source + ); + + //$this->add_paypal_meta( $wc_order, $order, $this->environment ); + + // TODO: inject dependency + PPCP::container()->get( 'session.handler' )->replace_order( $order ); + PPCP::container()->get( 'wcgateway.order-processor' )->process( $wc_order ); + + + +// $payment_source = array( +// 'oxxo' => array( +// 'name' => $wc_order->get_billing_first_name() . ' ' . $wc_order->get_billing_last_name(), +// 'email' => $wc_order->get_billing_email(), +// 'country_code' => $wc_order->get_billing_country(), +// ), +// ); +// $payment_method = $this->order_endpoint->confirm_payment_source( $order->id(), $payment_source ); +// foreach ( $payment_method->links as $link ) { +// if ( $link->rel === 'payer-action' ) { +// $payer_action = $link->href; +// $wc_order->add_meta_data( 'ppcp_oxxo_payer_action', $payer_action ); +// $wc_order->save_meta_data(); +// } +// } + } catch ( RuntimeException $exception ) { + $error = $exception->getMessage(); + if ( is_a( $exception, PayPalApiException::class ) ) { + $error = $exception->get_details( $error ); + } + + $this->logger->error( $error ); + wc_add_notice( $error, 'error' ); + + $wc_order->update_status( + 'failed', + $error + ); + + return array( + 'result' => 'failure', + 'redirect' => wc_get_checkout_url(), + ); + } WC()->cart->empty_cart(); @@ -143,4 +289,16 @@ class AxoGateway extends WC_Payment_Gateway { return '
' . implode( '', $images ) . '
'; } + /** + * Return transaction url for this gateway and given order. + * + * @param WC_Order $order WC order to get transaction url by. + * + * @return string + */ + public function get_transaction_url( $order ): string { + $this->view_transaction_url = $this->transaction_url_provider->get_transaction_url_base( $order ); + + return parent::get_transaction_url( $order ); + } } diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index b5ae54b82..6fcaebbe5 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -17,6 +17,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Entity\ApplicationContext; use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache; use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies; +use WooCommerce\PayPalCommerce\Axo\Gateway\AxoGateway; use WooCommerce\PayPalCommerce\Button\Helper\MessagesDisclaimers; use WooCommerce\PayPalCommerce\Common\Pattern\SingletonDecorator; use WooCommerce\PayPalCommerce\Onboarding\Environment; @@ -1495,6 +1496,7 @@ return array( PayUponInvoiceGateway::ID, CardButtonGateway::ID, OXXOGateway::ID, + AxoGateway::ID, ); }, 'wcgateway.gateway-repository' => static function ( ContainerInterface $container ): GatewayRepository { From b7d43d3c5b473ff9dd40b6a3267fa6412fdc54fe Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Fri, 1 Mar 2024 15:05:17 +0000 Subject: [PATCH 21/70] Restructure Advanced Card Processing settings --- modules/ppcp-applepay/extensions.php | 24 +++--- modules/ppcp-axo/extensions.php | 12 +-- modules/ppcp-axo/src/Gateway/AxoGateway.php | 4 +- modules/ppcp-googlepay/extensions.php | 16 ++-- modules/ppcp-wc-gateway/services.php | 91 ++++++++++++++++----- 5 files changed, 100 insertions(+), 47 deletions(-) diff --git a/modules/ppcp-applepay/extensions.php b/modules/ppcp-applepay/extensions.php index 857fd7575..a0dc3dbf6 100644 --- a/modules/ppcp-applepay/extensions.php +++ b/modules/ppcp-applepay/extensions.php @@ -91,7 +91,7 @@ return array( $connection_link = ''; return $insert_after( $fields, - 'allow_card_button_gateway', + 'digital_wallet_heading', array( 'applepay_button_enabled' => array( 'title' => __( 'Apple Pay Button', 'woocommerce-paypal-payments' ), @@ -109,7 +109,7 @@ return array( . '

', 'default' => 'yes', 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', + 'gateway' => 'dcc', 'requirements' => array(), 'custom_attributes' => array( 'data-ppcp-display' => wp_json_encode( @@ -129,7 +129,7 @@ return array( return $insert_after( $fields, - 'allow_card_button_gateway', + 'digital_wallet_heading', array( 'spacer' => array( 'title' => '', @@ -138,7 +138,7 @@ return array( 'class' => array(), 'classes' => array( 'ppcp-active-spacer' ), 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', + 'gateway' => 'dcc', 'requirements' => array(), ), 'applepay_button_enabled' => array( @@ -155,7 +155,7 @@ return array( . '

', 'default' => 'yes', 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', + 'gateway' => 'dcc', 'requirements' => array(), 'custom_attributes' => array( 'data-ppcp-display' => wp_json_encode( @@ -194,7 +194,7 @@ return array( 'classes' => array( 'ppcp-field-indent' ), 'class' => array(), 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', + 'gateway' => 'dcc', 'requirements' => array(), ), 'applepay_button_domain_validation' => array( @@ -217,7 +217,7 @@ return array( 'classes' => array( 'ppcp-field-indent' ), 'class' => array(), 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', + 'gateway' => 'dcc', 'requirements' => array(), ), 'applepay_button_device_eligibility' => array( @@ -235,7 +235,7 @@ return array( 'classes' => array( 'ppcp-field-indent' ), 'class' => array(), 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', + 'gateway' => 'dcc', 'requirements' => array(), ), 'applepay_button_type' => array( @@ -252,7 +252,7 @@ return array( 'default' => 'pay', 'options' => PropertiesDictionary::button_types(), 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', + 'gateway' => 'dcc', 'requirements' => array(), ), 'applepay_button_color' => array( @@ -270,7 +270,7 @@ return array( 'default' => 'black', 'options' => PropertiesDictionary::button_colors(), 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', + 'gateway' => 'dcc', 'requirements' => array(), ), 'applepay_button_language' => array( @@ -287,7 +287,7 @@ return array( 'default' => 'en', 'options' => PropertiesDictionary::button_languages(), 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', + 'gateway' => 'dcc', 'requirements' => array(), ), 'applepay_checkout_data_mode' => array( @@ -301,7 +301,7 @@ return array( 'default' => PropertiesDictionary::BILLING_DATA_MODE_DEFAULT, 'options' => PropertiesDictionary::billing_data_modes(), 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', + 'gateway' => 'dcc', 'requirements' => array(), ), ) diff --git a/modules/ppcp-axo/extensions.php b/modules/ppcp-axo/extensions.php index ae2e4580e..c1f7f933f 100644 --- a/modules/ppcp-axo/extensions.php +++ b/modules/ppcp-axo/extensions.php @@ -33,7 +33,7 @@ return array( // Standard Payments tab fields. return $insert_after( $fields, - 'vault_enabled', + 'vault_enabled_dcc', array( 'axo_enabled' => array( 'title' => __( 'Fastlane', 'woocommerce-paypal-payments' ), @@ -49,7 +49,7 @@ return array( . '

', 'default' => 'yes', 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', + 'gateway' => 'dcc', 'requirements' => array(), 'custom_attributes' => array( 'data-ppcp-display' => wp_json_encode( @@ -84,7 +84,7 @@ return array( State::STATE_ONBOARDED, ), 'requirements' => array(), - 'gateway' => 'paypal', + 'gateway' => 'dcc', ), 'axo_email_widget' => array( 'title' => __( 'Email Widget', 'woocommerce-paypal-payments' ), @@ -100,7 +100,7 @@ return array( 'default' => 'pay', 'options' => PropertiesDictionary::email_widget_options(), 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', + 'gateway' => 'dcc', 'requirements' => array(), ), 'axo_address_widget' => array( @@ -117,7 +117,7 @@ return array( 'default' => 'pay', 'options' => PropertiesDictionary::address_widget_options(), 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', + 'gateway' => 'dcc', 'requirements' => array(), ), 'axo_payment_widget' => array( @@ -135,7 +135,7 @@ return array( 'default' => 'black', 'options' => PropertiesDictionary::payment_widget_options(), 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', + 'gateway' => 'dcc', 'requirements' => array(), ), ) diff --git a/modules/ppcp-axo/src/Gateway/AxoGateway.php b/modules/ppcp-axo/src/Gateway/AxoGateway.php index 8b3523e88..32f9f83e9 100644 --- a/modules/ppcp-axo/src/Gateway/AxoGateway.php +++ b/modules/ppcp-axo/src/Gateway/AxoGateway.php @@ -181,7 +181,7 @@ class AxoGateway extends WC_Payment_Gateway { * @return array */ public function process_payment( $order_id ) { - $wc_order = wc_get_order( $order_id ); + $wc_order = wc_get_order( $order_id ); $purchase_unit = $this->purchase_unit_factory->from_wc_order( $wc_order ); $nonce = 'tokencc_bh_vkjzxd_tgpd3n_qmjs7k_3vhxzg_p82'; @@ -192,7 +192,7 @@ class AxoGateway extends WC_Payment_Gateway { 'checkout' ); - $payment_source_properties = new \stdClass(); + $payment_source_properties = new \stdClass(); $payment_source_properties->single_use_token = $nonce; $payment_source = new PaymentSource( diff --git a/modules/ppcp-googlepay/extensions.php b/modules/ppcp-googlepay/extensions.php index 5b4ce034b..6a023851b 100644 --- a/modules/ppcp-googlepay/extensions.php +++ b/modules/ppcp-googlepay/extensions.php @@ -62,7 +62,7 @@ return array( $connection_link = '
'; return $insert_after( $fields, - 'allow_card_button_gateway', + 'digital_wallet_heading', array( 'googlepay_button_enabled' => array( 'title' => __( 'Google Pay Button', 'woocommerce-paypal-payments' ), @@ -80,7 +80,7 @@ return array( . '

', 'default' => 'yes', 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', + 'gateway' => 'dcc', 'requirements' => array(), 'custom_attributes' => array( 'data-ppcp-display' => wp_json_encode( @@ -101,7 +101,7 @@ return array( // Standard Payments tab fields. return $insert_after( $fields, - 'allow_card_button_gateway', + 'digital_wallet_heading', array( 'googlepay_button_enabled' => array( 'title' => __( 'Google Pay Button', 'woocommerce-paypal-payments' ), @@ -117,7 +117,7 @@ return array( . '

', 'default' => 'yes', 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', + 'gateway' => 'dcc', 'requirements' => array(), 'custom_attributes' => array( 'data-ppcp-display' => wp_json_encode( @@ -149,7 +149,7 @@ return array( 'default' => 'pay', 'options' => PropertiesDictionary::button_types(), 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', + 'gateway' => 'dcc', 'requirements' => array(), ), 'googlepay_button_color' => array( @@ -167,7 +167,7 @@ return array( 'default' => 'black', 'options' => PropertiesDictionary::button_colors(), 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', + 'gateway' => 'dcc', 'requirements' => array(), ), 'googlepay_button_language' => array( @@ -184,7 +184,7 @@ return array( 'default' => 'en', 'options' => PropertiesDictionary::button_languages(), 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', + 'gateway' => 'dcc', 'requirements' => array(), ), 'googlepay_button_shipping_enabled' => array( @@ -199,7 +199,7 @@ return array( 'label' => __( 'Enable Google Pay shipping callback', 'woocommerce-paypal-payments' ), 'default' => 'no', 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', + 'gateway' => 'dcc', 'requirements' => array(), ), ) diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index 6fcaebbe5..73fe84d73 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -792,25 +792,6 @@ return array( ), 'gateway' => 'dcc', ), - 'vault_enabled_dcc' => array( - 'title' => __( 'Vaulting', 'woocommerce-paypal-payments' ), - 'type' => 'checkbox', - 'desc_tip' => true, - 'label' => sprintf( - // translators: %1$s and %2$s are the opening and closing of HTML
tag. - __( 'Securely store your customers’ credit cards for a seamless checkout experience and subscription features. Payment methods are saved in the secure %1$sPayPal Vault%2$s.', 'woocommerce-paypal-payments' ), - '', - '' - ), - 'description' => __( 'Allow registered buyers to save Credit Card payments.', 'woocommerce-paypal-payments' ), - 'default' => false, - 'screens' => array( - State::STATE_ONBOARDED, - ), - 'requirements' => array(), - 'gateway' => 'dcc', - 'input_class' => $container->get( 'wcgateway.helper.vaulting-scope' ) ? array() : array( 'ppcp-disabled-checkbox' ), - ), '3d_secure_heading' => array( 'heading' => __( '3D Secure', 'woocommerce-paypal-payments' ), 'type' => 'ppcp-heading', @@ -867,6 +848,52 @@ return array( ), 'gateway' => 'dcc', ), + 'saved_payments_heading' => array( + 'heading' => __( 'Saved Payments', 'woocommerce-paypal-payments' ), + 'type' => 'ppcp-heading', + 'description' => wp_kses_post( + sprintf( + // translators: %1$s and %2$s is a link tag. + __( + 'PayPal can securely store your customers’ payment methods for + %1$sfuture payments and subscriptions%2$s, simplifying the checkout + process and enabling recurring transactions on your website.', + 'woocommerce-paypal-payments' + ), + '', + '' + ) + ), + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array( + 'dcc', + ), + 'gateway' => 'dcc', + ), + 'vault_enabled_dcc' => array( + 'title' => __( 'Vaulting', 'woocommerce-paypal-payments' ), + 'type' => 'checkbox', + 'desc_tip' => true, + 'label' => sprintf( + // translators: %1$s and %2$s are the opening and closing of HTML tag. + __( 'Securely store your customers’ credit cards for a seamless checkout experience and subscription features. Payment methods are saved in the secure %1$sPayPal Vault%2$s.', 'woocommerce-paypal-payments' ), + '', + '' + ), + 'description' => __( 'Allow registered buyers to save Credit Card payments.', 'woocommerce-paypal-payments' ), + 'default' => false, + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array(), + 'gateway' => 'dcc', + 'input_class' => $container->get( 'wcgateway.helper.vaulting-scope' ) ? array() : array( 'ppcp-disabled-checkbox' ), + ), 'paypal_saved_payments' => array( 'heading' => __( 'Saved payments', 'woocommerce-paypal-payments' ), 'description' => sprintf( @@ -905,6 +932,32 @@ return array( 'gateway' => 'paypal', 'input_class' => $container->get( 'wcgateway.helper.vaulting-scope' ) ? array() : array( 'ppcp-disabled-checkbox' ), ), + 'digital_wallet_heading' => array( + 'heading' => __( 'Digital Wallet Services', 'woocommerce-paypal-payments' ), + 'type' => 'ppcp-heading', + 'description' => wp_kses_post( + sprintf( + // translators: %1$s and %2$s is a link tag. + __( + 'PayPal supports digital wallet services like Apple Pay or Google Pay + to give your buyers more options to pay without a PayPal account.', + 'woocommerce-paypal-payments' + ), + '', + '' + ) + ), + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array( + 'dcc', + ), + 'gateway' => 'dcc', + ), ); if ( ! $subscription_helper->plugin_is_active() ) { From 039cc53f40e9108cf94dc0c0aa1c63eeff2d1121 Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Fri, 8 Mar 2024 14:39:50 +0000 Subject: [PATCH 22/70] Refactor axo module --- .../src/Authentication/UserIdToken.php | 25 +- modules/ppcp-axo/resources/css/styles.scss | 4 + modules/ppcp-axo/resources/js/AxoManager.js | 428 ++++++++++++++++-- .../resources/js/Components/DomElement.js | 13 + .../js/Components/DomElementCollection.js | 57 +++ .../resources/js/Helper/FormFieldGroup.js | 105 +++++ 6 files changed, 581 insertions(+), 51 deletions(-) create mode 100644 modules/ppcp-axo/resources/js/Components/DomElement.js create mode 100644 modules/ppcp-axo/resources/js/Components/DomElementCollection.js create mode 100644 modules/ppcp-axo/resources/js/Helper/FormFieldGroup.js diff --git a/modules/ppcp-api-client/src/Authentication/UserIdToken.php b/modules/ppcp-api-client/src/Authentication/UserIdToken.php index 887440208..2ac1256d2 100644 --- a/modules/ppcp-api-client/src/Authentication/UserIdToken.php +++ b/modules/ppcp-api-client/src/Authentication/UserIdToken.php @@ -72,27 +72,20 @@ class UserIdToken { public function id_token( string $target_customer_id = '' ): string { $bearer = $this->bearer->bearer(); - $url = trailingslashit( $this->host ) . 'v1/oauth2/token?grant_type=client_credentials&response_type=id_token&intent=sdk_init'; + $url = trailingslashit( $this->host ) . 'v1/oauth2/token?grant_type=client_credentials&response_type=client_token&intent=sdk_init'; if ( $target_customer_id ) { -// $url = add_query_arg( -// array( -// 'target_customer_id' => $target_customer_id, -// ), -// $url -// ); + $url = add_query_arg( + array( + 'target_customer_id' => $target_customer_id, + ), + $url + ); } - // TODO fix this to use Bearer instead of Basic auth: - $settings = PPCP::container()->get( 'wcgateway.settings' ); - $key = $settings->has( 'client_id' ) && $settings->get( 'client_id' ) ? $settings->get( 'client_id' ) : null; - $secret = $settings->has( 'client_secret' ) && $settings->get( 'client_secret' ) ? $settings->get( 'client_secret' ) : null; - $args = array( 'method' => 'POST', 'headers' => array( -// 'Authorization' => 'Bearer ' . $bearer->token(), - 'Authorization' => 'Basic ' . base64_encode( "$key:$secret" ), - + 'Authorization' => 'Bearer ' . $bearer->token(), 'Content-Type' => 'application/x-www-form-urlencoded', ), ); @@ -108,6 +101,6 @@ class UserIdToken { throw new PayPalApiException( $json, $status_code ); } - return $json->id_token; + return $json->access_token; } } diff --git a/modules/ppcp-axo/resources/css/styles.scss b/modules/ppcp-axo/resources/css/styles.scss index 1c41b9ef9..7b9c3c3e9 100644 --- a/modules/ppcp-axo/resources/css/styles.scss +++ b/modules/ppcp-axo/resources/css/styles.scss @@ -30,3 +30,7 @@ font-weight: bold; color: #000000; } + +.ppcp-axo-field-hidden { + display: none; +} diff --git a/modules/ppcp-axo/resources/js/AxoManager.js b/modules/ppcp-axo/resources/js/AxoManager.js index c1b1fc839..75de6d97c 100644 --- a/modules/ppcp-axo/resources/js/AxoManager.js +++ b/modules/ppcp-axo/resources/js/AxoManager.js @@ -2,6 +2,7 @@ import Fastlane from "./Entity/Fastlane"; import MockData from "./Helper/MockData"; import {log} from "./Helper/Debug"; import {hide, show} from '../../../ppcp-button/resources/js/modules/Helper/Hiding'; +import FormFieldGroup from "./Helper/FormFieldGroup"; class AxoManager { @@ -13,6 +14,15 @@ class AxoManager { this.fastlane = new Fastlane(); this.$ = jQuery; + this.isConnectProfile = false; + this.hideGatewaySelection = false; + + this.data = { + billing: null, + shipping: null, + card: null, + } + this.elements = { gatewayRadioButton: { selector: '#payment_method_ppcp-axo-gateway', @@ -35,11 +45,24 @@ class AxoManager { selector: '#ppcp-axo-email-widget', className: 'ppcp-axo-email-widget' }, + shippingAddressContainer: { + id: 'ppcp-axo-shipping-address-container', + selector: '#ppcp-axo-shipping-address-container', + className: 'ppcp-axo-shipping-address-container', + anchorSelector: '.woocommerce-shipping-fields' + }, + billingAddressContainer: { + id: 'ppcp-axo-billing-address-container', + selector: '#ppcp-axo-billing-address-container', + className: 'ppcp-axo-billing-address-container', + anchorSelector: '.woocommerce-billing-fields__field-wrapper' + }, fieldBillingEmail: { selector: '#billing_email_field' }, submitButtonContainer: { - selector: '#ppcp-axo-submit-button-container' + selector: '#ppcp-axo-submit-button-container', + buttonSelector: '#ppcp-axo-submit-button-container button' }, } @@ -51,6 +74,226 @@ class AxoManager { this.locale = 'en_us'; + this.registerEventHandlers(); + + this.shippingFormFields = new FormFieldGroup({ + baseSelector: '.woocommerce-checkout', + contentSelector: this.elements.shippingAddressContainer.selector, + template: (data) => { + const valueOfSelect = (selectSelector, key) => { + const selectElement = document.querySelector(selectSelector); + const option = selectElement.querySelector(`option[value="${key}"]`); + return option ? option.textContent : key; + } + + if (data.isEditing()) { + return ` +
+

Shipping details Save

+
+ `; + } + if (data.isEmpty()) { + return ` +
+

Shipping details Edit

+
Please fill in your shipping details.
+
+ `; + } + return ` +
+

Shipping details Edit

+
${data.value('company')}
+
${data.value('firstName')} ${data.value('lastName')}
+
${data.value('street1')}
+
${data.value('street2')}
+
${data.value('postCode')} ${data.value('city')}
+
${valueOfSelect('#shipping_state', data.value('stateCode'))}
+
${valueOfSelect('#shipping_country', data.value('countryCode'))}
+
+ `; + }, + fields: { + firstName: { + 'key': 'firstName', + 'selector': '#shipping_first_name_field', + 'valuePath': 'shipping.name.firstName', + }, + lastName: { + 'selector': '#shipping_last_name_field', + 'valuePath': 'shipping.name.lastName', + }, + street1: { + 'selector': '#shipping_address_1_field', + 'valuePath': 'shipping.address.addressLine1', + }, + street2: { + 'selector': '#shipping_address_2_field', + 'valuePath': null + }, + postCode: { + 'selector': '#shipping_postcode_field', + 'valuePath': 'shipping.address.postalCode', + }, + city: { + 'selector': '#shipping_city_field', + 'valuePath': 'shipping.address.adminArea2', + }, + stateCode: { + 'selector': '#shipping_state_field', + 'valuePath': 'shipping.address.adminArea1', + }, + countryCode: { + 'selector': '#shipping_country_field', + 'valuePath': 'shipping.address.countryCode', + }, + company: { + 'selector': '#shipping_company_field', + 'valuePath': null, + }, + shipDifferentAddress: { + 'selector': '#ship-to-different-address', + 'valuePath': null, + } + } + }); + + this.billingFormFields = new FormFieldGroup({ + baseSelector: '.woocommerce-checkout', + contentSelector: this.elements.billingAddressContainer.selector, + template: (data) => { + const valueOfSelect = (selectSelector, key) => { + const selectElement = document.querySelector(selectSelector); + const option = selectElement.querySelector(`option[value="${key}"]`); + return option ? option.textContent : key; + } + + if (data.isEditing()) { + return ` +
+

Save

+
+ `; + } + if (data.isEmpty()) { + return ` +
+
Please fill in your billing details.
+

Edit

+
+ `; + } + return ` +
+

Billing address

+
${data.value('company')}
+
${data.value('firstName')} ${data.value('lastName')}
+
${data.value('street1')}
+
${data.value('street2')}
+
${data.value('postCode')} ${data.value('city')}
+
${valueOfSelect('#billing_state', data.value('stateCode'))}
+
${valueOfSelect('#billing_country', data.value('countryCode'))}
+
${data.value('phone')}
+

Edit

+
+ `; + }, + fields: { + firstName: { + 'selector': '#billing_first_name_field', + 'valuePath': 'billing.name.firstName', + }, + lastName: { + 'selector': '#billing_last_name_field', + 'valuePath': 'billing.name.lastName', + }, + street1: { + 'selector': '#billing_address_1_field', + 'valuePath': 'billing.address.addressLine1', + }, + street2: { + 'selector': '#billing_address_2_field', + 'valuePath': null + }, + postCode: { + 'selector': '#billing_postcode_field', + 'valuePath': 'billing.address.postalCode', + }, + city: { + 'selector': '#billing_city_field', + 'valuePath': 'billing.address.adminArea2', + }, + stateCode: { + 'selector': '#billing_state_field', + 'valuePath': 'billing.address.adminArea1', + }, + countryCode: { + 'selector': '#billing_country_field', + 'valuePath': 'billing.address.countryCode', + }, + company: { + 'selector': '#billing_company_field', + 'valuePath': null, + }, + phone: { + 'selector': '#billing_phone_field', + 'valuePath': null, + }, + } + }); + + this.cardFormFields = new FormFieldGroup({ + baseSelector: '.ppcp-axo-payment-container', + contentSelector: this.elements.paymentContainer.selector, + template: (data) => { + const selectOtherPaymentMethod = () => { + if (!this.hideGatewaySelection) { + return ''; + } + return `

Select other payment method

`; + }; + + if (data.isEmpty()) { + return ` +
+
Please fill in your card details.
+

Edit

+ ${selectOtherPaymentMethod()} +
+ `; + } + return ` +
+

Card Details Edit

+
${data.value('name')}
+
${data.value('brand')}
+
${data.value('lastDigits') ? '************' + data.value('lastDigits'): ''}
+
${data.value('expiry')}
+ ${selectOtherPaymentMethod()} +
+ `; + }, + fields: { + brand: { + 'valuePath': 'card.paymentSource.card.brand', + }, + expiry: { + 'valuePath': 'card.paymentSource.card.expiry', + }, + lastDigits: { + 'valuePath': 'card.paymentSource.card.lastDigits', + }, + name: { + 'valuePath': 'card.paymentSource.card.name', + }, + } + }); + + } + + registerEventHandlers() { + // Listen to Gateway Radio button changes. this.$(document).on('change', this.elements.gatewayRadioButton.selector, (ev) => { if (ev.target.checked) { @@ -64,16 +307,86 @@ class AxoManager { this.triggerGatewayChange(); }); - this.$(document).on('click', this.elements.submitButtonContainer.selector + ' button', () => { + // On checkout form submitted. + this.$(document).on('click', this.elements.submitButtonContainer.buttonSelector, () => { this.onClickSubmitButton(); return false; }); + // Click change shipping address link. + this.$(document).on('click', '*[data-ppcp-axo-change-shipping-address]', async () => { + + if (this.isConnectProfile) { + console.log('profile', this.fastlane.profile); + + //this.shippingFormFields.deactivate(); + + const { selectionChanged, selectedAddress } = await this.fastlane.profile.showShippingAddressSelector(); + + console.log('selectedAddress', selectedAddress); + + if (selectionChanged) { + this.setShipping(selectedAddress); + this.shippingFormFields.activate(); + } + } else { + let checkbox = document.querySelector('#ship-to-different-address-checkbox'); + if (checkbox && !checkbox.checked) { + jQuery(checkbox).trigger('click'); + } + this.shippingFormFields.deactivate(); + } + + }); + + this.$(document).on('click', '*[data-ppcp-axo-save-shipping-address]', async () => { + this.shippingFormFields.activate(); + }); + + // Click change billing address link. + this.$(document).on('click', '*[data-ppcp-axo-change-billing-address]', async () => { + if (this.isConnectProfile) { + this.$('*[data-ppcp-axo-change-card]').trigger('click'); + } else { + this.billingFormFields.deactivate(); + } + }); + + this.$(document).on('click', '*[data-ppcp-axo-save-billing-address]', async () => { + this.billingFormFields.activate(); + }); + + // Click change card link. + this.$(document).on('click', '*[data-ppcp-axo-change-card]', async () => { + console.log('profile', this.fastlane.profile); + + const response = await this.fastlane.profile.showCardSelector(); + + console.log('card response', response); + + if (response.selectionChanged) { + this.setCard(response.selectedCard); + this.setBilling({ + address: response.selectedCard.paymentSource.card.billingAddress + }); + } + }); + + // Cancel "continuation" mode. + this.$(document).on('click', '*[data-ppcp-axo-show-gateway-selection]', async () => { + this.hideGatewaySelection = false; + this.$('.wc_payment_methods label').show(); + this.cardFormFields.refresh(); + }); + } showAxo() { - this.initEmail(); - this.init(); + this.initPlacements(); + this.initFastlane(); + + this.shippingFormFields.activate(); + this.billingFormFields.activate(); show(this.elements.emailWidgetContainer.selector); show(this.elements.watermarkContainer.selector); @@ -87,6 +400,9 @@ class AxoManager { } hideAxo() { + this.shippingFormFields.deactivate(); + this.billingFormFields.deactivate(); + hide(this.elements.emailWidgetContainer.selector); hide(this.elements.watermarkContainer.selector); hide(this.elements.paymentContainer.selector); @@ -98,15 +414,31 @@ class AxoManager { } } - initEmail() { + initPlacements() { let emailRow = document.querySelector(this.elements.fieldBillingEmail.selector); + const bc = this.elements.billingAddressContainer; + const sc = this.elements.shippingAddressContainer; + const ec = this.elements.emailWidgetContainer; + + if (!document.querySelector(bc.selector)) { + document.querySelector(bc.anchorSelector).insertAdjacentHTML('beforeend', ` +
+ `); + } + + if (!document.querySelector(sc.selector)) { + document.querySelector(sc.anchorSelector).insertAdjacentHTML('afterbegin', ` +
+ `); + } + if (this.useEmailWidget()) { // Display email widget. - if (!document.querySelector(this.elements.emailWidgetContainer.selector)) { + if (!document.querySelector(ec.selector)) { emailRow.parentNode.insertAdjacentHTML('afterbegin', ` -
+
--- EMAIL WIDGET PLACEHOLDER ---
`); @@ -120,7 +452,7 @@ class AxoManager { } } - async init() { + async initFastlane() { if (this.initialized) { return; } @@ -188,30 +520,79 @@ class AxoManager { async onChangeEmail () { log('Email changed: ' + this.emailInput.value); + this.isConnectProfile = false; + this.hideGatewaySelection = false; + if (!this.emailInput.checkValidity()) { log('The email address is not valid.'); return; } - const { customerContextId } = await this.fastlane.identity.lookupCustomerByEmail(this.emailInput.value); + const lookupResponse = await this.fastlane.identity.lookupCustomerByEmail(this.emailInput.value); - if (customerContextId) { + show(this.elements.paymentContainer.selector); + + if (lookupResponse.customerContextId) { // Email is associated with a Connect profile or a PayPal member. // Authenticate the customer to get access to their profile. log('Email is associated with a Connect profile or a PayPal member'); + + // TODO : enter hideOtherGateways mode + + const authResponse = await this.fastlane.identity.triggerAuthenticationFlow(lookupResponse.customerContextId); + + log('AuthResponse', authResponse); + + if (authResponse.authenticationState === 'succeeded') { + log(JSON.stringify(authResponse)); + + // document.querySelector(this.elements.paymentContainer.selector).innerHTML = + // 'Change card'; + + // Add addresses + this.setShipping(authResponse.profileData.shippingAddress); + // TODO : set billing + this.setCard(authResponse.profileData.card); + + this.isConnectProfile = true; + this.hideGatewaySelection = true; + this.$('.wc_payment_methods label').hide(); + + this.shippingFormFields.activate(); + this.billingFormFields.activate(); + this.cardFormFields.activate(); + + } else { + // authentication failed or canceled by the customer + log("Authentication Failed") + } + } else { // No profile found with this email address. // This is a guest customer. log('No profile found with this email address.'); - document.querySelector(this.elements.paymentContainer.selector)?.classList.remove('hidden'); - this.cardComponent = await this.fastlane .FastlaneCardComponent(MockData.cardComponent()) .render(this.elements.paymentContainer.selector); } } + setShipping(shipping) { + this.data.shipping = shipping; + this.shippingFormFields.setData(this.data); + } + + setBilling(billing) { + this.data.billing = billing; + this.billingFormFields.setData(this.data); + } + + setCard(card) { + this.data.card = card; + this.cardFormFields.setData(this.data); + } + onClickSubmitButton() { try { this.cardComponent.tokenize(MockData.cardComponentTokenize()).then((response) => { @@ -227,29 +608,6 @@ class AxoManager { log('nonce: ' + nonce); alert('nonce: ' + nonce); - // fetch('submit.php', { - // method: 'POST', - // headers: { - // 'Content-Type': 'application/json', - // }, - // body: JSON.stringify({ - // nonce: nonce - // }), - // }) - // .then(response => { - // if (!response.ok) { - // throw new Error('Network response was not ok'); - // } - // return response.json(); - // }) - // .then(data => { - // log('Submit response', data); - // log(JSON.stringify(data)); - // }) - // .catch(error => { - // console.error('There has been a problem with your fetch operation:', error); - // }); - // Submit form. document.querySelector(this.elements.defaultSubmitButton.selector).click(); } diff --git a/modules/ppcp-axo/resources/js/Components/DomElement.js b/modules/ppcp-axo/resources/js/Components/DomElement.js new file mode 100644 index 000000000..225a3565c --- /dev/null +++ b/modules/ppcp-axo/resources/js/Components/DomElement.js @@ -0,0 +1,13 @@ + +class DomElement { + + constructor(config) { + this.config = config; + this.selector = this.config.selector; + this.id = this.config.selector || null; + this.className = this.config.selector || null; + } + +} + +export default DomElement; diff --git a/modules/ppcp-axo/resources/js/Components/DomElementCollection.js b/modules/ppcp-axo/resources/js/Components/DomElementCollection.js new file mode 100644 index 000000000..45dd46475 --- /dev/null +++ b/modules/ppcp-axo/resources/js/Components/DomElementCollection.js @@ -0,0 +1,57 @@ +import DomElement from "./DomElement"; + +class DomElementCollection { + + constructor() { + this.gatewayRadioButton = new DomElement({ + selector: '#payment_method_ppcp-axo-gateway', + }); + + this.defaultSubmitButton = new DomElement({ + selector: '#place_order', + }); + + this.paymentContainer = new DomElement({ + id: 'ppcp-axo-payment-container', + selector: '#ppcp-axo-payment-container', + className: 'ppcp-axo-payment-container' + }); + + this.watermarkContainer = new DomElement({ + id: 'ppcp-axo-watermark-container', + selector: '#ppcp-axo-watermark-container', + className: 'ppcp-axo-watermark-container' + }); + + this.emailWidgetContainer = new DomElement({ + id: 'ppcp-axo-email-widget', + selector: '#ppcp-axo-email-widget', + className: 'ppcp-axo-email-widget' + }); + + this.shippingAddressContainer = new DomElement({ + id: 'ppcp-axo-shipping-address-container', + selector: '#ppcp-axo-shipping-address-container', + className: 'ppcp-axo-shipping-address-container', + anchorSelector: '.woocommerce-shipping-fields' + }); + + this.billingAddressContainer = new DomElement({ + id: 'ppcp-axo-billing-address-container', + selector: '#ppcp-axo-billing-address-container', + className: 'ppcp-axo-billing-address-container', + anchorSelector: '.woocommerce-billing-fields__field-wrapper' + }); + + this.fieldBillingEmail = new DomElement({ + selector: '#billing_email_field' + }); + + this.submitButtonContainer = new DomElement({ + selector: '#ppcp-axo-submit-button-container', + buttonSelector: '#ppcp-axo-submit-button-container button' + }); + } +} + +export default DomElementCollection; diff --git a/modules/ppcp-axo/resources/js/Helper/FormFieldGroup.js b/modules/ppcp-axo/resources/js/Helper/FormFieldGroup.js new file mode 100644 index 000000000..3ce45ec5b --- /dev/null +++ b/modules/ppcp-axo/resources/js/Helper/FormFieldGroup.js @@ -0,0 +1,105 @@ + +class MockData { + + constructor(config) { + this.data = {}; + + this.baseSelector = config.baseSelector; + this.contentSelector = config.contentSelector; + this.fields = config.fields || {}; + this.template = config.template; + + this.active = false; + } + + setData(data) { + this.data = data; + this.refresh(); + } + + getDataValue(path) { + if (!path) { + return ''; + } + const value = path.split('.').reduce((acc, key) => (acc && acc[key] !== undefined) ? acc[key] : undefined, this.data); + return value ? value : ''; + } + + activate() { + this.active = true; + this.refresh(); + } + + deactivate() { + this.active = false; + this.refresh(); + } + + toggle() { + this.active ? this.deactivate() : this.activate(); + } + + refresh() { + let content = document.querySelector(this.contentSelector); + + if (!content) { + return; + } + + content.innerHTML = ''; + + Object.keys(this.fields).forEach((key) => { + const field = this.fields[key]; + + if (this.active) { + this.hideField(field.selector); + //this.showField(this.contentSelector); + } else { + this.showField(field.selector); + //this.hideField(this.contentSelector); + } + + if (typeof this.template === 'function') { + content.innerHTML = this.template({ + value: (valueKey) => { + return this.getDataValue(this.fields[valueKey].valuePath); + }, + isEmpty: () => { + let isEmpty = true; + Object.values(this.fields).forEach((valuefield) => { + console.log('isEmpty', valuefield.valuePath, this.data); + + if (this.getDataValue(valuefield.valuePath)) { + console.log('isEmpty', 'no'); + isEmpty = false; + return false; + } + }); + console.log('isEmpty', 'r:' + isEmpty); + return isEmpty; + }, + isEditing: () => { + return ! this.active; + }, + }); + } + + }); + } + + showField(selector) { + const field = document.querySelector(this.baseSelector + ' ' + selector); + if (field) { + field.classList.remove('ppcp-axo-field-hidden'); + } + } + + hideField(selector) { + const field = document.querySelector(this.baseSelector + ' ' + selector); + if (field) { + field.classList.add('ppcp-axo-field-hidden'); + } + } +} + +export default MockData; From 795c1fdcd6694705ef3c64e50421365e7a3beebc Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Fri, 8 Mar 2024 17:41:21 +0000 Subject: [PATCH 23/70] Refactoring AXO module --- modules/ppcp-axo/resources/js/AxoManager.js | 358 +++--------------- .../resources/js/Components/DomElement.js | 26 +- .../js/Components/DomElementCollection.js | 5 +- .../js/{Entity => Connection}/Fastlane.js | 0 .../resources/js/Helper/FormFieldGroup.js | 4 - .../resources/js/Views/BillingView.js | 105 +++++ .../ppcp-axo/resources/js/Views/CardView.js | 74 ++++ .../resources/js/Views/ShippingView.js | 104 +++++ 8 files changed, 365 insertions(+), 311 deletions(-) rename modules/ppcp-axo/resources/js/{Entity => Connection}/Fastlane.js (100%) create mode 100644 modules/ppcp-axo/resources/js/Views/BillingView.js create mode 100644 modules/ppcp-axo/resources/js/Views/CardView.js create mode 100644 modules/ppcp-axo/resources/js/Views/ShippingView.js diff --git a/modules/ppcp-axo/resources/js/AxoManager.js b/modules/ppcp-axo/resources/js/AxoManager.js index 75de6d97c..be6cb0971 100644 --- a/modules/ppcp-axo/resources/js/AxoManager.js +++ b/modules/ppcp-axo/resources/js/AxoManager.js @@ -1,8 +1,10 @@ -import Fastlane from "./Entity/Fastlane"; +import Fastlane from "./Connection/Fastlane"; import MockData from "./Helper/MockData"; import {log} from "./Helper/Debug"; -import {hide, show} from '../../../ppcp-button/resources/js/modules/Helper/Hiding'; -import FormFieldGroup from "./Helper/FormFieldGroup"; +import DomElementCollection from "./Components/DomElementCollection"; +import ShippingView from "./Views/ShippingView"; +import BillingView from "./Views/BillingView"; +import CardView from "./Views/CardView"; class AxoManager { @@ -23,48 +25,7 @@ class AxoManager { card: null, } - this.elements = { - gatewayRadioButton: { - selector: '#payment_method_ppcp-axo-gateway', - }, - defaultSubmitButton: { - selector: '#place_order', - }, - paymentContainer: { - id: 'ppcp-axo-payment-container', - selector: '#ppcp-axo-payment-container', - className: 'ppcp-axo-payment-container' - }, - watermarkContainer: { - id: 'ppcp-axo-watermark-container', - selector: '#ppcp-axo-watermark-container', - className: 'ppcp-axo-watermark-container' - }, - emailWidgetContainer: { - id: 'ppcp-axo-email-widget', - selector: '#ppcp-axo-email-widget', - className: 'ppcp-axo-email-widget' - }, - shippingAddressContainer: { - id: 'ppcp-axo-shipping-address-container', - selector: '#ppcp-axo-shipping-address-container', - className: 'ppcp-axo-shipping-address-container', - anchorSelector: '.woocommerce-shipping-fields' - }, - billingAddressContainer: { - id: 'ppcp-axo-billing-address-container', - selector: '#ppcp-axo-billing-address-container', - className: 'ppcp-axo-billing-address-container', - anchorSelector: '.woocommerce-billing-fields__field-wrapper' - }, - fieldBillingEmail: { - selector: '#billing_email_field' - }, - submitButtonContainer: { - selector: '#ppcp-axo-submit-button-container', - buttonSelector: '#ppcp-axo-submit-button-container button' - }, - } + this.el = new DomElementCollection(); this.styles = { root: { @@ -76,226 +37,15 @@ class AxoManager { this.registerEventHandlers(); - this.shippingFormFields = new FormFieldGroup({ - baseSelector: '.woocommerce-checkout', - contentSelector: this.elements.shippingAddressContainer.selector, - template: (data) => { - const valueOfSelect = (selectSelector, key) => { - const selectElement = document.querySelector(selectSelector); - const option = selectElement.querySelector(`option[value="${key}"]`); - return option ? option.textContent : key; - } - - if (data.isEditing()) { - return ` -
-

Shipping details Save

-
- `; - } - if (data.isEmpty()) { - return ` -
-

Shipping details Edit

-
Please fill in your shipping details.
-
- `; - } - return ` -
-

Shipping details Edit

-
${data.value('company')}
-
${data.value('firstName')} ${data.value('lastName')}
-
${data.value('street1')}
-
${data.value('street2')}
-
${data.value('postCode')} ${data.value('city')}
-
${valueOfSelect('#shipping_state', data.value('stateCode'))}
-
${valueOfSelect('#shipping_country', data.value('countryCode'))}
-
- `; - }, - fields: { - firstName: { - 'key': 'firstName', - 'selector': '#shipping_first_name_field', - 'valuePath': 'shipping.name.firstName', - }, - lastName: { - 'selector': '#shipping_last_name_field', - 'valuePath': 'shipping.name.lastName', - }, - street1: { - 'selector': '#shipping_address_1_field', - 'valuePath': 'shipping.address.addressLine1', - }, - street2: { - 'selector': '#shipping_address_2_field', - 'valuePath': null - }, - postCode: { - 'selector': '#shipping_postcode_field', - 'valuePath': 'shipping.address.postalCode', - }, - city: { - 'selector': '#shipping_city_field', - 'valuePath': 'shipping.address.adminArea2', - }, - stateCode: { - 'selector': '#shipping_state_field', - 'valuePath': 'shipping.address.adminArea1', - }, - countryCode: { - 'selector': '#shipping_country_field', - 'valuePath': 'shipping.address.countryCode', - }, - company: { - 'selector': '#shipping_company_field', - 'valuePath': null, - }, - shipDifferentAddress: { - 'selector': '#ship-to-different-address', - 'valuePath': null, - } - } - }); - - this.billingFormFields = new FormFieldGroup({ - baseSelector: '.woocommerce-checkout', - contentSelector: this.elements.billingAddressContainer.selector, - template: (data) => { - const valueOfSelect = (selectSelector, key) => { - const selectElement = document.querySelector(selectSelector); - const option = selectElement.querySelector(`option[value="${key}"]`); - return option ? option.textContent : key; - } - - if (data.isEditing()) { - return ` -
-

Save

-
- `; - } - if (data.isEmpty()) { - return ` -
-
Please fill in your billing details.
-

Edit

-
- `; - } - return ` -
-

Billing address

-
${data.value('company')}
-
${data.value('firstName')} ${data.value('lastName')}
-
${data.value('street1')}
-
${data.value('street2')}
-
${data.value('postCode')} ${data.value('city')}
-
${valueOfSelect('#billing_state', data.value('stateCode'))}
-
${valueOfSelect('#billing_country', data.value('countryCode'))}
-
${data.value('phone')}
-

Edit

-
- `; - }, - fields: { - firstName: { - 'selector': '#billing_first_name_field', - 'valuePath': 'billing.name.firstName', - }, - lastName: { - 'selector': '#billing_last_name_field', - 'valuePath': 'billing.name.lastName', - }, - street1: { - 'selector': '#billing_address_1_field', - 'valuePath': 'billing.address.addressLine1', - }, - street2: { - 'selector': '#billing_address_2_field', - 'valuePath': null - }, - postCode: { - 'selector': '#billing_postcode_field', - 'valuePath': 'billing.address.postalCode', - }, - city: { - 'selector': '#billing_city_field', - 'valuePath': 'billing.address.adminArea2', - }, - stateCode: { - 'selector': '#billing_state_field', - 'valuePath': 'billing.address.adminArea1', - }, - countryCode: { - 'selector': '#billing_country_field', - 'valuePath': 'billing.address.countryCode', - }, - company: { - 'selector': '#billing_company_field', - 'valuePath': null, - }, - phone: { - 'selector': '#billing_phone_field', - 'valuePath': null, - }, - } - }); - - this.cardFormFields = new FormFieldGroup({ - baseSelector: '.ppcp-axo-payment-container', - contentSelector: this.elements.paymentContainer.selector, - template: (data) => { - const selectOtherPaymentMethod = () => { - if (!this.hideGatewaySelection) { - return ''; - } - return `

Select other payment method

`; - }; - - if (data.isEmpty()) { - return ` -
-
Please fill in your card details.
-

Edit

- ${selectOtherPaymentMethod()} -
- `; - } - return ` -
-

Card Details Edit

-
${data.value('name')}
-
${data.value('brand')}
-
${data.value('lastDigits') ? '************' + data.value('lastDigits'): ''}
-
${data.value('expiry')}
- ${selectOtherPaymentMethod()} -
- `; - }, - fields: { - brand: { - 'valuePath': 'card.paymentSource.card.brand', - }, - expiry: { - 'valuePath': 'card.paymentSource.card.expiry', - }, - lastDigits: { - 'valuePath': 'card.paymentSource.card.lastDigits', - }, - name: { - 'valuePath': 'card.paymentSource.card.name', - }, - } - }); - + this.shippingView = new ShippingView(this.el.shippingAddressContainer.selector); + this.billingView = new BillingView(this.el.billingAddressContainer.selector); + this.cardView = new CardView(this.el.paymentContainer.selector, this); } registerEventHandlers() { // Listen to Gateway Radio button changes. - this.$(document).on('change', this.elements.gatewayRadioButton.selector, (ev) => { + this.el.gatewayRadioButton.on('change', (ev) => { if (ev.target.checked) { this.showAxo(); } else { @@ -308,7 +58,7 @@ class AxoManager { }); // On checkout form submitted. - this.$(document).on('click', this.elements.submitButtonContainer.buttonSelector, () => { + this.$(document).on('click', this.el.submitButton.selector, () => { this.onClickSubmitButton(); return false; }); @@ -319,7 +69,7 @@ class AxoManager { if (this.isConnectProfile) { console.log('profile', this.fastlane.profile); - //this.shippingFormFields.deactivate(); + //this.shippingView.deactivate(); const { selectionChanged, selectedAddress } = await this.fastlane.profile.showShippingAddressSelector(); @@ -327,20 +77,20 @@ class AxoManager { if (selectionChanged) { this.setShipping(selectedAddress); - this.shippingFormFields.activate(); + this.shippingView.activate(); } } else { let checkbox = document.querySelector('#ship-to-different-address-checkbox'); if (checkbox && !checkbox.checked) { jQuery(checkbox).trigger('click'); } - this.shippingFormFields.deactivate(); + this.shippingView.deactivate(); } }); this.$(document).on('click', '*[data-ppcp-axo-save-shipping-address]', async () => { - this.shippingFormFields.activate(); + this.shippingView.activate(); }); // Click change billing address link. @@ -348,12 +98,12 @@ class AxoManager { if (this.isConnectProfile) { this.$('*[data-ppcp-axo-change-card]').trigger('click'); } else { - this.billingFormFields.deactivate(); + this.billingView.deactivate(); } }); this.$(document).on('click', '*[data-ppcp-axo-save-billing-address]', async () => { - this.billingFormFields.activate(); + this.billingView.activate(); }); // Click change card link. @@ -376,7 +126,7 @@ class AxoManager { this.$(document).on('click', '*[data-ppcp-axo-show-gateway-selection]', async () => { this.hideGatewaySelection = false; this.$('.wc_payment_methods label').show(); - this.cardFormFields.refresh(); + this.cardView.refresh(); }); } @@ -385,41 +135,41 @@ class AxoManager { this.initPlacements(); this.initFastlane(); - this.shippingFormFields.activate(); - this.billingFormFields.activate(); + this.shippingView.activate(); + this.billingView.activate(); - show(this.elements.emailWidgetContainer.selector); - show(this.elements.watermarkContainer.selector); - show(this.elements.paymentContainer.selector); - show(this.elements.submitButtonContainer.selector); - hide(this.elements.defaultSubmitButton.selector); + this.el.emailWidgetContainer.show(); + this.el.watermarkContainer.show(); + this.el.paymentContainer.show(); + this.el.submitButtonContainer.show(); + this.el.defaultSubmitButton.hide(); if (this.useEmailWidget()) { - hide(this.elements.fieldBillingEmail.selector); + this.el.fieldBillingEmail.hide(); } } hideAxo() { - this.shippingFormFields.deactivate(); - this.billingFormFields.deactivate(); + this.shippingView.deactivate(); + this.billingView.deactivate(); - hide(this.elements.emailWidgetContainer.selector); - hide(this.elements.watermarkContainer.selector); - hide(this.elements.paymentContainer.selector); - hide(this.elements.submitButtonContainer.selector); - show(this.elements.defaultSubmitButton.selector); + this.el.emailWidgetContainer.hide(); + this.el.watermarkContainer.hide(); + this.el.paymentContainer.hide(); + this.el.submitButtonContainer.hide(); + this.el.defaultSubmitButton.show(); if (this.useEmailWidget()) { - show(this.elements.fieldBillingEmail.selector); + this.el.fieldBillingEmail.show(); } } initPlacements() { - let emailRow = document.querySelector(this.elements.fieldBillingEmail.selector); + let emailRow = document.querySelector(this.el.fieldBillingEmail.selector); - const bc = this.elements.billingAddressContainer; - const sc = this.elements.shippingAddressContainer; - const ec = this.elements.emailWidgetContainer; + const bc = this.el.billingAddressContainer; + const sc = this.el.shippingAddressContainer; + const ec = this.el.emailWidgetContainer; if (!document.querySelector(bc.selector)) { document.querySelector(bc.anchorSelector).insertAdjacentHTML('beforeend', ` @@ -476,25 +226,25 @@ class AxoManager { } insertDomElements() { - this.emailInput = document.querySelector(this.elements.fieldBillingEmail.selector + ' input'); + this.emailInput = document.querySelector(this.el.fieldBillingEmail.selector + ' input'); this.emailInput.insertAdjacentHTML('afterend', ` -
+
`); const gatewayPaymentContainer = document.querySelector('.payment_method_ppcp-axo-gateway'); gatewayPaymentContainer.insertAdjacentHTML('beforeend', ` - + `); } triggerGatewayChange() { - this.$(this.elements.gatewayRadioButton.selector).trigger('change'); + this.el.gatewayRadioButton.trigger('change'); } renderWatermark() { this.fastlane.FastlaneWatermarkComponent({ includeAdditionalInfo: true - }).render(this.elements.watermarkContainer.selector); + }).render(this.el.watermarkContainer.selector); } watchEmail() { @@ -505,7 +255,7 @@ class AxoManager { } else { - this.emailInput = document.querySelector(this.elements.fieldBillingEmail.selector + ' input'); + this.emailInput = document.querySelector(this.el.fieldBillingEmail.selector + ' input'); this.emailInput.addEventListener('change', async ()=> { this.onChangeEmail(); }); @@ -530,7 +280,7 @@ class AxoManager { const lookupResponse = await this.fastlane.identity.lookupCustomerByEmail(this.emailInput.value); - show(this.elements.paymentContainer.selector); + this.el.paymentContainer.show(); if (lookupResponse.customerContextId) { // Email is associated with a Connect profile or a PayPal member. @@ -546,7 +296,7 @@ class AxoManager { if (authResponse.authenticationState === 'succeeded') { log(JSON.stringify(authResponse)); - // document.querySelector(this.elements.paymentContainer.selector).innerHTML = + // document.querySelector(this.el.paymentContainer.selector).innerHTML = // 'Change card'; // Add addresses @@ -558,9 +308,9 @@ class AxoManager { this.hideGatewaySelection = true; this.$('.wc_payment_methods label').hide(); - this.shippingFormFields.activate(); - this.billingFormFields.activate(); - this.cardFormFields.activate(); + this.shippingView.activate(); + this.billingView.activate(); + this.cardView.activate(); } else { // authentication failed or canceled by the customer @@ -574,23 +324,23 @@ class AxoManager { this.cardComponent = await this.fastlane .FastlaneCardComponent(MockData.cardComponent()) - .render(this.elements.paymentContainer.selector); + .render(this.el.paymentContainer.selector); } } setShipping(shipping) { this.data.shipping = shipping; - this.shippingFormFields.setData(this.data); + this.shippingView.setData(this.data); } setBilling(billing) { this.data.billing = billing; - this.billingFormFields.setData(this.data); + this.billingView.setData(this.data); } setCard(card) { this.data.card = card; - this.cardFormFields.setData(this.data); + this.cardView.setData(this.data); } onClickSubmitButton() { @@ -609,7 +359,7 @@ class AxoManager { alert('nonce: ' + nonce); // Submit form. - document.querySelector(this.elements.defaultSubmitButton.selector).click(); + this.el.defaultSubmitButton.click(); } useEmailWidget() { diff --git a/modules/ppcp-axo/resources/js/Components/DomElement.js b/modules/ppcp-axo/resources/js/Components/DomElement.js index 225a3565c..7c6d185bd 100644 --- a/modules/ppcp-axo/resources/js/Components/DomElement.js +++ b/modules/ppcp-axo/resources/js/Components/DomElement.js @@ -1,13 +1,35 @@ +import {setVisible} from "../../../../ppcp-button/resources/js/modules/Helper/Hiding"; class DomElement { constructor(config) { + this.$ = jQuery; this.config = config; this.selector = this.config.selector; - this.id = this.config.selector || null; - this.className = this.config.selector || null; + this.id = this.config.id || null; + this.className = this.config.className || null; + this.anchorSelector = this.config.anchorSelector || null; } + trigger(action) { + this.$(this.selector).trigger(action); + } + + on(action, callable) { + this.$(document).on(action, this.selector, callable); + } + + hide(important = false) { + setVisible(this.selector, false, important); + } + + show() { + setVisible(this.selector, true); + } + + click() { + document.querySelector(this.selector).click(); + } } export default DomElement; diff --git a/modules/ppcp-axo/resources/js/Components/DomElementCollection.js b/modules/ppcp-axo/resources/js/Components/DomElementCollection.js index 45dd46475..230db6c9a 100644 --- a/modules/ppcp-axo/resources/js/Components/DomElementCollection.js +++ b/modules/ppcp-axo/resources/js/Components/DomElementCollection.js @@ -49,7 +49,10 @@ class DomElementCollection { this.submitButtonContainer = new DomElement({ selector: '#ppcp-axo-submit-button-container', - buttonSelector: '#ppcp-axo-submit-button-container button' + }); + + this.submitButton = new DomElement({ + selector: '#ppcp-axo-submit-button-container button' }); } } diff --git a/modules/ppcp-axo/resources/js/Entity/Fastlane.js b/modules/ppcp-axo/resources/js/Connection/Fastlane.js similarity index 100% rename from modules/ppcp-axo/resources/js/Entity/Fastlane.js rename to modules/ppcp-axo/resources/js/Connection/Fastlane.js diff --git a/modules/ppcp-axo/resources/js/Helper/FormFieldGroup.js b/modules/ppcp-axo/resources/js/Helper/FormFieldGroup.js index 3ce45ec5b..3cfd03eb0 100644 --- a/modules/ppcp-axo/resources/js/Helper/FormFieldGroup.js +++ b/modules/ppcp-axo/resources/js/Helper/FormFieldGroup.js @@ -67,15 +67,11 @@ class MockData { isEmpty: () => { let isEmpty = true; Object.values(this.fields).forEach((valuefield) => { - console.log('isEmpty', valuefield.valuePath, this.data); - if (this.getDataValue(valuefield.valuePath)) { - console.log('isEmpty', 'no'); isEmpty = false; return false; } }); - console.log('isEmpty', 'r:' + isEmpty); return isEmpty; }, isEditing: () => { diff --git a/modules/ppcp-axo/resources/js/Views/BillingView.js b/modules/ppcp-axo/resources/js/Views/BillingView.js new file mode 100644 index 000000000..6736c9d1f --- /dev/null +++ b/modules/ppcp-axo/resources/js/Views/BillingView.js @@ -0,0 +1,105 @@ +import FormFieldGroup from "../Helper/FormFieldGroup"; + +class BillingView { + + constructor(selector) { + this.billingFormFields = new FormFieldGroup({ + baseSelector: '.woocommerce-checkout', + contentSelector: selector, + template: (data) => { + const valueOfSelect = (selectSelector, key) => { + const selectElement = document.querySelector(selectSelector); + const option = selectElement.querySelector(`option[value="${key}"]`); + return option ? option.textContent : key; + } + + if (data.isEditing()) { + return ` +
+

Save

+
+ `; + } + if (data.isEmpty()) { + return ` +
+
Please fill in your billing details.
+

Edit

+
+ `; + } + return ` +
+

Billing address

+
${data.value('company')}
+
${data.value('firstName')} ${data.value('lastName')}
+
${data.value('street1')}
+
${data.value('street2')}
+
${data.value('postCode')} ${data.value('city')}
+
${valueOfSelect('#billing_state', data.value('stateCode'))}
+
${valueOfSelect('#billing_country', data.value('countryCode'))}
+
${data.value('phone')}
+

Edit

+
+ `; + }, + fields: { + firstName: { + 'selector': '#billing_first_name_field', + 'valuePath': 'billing.name.firstName', + }, + lastName: { + 'selector': '#billing_last_name_field', + 'valuePath': 'billing.name.lastName', + }, + street1: { + 'selector': '#billing_address_1_field', + 'valuePath': 'billing.address.addressLine1', + }, + street2: { + 'selector': '#billing_address_2_field', + 'valuePath': null + }, + postCode: { + 'selector': '#billing_postcode_field', + 'valuePath': 'billing.address.postalCode', + }, + city: { + 'selector': '#billing_city_field', + 'valuePath': 'billing.address.adminArea2', + }, + stateCode: { + 'selector': '#billing_state_field', + 'valuePath': 'billing.address.adminArea1', + }, + countryCode: { + 'selector': '#billing_country_field', + 'valuePath': 'billing.address.countryCode', + }, + company: { + 'selector': '#billing_company_field', + 'valuePath': null, + }, + phone: { + 'selector': '#billing_phone_field', + 'valuePath': null, + }, + } + }); + } + + activate() { + this.billingFormFields.activate(); + } + + deactivate() { + this.billingFormFields.deactivate(); + } + + setData(data) { + this.billingFormFields.setData(data); + } + +} + +export default BillingView; diff --git a/modules/ppcp-axo/resources/js/Views/CardView.js b/modules/ppcp-axo/resources/js/Views/CardView.js new file mode 100644 index 000000000..7acd683b0 --- /dev/null +++ b/modules/ppcp-axo/resources/js/Views/CardView.js @@ -0,0 +1,74 @@ +import FormFieldGroup from "../Helper/FormFieldGroup"; + +class CardView { + + constructor(selector, manager) { + this.manager = manager; + + this.cardFormFields = new FormFieldGroup({ + baseSelector: '.ppcp-axo-payment-container', + contentSelector: selector, + template: (data) => { + const selectOtherPaymentMethod = () => { + if (!this.manager.hideGatewaySelection) { + return ''; + } + return `

Select other payment method

`; + }; + + if (data.isEmpty()) { + return ` +
+
Please fill in your card details.
+

Edit

+ ${selectOtherPaymentMethod()} +
+ `; + } + return ` +
+

Card Details Edit

+
${data.value('name')}
+
${data.value('brand')}
+
${data.value('lastDigits') ? '************' + data.value('lastDigits'): ''}
+
${data.value('expiry')}
+ ${selectOtherPaymentMethod()} +
+ `; + }, + fields: { + brand: { + 'valuePath': 'card.paymentSource.card.brand', + }, + expiry: { + 'valuePath': 'card.paymentSource.card.expiry', + }, + lastDigits: { + 'valuePath': 'card.paymentSource.card.lastDigits', + }, + name: { + 'valuePath': 'card.paymentSource.card.name', + }, + } + }); + } + + activate() { + this.cardFormFields.activate(); + } + + deactivate() { + this.cardFormFields.deactivate(); + } + + refresh() { + this.cardFormFields.refresh(); + } + + setData(data) { + this.cardFormFields.setData(data); + } + +} + +export default CardView; diff --git a/modules/ppcp-axo/resources/js/Views/ShippingView.js b/modules/ppcp-axo/resources/js/Views/ShippingView.js new file mode 100644 index 000000000..8f972866e --- /dev/null +++ b/modules/ppcp-axo/resources/js/Views/ShippingView.js @@ -0,0 +1,104 @@ +import FormFieldGroup from "../Helper/FormFieldGroup"; + +class ShippingView { + + constructor(selector) { + this.shippingFormFields = new FormFieldGroup({ + baseSelector: '.woocommerce-checkout', + contentSelector: selector, + template: (data) => { + const valueOfSelect = (selectSelector, key) => { + const selectElement = document.querySelector(selectSelector); + const option = selectElement.querySelector(`option[value="${key}"]`); + return option ? option.textContent : key; + } + + if (data.isEditing()) { + return ` +
+

Shipping details Save

+
+ `; + } + if (data.isEmpty()) { + return ` +
+

Shipping details Edit

+
Please fill in your shipping details.
+
+ `; + } + return ` +
+

Shipping details Edit

+
${data.value('company')}
+
${data.value('firstName')} ${data.value('lastName')}
+
${data.value('street1')}
+
${data.value('street2')}
+
${data.value('postCode')} ${data.value('city')}
+
${valueOfSelect('#shipping_state', data.value('stateCode'))}
+
${valueOfSelect('#shipping_country', data.value('countryCode'))}
+
+ `; + }, + fields: { + firstName: { + 'key': 'firstName', + 'selector': '#shipping_first_name_field', + 'valuePath': 'shipping.name.firstName', + }, + lastName: { + 'selector': '#shipping_last_name_field', + 'valuePath': 'shipping.name.lastName', + }, + street1: { + 'selector': '#shipping_address_1_field', + 'valuePath': 'shipping.address.addressLine1', + }, + street2: { + 'selector': '#shipping_address_2_field', + 'valuePath': null + }, + postCode: { + 'selector': '#shipping_postcode_field', + 'valuePath': 'shipping.address.postalCode', + }, + city: { + 'selector': '#shipping_city_field', + 'valuePath': 'shipping.address.adminArea2', + }, + stateCode: { + 'selector': '#shipping_state_field', + 'valuePath': 'shipping.address.adminArea1', + }, + countryCode: { + 'selector': '#shipping_country_field', + 'valuePath': 'shipping.address.countryCode', + }, + company: { + 'selector': '#shipping_company_field', + 'valuePath': null, + }, + shipDifferentAddress: { + 'selector': '#ship-to-different-address', + 'valuePath': null, + } + } + }); + } + + activate() { + this.shippingFormFields.activate(); + } + + deactivate() { + this.shippingFormFields.deactivate(); + } + + setData(data) { + this.shippingFormFields.setData(data); + } + +} + +export default ShippingView; From ac74bc0ddb799b909da4300a8ebf2cb083c9318a Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 11 Mar 2024 15:55:53 +0100 Subject: [PATCH 24/70] Add `gateway_scheduled_payments` support if subscriptions mode is PayPal subscriptions --- modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php b/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php index 73afb085b..5c3418d86 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php @@ -259,6 +259,8 @@ class PayPalGateway extends \WC_Payment_Gateway { 'subscription_payment_method_change_admin', 'multiple_subscriptions' ); + } elseif ( $this->config->has( 'subscriptions_mode' ) && $this->config->get( 'subscriptions_mode' ) === 'subscriptions_api' ) { + $this->supports[] = 'gateway_scheduled_payments'; } elseif ( $this->config->has( 'vault_enabled_dcc' ) && $this->config->get( 'vault_enabled_dcc' ) ) { $this->supports[] = 'tokenization'; } From a9d2f97a80263008a1134990a5a650eb06669101 Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Tue, 12 Mar 2024 09:23:42 +0000 Subject: [PATCH 25/70] Refactoring axo element handling --- modules/ppcp-axo/resources/js/AxoManager.js | 89 ++++++++++--------- .../resources/js/Components/DomElement.js | 7 +- .../js/Components/DomElementCollection.js | 25 ++++++ .../{Helper => Components}/FormFieldGroup.js | 9 +- .../resources/js/Views/BillingView.js | 25 +++--- .../ppcp-axo/resources/js/Views/CardView.js | 11 +-- .../resources/js/Views/ShippingView.js | 20 ++--- 7 files changed, 109 insertions(+), 77 deletions(-) rename modules/ppcp-axo/resources/js/{Helper => Components}/FormFieldGroup.js (92%) diff --git a/modules/ppcp-axo/resources/js/AxoManager.js b/modules/ppcp-axo/resources/js/AxoManager.js index be6cb0971..e02846b67 100644 --- a/modules/ppcp-axo/resources/js/AxoManager.js +++ b/modules/ppcp-axo/resources/js/AxoManager.js @@ -17,6 +17,7 @@ class AxoManager { this.$ = jQuery; this.isConnectProfile = false; + this.isNewProfile = false; this.hideGatewaySelection = false; this.data = { @@ -37,9 +38,9 @@ class AxoManager { this.registerEventHandlers(); - this.shippingView = new ShippingView(this.el.shippingAddressContainer.selector); - this.billingView = new BillingView(this.el.billingAddressContainer.selector); - this.cardView = new CardView(this.el.paymentContainer.selector, this); + this.shippingView = new ShippingView(this.el.shippingAddressContainer.selector, this.el); + this.billingView = new BillingView(this.el.billingAddressContainer.selector, this.el); + this.cardView = new CardView(this.el.paymentContainer.selector, this.el, this); } registerEventHandlers() { @@ -58,14 +59,13 @@ class AxoManager { }); // On checkout form submitted. - this.$(document).on('click', this.el.submitButton.selector, () => { + this.el.submitButton.on('click', () => { this.onClickSubmitButton(); return false; - }); + }) // Click change shipping address link. - this.$(document).on('click', '*[data-ppcp-axo-change-shipping-address]', async () => { - + this.el.changeShippingAddressLink.on('click', async () => { if (this.isConnectProfile) { console.log('profile', this.fastlane.profile); @@ -79,35 +79,18 @@ class AxoManager { this.setShipping(selectedAddress); this.shippingView.activate(); } - } else { - let checkbox = document.querySelector('#ship-to-different-address-checkbox'); - if (checkbox && !checkbox.checked) { - jQuery(checkbox).trigger('click'); - } - this.shippingView.deactivate(); } - - }); - - this.$(document).on('click', '*[data-ppcp-axo-save-shipping-address]', async () => { - this.shippingView.activate(); }); // Click change billing address link. - this.$(document).on('click', '*[data-ppcp-axo-change-billing-address]', async () => { + this.el.changeBillingAddressLink.on('click', async () => { if (this.isConnectProfile) { - this.$('*[data-ppcp-axo-change-card]').trigger('click'); - } else { - this.billingView.deactivate(); + this.el.changeCardLink.trigger('click'); } }); - this.$(document).on('click', '*[data-ppcp-axo-save-billing-address]', async () => { - this.billingView.activate(); - }); - // Click change card link. - this.$(document).on('click', '*[data-ppcp-axo-change-card]', async () => { + this.el.changeCardLink.on('click', async () => { console.log('profile', this.fastlane.profile); const response = await this.fastlane.profile.showCardSelector(); @@ -123,7 +106,7 @@ class AxoManager { }); // Cancel "continuation" mode. - this.$(document).on('click', '*[data-ppcp-axo-show-gateway-selection]', async () => { + this.el.showGatewaySelectionLink.on('click', async () => { this.hideGatewaySelection = false; this.$('.wc_payment_methods label').show(); this.cardView.refresh(); @@ -135,21 +118,35 @@ class AxoManager { this.initPlacements(); this.initFastlane(); - this.shippingView.activate(); - this.billingView.activate(); + if (!this.isNewProfile && !this.isConnectProfile) { + this.el.allFields.hide(); + } + + if (this.useEmailWidget()) { + this.el.emailWidgetContainer.show(); + this.el.fieldBillingEmail.hide(); + } else { + this.el.emailWidgetContainer.hide(); + this.el.fieldBillingEmail.show(); + } + + if (this.isConnectProfile) { + this.shippingView.activate(); + this.billingView.activate(); + + this.el.emailWidgetContainer.hide(); + this.el.fieldBillingEmail.hide(); + } - this.el.emailWidgetContainer.show(); this.el.watermarkContainer.show(); this.el.paymentContainer.show(); this.el.submitButtonContainer.show(); this.el.defaultSubmitButton.hide(); - - if (this.useEmailWidget()) { - this.el.fieldBillingEmail.hide(); - } } hideAxo() { + this.el.allFields.show(); + this.shippingView.deactivate(); this.billingView.deactivate(); @@ -159,9 +156,8 @@ class AxoManager { this.el.submitButtonContainer.hide(); this.el.defaultSubmitButton.show(); - if (this.useEmailWidget()) { - this.el.fieldBillingEmail.show(); - } + this.el.emailWidgetContainer.hide(); + this.el.fieldBillingEmail.show(); } initPlacements() { @@ -271,16 +267,20 @@ class AxoManager { log('Email changed: ' + this.emailInput.value); this.isConnectProfile = false; + this.isNewProfile = false; this.hideGatewaySelection = false; + this.el.allFields.hide(); + if (!this.emailInput.checkValidity()) { log('The email address is not valid.'); return; } - const lookupResponse = await this.fastlane.identity.lookupCustomerByEmail(this.emailInput.value); + this.data.email = this.emailInput.value; + this.billingView.setData(this.data); - this.el.paymentContainer.show(); + const lookupResponse = await this.fastlane.identity.lookupCustomerByEmail(this.emailInput.value); if (lookupResponse.customerContextId) { // Email is associated with a Connect profile or a PayPal member. @@ -296,6 +296,10 @@ class AxoManager { if (authResponse.authenticationState === 'succeeded') { log(JSON.stringify(authResponse)); + this.el.allFields.show(); + this.el.paymentContainer.show(); + + // document.querySelector(this.el.paymentContainer.selector).innerHTML = // 'Change card'; @@ -322,6 +326,11 @@ class AxoManager { // This is a guest customer. log('No profile found with this email address.'); + this.el.allFields.show(); + this.el.paymentContainer.show(); + + this.isNewProfile = true; + this.cardComponent = await this.fastlane .FastlaneCardComponent(MockData.cardComponent()) .render(this.el.paymentContainer.selector); diff --git a/modules/ppcp-axo/resources/js/Components/DomElement.js b/modules/ppcp-axo/resources/js/Components/DomElement.js index 7c6d185bd..f84ab1725 100644 --- a/modules/ppcp-axo/resources/js/Components/DomElement.js +++ b/modules/ppcp-axo/resources/js/Components/DomElement.js @@ -8,6 +8,7 @@ class DomElement { this.selector = this.config.selector; this.id = this.config.id || null; this.className = this.config.className || null; + this.attributes = this.config.attributes || null; this.anchorSelector = this.config.anchorSelector || null; } @@ -19,12 +20,12 @@ class DomElement { this.$(document).on(action, this.selector, callable); } - hide(important = false) { - setVisible(this.selector, false, important); + hide() { + this.$(this.selector).hide(); } show() { - setVisible(this.selector, true); + this.$(this.selector).show(); } click() { diff --git a/modules/ppcp-axo/resources/js/Components/DomElementCollection.js b/modules/ppcp-axo/resources/js/Components/DomElementCollection.js index 230db6c9a..d6840242a 100644 --- a/modules/ppcp-axo/resources/js/Components/DomElementCollection.js +++ b/modules/ppcp-axo/resources/js/Components/DomElementCollection.js @@ -23,6 +23,10 @@ class DomElementCollection { className: 'ppcp-axo-watermark-container' }); + this.allFields = new DomElement({ + selector: '#customer_details .form-row, #customer_details .woocommerce-shipping-fields' + }); + this.emailWidgetContainer = new DomElement({ id: 'ppcp-axo-email-widget', selector: '#ppcp-axo-email-widget', @@ -54,6 +58,27 @@ class DomElementCollection { this.submitButton = new DomElement({ selector: '#ppcp-axo-submit-button-container button' }); + + this.changeShippingAddressLink = new DomElement({ + selector: '*[data-ppcp-axo-change-shipping-address]', + attributes: 'data-ppcp-axo-change-shipping-address', + }); + + this.changeBillingAddressLink = new DomElement({ + selector: '*[data-ppcp-axo-change-billing-address]', + attributes: 'data-ppcp-axo-change-billing-address', + }); + + this.changeCardLink = new DomElement({ + selector: '*[data-ppcp-axo-change-card]', + attributes: 'data-ppcp-axo-change-card', + }); + + this.showGatewaySelectionLink = new DomElement({ + selector: '*[data-ppcp-axo-show-gateway-selection]', + attributes: 'data-ppcp-axo-show-gateway-selection', + }); + } } diff --git a/modules/ppcp-axo/resources/js/Helper/FormFieldGroup.js b/modules/ppcp-axo/resources/js/Components/FormFieldGroup.js similarity index 92% rename from modules/ppcp-axo/resources/js/Helper/FormFieldGroup.js rename to modules/ppcp-axo/resources/js/Components/FormFieldGroup.js index 3cfd03eb0..d35b54349 100644 --- a/modules/ppcp-axo/resources/js/Helper/FormFieldGroup.js +++ b/modules/ppcp-axo/resources/js/Components/FormFieldGroup.js @@ -66,17 +66,14 @@ class MockData { }, isEmpty: () => { let isEmpty = true; - Object.values(this.fields).forEach((valuefield) => { - if (this.getDataValue(valuefield.valuePath)) { + Object.values(this.fields).forEach((valueField) => { + if (this.getDataValue(valueField.valuePath)) { isEmpty = false; return false; } }); return isEmpty; - }, - isEditing: () => { - return ! this.active; - }, + } }); } diff --git a/modules/ppcp-axo/resources/js/Views/BillingView.js b/modules/ppcp-axo/resources/js/Views/BillingView.js index 6736c9d1f..7e61388c3 100644 --- a/modules/ppcp-axo/resources/js/Views/BillingView.js +++ b/modules/ppcp-axo/resources/js/Views/BillingView.js @@ -1,36 +1,35 @@ -import FormFieldGroup from "../Helper/FormFieldGroup"; +import FormFieldGroup from "../Components/FormFieldGroup"; class BillingView { - constructor(selector) { + constructor(selector, elements) { + this.el = elements; + this.billingFormFields = new FormFieldGroup({ baseSelector: '.woocommerce-checkout', contentSelector: selector, template: (data) => { const valueOfSelect = (selectSelector, key) => { + if (!key) { + return ''; + } const selectElement = document.querySelector(selectSelector); const option = selectElement.querySelector(`option[value="${key}"]`); return option ? option.textContent : key; } - if (data.isEditing()) { - return ` -
-

Save

-
- `; - } if (data.isEmpty()) { return `
+

Billing details Edit

Please fill in your billing details.
-

Edit

`; } return `
-

Billing address

+

Billing details Edit

+
${data.value('email')}
${data.value('company')}
${data.value('firstName')} ${data.value('lastName')}
${data.value('street1')}
@@ -39,11 +38,13 @@ class BillingView {
${valueOfSelect('#billing_state', data.value('stateCode'))}
${valueOfSelect('#billing_country', data.value('countryCode'))}
${data.value('phone')}
-

Edit

`; }, fields: { + email: { + 'valuePath': 'email', + }, firstName: { 'selector': '#billing_first_name_field', 'valuePath': 'billing.name.firstName', diff --git a/modules/ppcp-axo/resources/js/Views/CardView.js b/modules/ppcp-axo/resources/js/Views/CardView.js index 7acd683b0..fd188d7c7 100644 --- a/modules/ppcp-axo/resources/js/Views/CardView.js +++ b/modules/ppcp-axo/resources/js/Views/CardView.js @@ -1,8 +1,9 @@ -import FormFieldGroup from "../Helper/FormFieldGroup"; +import FormFieldGroup from "../Components/FormFieldGroup"; class CardView { - constructor(selector, manager) { + constructor(selector, elements, manager) { + this.el = elements; this.manager = manager; this.cardFormFields = new FormFieldGroup({ @@ -13,21 +14,21 @@ class CardView { if (!this.manager.hideGatewaySelection) { return ''; } - return `

Select other payment method

`; + return `

Select other payment method

`; }; if (data.isEmpty()) { return `
Please fill in your card details.
-

Edit

+

Edit

${selectOtherPaymentMethod()}
`; } return `
-

Card Details Edit

+

Card Details Edit

${data.value('name')}
${data.value('brand')}
${data.value('lastDigits') ? '************' + data.value('lastDigits'): ''}
diff --git a/modules/ppcp-axo/resources/js/Views/ShippingView.js b/modules/ppcp-axo/resources/js/Views/ShippingView.js index 8f972866e..ab154913a 100644 --- a/modules/ppcp-axo/resources/js/Views/ShippingView.js +++ b/modules/ppcp-axo/resources/js/Views/ShippingView.js @@ -1,36 +1,34 @@ -import FormFieldGroup from "../Helper/FormFieldGroup"; +import FormFieldGroup from "../Components/FormFieldGroup"; class ShippingView { - constructor(selector) { + constructor(selector, elements) { + this.el = elements; + this.shippingFormFields = new FormFieldGroup({ baseSelector: '.woocommerce-checkout', contentSelector: selector, template: (data) => { const valueOfSelect = (selectSelector, key) => { + if (!key) { + return ''; + } const selectElement = document.querySelector(selectSelector); const option = selectElement.querySelector(`option[value="${key}"]`); return option ? option.textContent : key; } - if (data.isEditing()) { - return ` -
-

Shipping details Save

-
- `; - } if (data.isEmpty()) { return `
-

Shipping details Edit

+

Shipping details Edit

Please fill in your shipping details.
`; } return `
-

Shipping details Edit

+

Shipping details Edit

${data.value('company')}
${data.value('firstName')} ${data.value('lastName')}
${data.value('street1')}
From 578a5426dc09599f13bae486ad813946e615b4fd Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Thu, 14 Mar 2024 10:54:15 +0000 Subject: [PATCH 26/70] Refactor AXO module --- modules/ppcp-axo/resources/js/AxoManager.js | 332 +++++++++++++----- .../js/Components/DomElementCollection.js | 17 +- .../resources/js/Components/FormFieldGroup.js | 46 +-- .../resources/js/Views/BillingView.js | 12 +- .../ppcp-axo/resources/js/Views/CardView.js | 35 +- .../resources/js/Views/ShippingView.js | 12 +- modules/ppcp-axo/src/Gateway/AxoGateway.php | 4 +- 7 files changed, 337 insertions(+), 121 deletions(-) diff --git a/modules/ppcp-axo/resources/js/AxoManager.js b/modules/ppcp-axo/resources/js/AxoManager.js index e02846b67..905270867 100644 --- a/modules/ppcp-axo/resources/js/AxoManager.js +++ b/modules/ppcp-axo/resources/js/AxoManager.js @@ -16,15 +16,20 @@ class AxoManager { this.fastlane = new Fastlane(); this.$ = jQuery; - this.isConnectProfile = false; - this.isNewProfile = false; this.hideGatewaySelection = false; + this.status = { + active: false, + validEmail: false, + hasProfile: false, + useEmailWidget: this.useEmailWidget() + }; + this.data = { billing: null, shipping: null, card: null, - } + }; this.el = new DomElementCollection(); @@ -32,7 +37,7 @@ class AxoManager { root: { backgroundColorPrimary: '#ffffff' } - } + }; this.locale = 'en_us'; @@ -40,7 +45,11 @@ class AxoManager { this.shippingView = new ShippingView(this.el.shippingAddressContainer.selector, this.el); this.billingView = new BillingView(this.el.billingAddressContainer.selector, this.el); - this.cardView = new CardView(this.el.paymentContainer.selector, this.el, this); + this.cardView = new CardView(this.el.paymentContainer.selector + '-details', this.el, this); + + document.testAxoStatus = (key, value) => { + this.setStatus(key, value); + } } registerEventHandlers() { @@ -48,9 +57,9 @@ class AxoManager { // Listen to Gateway Radio button changes. this.el.gatewayRadioButton.on('change', (ev) => { if (ev.target.checked) { - this.showAxo(); + this.activateAxo(); } else { - this.hideAxo(); + this.deactivateAxo(); } }); @@ -66,33 +75,27 @@ class AxoManager { // Click change shipping address link. this.el.changeShippingAddressLink.on('click', async () => { - if (this.isConnectProfile) { - console.log('profile', this.fastlane.profile); - - //this.shippingView.deactivate(); - + if (this.status.hasProfile) { const { selectionChanged, selectedAddress } = await this.fastlane.profile.showShippingAddressSelector(); console.log('selectedAddress', selectedAddress); if (selectionChanged) { this.setShipping(selectedAddress); - this.shippingView.activate(); + this.shippingView.refresh(); } } }); // Click change billing address link. this.el.changeBillingAddressLink.on('click', async () => { - if (this.isConnectProfile) { + if (this.status.hasProfile) { this.el.changeCardLink.trigger('click'); } }); // Click change card link. this.el.changeCardLink.on('click', async () => { - console.log('profile', this.fastlane.profile); - const response = await this.fastlane.profile.showCardSelector(); console.log('card response', response); @@ -114,67 +117,229 @@ class AxoManager { } - showAxo() { - this.initPlacements(); - this.initFastlane(); + rerender() { + /** + * active | 0 1 1 1 + * validEmail | * 0 1 1 + * hasProfile | * * 0 1 + * -------------------------------- + * defaultSubmitButton | 1 0 0 0 + * defaultEmailField | 1 0 0 0 + * defaultFormFields | 1 0 1 0 + * extraFormFields | 0 0 0 1 + * axoEmailField | 0 1 0 0 + * axoProfileViews | 0 0 0 1 + * axoPaymentContainer | 0 0 1 1 + * axoSubmitButton | 0 0 1 1 + */ + const scenario = this.identifyScenario( + this.status.active, + this.status.validEmail, + this.status.hasProfile + ); - if (!this.isNewProfile && !this.isConnectProfile) { - this.el.allFields.hide(); + log('Scenario', scenario); + + // Reset some elements to a default status. + this.el.watermarkContainer.hide(); + + if (scenario.defaultSubmitButton) { + this.el.defaultSubmitButton.show(); + } else { + this.el.defaultSubmitButton.hide(); } - if (this.useEmailWidget()) { + if (scenario.defaultEmailField) { + this.el.fieldBillingEmail.show(); + } else { + this.el.fieldBillingEmail.hide(); + } + + if (scenario.defaultFormFields) { + this.el.customerDetails.show(); + } else { + this.el.customerDetails.hide(); + } + + if (scenario.extraFormFields) { + this.el.customerDetails.show(); + // Hiding of unwanted will be handled by the axoProfileViews handler. + } + + if (scenario.axoEmailField) { + this.showAxoEmailField(); + this.el.watermarkContainer.show(); + + // Move watermark to after email. + this.$(this.el.fieldBillingEmail.selector).append( + this.$(this.el.watermarkContainer.selector) + ); + + } else { + this.el.emailWidgetContainer.hide(); + if (!scenario.defaultEmailField) { + this.el.fieldBillingEmail.hide(); + } + } + + if (scenario.axoProfileViews) { + this.shippingView.activate(); + this.billingView.activate(); + this.cardView.activate(); + + // Move watermark to after shipping. + this.$(this.el.shippingAddressContainer.selector).after( + this.$(this.el.watermarkContainer.selector) + ); + + this.el.watermarkContainer.show(); + + } else { + this.shippingView.deactivate(); + this.billingView.deactivate(); + this.cardView.deactivate(); + } + + if (scenario.axoPaymentContainer) { + this.el.paymentContainer.show(); + } else { + this.el.paymentContainer.hide(); + } + + if (scenario.axoSubmitButton) { + this.el.submitButtonContainer.show(); + } else { + this.el.submitButtonContainer.hide(); + } + + this.ensureBillingFieldsConsistency(); + this.ensureShippingFieldsConsistency(); + } + + identifyScenario(active, validEmail, hasProfile) { + let response = { + defaultSubmitButton: false, + defaultEmailField: false, + defaultFormFields: false, + extraFormFields: false, + axoEmailField: false, + axoProfileViews: false, + axoPaymentContainer: false, + axoSubmitButton: false, + } + + if (active && validEmail && hasProfile) { + response.extraFormFields = true; + response.axoProfileViews = true; + response.axoPaymentContainer = true; + response.axoSubmitButton = true; + return response; + } + if (active && validEmail && !hasProfile) { + response.defaultFormFields = true; + response.axoEmailField = true; + response.axoPaymentContainer = true; + response.axoSubmitButton = true; + return response; + } + if (active && !validEmail) { + response.axoEmailField = true; + return response; + } + if (!active) { + response.defaultSubmitButton = true; + response.defaultEmailField = true; + response.defaultFormFields = true; + return response; + } + throw new Error('Invalid scenario.'); + } + + ensureBillingFieldsConsistency() { + const $billingFields = this.$('.woocommerce-billing-fields .form-row:visible'); + const $billingHeaders = this.$('.woocommerce-billing-fields h3'); + if (this.billingView.isActive()) { + if ($billingFields.length) { + $billingHeaders.show(); + } else { + $billingHeaders.hide(); + } + } else { + $billingHeaders.show(); + } + } + + ensureShippingFieldsConsistency() { + const $shippingFields = this.$('.woocommerce-shipping-fields .form-row:visible'); + const $shippingHeaders = this.$('.woocommerce-shipping-fields h3'); + if (this.shippingView.isActive()) { + if ($shippingFields.length) { + $shippingHeaders.show(); + } else { + $shippingHeaders.hide(); + } + } else { + $shippingHeaders.show(); + } + } + + showAxoEmailField() { + if (this.status.useEmailWidget) { this.el.emailWidgetContainer.show(); this.el.fieldBillingEmail.hide(); } else { this.el.emailWidgetContainer.hide(); this.el.fieldBillingEmail.show(); } - - if (this.isConnectProfile) { - this.shippingView.activate(); - this.billingView.activate(); - - this.el.emailWidgetContainer.hide(); - this.el.fieldBillingEmail.hide(); - } - - this.el.watermarkContainer.show(); - this.el.paymentContainer.show(); - this.el.submitButtonContainer.show(); - this.el.defaultSubmitButton.hide(); } - hideAxo() { - this.el.allFields.show(); + setStatus(key, value) { + this.status[key] = value; - this.shippingView.deactivate(); - this.billingView.deactivate(); + log('Status updated', JSON.parse(JSON.stringify(this.status))); - this.el.emailWidgetContainer.hide(); - this.el.watermarkContainer.hide(); - this.el.paymentContainer.hide(); - this.el.submitButtonContainer.hide(); - this.el.defaultSubmitButton.show(); + this.rerender(); + } - this.el.emailWidgetContainer.hide(); - this.el.fieldBillingEmail.show(); + activateAxo() { + this.initPlacements(); + this.initFastlane(); + this.setStatus('active', true); + + const emailInput = document.querySelector(this.el.fieldBillingEmail.selector + ' input'); + if (emailInput && this.lastEmailCheckedIdentity !== emailInput.value) { + this.onChangeEmail(); + } + } + + deactivateAxo() { + this.setStatus('active', false); } initPlacements() { - let emailRow = document.querySelector(this.el.fieldBillingEmail.selector); + const wrapper = this.el.axoCustomerDetails; + // Customer details container. + if (!document.querySelector(wrapper.selector)) { + document.querySelector(wrapper.anchorSelector).insertAdjacentHTML('afterbegin', ` +
+ `); + } + + const wrapperElement = document.querySelector(wrapper.selector); + + // Billing view container. const bc = this.el.billingAddressContainer; - const sc = this.el.shippingAddressContainer; - const ec = this.el.emailWidgetContainer; - if (!document.querySelector(bc.selector)) { - document.querySelector(bc.anchorSelector).insertAdjacentHTML('beforeend', ` + wrapperElement.insertAdjacentHTML('beforeend', `
`); } + // Shipping view container. + const sc = this.el.shippingAddressContainer; if (!document.querySelector(sc.selector)) { - document.querySelector(sc.anchorSelector).insertAdjacentHTML('afterbegin', ` + wrapperElement.insertAdjacentHTML('beforeend', `
`); } @@ -182,8 +347,9 @@ class AxoManager { if (this.useEmailWidget()) { // Display email widget. + const ec = this.el.emailWidgetContainer; if (!document.querySelector(ec.selector)) { - emailRow.parentNode.insertAdjacentHTML('afterbegin', ` + wrapperElement.insertAdjacentHTML('afterbegin', `
--- EMAIL WIDGET PLACEHOLDER ---
@@ -192,8 +358,9 @@ class AxoManager { } else { - // Move email row to first place. - emailRow.parentNode.prepend(emailRow); + // Move email to the AXO container. + let emailRow = document.querySelector(this.el.fieldBillingEmail.selector); + wrapperElement.prepend(emailRow); emailRow.querySelector('input').focus(); } } @@ -229,7 +396,10 @@ class AxoManager { const gatewayPaymentContainer = document.querySelector('.payment_method_ppcp-axo-gateway'); gatewayPaymentContainer.insertAdjacentHTML('beforeend', ` - + `); } @@ -259,20 +429,33 @@ class AxoManager { if (this.emailInput.value) { this.onChangeEmail(); } - } } async onChangeEmail () { - log('Email changed: ' + this.emailInput.value); + if (!this.status.active) { + log('Email checking skipped, AXO not active.'); + return; + } + + if (!this.emailInput) { + log('Email field not initialized.'); + return; + } + + log('Email changed: ' + (this.emailInput ? this.emailInput.value : '')); + + this.$(this.el.paymentContainer.selector + '-detail').html(''); + this.$(this.el.paymentContainer.selector + '-form').html(''); + + this.setStatus('validEmail', false); + this.setStatus('hasProfile', false); - this.isConnectProfile = false; - this.isNewProfile = false; this.hideGatewaySelection = false; - this.el.allFields.hide(); + this.lastEmailCheckedIdentity = this.emailInput.value; - if (!this.emailInput.checkValidity()) { + if (!this.emailInput.value || !this.emailInput.checkValidity()) { log('The email address is not valid.'); return; } @@ -287,8 +470,6 @@ class AxoManager { // Authenticate the customer to get access to their profile. log('Email is associated with a Connect profile or a PayPal member'); - // TODO : enter hideOtherGateways mode - const authResponse = await this.fastlane.identity.triggerAuthenticationFlow(lookupResponse.customerContextId); log('AuthResponse', authResponse); @@ -296,25 +477,18 @@ class AxoManager { if (authResponse.authenticationState === 'succeeded') { log(JSON.stringify(authResponse)); - this.el.allFields.show(); - this.el.paymentContainer.show(); - - - // document.querySelector(this.el.paymentContainer.selector).innerHTML = - // 'Change card'; - // Add addresses this.setShipping(authResponse.profileData.shippingAddress); // TODO : set billing this.setCard(authResponse.profileData.card); - this.isConnectProfile = true; + this.setStatus('validEmail', true); + this.setStatus('hasProfile', true); + this.hideGatewaySelection = true; this.$('.wc_payment_methods label').hide(); - this.shippingView.activate(); - this.billingView.activate(); - this.cardView.activate(); + this.rerender(); } else { // authentication failed or canceled by the customer @@ -326,14 +500,12 @@ class AxoManager { // This is a guest customer. log('No profile found with this email address.'); - this.el.allFields.show(); - this.el.paymentContainer.show(); - - this.isNewProfile = true; + this.setStatus('validEmail', true); + this.setStatus('hasProfile', false); this.cardComponent = await this.fastlane .FastlaneCardComponent(MockData.cardComponent()) - .render(this.el.paymentContainer.selector); + .render(this.el.paymentContainer.selector + '-form'); } } diff --git a/modules/ppcp-axo/resources/js/Components/DomElementCollection.js b/modules/ppcp-axo/resources/js/Components/DomElementCollection.js index d6840242a..8cc1deb1a 100644 --- a/modules/ppcp-axo/resources/js/Components/DomElementCollection.js +++ b/modules/ppcp-axo/resources/js/Components/DomElementCollection.js @@ -23,8 +23,15 @@ class DomElementCollection { className: 'ppcp-axo-watermark-container' }); - this.allFields = new DomElement({ - selector: '#customer_details .form-row, #customer_details .woocommerce-shipping-fields' + this.customerDetails = new DomElement({ + selector: '#customer_details > *:not(#ppcp-axo-customer-details)' + }); + + this.axoCustomerDetails = new DomElement({ + id: 'ppcp-axo-customer-details', + selector: '#ppcp-axo-customer-details', + className: 'ppcp-axo-customer-details', + anchorSelector: '#customer_details' }); this.emailWidgetContainer = new DomElement({ @@ -36,15 +43,13 @@ class DomElementCollection { this.shippingAddressContainer = new DomElement({ id: 'ppcp-axo-shipping-address-container', selector: '#ppcp-axo-shipping-address-container', - className: 'ppcp-axo-shipping-address-container', - anchorSelector: '.woocommerce-shipping-fields' + className: 'ppcp-axo-shipping-address-container' }); this.billingAddressContainer = new DomElement({ id: 'ppcp-axo-billing-address-container', selector: '#ppcp-axo-billing-address-container', - className: 'ppcp-axo-billing-address-container', - anchorSelector: '.woocommerce-billing-fields__field-wrapper' + className: 'ppcp-axo-billing-address-container' }); this.fieldBillingEmail = new DomElement({ diff --git a/modules/ppcp-axo/resources/js/Components/FormFieldGroup.js b/modules/ppcp-axo/resources/js/Components/FormFieldGroup.js index d35b54349..c308bef3f 100644 --- a/modules/ppcp-axo/resources/js/Components/FormFieldGroup.js +++ b/modules/ppcp-axo/resources/js/Components/FormFieldGroup.js @@ -48,36 +48,40 @@ class MockData { content.innerHTML = ''; + if (!this.active) { + this.hideField(this.contentSelector); + } else { + this.showField(this.contentSelector); + } + Object.keys(this.fields).forEach((key) => { const field = this.fields[key]; if (this.active) { this.hideField(field.selector); - //this.showField(this.contentSelector); } else { this.showField(field.selector); - //this.hideField(this.contentSelector); } - - if (typeof this.template === 'function') { - content.innerHTML = this.template({ - value: (valueKey) => { - return this.getDataValue(this.fields[valueKey].valuePath); - }, - isEmpty: () => { - let isEmpty = true; - Object.values(this.fields).forEach((valueField) => { - if (this.getDataValue(valueField.valuePath)) { - isEmpty = false; - return false; - } - }); - return isEmpty; - } - }); - } - }); + + if (typeof this.template === 'function') { + content.innerHTML = this.template({ + value: (valueKey) => { + return this.getDataValue(this.fields[valueKey].valuePath); + }, + isEmpty: () => { + let isEmpty = true; + Object.values(this.fields).forEach((valueField) => { + if (this.getDataValue(valueField.valuePath)) { + isEmpty = false; + return false; + } + }); + return isEmpty; + } + }); + } + } showField(selector) { diff --git a/modules/ppcp-axo/resources/js/Views/BillingView.js b/modules/ppcp-axo/resources/js/Views/BillingView.js index 7e61388c3..4163c19c1 100644 --- a/modules/ppcp-axo/resources/js/Views/BillingView.js +++ b/modules/ppcp-axo/resources/js/Views/BillingView.js @@ -21,14 +21,14 @@ class BillingView { if (data.isEmpty()) { return `
-

Billing details Edit

+

Billing Edit

Please fill in your billing details.
`; } return `
-

Billing details Edit

+

Billing Edit

${data.value('email')}
${data.value('company')}
${data.value('firstName')} ${data.value('lastName')}
@@ -89,6 +89,10 @@ class BillingView { }); } + isActive() { + return this.billingFormFields.active; + } + activate() { this.billingFormFields.activate(); } @@ -97,6 +101,10 @@ class BillingView { this.billingFormFields.deactivate(); } + refresh() { + this.billingFormFields.refresh(); + } + setData(data) { this.billingFormFields.setData(data); } diff --git a/modules/ppcp-axo/resources/js/Views/CardView.js b/modules/ppcp-axo/resources/js/Views/CardView.js index fd188d7c7..84a6555d2 100644 --- a/modules/ppcp-axo/resources/js/Views/CardView.js +++ b/modules/ppcp-axo/resources/js/Views/CardView.js @@ -20,19 +20,40 @@ class CardView { if (data.isEmpty()) { return `
-
Please fill in your card details.
-

Edit

+
+
Please fill in your card details.
+
+

Add card details

${selectOtherPaymentMethod()}
`; } + + const expiry = data.value('expiry').split('-'); + + const cardIcons = { + 'VISA': 'visa-dark.svg', + 'MASTERCARD_CARD': 'mastercard-dark.svg', + 'AMEX': 'amex.svg', + 'DISCOVER': 'discover.svg', + }; + return `
-

Card Details Edit

-
${data.value('name')}
-
${data.value('brand')}
-
${data.value('lastDigits') ? '************' + data.value('lastDigits'): ''}
-
${data.value('expiry')}
+

Card Details Edit

+
+
+ ${data.value('brand')} +
+
${data.value('lastDigits') ? '**** **** **** ' + data.value('lastDigits'): ''}
+
${expiry[1]}/${expiry[0]}
+
${data.value('name')}
+
${selectOtherPaymentMethod()}
`; diff --git a/modules/ppcp-axo/resources/js/Views/ShippingView.js b/modules/ppcp-axo/resources/js/Views/ShippingView.js index ab154913a..24c95cf87 100644 --- a/modules/ppcp-axo/resources/js/Views/ShippingView.js +++ b/modules/ppcp-axo/resources/js/Views/ShippingView.js @@ -21,14 +21,14 @@ class ShippingView { if (data.isEmpty()) { return `
-

Shipping details Edit

+

Shipping Edit

Please fill in your shipping details.
`; } return `
-

Shipping details Edit

+

Shipping Edit

${data.value('company')}
${data.value('firstName')} ${data.value('lastName')}
${data.value('street1')}
@@ -85,6 +85,10 @@ class ShippingView { }); } + isActive() { + return this.shippingFormFields.active; + } + activate() { this.shippingFormFields.activate(); } @@ -93,6 +97,10 @@ class ShippingView { this.shippingFormFields.deactivate(); } + refresh() { + this.shippingFormFields.refresh(); + } + setData(data) { this.shippingFormFields.setData(data); } diff --git a/modules/ppcp-axo/src/Gateway/AxoGateway.php b/modules/ppcp-axo/src/Gateway/AxoGateway.php index 32f9f83e9..d3ef227ed 100644 --- a/modules/ppcp-axo/src/Gateway/AxoGateway.php +++ b/modules/ppcp-axo/src/Gateway/AxoGateway.php @@ -214,12 +214,10 @@ class AxoGateway extends WC_Payment_Gateway { //$this->add_paypal_meta( $wc_order, $order, $this->environment ); - // TODO: inject dependency + // TODO: inject dependency. PPCP::container()->get( 'session.handler' )->replace_order( $order ); PPCP::container()->get( 'wcgateway.order-processor' )->process( $wc_order ); - - // $payment_source = array( // 'oxxo' => array( // 'name' => $wc_order->get_billing_first_name() . ' ' . $wc_order->get_billing_last_name(), From 385d89f07167173710e874032be276662168907c Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Thu, 14 Mar 2024 16:00:37 +0000 Subject: [PATCH 27/70] Fix mastercard logo icon in AXO. --- modules/ppcp-axo/resources/js/Views/CardView.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/ppcp-axo/resources/js/Views/CardView.js b/modules/ppcp-axo/resources/js/Views/CardView.js index 84a6555d2..67d9aa01e 100644 --- a/modules/ppcp-axo/resources/js/Views/CardView.js +++ b/modules/ppcp-axo/resources/js/Views/CardView.js @@ -32,10 +32,10 @@ class CardView { const expiry = data.value('expiry').split('-'); const cardIcons = { - 'VISA': 'visa-dark.svg', - 'MASTERCARD_CARD': 'mastercard-dark.svg', - 'AMEX': 'amex.svg', - 'DISCOVER': 'discover.svg', + 'VISA': 'visa-dark.svg', + 'MASTER_CARD': 'mastercard-dark.svg', + 'AMEX': 'amex.svg', + 'DISCOVER': 'discover.svg', }; return ` From 4e4f00acf5d93ffc7fc43093be9692212126779f Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Mon, 25 Mar 2024 10:28:15 +0000 Subject: [PATCH 28/70] Integrate AXO and Insights --- modules/ppcp-axo/resources/js/AxoManager.js | 2 +- .../resources/js/Insights/PayPalInsights.js | 55 +++++++++++++++ modules/ppcp-axo/resources/js/boot.js | 2 +- modules/ppcp-axo/services.php | 48 +++++++++++++ modules/ppcp-axo/src/AxoModule.php | 47 +++++++++---- modules/ppcp-axo/src/Helper/ApmApplies.php | 68 +++++++++++++++++++ 6 files changed, 206 insertions(+), 16 deletions(-) create mode 100644 modules/ppcp-axo/resources/js/Insights/PayPalInsights.js create mode 100644 modules/ppcp-axo/src/Helper/ApmApplies.php diff --git a/modules/ppcp-axo/resources/js/AxoManager.js b/modules/ppcp-axo/resources/js/AxoManager.js index 905270867..228b4e58f 100644 --- a/modules/ppcp-axo/resources/js/AxoManager.js +++ b/modules/ppcp-axo/resources/js/AxoManager.js @@ -540,7 +540,7 @@ class AxoManager { alert('nonce: ' + nonce); // Submit form. - this.el.defaultSubmitButton.click(); +// this.el.defaultSubmitButton.click(); } useEmailWidget() { diff --git a/modules/ppcp-axo/resources/js/Insights/PayPalInsights.js b/modules/ppcp-axo/resources/js/Insights/PayPalInsights.js new file mode 100644 index 000000000..eb9b24d10 --- /dev/null +++ b/modules/ppcp-axo/resources/js/Insights/PayPalInsights.js @@ -0,0 +1,55 @@ + +class PayPalInsights { + + constructor() { + window.paypalInsightDataLayer = window.paypalInsightDataLayer || []; + document.paypalInsight = () => { + paypalInsightDataLayer.push(arguments); + } + } + + /** + * @returns {PayPalInsights} + */ + static getInstance() { + if (!PayPalInsights.instance) { + PayPalInsights.instance = new PayPalInsights(); + } + return PayPalInsights.instance; + } + + track(eventName, data) { + paypalInsight('event', eventName, data); + } + + static config (clientId, data) { + paypalInsight('config', clientId, data); + } + + static setSessionId (sessionId) { + paypalInsight('set', { session_id: sessionId }); + } + + static trackJsLoad () { + PayPalInsights.getInstance().track('js_load', { timestamp: Date.now() }); + } + + static trackBeginCheckout (data) { + PayPalInsights.getInstance().track('begin_checkout', data); + } + + static trackSubmitCheckoutEmail (data) { + PayPalInsights.getInstance().track('submit_checkout_email', data); + } + + static trackSelectPaymentMethod (data) { + PayPalInsights.getInstance().track('select_payment_method', data); + } + + static trackEndCheckout (data) { + PayPalInsights.getInstance().track('end_checkout', data); + } + +} + +export default PayPalInsights; diff --git a/modules/ppcp-axo/resources/js/boot.js b/modules/ppcp-axo/resources/js/boot.js index a8fb85a50..6b8b35893 100644 --- a/modules/ppcp-axo/resources/js/boot.js +++ b/modules/ppcp-axo/resources/js/boot.js @@ -8,7 +8,7 @@ import {loadPaypalScript} from "../../../ppcp-button/resources/js/modules/Helper }) { const bootstrap = () => { - const axo = new AxoManager(axoConfig, ppcpConfig); + new AxoManager(axoConfig, ppcpConfig); } document.addEventListener( diff --git a/modules/ppcp-axo/services.php b/modules/ppcp-axo/services.php index c1044f0d0..71fd62d32 100644 --- a/modules/ppcp-axo/services.php +++ b/modules/ppcp-axo/services.php @@ -11,10 +11,41 @@ namespace WooCommerce\PayPalCommerce\Axo; use WooCommerce\PayPalCommerce\Axo\Assets\AxoManager; use WooCommerce\PayPalCommerce\Axo\Gateway\AxoGateway; +use WooCommerce\PayPalCommerce\Axo\Helper\ApmApplies; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; return array( + // If AXO can be configured. + 'axo.eligible' => static function ( ContainerInterface $container ): bool { + $apm_applies = $container->get( 'axo.helpers.apm-applies' ); + assert( $apm_applies instanceof ApmApplies ); + + return $apm_applies->for_country_currency(); + }, + + 'axo.helpers.apm-applies' => static function ( ContainerInterface $container ) : ApmApplies { + return new ApmApplies( + $container->get( 'axo.supported-country-currency-matrix' ), + $container->get( 'api.shop.currency' ), + $container->get( 'api.shop.country' ) + ); + }, + + // If AXO is configured and onboarded. + 'axo.available' => static function ( ContainerInterface $container ): bool { +// TODO +// if ( apply_filters( 'woocommerce_paypal_payments_googlepay_validate_product_status', true ) ) { +// $status = $container->get( 'googlepay.helpers.apm-product-status' ); +// assert( $status instanceof ApmProductStatus ); +// /** +// * If merchant isn't onboarded via /v1/customer/partner-referrals this returns false as the API call fails. +// */ +// return apply_filters( 'woocommerce_paypal_payments_googlepay_product_status', $status->is_active() ); +// } + return true; + }, + 'axo.url' => static function ( ContainerInterface $container ): string { $path = realpath( __FILE__ ); if ( false === $path ) { @@ -74,4 +105,21 @@ return array( ); }, + /** + * The matrix which countries and currency combinations can be used for AXO. + */ + 'axo.supported-country-currency-matrix' => static function ( ContainerInterface $container ) : array { + /** + * Returns which countries and currency combinations can be used for AXO. + */ + return apply_filters( + 'woocommerce_paypal_payments_axo_supported_country_currency_matrix', + array( + 'US' => array( + 'USD', + ), + ) + ); + }, + ); diff --git a/modules/ppcp-axo/src/AxoModule.php b/modules/ppcp-axo/src/AxoModule.php index 3b07dcf62..e44e2ba9a 100644 --- a/modules/ppcp-axo/src/AxoModule.php +++ b/modules/ppcp-axo/src/AxoModule.php @@ -38,29 +38,35 @@ class AxoModule implements ModuleInterface { add_filter( 'woocommerce_payment_gateways', function ( $methods ) use ( $c ): array { - $methods[] = $c->get( 'axo.gateway' ); + $gateway = $c->get( 'axo.gateway' ); + + // Check if the module is applicable, correct country, currency, ... etc. + if ( ! $c->get( 'axo.eligible' ) ) { + return $methods; + } + + // TODO: check product status eligibility. + + if ( is_user_logged_in() ) { + return $methods; + } + + $methods[] = $gateway; return $methods; }, 1, 9 ); - /** - * Param types removed to avoid third-party issues. - * - * @psalm-suppress MissingClosureParamType - */ - add_filter( - 'woocommerce_paypal_payments_sdk_components_hook', - function( $components ) { - $components[] = 'connect'; - return $components; - } - ); - add_action( 'init', static function () use ( $c ) { + + // Check if the module is applicable, correct country, currency, ... etc. + if ( ! $c->get( 'axo.eligible' ) ) { + return; + } + $manager = $c->get( 'axo.manager' ); assert( $manager instanceof AxoManager ); @@ -85,6 +91,19 @@ class AxoModule implements ModuleInterface { } ); + /** + * Param types removed to avoid third-party issues. + * + * @psalm-suppress MissingClosureParamType + */ + add_filter( + 'woocommerce_paypal_payments_sdk_components_hook', + function( $components ) { + $components[] = 'connect'; + return $components; + } + ); + }, 1 ); diff --git a/modules/ppcp-axo/src/Helper/ApmApplies.php b/modules/ppcp-axo/src/Helper/ApmApplies.php new file mode 100644 index 000000000..9a3f5aaa8 --- /dev/null +++ b/modules/ppcp-axo/src/Helper/ApmApplies.php @@ -0,0 +1,68 @@ +allowed_country_currency_matrix = $allowed_country_currency_matrix; + $this->currency = $currency; + $this->country = $country; + } + + /** + * Returns whether GooglePay can be used in the current country and the current currency used. + * + * @return bool + */ + public function for_country_currency(): bool { + if ( ! in_array( $this->country, array_keys( $this->allowed_country_currency_matrix ), true ) ) { + return false; + } + return in_array( $this->currency, $this->allowed_country_currency_matrix[ $this->country ], true ); + } + +} From 2bf3c14feb33b1d6fabebb0d61d75f27b62fec15 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 3 Apr 2024 09:59:40 +0200 Subject: [PATCH 29/70] Do not execute current free trial flow if vault v3 enabled --- modules/ppcp-button/resources/js/button.js | 7 +++++- modules/ppcp-button/services.php | 1 + .../ppcp-button/src/Assets/SmartButton.php | 25 +++++++++++++------ .../resources/js/add-payment-method.js | 2 +- .../src/SavePaymentMethodsModule.php | 8 ++++++ modules/ppcp-vaulting/services.php | 3 +++ 6 files changed, 37 insertions(+), 9 deletions(-) diff --git a/modules/ppcp-button/resources/js/button.js b/modules/ppcp-button/resources/js/button.js index 0f5d573ce..d0e1a8543 100644 --- a/modules/ppcp-button/resources/js/button.js +++ b/modules/ppcp-button/resources/js/button.js @@ -137,7 +137,12 @@ const bootstrap = () => { } const isFreeTrial = PayPalCommerceGateway.is_free_trial_cart; - if (isFreeTrial && data.fundingSource !== 'card' && ! PayPalCommerceGateway.subscription_plan_id) { + if ( + isFreeTrial + && data.fundingSource !== 'card' + && ! PayPalCommerceGateway.subscription_plan_id + && ! PayPalCommerceGateway.vault_v3_enabled + ) { freeTrialHandler.handle(); return actions.reject(); } diff --git a/modules/ppcp-button/services.php b/modules/ppcp-button/services.php index a812dc53e..13c130c2a 100644 --- a/modules/ppcp-button/services.php +++ b/modules/ppcp-button/services.php @@ -145,6 +145,7 @@ return array( $container->get( 'button.early-wc-checkout-validation-enabled' ), $container->get( 'button.pay-now-contexts' ), $container->get( 'wcgateway.funding-sources-without-redirect' ), + $container->get('vaulting.vault-v3-enabled'), $container->get( 'woocommerce.logger.woocommerce' ) ); }, diff --git a/modules/ppcp-button/src/Assets/SmartButton.php b/modules/ppcp-button/src/Assets/SmartButton.php index c33bdcf47..e2c64ac0c 100644 --- a/modules/ppcp-button/src/Assets/SmartButton.php +++ b/modules/ppcp-button/src/Assets/SmartButton.php @@ -184,13 +184,6 @@ class SmartButton implements SmartButtonInterface { */ private $funding_sources_without_redirect; - /** - * The logger. - * - * @var LoggerInterface - */ - private $logger; - /** * Session handler. * @@ -198,6 +191,20 @@ class SmartButton implements SmartButtonInterface { */ private $session_handler; + /** + * Whether Vault v3 module is enabled. + * + * @var bool + */ + private $vault_v3_enabled; + + /** + * The logger. + * + * @var LoggerInterface + */ + private $logger; + /** * SmartButton constructor. * @@ -220,6 +227,7 @@ class SmartButton implements SmartButtonInterface { * @param bool $early_validation_enabled Whether to execute WC validation of the checkout form. * @param array $pay_now_contexts The contexts that should have the Pay Now button. * @param string[] $funding_sources_without_redirect The sources that do not cause issues about redirecting (on mobile, ...) and sometimes not returning back. + * @param bool $vault_v3_enabled Whether Vault v3 module is enabled. * @param LoggerInterface $logger The logger. */ public function __construct( @@ -242,6 +250,7 @@ class SmartButton implements SmartButtonInterface { bool $early_validation_enabled, array $pay_now_contexts, array $funding_sources_without_redirect, + bool $vault_v3_enabled, LoggerInterface $logger ) { @@ -264,6 +273,7 @@ class SmartButton implements SmartButtonInterface { $this->early_validation_enabled = $early_validation_enabled; $this->pay_now_contexts = $pay_now_contexts; $this->funding_sources_without_redirect = $funding_sources_without_redirect; + $this->vault_v3_enabled = $vault_v3_enabled; $this->logger = $logger; } @@ -1063,6 +1073,7 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages ), 'cart_contains_subscription' => $this->subscription_helper->cart_contains_subscription(), 'subscription_plan_id' => $this->subscription_helper->paypal_subscription_id(), + 'vault_v3_enabled' => $this->vault_v3_enabled, 'variable_paypal_subscription_variations' => $this->subscription_helper->variable_paypal_subscription_variations(), 'subscription_product_allowed' => $this->subscription_helper->checkout_subscription_product_allowed(), 'locations_with_subscription_product' => $this->subscription_helper->locations_with_subscription_product(), diff --git a/modules/ppcp-save-payment-methods/resources/js/add-payment-method.js b/modules/ppcp-save-payment-methods/resources/js/add-payment-method.js index 8aca64f7c..6178bd92c 100644 --- a/modules/ppcp-save-payment-methods/resources/js/add-payment-method.js +++ b/modules/ppcp-save-payment-methods/resources/js/add-payment-method.js @@ -12,7 +12,7 @@ import ErrorHandler from "../../../ppcp-button/resources/js/modules/ErrorHandler import {cardFieldStyles} from "../../../ppcp-button/resources/js/modules/Helper/CardFieldsHelper"; const errorHandler = new ErrorHandler( - PayPalCommerceGateway.labels.error.generic, + ppcp_add_payment_method.labels.error.generic, document.querySelector('.woocommerce-notices-wrapper') ); diff --git a/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php b/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php index 59010f6ca..f93a75a66 100644 --- a/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php +++ b/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php @@ -312,6 +312,14 @@ class SavePaymentMethodsModule implements ModuleInterface { 'nonce' => wp_create_nonce( SubscriptionChangePaymentMethod::nonce() ), ), ), + 'labels' => array( + 'error' => array( + 'generic' => __( + 'Something went wrong. Please try again or choose another payment source.', + 'woocommerce-paypal-payments' + ), + ) + ), ) ); } catch ( RuntimeException $exception ) { diff --git a/modules/ppcp-vaulting/services.php b/modules/ppcp-vaulting/services.php index 7d3c5e485..45355225d 100644 --- a/modules/ppcp-vaulting/services.php +++ b/modules/ppcp-vaulting/services.php @@ -64,4 +64,7 @@ return array( $container->get( 'woocommerce.logger.woocommerce' ) ); }, + 'vaulting.vault-v3-enabled' => static function( ContainerInterface $container ): bool { + return $container->has( 'save-payment-methods.eligible' ) && $container->get( 'save-payment-methods.eligible' ); + }, ); From bc0eb5746079ec31ca459196f025c79307694c08 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Thu, 4 Apr 2024 16:52:08 +0200 Subject: [PATCH 30/70] Do not create PayPal order for free trial subscription if user has payment token --- modules/ppcp-button/services.php | 3 +- .../ppcp-button/src/Assets/SmartButton.php | 24 +++++++++++++++- modules/ppcp-wc-gateway/services.php | 3 +- .../src/Gateway/PayPalGateway.php | 28 +++++++++++++++++-- 4 files changed, 52 insertions(+), 6 deletions(-) diff --git a/modules/ppcp-button/services.php b/modules/ppcp-button/services.php index 13c130c2a..6f781911b 100644 --- a/modules/ppcp-button/services.php +++ b/modules/ppcp-button/services.php @@ -145,7 +145,8 @@ return array( $container->get( 'button.early-wc-checkout-validation-enabled' ), $container->get( 'button.pay-now-contexts' ), $container->get( 'wcgateway.funding-sources-without-redirect' ), - $container->get('vaulting.vault-v3-enabled'), + $container->get( 'vaulting.vault-v3-enabled' ), + $container->get( 'api.endpoint.payment-tokens' ), $container->get( 'woocommerce.logger.woocommerce' ) ); }, diff --git a/modules/ppcp-button/src/Assets/SmartButton.php b/modules/ppcp-button/src/Assets/SmartButton.php index 22ff4e208..605006d12 100644 --- a/modules/ppcp-button/src/Assets/SmartButton.php +++ b/modules/ppcp-button/src/Assets/SmartButton.php @@ -14,6 +14,7 @@ use Psr\Log\LoggerInterface; use WC_Order; use WC_Product; use WC_Product_Variation; +use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentTokensEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Entity\Money; use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentToken; use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory; @@ -198,6 +199,13 @@ class SmartButton implements SmartButtonInterface { */ private $vault_v3_enabled; + /** + * Payment tokens endpoint. + * + * @var PaymentTokensEndpoint + */ + private $payment_tokens_endpoint; + /** * The logger. * @@ -228,6 +236,7 @@ class SmartButton implements SmartButtonInterface { * @param array $pay_now_contexts The contexts that should have the Pay Now button. * @param string[] $funding_sources_without_redirect The sources that do not cause issues about redirecting (on mobile, ...) and sometimes not returning back. * @param bool $vault_v3_enabled Whether Vault v3 module is enabled. + * @param PaymentTokensEndpoint $payment_tokens_endpoint Payment tokens endpoint. * @param LoggerInterface $logger The logger. */ public function __construct( @@ -251,6 +260,7 @@ class SmartButton implements SmartButtonInterface { array $pay_now_contexts, array $funding_sources_without_redirect, bool $vault_v3_enabled, + PaymentTokensEndpoint $payment_tokens_endpoint, LoggerInterface $logger ) { @@ -275,6 +285,7 @@ class SmartButton implements SmartButtonInterface { $this->funding_sources_without_redirect = $funding_sources_without_redirect; $this->vault_v3_enabled = $vault_v3_enabled; $this->logger = $logger; + $this->payment_tokens_endpoint = $payment_tokens_endpoint; } /** @@ -1915,8 +1926,18 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages */ private function get_vaulted_paypal_email(): string { try { - $tokens = $this->get_payment_tokens(); + $customer_id = get_user_meta( get_current_user_id(), '_ppcp_target_customer_id', true ); + if ( $customer_id ) { + $customer_tokens = $this->payment_tokens_endpoint->payment_tokens_for_customer( $customer_id ); + foreach ( $customer_tokens as $token ) { + $email_address = $token['payment_source']->properties()->email_address ?? ''; + if ( $email_address ) { + return $email_address; + } + } + } + $tokens = $this->get_payment_tokens(); foreach ( $tokens as $token ) { if ( isset( $token->source()->paypal ) ) { return $token->source()->paypal->payer->email_address; @@ -1925,6 +1946,7 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages } catch ( Exception $exception ) { $this->logger->error( 'Failed to get PayPal vaulted email. ' . $exception->getMessage() ); } + return ''; } diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index 9802e1951..a78a6527e 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -103,7 +103,8 @@ return array( $api_shop_country, $container->get( 'api.endpoint.order' ), $container->get( 'api.factory.paypal-checkout-url' ), - $container->get( 'wcgateway.place-order-button-text' ) + $container->get( 'wcgateway.place-order-button-text' ), + $container->get( 'api.endpoint.payment-tokens' ) ); }, 'wcgateway.credit-card-gateway' => static function ( ContainerInterface $container ): CreditCardGateway { diff --git a/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php b/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php index 19ebe9d08..14beda474 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php @@ -14,6 +14,7 @@ use Psr\Log\LoggerInterface; use WC_Order; use WC_Payment_Tokens; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint; +use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentTokensEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus; use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentToken; use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException; @@ -179,6 +180,13 @@ class PayPalGateway extends \WC_Payment_Gateway { */ private $paypal_checkout_url_factory; + /** + * Payment tokens endpoint. + * + * @var PaymentTokensEndpoint + */ + private $payment_tokens_endpoint; + /** * PayPalGateway constructor. * @@ -199,6 +207,7 @@ class PayPalGateway extends \WC_Payment_Gateway { * @param OrderEndpoint $order_endpoint The order endpoint. * @param callable(string):string $paypal_checkout_url_factory The function return the PayPal checkout URL for the given order ID. * @param string $place_order_button_text The text for the standard "Place order" button. + * @param PaymentTokensEndpoint $payment_tokens_endpoint Payment tokens endpoint. */ public function __construct( SettingsRenderer $settings_renderer, @@ -217,7 +226,8 @@ class PayPalGateway extends \WC_Payment_Gateway { string $api_shop_country, OrderEndpoint $order_endpoint, callable $paypal_checkout_url_factory, - string $place_order_button_text + string $place_order_button_text, + PaymentTokensEndpoint $payment_tokens_endpoint ) { $this->id = self::ID; $this->settings_renderer = $settings_renderer; @@ -300,7 +310,8 @@ class PayPalGateway extends \WC_Payment_Gateway { ) ); - $this->order_endpoint = $order_endpoint; + $this->order_endpoint = $order_endpoint; + $this->payment_tokens_endpoint = $payment_tokens_endpoint; } /** @@ -501,6 +512,18 @@ class PayPalGateway extends \WC_Payment_Gateway { } if ( 'card' !== $funding_source && $this->is_free_trial_order( $wc_order ) && ! $this->subscription_helper->paypal_subscription_id() ) { + $customer_id = get_user_meta( $wc_order->get_customer_id(), '_ppcp_target_customer_id', true ); + if ( $customer_id ) { + $customer_tokens = $this->payment_tokens_endpoint->payment_tokens_for_customer( $customer_id ); + foreach ( $customer_tokens as $token ) { + $payment_source_name = $token['payment_source']->name() ?? ''; + if ( $payment_source_name === 'paypal' || $payment_source_name === 'venmo' ) { + $wc_order->payment_complete(); + return $this->handle_payment_success( $wc_order ); + } + } + } + $user_id = (int) $wc_order->get_customer_id(); $tokens = $this->payment_token_repository->all_for_user_id( $user_id ); if ( ! array_filter( @@ -513,7 +536,6 @@ class PayPalGateway extends \WC_Payment_Gateway { } $wc_order->payment_complete(); - return $this->handle_payment_success( $wc_order ); } From 457e9f0182d07a45e73f49040ac576694aa5d3e4 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Sat, 6 Apr 2024 17:31:57 +0200 Subject: [PATCH 31/70] Allow free trial subscription without payment token for logged-in users --- .../ActionHandler/CheckoutActionHandler.js | 49 +++++++++++++++++++ .../ContextBootstrap/CheckoutBootstap.js | 8 +++ .../ppcp-button/src/Assets/SmartButton.php | 10 ++++ .../tests/save-payment-methods.spec.js | 47 ++++++++++++++++++ 4 files changed, 114 insertions(+) diff --git a/modules/ppcp-button/resources/js/modules/ActionHandler/CheckoutActionHandler.js b/modules/ppcp-button/resources/js/modules/ActionHandler/CheckoutActionHandler.js index 93ecf7c8f..f3afd4f30 100644 --- a/modules/ppcp-button/resources/js/modules/ActionHandler/CheckoutActionHandler.js +++ b/modules/ppcp-button/resources/js/modules/ActionHandler/CheckoutActionHandler.js @@ -144,6 +144,55 @@ class CheckoutActionHandler { } } } + + addPaymentMethodConfiguration() { + return { + createVaultSetupToken: async () => { + const response = await fetch(this.config.ajax.create_setup_token.endpoint, { + method: "POST", + credentials: 'same-origin', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + nonce: this.config.ajax.create_setup_token.nonce, + }) + }); + + const result = await response.json() + if (result.data.id) { + return result.data.id + } + + console.error(result) + }, + onApprove: async ({vaultSetupToken}) => { + const response = await fetch(this.config.ajax.create_payment_token.endpoint, { + method: "POST", + credentials: 'same-origin', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + nonce: this.config.ajax.create_payment_token.nonce, + vault_setup_token: vaultSetupToken, + return_url: location.href, + }) + }) + + const result = await response.json(); + if (result.success === true) { + window.location.href = location.href; + return; + } + + console.error(result) + }, + onError: (error) => { + console.error(error) + } + } + } } export default CheckoutActionHandler; diff --git a/modules/ppcp-button/resources/js/modules/ContextBootstrap/CheckoutBootstap.js b/modules/ppcp-button/resources/js/modules/ContextBootstrap/CheckoutBootstap.js index e7fae33b0..26a278736 100644 --- a/modules/ppcp-button/resources/js/modules/ContextBootstrap/CheckoutBootstap.js +++ b/modules/ppcp-button/resources/js/modules/ContextBootstrap/CheckoutBootstap.js @@ -116,6 +116,14 @@ class CheckoutBootstap { return; } + if( + PayPalCommerceGateway.is_free_trial_cart + && PayPalCommerceGateway.vault_v3_enabled + ) { + this.renderer.render(actionHandler.addPaymentMethodConfiguration(), {}, actionHandler.configuration()); + return; + } + this.renderer.render(actionHandler.configuration(), {}, actionHandler.configuration()); } diff --git a/modules/ppcp-button/src/Assets/SmartButton.php b/modules/ppcp-button/src/Assets/SmartButton.php index 605006d12..db703c7f1 100644 --- a/modules/ppcp-button/src/Assets/SmartButton.php +++ b/modules/ppcp-button/src/Assets/SmartButton.php @@ -34,6 +34,8 @@ use WooCommerce\PayPalCommerce\Button\Helper\ContextTrait; use WooCommerce\PayPalCommerce\Button\Helper\MessagesApply; use WooCommerce\PayPalCommerce\Onboarding\Environment; use WooCommerce\PayPalCommerce\PayLaterBlock\PayLaterBlockModule; +use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreatePaymentToken; +use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreateSetupToken; use WooCommerce\PayPalCommerce\Session\SessionHandler; use WooCommerce\PayPalCommerce\WcSubscriptions\FreeTrialHandlerTrait; use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper; @@ -1091,6 +1093,14 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages 'cart_script_params' => array( 'endpoint' => \WC_AJAX::get_endpoint( CartScriptParamsEndpoint::ENDPOINT ), ), + 'create_setup_token' => array( + 'endpoint' => \WC_AJAX::get_endpoint( CreateSetupToken::ENDPOINT ), + 'nonce' => wp_create_nonce( CreateSetupToken::nonce() ), + ), + 'create_payment_token' => array( + 'endpoint' => \WC_AJAX::get_endpoint( CreatePaymentToken::ENDPOINT ), + 'nonce' => wp_create_nonce( CreatePaymentToken::nonce() ), + ), ), 'cart_contains_subscription' => $this->subscription_helper->cart_contains_subscription(), 'subscription_plan_id' => $this->subscription_helper->paypal_subscription_id(), diff --git a/tests/Playwright/tests/save-payment-methods.spec.js b/tests/Playwright/tests/save-payment-methods.spec.js index 4648eebca..f6fb46315 100644 --- a/tests/Playwright/tests/save-payment-methods.spec.js +++ b/tests/Playwright/tests/save-payment-methods.spec.js @@ -37,6 +37,53 @@ test('Save during purchase', async ({page}) => { await expectOrderReceivedPage(page); }); +test('PayPal add payment method', async ({page}) => { + await loginAsCustomer(page); + await page.goto('/my-account/add-payment-method'); + + const popup = await openPaypalPopup(page); + await loginIntoPaypal(popup); + popup.locator('#consentButton').click(); + + await page.waitForURL('/my-account/payment-methods'); +}); + +test('ACDC add payment method', async ({page}) => { + await loginAsCustomer(page); + await page.goto('/my-account/add-payment-method'); + + await page.click("text=Debit & Credit Cards"); + + const creditCardNumber = await page.frameLocator('[title="paypal_card_number_field"]').locator('.card-field-number'); + await creditCardNumber.fill('4005519200000004'); + + const expirationDate = await page.frameLocator('[title="paypal_card_expiry_field"]').locator('.card-field-expiry'); + await expirationDate.fill('01/25'); + + const cvv = await page.frameLocator('[title="paypal_card_cvv_field"]').locator('.card-field-cvv'); + await cvv.fill('123'); + + await page.waitForURL('/my-account/payment-methods'); +}); + +test('PayPal logged-in user free trial subscription without payment token', async ({page}) => { + await loginAsCustomer(page); + + await page.goto('/shop'); + await page.click("text=Sign up now"); + await page.goto('/classic-checkout'); + + const popup = await openPaypalPopup(page); + await loginIntoPaypal(popup); + popup.locator('#consentButton').click(); + + await page.click("text=Proceed to PayPal"); + + const title = await page.locator('.entry-title'); + await expect(title).toHaveText('Order received'); +}) + + From 9391865217e3c965259f241a2d180ba457452b3a Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Mon, 8 Apr 2024 11:31:12 +0100 Subject: [PATCH 32/70] Integrate AXO payment process --- modules/ppcp-axo/resources/js/AxoManager.js | 86 ++++++++++++++++--- .../resources/js/Components/DomElement.js | 7 +- .../js/Components/DomElementCollection.js | 5 ++ .../resources/js/Components/FormFieldGroup.js | 9 +- .../resources/js/Connection/Fastlane.js | 12 ++- .../resources/js/Views/BillingView.js | 8 ++ modules/ppcp-axo/src/AxoModule.php | 2 +- modules/ppcp-axo/src/Gateway/AxoGateway.php | 2 +- 8 files changed, 109 insertions(+), 22 deletions(-) diff --git a/modules/ppcp-axo/resources/js/AxoManager.js b/modules/ppcp-axo/resources/js/AxoManager.js index 228b4e58f..dcf4729bd 100644 --- a/modules/ppcp-axo/resources/js/AxoManager.js +++ b/modules/ppcp-axo/resources/js/AxoManager.js @@ -1,5 +1,4 @@ import Fastlane from "./Connection/Fastlane"; -import MockData from "./Helper/MockData"; import {log} from "./Helper/Debug"; import DomElementCollection from "./Components/DomElementCollection"; import ShippingView from "./Views/ShippingView"; @@ -26,6 +25,7 @@ class AxoManager { }; this.data = { + email: null, billing: null, shipping: null, card: null, @@ -101,6 +101,9 @@ class AxoManager { console.log('card response', response); if (response.selectionChanged) { + + console.log('response.selectedCard.paymentToken', response.selectedCard.paymentToken); + this.setCard(response.selectedCard); this.setBilling({ address: response.selectedCard.paymentSource.card.billingAddress @@ -433,6 +436,8 @@ class AxoManager { } async onChangeEmail () { + this.clearData(); + if (!this.status.active) { log('Email checking skipped, AXO not active.'); return; @@ -479,9 +484,13 @@ class AxoManager { // Add addresses this.setShipping(authResponse.profileData.shippingAddress); - // TODO : set billing + this.setBilling({ + address: authResponse.profileData.card.paymentSource.card.billingAddress + }); this.setCard(authResponse.profileData.card); + console.log('authResponse', authResponse); + this.setStatus('validEmail', true); this.setStatus('hasProfile', true); @@ -503,12 +512,25 @@ class AxoManager { this.setStatus('validEmail', true); this.setStatus('hasProfile', false); + console.log('this.cardComponentData()', this.cardComponentData()); + this.cardComponent = await this.fastlane - .FastlaneCardComponent(MockData.cardComponent()) + .FastlaneCardComponent( + this.cardComponentData() + ) .render(this.el.paymentContainer.selector + '-form'); } } + clearData() { + this.data = { + email: null, + billing: null, + shipping: null, + card: null, + }; + } + setShipping(shipping) { this.data.shipping = shipping; this.shippingView.setData(this.data); @@ -525,12 +547,50 @@ class AxoManager { } onClickSubmitButton() { - try { - this.cardComponent.tokenize(MockData.cardComponentTokenize()).then((response) => { - this.submit(response.nonce); - }); - } catch (e) { - log('Error tokenizing.'); + if (this.data.card) { // Ryan flow + log('Ryan flow.'); + this.submit(this.data.card.paymentToken); + + } else { // Gary flow + log('Gary flow.'); + console.log('this.tokenizeData()', this.tokenizeData()); + + try { + this.cardComponent.tokenize( + this.tokenizeData() + ).then((response) => { + this.submit(response.nonce); + }); + } catch (e) { + log('Error tokenizing.'); + } + } + } + + cardComponentData() { + return { + fields: { + phoneNumber: { + prefill: this.billingView.inputValue('phone') + }, + cardholderName: {} // optionally pass this to show the card holder name + } + } + } + + tokenizeData() { + return { + name: { + fullName: this.billingView.fullName() + }, + billingAddress: { + addressLine1: this.billingView.inputValue('street1'), + addressLine2: this.billingView.inputValue('street2'), + adminArea1: this.billingView.inputValue('city'), + adminArea2: this.billingView.inputValue('stateCode'), + postalCode: this.billingView.inputValue('postCode'), + countryCode: this.billingView.inputValue('countryCode'), + } } } @@ -540,7 +600,13 @@ class AxoManager { alert('nonce: ' + nonce); // Submit form. -// this.el.defaultSubmitButton.click(); + if (!this.el.axoNonceInput.get()) { + this.$('.woocommerce-checkout').append(``); + } + + this.el.axoNonceInput.get().value = nonce; + + this.el.defaultSubmitButton.click(); } useEmailWidget() { diff --git a/modules/ppcp-axo/resources/js/Components/DomElement.js b/modules/ppcp-axo/resources/js/Components/DomElement.js index f84ab1725..8e789d485 100644 --- a/modules/ppcp-axo/resources/js/Components/DomElement.js +++ b/modules/ppcp-axo/resources/js/Components/DomElement.js @@ -29,8 +29,13 @@ class DomElement { } click() { - document.querySelector(this.selector).click(); + this.get().click(); } + + get() { + return document.querySelector(this.selector); + } + } export default DomElement; diff --git a/modules/ppcp-axo/resources/js/Components/DomElementCollection.js b/modules/ppcp-axo/resources/js/Components/DomElementCollection.js index 8cc1deb1a..fc44a2823 100644 --- a/modules/ppcp-axo/resources/js/Components/DomElementCollection.js +++ b/modules/ppcp-axo/resources/js/Components/DomElementCollection.js @@ -84,6 +84,11 @@ class DomElementCollection { attributes: 'data-ppcp-axo-show-gateway-selection', }); + this.axoNonceInput = new DomElement({ + id: 'ppcp-axo-nonce', + selector: '#ppcp-axo-nonce', + }); + } } diff --git a/modules/ppcp-axo/resources/js/Components/FormFieldGroup.js b/modules/ppcp-axo/resources/js/Components/FormFieldGroup.js index c308bef3f..fc6851f4e 100644 --- a/modules/ppcp-axo/resources/js/Components/FormFieldGroup.js +++ b/modules/ppcp-axo/resources/js/Components/FormFieldGroup.js @@ -1,5 +1,5 @@ -class MockData { +class FormFieldGroup { constructor(config) { this.data = {}; @@ -97,6 +97,11 @@ class MockData { field.classList.add('ppcp-axo-field-hidden'); } } + + inputValue(name) { + return document.querySelector(this.fields[name].selector).value; + } + } -export default MockData; +export default FormFieldGroup; diff --git a/modules/ppcp-axo/resources/js/Connection/Fastlane.js b/modules/ppcp-axo/resources/js/Connection/Fastlane.js index 5f3c16e7f..49c28174a 100644 --- a/modules/ppcp-axo/resources/js/Connection/Fastlane.js +++ b/modules/ppcp-axo/resources/js/Connection/Fastlane.js @@ -6,14 +6,13 @@ class Fastlane { this.identity = null; this.profile = null; this.FastlaneCardComponent = null; - this.FastlaneEmailComponent = null; - this.FastlaneAddressComponent = null; + this.FastlanePaymentComponent = null; this.FastlaneWatermarkComponent = null; } connect(config) { return new Promise((resolve, reject) => { - window.paypal.Connect(config) // TODO: migrate from 0.6 to 0.7 + window.paypal.Fastlane(config) .then((result) => { this.init(result); console.log('[AXO] Connected', result); @@ -30,10 +29,9 @@ class Fastlane { this.connection = connection; this.identity = this.connection.identity; this.profile = this.connection.profile; - this.FastlaneCardComponent = this.connection.ConnectCardComponent; // TODO: migrate from 0.6 to 0.7 - this.FastlaneEmailComponent = null; // TODO: migrate from 0.6 to 0.7 - this.FastlaneAddressComponent = null; // TODO: migrate from 0.6 to 0.7 - this.FastlaneWatermarkComponent = this.connection.ConnectWatermarkComponent // TODO: migrate from 0.6 to 0.7 + this.FastlaneCardComponent = this.connection.FastlaneCardComponent; + this.FastlanePaymentComponent = this.connection.FastlanePaymentComponent; + this.FastlaneWatermarkComponent = this.connection.FastlaneWatermarkComponent console.log('[AXO] Fastlane initialized', this); } diff --git a/modules/ppcp-axo/resources/js/Views/BillingView.js b/modules/ppcp-axo/resources/js/Views/BillingView.js index 4163c19c1..72fb1f5a7 100644 --- a/modules/ppcp-axo/resources/js/Views/BillingView.js +++ b/modules/ppcp-axo/resources/js/Views/BillingView.js @@ -109,6 +109,14 @@ class BillingView { this.billingFormFields.setData(data); } + inputValue(name) { + return this.billingFormFields.inputValue(name); + } + + fullName() { + return `${this.inputValue('firstName')} ${this.inputValue('lastName')}`.trim(); + } + } export default BillingView; diff --git a/modules/ppcp-axo/src/AxoModule.php b/modules/ppcp-axo/src/AxoModule.php index e44e2ba9a..847d204eb 100644 --- a/modules/ppcp-axo/src/AxoModule.php +++ b/modules/ppcp-axo/src/AxoModule.php @@ -99,7 +99,7 @@ class AxoModule implements ModuleInterface { add_filter( 'woocommerce_paypal_payments_sdk_components_hook', function( $components ) { - $components[] = 'connect'; + $components[] = 'fastlane'; return $components; } ); diff --git a/modules/ppcp-axo/src/Gateway/AxoGateway.php b/modules/ppcp-axo/src/Gateway/AxoGateway.php index d3ef227ed..313322549 100644 --- a/modules/ppcp-axo/src/Gateway/AxoGateway.php +++ b/modules/ppcp-axo/src/Gateway/AxoGateway.php @@ -184,7 +184,7 @@ class AxoGateway extends WC_Payment_Gateway { $wc_order = wc_get_order( $order_id ); $purchase_unit = $this->purchase_unit_factory->from_wc_order( $wc_order ); - $nonce = 'tokencc_bh_vkjzxd_tgpd3n_qmjs7k_3vhxzg_p82'; + $nonce = wc_clean( wp_unslash( $_POST['axo_nonce'] ?? '' ) ); try { $shipping_preference = $this->shipping_preference_factory->from_state( From efc20c390d51eac93dd49d5061b3065963cd24dd Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 8 Apr 2024 13:38:18 +0200 Subject: [PATCH 33/70] Rename method --- .../src/Endpoint/PaymentMethodTokensEndpoint.php | 2 +- .../src/Endpoint/CreatePaymentToken.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-api-client/src/Endpoint/PaymentMethodTokensEndpoint.php b/modules/ppcp-api-client/src/Endpoint/PaymentMethodTokensEndpoint.php index ab8d5473b..1b547a11e 100644 --- a/modules/ppcp-api-client/src/Endpoint/PaymentMethodTokensEndpoint.php +++ b/modules/ppcp-api-client/src/Endpoint/PaymentMethodTokensEndpoint.php @@ -115,7 +115,7 @@ class PaymentMethodTokensEndpoint { * @throws RuntimeException When something when wrong with the request. * @throws PayPalApiException When something when wrong setting up the token. */ - public function payment_tokens( PaymentSource $payment_source ): stdClass { + public function create_payment_token(PaymentSource $payment_source ): stdClass { $data = array( 'payment_source' => array( $payment_source->name() => $payment_source->properties(), diff --git a/modules/ppcp-save-payment-methods/src/Endpoint/CreatePaymentToken.php b/modules/ppcp-save-payment-methods/src/Endpoint/CreatePaymentToken.php index ca4839372..4fea1f188 100644 --- a/modules/ppcp-save-payment-methods/src/Endpoint/CreatePaymentToken.php +++ b/modules/ppcp-save-payment-methods/src/Endpoint/CreatePaymentToken.php @@ -94,7 +94,7 @@ class CreatePaymentToken implements EndpointInterface { ) ); - $result = $this->payment_method_tokens_endpoint->payment_tokens( $payment_source ); + $result = $this->payment_method_tokens_endpoint->create_payment_token( $payment_source ); if ( is_user_logged_in() && isset( $result->customer->id ) ) { $current_user_id = get_current_user_id(); From fe03215799ec86f136652d37e03298dbe1d8c533 Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Mon, 8 Apr 2024 14:53:09 +0100 Subject: [PATCH 34/70] Testing AXO --- modules/ppcp-axo/resources/js/AxoManager.js | 2 +- modules/ppcp-axo/resources/js/Components/FormFieldGroup.js | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-axo/resources/js/AxoManager.js b/modules/ppcp-axo/resources/js/AxoManager.js index dcf4729bd..e6ebb0fdd 100644 --- a/modules/ppcp-axo/resources/js/AxoManager.js +++ b/modules/ppcp-axo/resources/js/AxoManager.js @@ -549,7 +549,7 @@ class AxoManager { onClickSubmitButton() { if (this.data.card) { // Ryan flow log('Ryan flow.'); - this.submit(this.data.card.paymentToken); + this.submit(this.data.card.getPaymentToken()); } else { // Gary flow log('Gary flow.'); diff --git a/modules/ppcp-axo/resources/js/Components/FormFieldGroup.js b/modules/ppcp-axo/resources/js/Components/FormFieldGroup.js index fc6851f4e..27beff7d6 100644 --- a/modules/ppcp-axo/resources/js/Components/FormFieldGroup.js +++ b/modules/ppcp-axo/resources/js/Components/FormFieldGroup.js @@ -99,6 +99,8 @@ class FormFieldGroup { } inputValue(name) { + console.log('inputValue:name', this.fields[name].selector); + return document.querySelector(this.fields[name].selector).value; } From 636541c6c1e2ac292d32deeb094b0f8dbd463b4f Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 9 Apr 2024 11:10:40 +0200 Subject: [PATCH 35/70] Inject vault v3 enabled service into PayPal gateway --- modules/ppcp-wc-gateway/services.php | 3 ++- .../src/Gateway/PayPalGateway.php | 17 +++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index a78a6527e..83c55ae1a 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -104,7 +104,8 @@ return array( $container->get( 'api.endpoint.order' ), $container->get( 'api.factory.paypal-checkout-url' ), $container->get( 'wcgateway.place-order-button-text' ), - $container->get( 'api.endpoint.payment-tokens' ) + $container->get( 'api.endpoint.payment-tokens' ), + $container->get( 'vaulting.vault-v3-enabled' ) ); }, 'wcgateway.credit-card-gateway' => static function ( ContainerInterface $container ): CreditCardGateway { diff --git a/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php b/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php index 14beda474..d341000dd 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php @@ -187,6 +187,13 @@ class PayPalGateway extends \WC_Payment_Gateway { */ private $payment_tokens_endpoint; + /** + * Whether Vault v3 module is enabled. + * + * @var bool + */ + private $vault_v3_enabled; + /** * PayPalGateway constructor. * @@ -208,6 +215,7 @@ class PayPalGateway extends \WC_Payment_Gateway { * @param callable(string):string $paypal_checkout_url_factory The function return the PayPal checkout URL for the given order ID. * @param string $place_order_button_text The text for the standard "Place order" button. * @param PaymentTokensEndpoint $payment_tokens_endpoint Payment tokens endpoint. + * @param bool $vault_v3_enabled Whether Vault v3 module is enabled. */ public function __construct( SettingsRenderer $settings_renderer, @@ -227,7 +235,8 @@ class PayPalGateway extends \WC_Payment_Gateway { OrderEndpoint $order_endpoint, callable $paypal_checkout_url_factory, string $place_order_button_text, - PaymentTokensEndpoint $payment_tokens_endpoint + PaymentTokensEndpoint $payment_tokens_endpoint, + bool $vault_v3_enabled ) { $this->id = self::ID; $this->settings_renderer = $settings_renderer; @@ -247,6 +256,9 @@ class PayPalGateway extends \WC_Payment_Gateway { $this->api_shop_country = $api_shop_country; $this->paypal_checkout_url_factory = $paypal_checkout_url_factory; $this->order_button_text = $place_order_button_text; + $this->order_endpoint = $order_endpoint; + $this->payment_tokens_endpoint = $payment_tokens_endpoint; + $this->vault_v3_enabled = $vault_v3_enabled; if ( $this->onboarded ) { $this->supports = array( 'refunds', 'tokenization' ); @@ -309,9 +321,6 @@ class PayPalGateway extends \WC_Payment_Gateway { 'process_admin_options', ) ); - - $this->order_endpoint = $order_endpoint; - $this->payment_tokens_endpoint = $payment_tokens_endpoint; } /** From ad6b924f7f2d97aef65cdd3507a9036ca6bde16f Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Tue, 9 Apr 2024 14:31:22 +0200 Subject: [PATCH 36/70] Save Card Last Digits in order meta (PCP-2935) --- .../src/Processor/CreditCardOrderInfoHandlingTrait.php | 2 +- package.json | 3 ++- yarn.lock | 5 +++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-wc-gateway/src/Processor/CreditCardOrderInfoHandlingTrait.php b/modules/ppcp-wc-gateway/src/Processor/CreditCardOrderInfoHandlingTrait.php index 4cd9e07ed..7a1b09035 100644 --- a/modules/ppcp-wc-gateway/src/Processor/CreditCardOrderInfoHandlingTrait.php +++ b/modules/ppcp-wc-gateway/src/Processor/CreditCardOrderInfoHandlingTrait.php @@ -140,7 +140,7 @@ trait CreditCardOrderInfoHandlingTrait { ); $wc_order->add_order_note( $cvv_response_order_note ); - $meta_details = array_merge( $fraud_responses, array( 'card_brand' => $card_brand ) ); + $meta_details = array_merge( $fraud_responses, array( 'card_brand' => $card_brand, 'card_last_digits' => $card_last_digits ) ); $wc_order->update_meta_data( PayPalGateway::FRAUD_RESULT_META_KEY, $meta_details ); $wc_order->save_meta_data(); diff --git a/package.json b/package.json index a0e2d325a..beed09ccb 100644 --- a/package.json +++ b/package.json @@ -86,6 +86,7 @@ }, "dependencies": { "dotenv": "^16.0.3", - "npm-run-all": "^4.1.5" + "npm-run-all": "^4.1.5", + "run-s": "^0.0.0" } } diff --git a/yarn.lock b/yarn.lock index 9316efe59..b9ab4e05f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -454,6 +454,11 @@ resolve@^1.10.0: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +run-s@^0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/run-s/-/run-s-0.0.0.tgz#599912be20c00ba7698655c9936d075d31b71754" + integrity sha512-KPDNauF2Tpnm3nG0+0LJuJxwBFrhAdthpM8bVdDvjWQA7pWP7QoNwEl1+dJ7WVJj81AQP/i6kl6JUmAk7tg3Og== + safe-regex-test@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" From 4f2e98e9cc57d57cdd90b42523119a9d5d62d9e3 Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Tue, 9 Apr 2024 14:43:00 +0200 Subject: [PATCH 37/70] Remove the yarn.lock and package.json changes --- package.json | 3 +-- yarn.lock | 5 ----- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/package.json b/package.json index beed09ccb..a0e2d325a 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,6 @@ }, "dependencies": { "dotenv": "^16.0.3", - "npm-run-all": "^4.1.5", - "run-s": "^0.0.0" + "npm-run-all": "^4.1.5" } } diff --git a/yarn.lock b/yarn.lock index b9ab4e05f..9316efe59 100644 --- a/yarn.lock +++ b/yarn.lock @@ -454,11 +454,6 @@ resolve@^1.10.0: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -run-s@^0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/run-s/-/run-s-0.0.0.tgz#599912be20c00ba7698655c9936d075d31b71754" - integrity sha512-KPDNauF2Tpnm3nG0+0LJuJxwBFrhAdthpM8bVdDvjWQA7pWP7QoNwEl1+dJ7WVJj81AQP/i6kl6JUmAk7tg3Og== - safe-regex-test@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" From ac81bb5e5ac5dc223fb57e2e7fdd781f1c6176a3 Mon Sep 17 00:00:00 2001 From: Daniel Dudzic Date: Tue, 9 Apr 2024 15:35:19 +0200 Subject: [PATCH 38/70] Fix PHPCS errors --- .../src/Processor/CreditCardOrderInfoHandlingTrait.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-wc-gateway/src/Processor/CreditCardOrderInfoHandlingTrait.php b/modules/ppcp-wc-gateway/src/Processor/CreditCardOrderInfoHandlingTrait.php index 7a1b09035..51a3a741c 100644 --- a/modules/ppcp-wc-gateway/src/Processor/CreditCardOrderInfoHandlingTrait.php +++ b/modules/ppcp-wc-gateway/src/Processor/CreditCardOrderInfoHandlingTrait.php @@ -140,7 +140,13 @@ trait CreditCardOrderInfoHandlingTrait { ); $wc_order->add_order_note( $cvv_response_order_note ); - $meta_details = array_merge( $fraud_responses, array( 'card_brand' => $card_brand, 'card_last_digits' => $card_last_digits ) ); + $meta_details = array_merge( + $fraud_responses, + array( + 'card_brand' => $card_brand, + 'card_last_digits' => $card_last_digits, + ) + ); $wc_order->update_meta_data( PayPalGateway::FRAUD_RESULT_META_KEY, $meta_details ); $wc_order->save_meta_data(); From 197e70607a6ffcb620b9688ed74d377bd80425c6 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 9 Apr 2024 17:34:51 +0200 Subject: [PATCH 39/70] Add payment token for guest free trial subscription (WIP) --- .../ActionHandler/CheckoutActionHandler.js | 7 +- .../ppcp-button/src/Assets/SmartButton.php | 5 ++ .../ppcp-save-payment-methods/services.php | 7 ++ .../Endpoint/CreatePaymentTokenForGuest.php | 90 +++++++++++++++++++ .../src/SavePaymentMethodsModule.php | 11 +++ modules/ppcp-wc-gateway/services.php | 3 +- .../src/Gateway/PayPalGateway.php | 45 +++++++++- 7 files changed, 161 insertions(+), 7 deletions(-) create mode 100644 modules/ppcp-save-payment-methods/src/Endpoint/CreatePaymentTokenForGuest.php diff --git a/modules/ppcp-button/resources/js/modules/ActionHandler/CheckoutActionHandler.js b/modules/ppcp-button/resources/js/modules/ActionHandler/CheckoutActionHandler.js index f3afd4f30..ed5926816 100644 --- a/modules/ppcp-button/resources/js/modules/ActionHandler/CheckoutActionHandler.js +++ b/modules/ppcp-button/resources/js/modules/ActionHandler/CheckoutActionHandler.js @@ -167,22 +167,21 @@ class CheckoutActionHandler { console.error(result) }, onApprove: async ({vaultSetupToken}) => { - const response = await fetch(this.config.ajax.create_payment_token.endpoint, { + const response = await fetch(this.config.ajax.create_payment_token_for_guest.endpoint, { method: "POST", credentials: 'same-origin', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ - nonce: this.config.ajax.create_payment_token.nonce, + nonce: this.config.ajax.create_payment_token_for_guest.nonce, vault_setup_token: vaultSetupToken, - return_url: location.href, }) }) const result = await response.json(); if (result.success === true) { - window.location.href = location.href; + document.querySelector('#place_order').click() return; } diff --git a/modules/ppcp-button/src/Assets/SmartButton.php b/modules/ppcp-button/src/Assets/SmartButton.php index db703c7f1..6f73e6d88 100644 --- a/modules/ppcp-button/src/Assets/SmartButton.php +++ b/modules/ppcp-button/src/Assets/SmartButton.php @@ -36,6 +36,7 @@ use WooCommerce\PayPalCommerce\Onboarding\Environment; use WooCommerce\PayPalCommerce\PayLaterBlock\PayLaterBlockModule; use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreatePaymentToken; use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreateSetupToken; +use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreatePaymentTokenForGuest; use WooCommerce\PayPalCommerce\Session\SessionHandler; use WooCommerce\PayPalCommerce\WcSubscriptions\FreeTrialHandlerTrait; use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper; @@ -1101,6 +1102,10 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages 'endpoint' => \WC_AJAX::get_endpoint( CreatePaymentToken::ENDPOINT ), 'nonce' => wp_create_nonce( CreatePaymentToken::nonce() ), ), + 'create_payment_token_for_guest' => array( + 'endpoint' => \WC_AJAX::get_endpoint( CreatePaymentTokenForGuest::ENDPOINT ), + 'nonce' => wp_create_nonce( CreatePaymentTokenForGuest::nonce() ), + ), ), 'cart_contains_subscription' => $this->subscription_helper->cart_contains_subscription(), 'subscription_plan_id' => $this->subscription_helper->paypal_subscription_id(), diff --git a/modules/ppcp-save-payment-methods/services.php b/modules/ppcp-save-payment-methods/services.php index 2453235c8..a0b43f021 100644 --- a/modules/ppcp-save-payment-methods/services.php +++ b/modules/ppcp-save-payment-methods/services.php @@ -11,6 +11,7 @@ namespace WooCommerce\PayPalCommerce\SavePaymentMethods; use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreatePaymentToken; use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreateSetupToken; +use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreatePaymentTokenForGuest; use WooCommerce\PayPalCommerce\SavePaymentMethods\Helper\SavePaymentMethodsApplies; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; @@ -811,4 +812,10 @@ return array( $container->get( 'vaulting.wc-payment-tokens' ) ); }, + 'save-payment-methods.endpoint.create-payment-token-for-guest' => static function ( ContainerInterface $container ): CreatePaymentTokenForGuest { + return new CreatePaymentTokenForGuest( + $container->get( 'button.request-data' ), + $container->get( 'api.endpoint.payment-method-tokens' ) + ); + }, ); diff --git a/modules/ppcp-save-payment-methods/src/Endpoint/CreatePaymentTokenForGuest.php b/modules/ppcp-save-payment-methods/src/Endpoint/CreatePaymentTokenForGuest.php new file mode 100644 index 000000000..e458c96be --- /dev/null +++ b/modules/ppcp-save-payment-methods/src/Endpoint/CreatePaymentTokenForGuest.php @@ -0,0 +1,90 @@ +request_data = $request_data; + $this->payment_method_tokens_endpoint = $payment_method_tokens_endpoint; + } + + /** + * Returns the nonce. + * + * @return string + */ + public static function nonce(): string { + return self::ENDPOINT; + } + + /** + * Handles the request. + * + * @return bool + * @throws Exception On Error. + */ + public function handle_request(): bool { + $data = $this->request_data->read_request( $this->nonce() ); + + /** + * Suppress ArgumentTypeCoercion + * + * @psalm-suppress ArgumentTypeCoercion + */ + $payment_source = new PaymentSource( + 'token', + (object) array( + 'id' => $data['vault_setup_token'], + 'type' => 'SETUP_TOKEN', + ) + ); + + $result = $this->payment_method_tokens_endpoint->create_payment_token( $payment_source ); + WC()->session->set( 'ppcp_guest_payment_for_free_trial', $result ); + + wp_send_json_success(); + return true; + } +} diff --git a/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php b/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php index 61244b2e2..8daafae4a 100644 --- a/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php +++ b/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php @@ -20,6 +20,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException; use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; use WooCommerce\PayPalCommerce\Button\Helper\ContextTrait; use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreatePaymentToken; +use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreatePaymentTokenForGuest; use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreateSetupToken; use WooCommerce\PayPalCommerce\Vaulting\WooCommercePaymentTokens; use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider; @@ -371,6 +372,16 @@ class SavePaymentMethodsModule implements ModuleInterface { } ); + add_action( + 'wc_ajax_' . CreatePaymentTokenForGuest::ENDPOINT, + static function () use ( $c ) { + $endpoint = $c->get( 'save-payment-methods.endpoint.create-payment-token-for-guest' ); + assert( $endpoint instanceof CreatePaymentTokenForGuest ); + + $endpoint->handle_request(); + } + ); + add_action( 'woocommerce_paypal_payments_before_delete_payment_token', function( string $token_id ) use ( $c ) { diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index 83c55ae1a..6bf3d5ecf 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -105,7 +105,8 @@ return array( $container->get( 'api.factory.paypal-checkout-url' ), $container->get( 'wcgateway.place-order-button-text' ), $container->get( 'api.endpoint.payment-tokens' ), - $container->get( 'vaulting.vault-v3-enabled' ) + $container->get( 'vaulting.vault-v3-enabled' ), + $container->get( 'vaulting.wc-payment-tokens' ) ); }, 'wcgateway.credit-card-gateway' => static function ( ContainerInterface $container ): CreditCardGateway { diff --git a/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php b/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php index d341000dd..20b7a840d 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php @@ -21,6 +21,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException; use WooCommerce\PayPalCommerce\Onboarding\Environment; use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Session\SessionHandler; +use WooCommerce\PayPalCommerce\Vaulting\WooCommercePaymentTokens; use WooCommerce\PayPalCommerce\WcSubscriptions\FreeTrialHandlerTrait; use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper; use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenRepository; @@ -194,6 +195,13 @@ class PayPalGateway extends \WC_Payment_Gateway { */ private $vault_v3_enabled; + /** + * WooCommerce payment tokens. + * + * @var WooCommercePaymentTokens + */ + private $wc_payment_tokens; + /** * PayPalGateway constructor. * @@ -216,6 +224,7 @@ class PayPalGateway extends \WC_Payment_Gateway { * @param string $place_order_button_text The text for the standard "Place order" button. * @param PaymentTokensEndpoint $payment_tokens_endpoint Payment tokens endpoint. * @param bool $vault_v3_enabled Whether Vault v3 module is enabled. + * @param WooCommercePaymentTokens $wc_payment_tokens WooCommerce payment tokens. */ public function __construct( SettingsRenderer $settings_renderer, @@ -236,7 +245,8 @@ class PayPalGateway extends \WC_Payment_Gateway { callable $paypal_checkout_url_factory, string $place_order_button_text, PaymentTokensEndpoint $payment_tokens_endpoint, - bool $vault_v3_enabled + bool $vault_v3_enabled, + WooCommercePaymentTokens $wc_payment_tokens ) { $this->id = self::ID; $this->settings_renderer = $settings_renderer; @@ -259,6 +269,7 @@ class PayPalGateway extends \WC_Payment_Gateway { $this->order_endpoint = $order_endpoint; $this->payment_tokens_endpoint = $payment_tokens_endpoint; $this->vault_v3_enabled = $vault_v3_enabled; + $this->wc_payment_tokens = $wc_payment_tokens; if ( $this->onboarded ) { $this->supports = array( 'refunds', 'tokenization' ); @@ -520,7 +531,37 @@ class PayPalGateway extends \WC_Payment_Gateway { $wc_order->save(); } - if ( 'card' !== $funding_source && $this->is_free_trial_order( $wc_order ) && ! $this->subscription_helper->paypal_subscription_id() ) { + if ( + 'card' !== $funding_source + && $this->is_free_trial_order( $wc_order ) + && ! $this->subscription_helper->paypal_subscription_id() + ) { + $ppcp_guest_payment_for_free_trial = WC()->session->get( 'ppcp_guest_payment_for_free_trial') ?? null; + if($this->vault_v3_enabled && $ppcp_guest_payment_for_free_trial) { + $customer_id = $ppcp_guest_payment_for_free_trial->customer->id ?? ''; + if($customer_id) { + update_user_meta( $wc_order->get_customer_id(), '_ppcp_target_customer_id', $customer_id ); + } + + if ( isset( $ppcp_guest_payment_for_free_trial->payment_source->paypal ) ) { + $email = ''; + if ( isset( $ppcp_guest_payment_for_free_trial->payment_source->paypal->email_address ) ) { + $email = $ppcp_guest_payment_for_free_trial->payment_source->paypal->email_address; + } + + $this->wc_payment_tokens->create_payment_token_paypal( + $wc_order->get_customer_id(), + $ppcp_guest_payment_for_free_trial->id, + $email + ); + } + + WC()->session->set( 'ppcp_guest_payment_for_free_trial', null); + + $wc_order->payment_complete(); + return $this->handle_payment_success( $wc_order ); + } + $customer_id = get_user_meta( $wc_order->get_customer_id(), '_ppcp_target_customer_id', true ); if ( $customer_id ) { $customer_tokens = $this->payment_tokens_endpoint->payment_tokens_for_customer( $customer_id ); From 93f74aac92aa3373a7280c07994ac167ab5531e5 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 10 Apr 2024 16:13:44 +0200 Subject: [PATCH 40/70] Ensure free trial with saved card payment does not create PayPal order --- .../src/Gateway/CreditCardGateway.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php b/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php index 4032869d1..6e21e70df 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php @@ -33,6 +33,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Processor\PaymentsStatusHandlingTrait; use WooCommerce\PayPalCommerce\WcGateway\Processor\RefundProcessor; use WooCommerce\PayPalCommerce\WcGateway\Processor\TransactionIdHandlingTrait; use WooCommerce\PayPalCommerce\WcGateway\Settings\SettingsRenderer; +use WooCommerce\PayPalCommerce\WcSubscriptions\FreeTrialHandlerTrait; use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper; /** @@ -40,7 +41,7 @@ use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper; */ class CreditCardGateway extends \WC_Payment_Gateway_CC { - use ProcessPaymentTrait, GatewaySettingsRendererTrait, TransactionIdHandlingTrait, PaymentsStatusHandlingTrait; + use ProcessPaymentTrait, GatewaySettingsRendererTrait, TransactionIdHandlingTrait, PaymentsStatusHandlingTrait, FreeTrialHandlerTrait; const ID = 'ppcp-credit-card-gateway'; @@ -454,6 +455,17 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC { // phpcs:ignore WordPress.Security.NonceVerification.Missing $card_payment_token_id = wc_clean( wp_unslash( $_POST['wc-ppcp-credit-card-gateway-payment-token'] ?? '' ) ); + + if ( $this->is_free_trial_order( $wc_order ) && $card_payment_token_id ) { + $customer_tokens = $this->wc_payment_tokens->customer_tokens( get_current_user_id() ); + foreach ( $customer_tokens as $token ) { + if ( $token['payment_source']->name() === 'card' ) { + $wc_order->payment_complete(); + return $this->handle_payment_success( $wc_order ); + } + } + } + if ( $card_payment_token_id ) { $customer_tokens = $this->wc_payment_tokens->customer_tokens( get_current_user_id() ); From dd53229e8d545141dbfde5f18892355f89c9474b Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 10 Apr 2024 16:36:41 +0200 Subject: [PATCH 41/70] Fix phpunit --- tests/PHPUnit/WcGateway/Gateway/WcGatewayTest.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tests/PHPUnit/WcGateway/Gateway/WcGatewayTest.php b/tests/PHPUnit/WcGateway/Gateway/WcGatewayTest.php index 5239af7dc..041141492 100644 --- a/tests/PHPUnit/WcGateway/Gateway/WcGatewayTest.php +++ b/tests/PHPUnit/WcGateway/Gateway/WcGatewayTest.php @@ -6,11 +6,13 @@ namespace WooCommerce\PayPalCommerce\WcGateway\Gateway; use Exception; use Psr\Log\LoggerInterface; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint; +use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentTokensEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Entity\Order; use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus; use WooCommerce\PayPalCommerce\Onboarding\Environment; use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Session\SessionHandler; +use WooCommerce\PayPalCommerce\Vaulting\WooCommercePaymentTokens; use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper; use WooCommerce\PayPalCommerce\TestCase; use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenRepository; @@ -44,6 +46,9 @@ class WcGatewayTest extends TestCase private $logger; private $apiShopCountry; private $orderEndpoint; + private $paymentTokensEndpoint; + private $vaultV3Enabled; + private $wcPaymentTokens; public function setUp(): void { parent::setUp(); @@ -88,6 +93,10 @@ class WcGatewayTest extends TestCase $this->logger->shouldReceive('info'); $this->logger->shouldReceive('error'); + + $this->paymentTokensEndpoint = Mockery::mock(PaymentTokensEndpoint::class); + $this->vaultV3Enabled = true; + $this->wcPaymentTokens = Mockery::mock(WooCommercePaymentTokens::class); } private function createGateway() @@ -111,7 +120,10 @@ class WcGatewayTest extends TestCase function ($id) { return 'checkoutnow=' . $id; }, - 'Pay via PayPal' + 'Pay via PayPal', + $this->paymentTokensEndpoint, + $this->vaultV3Enabled, + $this->wcPaymentTokens ); } From c71c56973acc2c5f29d6fd797505d2d4e20d1eb5 Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Wed, 10 Apr 2024 15:51:19 +0100 Subject: [PATCH 42/70] Implement AXO order processing Implement AXO settings --- .../ppcp-applepay/assets/images/applepay.png | Bin 0 -> 13805 bytes modules/ppcp-applepay/extensions.php | 14 + modules/ppcp-axo/assets/images/fastlane.png | Bin 0 -> 17082 bytes modules/ppcp-axo/extensions.php | 304 ++++++++++++++++-- modules/ppcp-axo/resources/js/AxoManager.js | 9 +- .../resources/js/Components/FormFieldGroup.js | 14 +- .../ppcp-axo/resources/js/Helper/MockData.js | 32 -- modules/ppcp-axo/services.php | 10 +- modules/ppcp-axo/src/AxoModule.php | 23 ++ modules/ppcp-axo/src/Gateway/AxoGateway.php | 65 ++-- .../src/Helper/PropertiesDictionary.php | 18 +- .../assets/images/googlepay.png | Bin 0 -> 19520 bytes modules/ppcp-googlepay/extensions.php | 14 + .../src/Render/OnboardingOptionsRenderer.php | 8 +- .../ppcp-wc-gateway/resources/css/common.scss | 81 +++-- .../display-manager/ConditionFactory.js | 3 + .../condition/JsVariableCondition.js | 24 ++ .../src/Helper/DisplayRule.php | 23 +- .../src/Processor/OrderProcessor.php | 34 ++ .../src/Settings/SettingsRenderer.php | 6 +- 20 files changed, 546 insertions(+), 136 deletions(-) create mode 100644 modules/ppcp-applepay/assets/images/applepay.png create mode 100644 modules/ppcp-axo/assets/images/fastlane.png delete mode 100644 modules/ppcp-axo/resources/js/Helper/MockData.js create mode 100644 modules/ppcp-googlepay/assets/images/googlepay.png create mode 100644 modules/ppcp-wc-gateway/resources/js/common/display-manager/condition/JsVariableCondition.js diff --git a/modules/ppcp-applepay/assets/images/applepay.png b/modules/ppcp-applepay/assets/images/applepay.png new file mode 100644 index 0000000000000000000000000000000000000000..d46807d77f7c31c33d8ea4b3cb7f5f2ee927d2fb GIT binary patch literal 13805 zcmeHrby$?$y7$l}9fH&_gdjCAba!`2!!QgmbeDj1i2?$GG!g;=5(0uC-Q5k+Qc9PA z-{`ydyW^bmowKj+JO6F2>tWWjo_qb)x_@`9b*~sL4Mlw12e<$L0AE>2P6zp0fPCz* zF_HhD=9s(y07y^#^$b0ApgurXcNZIkBOK`I=L!eHeGxVQfbaZJ`eSeEPw5dWJh61B z?vw;;#y*%nm$yy{FUbR6><8KPfer1weSeQPB02PN7xW*t zQvz>Y-)u*O+HCk$Wk0kZA;tK4*($C%N#W1Cz|o>5*)2%^8zry;ywz@apoX zVf3;}6IIhn8^!<(djtplR-ROZ8x(&%yLMUj<(~bFpXrKQ{L>EE;c*XOf0_BuV|u?ZC%-`HgMMqJ~=jTvw6^;Nk^riu;_=$zXWKx3A)iXN25}Utu`(oo;?!k zQu=nEBs}A5Yis98zL}}f2q_VWD=RGy&pNoQA;HwU&etN7F(C!rt4IUwN*dEtC5T6v zUbc6m$lokoHujTj;Bk|aFFlx%l+9YC>}8%twUyeWW=zV1UABTWo&J)7bmLv&jWpfi z2I~+j?mRhNoxZ|kW8)qENkiQsplx~i`IZTQPj{gF!@TicmV@{3qHrT5_hMC%(v1mn z%$ol~;JP5dCKIt`WRvA|zWLbjOHcp1vc~z{1t%3mPnP#Gd}hg8GI<_nt20SMVsmmr zU#Lutb$0`08`{?Eud!F=bSAQ*Z4G_?GlB9cX-%LS7L5+ z6FcqVRUwK!0X`2igN+f`GtDV#Rm03J-k=*XMAzySK0P4JWMh;4$th`sHK*B3EwVp- z6>J~;CcKxV8)9UBxz?;}=zV*PeR3lES)%>sCgFWN_-SKuykeT9S$_}X;7Y{h4Dr@? znVUWAlc$OPw9udcrKKb7a!)_KS`GV?d!&!Bx}N}PtE^r}Oyg!2rr_$PR4K^wrSPjR zwV{a*m6Hu92i`{s$?;5sC=GdamJJPMaV+^8yM6#X6pm4iZx{BMli)7M&k6hvz`lbKJ7`N?QM|mPPSQQHYDKSqXxx#_+=9HHm+B^b<0!SAUh#LvfiKFf=IF z`W8bVOWgahm}ya08{5u&rzCf?R_)FAoFPg4OP>*mw8(Be2gRH?H^1PZCGZrpD6tuyegjk@!rs`(toN zf+7VjsvXz0(%Ns$if8Hj=blTOyf<=cM#=T<2%MU}kHYK^-h!u^?ZlI)o9v*65fW4| zjFv?140s`53?@4(gg}H=UuuN8pnZOv(1W|tr(8Q(vBOwU7(YCiv=P6Sq=ZVvs5l~w zhK?^_x5{Rh-awKsXzNWf`b<>`M^ki>koCyqYC^fviH`PUXvr-!S@#na0JOx?n~tf!NU65otJC(WGQtg6LmAi`W-58c9Pugdll zz`lKZMv#W?0}M!I`M{vr zkIKDIe&V#I_vk}p&zv3fASMCiXd#RyJxju)vS}qHo}h}2Pub`tU-_U+Rz_s3*BOoqOB%+hSi9(lH8yvFqzt@@G`Ti{WTN}7vvXhrM4 zgl^pKFB98!ko^*kF<&B|0&}e&1&i@J7~;7BISiF9*PV#q5aFB*DA2+$kNPh9(fH9@ zD7+0(o3DqDU);HwVs`d|6mU^I8dE1)i}E(xxYiH9rBS*0qVM%~=i6(B)VT7(Trpl~ zinI|Zr&#?;0r!=W1}>YO4=x4qjvusKx}1y58pWc{0E^pzD`o4OpO-Ln>s#%^nEY=A z{gHs%1BMDW=tALc>D=UMv_C_xDW)wBrL4<=?=(5F$46E9Yji@0%DR=BW?5S)fkH@u zsg&4#4D?%cF}-os z=7n)qS%#^D>6BtWM;#jkYu<090?HO f9GcMSv>+i6?F+b@lXEo6-N+8z#dJTt^- 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 'applepay.url' ); + // Connection tab fields. $fields = $insert_after( $fields, @@ -95,6 +97,11 @@ return array( array( 'applepay_button_enabled' => array( 'title' => __( 'Apple Pay Button', 'woocommerce-paypal-payments' ), + 'title_html' => sprintf( + '%s', + $module_url, + __( 'Apple Pay', 'woocommerce-paypal-payments' ) + ), 'type' => 'checkbox', 'class' => array( 'ppcp-grayed-out-text' ), 'input_class' => array( 'ppcp-disabled-checkbox' ), @@ -122,6 +129,7 @@ return array( ) ), ), + 'classes' => array( 'ppcp-valign-label-middle', 'ppcp-align-label-center' ) ), ) ); @@ -143,6 +151,11 @@ return array( ), 'applepay_button_enabled' => array( 'title' => __( 'Apple Pay Button', 'woocommerce-paypal-payments' ), + 'title_html' => sprintf( + '%s', + $module_url, + __( 'Apple Pay', 'woocommerce-paypal-payments' ) + ), 'type' => 'checkbox', 'label' => __( 'Enable Apple Pay button', 'woocommerce-paypal-payments' ) . '

' @@ -175,6 +188,7 @@ return array( ) ), ), + 'classes' => array( 'ppcp-valign-label-middle', 'ppcp-align-label-center' ) ), 'applepay_button_domain_registration' => array( 'title' => __( 'Domain Registration', 'woocommerce-paypal-payments' ), diff --git a/modules/ppcp-axo/assets/images/fastlane.png b/modules/ppcp-axo/assets/images/fastlane.png new file mode 100644 index 0000000000000000000000000000000000000000..f09040a0353353d45f52ffe54e81e4c391856434 GIT binary patch literal 17082 zcmeIZWmKHOvMxLX3GOZl5G45EF2NzV6I=#&_uvvdxVyW%ySqzp2oAyZ4%z$cv(NhO zpR?9?|83TS^-gznb@kKL^*&WSgviN=A;IIr0{{Rd32|Ws005!{{LBOk1OB^(as_Tc zu5?pUb5PK8CbqG&HZrvU5j(irfQUgZrbYmO%W_$g<UtBZ3pDWHzE6%wQl#zQK7Jia#zq-2K zK5NN+Xz$>k;s3RByDdAE5y}(U2m8sUa7$tnDHS0BAz#_Q2TS1~AG73wl3_&4|VS2AoqJvuKwDU~%$ zQ0?AU=imIfw~h?;`gQR5ad_h4ej)Dh!DFQ3<3h@OMUjga-ECXTFXx$K{drOPWn;x( z8Xg_C>0;QwO7I!JoBJKRaW6z`uz52f?s|?5c*o$-q?@%{*NPq>OG|9E}g@s z@a~_IqrRJcnB3sJINe-1pV{0(7SifeX@6lScow)ct5F$fVODG3(Yjbs+XLkJJ^Yy4 z+0y$XVClERKWruPDe@G3#mZfgbI5`D*k#4bv$aQ)OOm1E=40O`|J3Tw%_b*Z4+M^B zyPYg+G^{@!*zd?^S`%IUJ}?O5RClo3ADWB7*t`(_Psi@i; zZ?VAlXs~QKtJ;r-jFsVaTdta!;P~^dPQ$9rZKZDihWqrXXt|*DyX!MUF-1o4Zi9kt z_OnuWj?u4%s7#FTWce!10AB*-9DM+{S$y+sa*mxm^Hp&9q*N4s-x4}jj_7H;TU(67mG+vR!{1u6pINtI{B)hito>8SJvvWJqfV*Hu6c{a!54B#=ksbT4Aq zF&T?#G`6g1^h0d&Vfydnl1QH5J>F2&c`6<%YD9)LOYQ0p&zDC z#rUc0kndI!&+7Gj)AZ+5iTcMPC3mjQ+-f0^^OkHcO3#8GHKzPd*g?hzi#mGmMU=!L zxc&XpmJwco$%0wW&*!^R3H{$!ph^kxPu)_qO^QVF(W=`KTU&nS>}&%B>e-Sfuuaf9 z+6d2+=*#Q{dezEMpO!YJQUKEY>lGXa9gSfxE^`RFs@y~xHh;3i8{ICx!g;Wx2uOax zN~n;s@6FB;&M1^!amkh0_x!yq^ORh3Ekd_@BaY*mJ+z|qY4GcM@wQoYFO}N>egPtV z)DTP=PhG0i%B<6mo3W3vEq?v2=4o1e?zZJsp-kM(B`JKTTnIl$PL24zTPL2PWJ~_Q zw0FPGS$=vSK8_(+u~T(psScjw!kF)19d%1_xD0`mm?@xcBVC-?yZoeDjH`xd zLJfnkx@ucyE;tq2%6;vn7V+JFa7&N8sg1~<&c>CpIFO*uHZ_&7S`cX{hmMwpSi41P z5JD+j<%0~1U32*_R*l=-W0IB{xg#5(NHc}~JQOd@(XEmeQ(r<{L`dKb`h`#*VmBue z*)Ef=)7Eevs#%m z@e!+OiBZa>6cS9{Bzm>5cRByj*t)M^F$OqD#$5PJ@j ze2yYaJk3r|e;l%abY!|y@`p-*XZGtwg{H376P?XQ8Fo_dNvEzsXG;MjFcNGo2EWWA z!2O(Csy~=EkysAu=MPFbEJxa+TKLW5a%VW0!G+0Wf>0oEw1?>iD-`bO>&ed2-N!%-I6GMXNrpi- z(vQ`w-)JDOS#30ZA=8kB2Z>BRa#dI5tfzDPsV*|#MdPp+e`R%T@i82{)i8AiT1~q)}&Ltw0LZzuHlV@KX65 z!|(r~Mo zePId*Ojdb`jh#hCxA_GWx+cU6i2xx%d<3Tlv+u?&emu61Y|0ko5E?dFq|ZBl*>c_4 zA~eV5&=}lpzgAnd7v1$re7~_Wuc{%@g7^rS^sh~mBrr$_Dumdv&H9D%-C}8jfIzr2 z$v^Br)52^WNe)plK6lHkBlgfMJ~fcGXh6(SO+t4;r*kJ8yF~3>(M!->4feG2y5tGH zle3E@fY`fPzz85#4Ln2zVq^9!55S)_5d+;Y#=n|87{+>NHgW}rwFr(T((zG$7Wl=J zSS+ZlJ>~m_d4nDwkX}C7tKty}iOB=ODX6$vB%0#b*~X>T6`?POM1qVZHA#2k4~PNe zBKwFJa_!>6PQyND38I`L|Mbp1U=9!Z%vtk;s&u^F1eEL$eG)i_GUL*{*k6OS<8+&N zh7~%y%LjjxR~0zWSDD}--cF>v zd@#&p5+kjq;yu|H8HYF#}(mL(s+In7$U0Yxo!+Y94#z$hamHx^QcQqBBr z+oBmx1INLc%R6)*(eFpR%{y|u+eqwW-F-B>!X+m?86g30(Lkq6HIw}Q-2T%irOuz! zT6`?6cKzo}!x2$zPY@54Ceh;LGdm>-W%+f`2YT!bjg2NaF5d5*0BJ+S+txNGP{KH> zPJX>Cee<{;I!TC?8gV0B{dH3fci#{KbTK>A4XN#i+%xejLPD+C=UOxC)+Ga$26BcY zHoZ#aIOur3X|tzV!YD+2G_3v%jRAe5ZF@kId(m5|YJ^IL0lPT)epw)$bx*xm7zt0) zHYr5UYbn}Bjm)i8I>^WBQ>zg&N`Gr$JnNyy(pk@A7%X9nh&V1;uU_S*%M$@4B3oVn zKMNrwp{O3#tCj|=bl}sMU!bh_R3=rML|1i`+eA?0xj8^1++y#Q7PS-+w+RQR0W45K z3o)UKVvdNMUyvzNI;5tz4_0ErQV<2F^aVCvQ{M=>ct$5)PUwQZAZ1^WX6+RczY(qx zT5}qi?(T*{{hltwgilCX`PIQOQoBrLyu) zo@uCCXprYzNZAUHBntOzEN}Sy(LcCv7N;HK*{?k+h?`i2mP}9#xVeJ8IY{V}qq`9{ zx49PH6hXZ?@qq;)LE4wt-e%z)-J)1?7g16W=E^!QQfjj^G$j>>34u2AXw3%S#RLW+ zBZhJ#M0%E~o^C#VZKkN5B;@SqX(rs0UUPTN7kkW4(xsy=8M7NA=WeoyI$`f@czfl0 zur>%2N$}H|L_d$d`(uJ*LY1!OPmXJ2v?6De&o|c{njslF8-@%-UI{jxLZiDJaEKfb z{|uQSg0?LMj~9M4xn4!)fa>SlE?bk6l+n#OevB}6%=L-WgoW!J!}!gqAaP>^@au6I z`BUnE_cSDHSUjVufG`S2p*Go`0}}c|vi?KHT`HFS@YgNGBg_ykK%FFJt%hh7@aMP% z(oh`sE6&!>dwaHBZ5P7UUq{G8?~l_REc(9?e{mk9_ke!$n+ffOgqdNudC(l7IdQv%+aVnHw(YX<`(cut|*7iU>pavjab0 zuOSSayyW3oaYBBIb?)^8_8a&6W9PW?z-l+f1=)>zxqA)>N}8U@X=i3IC(bfUHAz5t zbx86YN@hk&ly)?Su_WbiAyC!(@)?wjRg^+a3y2xCKmONWU_wf6BK`3Cuk`y;y~`i z%g3*4!HuP#E*6P@zJu(4O)e4+cRr9$J&@7tF7cayGyL#5U`981ZCSlazI2TALvS(- zU4kCkg@R2xRF|F-jt8pa&IXMM3T*g$N#{?@6CHqEkOK6ix6vFpne{gn5|Wb;68f*) z6`ZlAdPeh#_XrU7>CF82R*dWvwH1}crH+baJ4=n3C0K~{%@Npig^W{Z5qKfQYTRg?Xa1U1=3XtHQx%F+cMB->TPuAwz5 z<<)z<_Xt9qo}i>7mZ0cP(s<%8{=U?bpL;~ZRMIdJsFow0&&4!5gy{n`$$@sXneULQ zkKVuf{aX8WKLp1>h55c&n{eL%>R=rjpLO7#&ndMZZsR*l`Bi*}!h0h=iHf?~12kYI zlBQ+@3kaog6(TSlaVb`7NLF!h-{+S3A)glLf&jJ9x}N;V;R%?YRlbDb;D8cEh;3Wn zT;n}ydv?!X`h|j)Z0DWJVU7*1G2okmYYA%7Qd|bsmh^gt*7_iN7fTy(tpNbw;dimo zGcX4^5bJ}CO|5uI&ssW2iA@c8NmW^;fzmcYAQMw@H#?A`n~aiyo4Em}At^r}JdX<( zSb-(TL66wQ(!$D~%Y~QpuU;xlpDMtgcu7qh9BjB47@VD*>7AMBt?i5%7&$pP8GuX-OiXlO2|9aM zD+fInIxG86Zz}$wLl|UlU}tLMU}|ke{H9Y+-`dfEmy{ITPyAo@S=vZT|4Y4<{XbCv zIyxUI|>1VNd|sTRYkrfJB@?Rt}&3U4@~+ zzsB1*+FATvj-deq$O2>u7PSY@%J@H~6qAsa``3s!2#ifFZT^k|gZ)1w9ZZe>8?66f z+gs1y<@~!L;Nkz$`#+@rYwdr9!BW!FT*B4{j&J5k2=kJ@&Cg|MZD4B1_4iQ^#LCIe zX2?OuZoq6r$HHl#Pp8Mo$Vdm|&^Izi-a(Yv+9u5v5 z8#9v;2OTpz6AK-S0Z@;Qlbv0k4#>`7q|eF0sK*3i{x=n}cBWvj)U)_^tKL)@f>jxU zI1Tg+Ihp7TISg6pSQyz^>Gbu1Ms!Si931S7%tmYmoNRwp85(ejTH9Ibfw$AtQqLH~ zU}I(cx8n_Pt}k*DyrfL@!2fQMv(R%e0uSINl`^$*bouWNB~wd~qJ!QWnvCp>OzbR7 zoSZ-oCKfhkj{g=?0omDuJ@HK^Baoi?A9~-mg$ukGFtB=WehOCbcLaDXTtaprJqK$$ zC2MO7UeY&{h~Gy3b#G#xe{71lsXbW2_08k|>GO&p+kbre#}TkF{o6%M{MT){^bGzn ziM^f^$nft(;Bo)xGBDAzG6sRq_dgx#zuHazFPo*$%Bj!FX2?v(q6Y?-g`Jayj>CY> zkj?-EWaMOGGSFi(_{TE-rQP1z$iZ394)nzsyi)LLzz+0xHN>C)LP_;MZE-dMz2O7| zdjgP!juEKD2;^dA<6;KV02#S}KvIT(E|}r%RR7nCc^LjbnDG1+__r_s9`}znaCiYn zD~5lCtAAql2IK$3pMUPf|HB!;s{b>|{|ev#!1W)v{#OY6uY~{OUH^gWe}%yRO87tC z_5T}O@c*6gfULkQ$Qhi@q}(8|f>R+F{jXxef0H7>y->?{@DrSkxVk+6K>z9O7s4fv z-wE9K-a$fI4gNy?J z5CbHHzbLsZpJ=&i$}AClw=$X252+oV_sv%NG1xvDw#IT}IL3r)0K_#&!gHTTB_F{> zB6X*N%ZvS7+<@!nMuTY?eE<9DCA0O+^Ne?Y_13c|@$uQ?!DIc@?zCy5?UrZXb^O-# z0}PM@!}SAsUao+2PnppBgO!^1kfgJ|yd58fQS9wpZd*7wzr3!u3A=`(l5FYtEjOmQ zqr6;v;kvj|ueu|f4YuFUseBAg=femf6Z8-Mj>WTJSo~%lcsRo0kn5#pO8BaxaH`IzNGC%Rurj-V z;1*bXJc5?ms)n!SzmNLK$y6}0u}tU6wGgh34*h(5UTU2zSFB#6Zqw7Ls_r#;F_dpb zeGlU9pLY5j8I+&*2z7B@S_z{~rpr!_Hwy0mn5|YzwmG_A&E@7kC;g=T?DCZ8(i&0E z*64Tu@WSwnJ6&1&BmbG46>Hk60D)^_x|`6=&Gk#hhqbDO^-4zqY8r-$m)DCJN;$PF z%oQiUhwCicPXYVpqMLb;^|?w!c@rg~%|B%zCtg$=YlEgP$SSe>gAgOz2K z>qzFyZs=Gy8lq`(^lU^ae0G>1wz96CvANE2<&x^;yAR_7n!+u&r!iHrW4db$G<*ao z1BpY5Q%4IG&>8!uhw$kih|C?PAE>%l25{hb3(C_!I2<0pv>S+iS2!C>4G5Lf_ToEh z>|0ur$Dqu4eLnlX82ET|2)8X3URsqB9^CHTEtBrQrN6si$JOHOpQK1JgEB5Z_4#U% z(dFeSK+?0Kq~Lu{`v>H;7AN_}o4iJsTFV8iy1~KaVR#ifjp51m#&6%gRa);mW%Ja- zoC+pvS5U7pJU2V)tq!7X{N9Xcn{OV|kW&~&T7SMin)5q(IZC3=vpq0L%ru@U$|3OJ zcKo4mb}(FbG+B0qm7lFuJ~^(?&>J0eotqN|18BjS-Me=s)FX#4JwcTo zbIH>&#uJ`5b_sjCMXUXFM^qcz>_lthg9qnD>jm}k*Z>>31&p6R6;1IEe$OeNEOXG4 zRK0CD_;3naTE0AQKpexu_qR9ldCg4ee19!-A`uhS@eVFDX)@;@Rn{>={hBoTk+W&K zPZ$(1q*ARjhnp20Z9}LiVlQcG+O8&P&s~g?#4`Wp` z`(Bay6}?5*L>%~D@7~?|2g8SocKvDe_Rp@aMo0F4f!jBr_nHTrj#HSImcXFXU2()t z<~i!#wadc#-58hZb4lu|W>DnR(hdQ;AKTetkurwmfM$Dli}lncRItR$c{jI9x;hxxVWo7=qI1N1b-J@M*~Y@LHBSMBOYsy+z&j6}%*(e(4LL zTt^`g8j(AkjF>RkF7GF?$l2xf7on_-hE{m^+4=bt_PF!4$WXpz?&Ki(RwHSt#yBi# ziU8`{Fs>?An*@e2+j=?;!CQJQU4d7xjmG7m^hKA>p2aD_;~F8 zDs;(ySw0ZgoE6#~fx39knOv)3%I7xrtk{>U;JB>0xDt`Z6=0RWMw(tyP=GLgveK2{ z`6zJqVJ)i-axFD*g8D~x;&r>+3>j(12AZLGwG^WYxFYS7m`#22eBp|cdMFT~)OIhf z)8=-qu2Gk3!Rkv_*XjYb;Z=SoASG5M;N-L1f_lDOd91LbID2aQM-D{20w0<%6Ix>8B7&&92x^uAj^_4TPq9h0M+5J{w zQCm~t9XME4#&rH+Wc-*nG0pRGF)~^^q$dN4D-fOeiE{gKn4vTVGp?-+sRDeaarsi5z7DUZL|U3Ic$OvcbRcnzgpQZ?3C7TT9YQuH z7JoPc(G2^Oja6AE_qRnxeiDNqxoiy?>8sT<2S`fwcerzLah;#U4$-77bJA}v>xh); z+^m?zM#qU%GBJ}db8G03=i~RaiE4$3a4JyoxT_#yVPWYS8p4K$hwB}5LcB_jIfFtlZY6)MtW^&Wj>E)r}+}{DwLP_>}l<`n|sxMi4`rXn$M~Yx*fnz->z5u}8EAO0ev#!zxJf^TUH`NoI(YWJXt~kr z&z@-Vg)hP%qcOwU+Pj1nhF)#b8xlHRPk}e=&o68o?p!!X8P)mOY^<1TC|u?%&JN_) zl-s;6!&AP9$)=XZ;~){>1q}~Nu7^ls?a-DWB+%!ib+PK$Om&aaHXb&#^L;;6rjwR_ z^CAC0sF)QNb^=(yW-iWjmg5nI^2dSX@RX2~4_x?x?RZ>JCA1yjb1sn}ZUOk?_D3W# zhL$v0iUh?z4{7L9UP#ozrg}?>@>j_EQZ2ue z9!#B&f&JuTMw~zOTLFMTBtn3t#){&j$iwX^bZ%n;Vu)J|tWpyMnA{+WXcIeM>&eiU z{%S5e14q`(kNaMflp5QCNLW0?30vFIQRsY(3G{!E+3va;bg}!9gn2qdn9bL(r@p`3 z2idz1kIiBgkdopQ7#npnm@m*A-`-q^SRS9~4xw0UHNZ(o6h-6c6E4QLmPc6C?Hh}o zf9S=k)Sj4xZNJV5y+Oo>WHQNdeOm`W8Am z4fy>L!kb@|hgC&HO_3R(ubKL4-BH*_mrKIjvhQn@#swb(Q{Lqh&d2N4*+qN}C3kda zQezyrxcy5y7HX)#dg6m{Uy|o-`LdQ_NBJ*r1?x3z2kpbMl}f|i3X{IAk0ocePY3ew z&yli?qH9Jbi;S2~Te+)jsgE+)jfo`oigtV>2@LlO3`Ztr)D{};Zb;wSY%!|Vz^|i? zlkHIO{vjDpi20FmriO+23Rb1rg3yCXJuQ?W5p9BIZTZg(5df2oE+cW=Ng`aH&UBrUV&tha=Gg#qU%h=y2kc}<*PCW{5vDXJ?ZL{REj= zo5#EdOs>sf2qM48w{?JNQZ{`*X)+nW;#L)A@e>w;NgNPut#{SI2vOgM2uka33MD z$jkn;#wG{vPUntK+1c`yKYph+>Mr;Cv&s+k5RT-O8oqx`7K|*{+Qj*>?R#?~_8VQI zuBnAlpe}bFiC|WBk^})WVg04d)o=@YV?wQRXjH zv~KXMSY)ZOKQE23dL*=PIXw~ur*+u4935u(h|qi%8FdC^)5`O&Z;#eO@i_I+-Tv%7 zLlN^uAHyTwKNEAg^BuOamgRgJOjczZ5ATJ#va$F4e%wEn$5OQ8?|YJOVFrTJMEs>% zp(7+|b_h=?Cel4#6;m*omGh)WX0A$WZ1^j~F{J_irgv3b`jLd?c+o5s>u__=3~*lG z-EDpJY`*hs9QnQxgt!!yWCjvxi2z#9H*^s#H>6}*h}jy37*NV)C61_WOl=Pc)Tq%y zX=2%N*IKi0PEG-Tf%}SPZ~RgTU7}(j*ZJB9$MdnCT5TZa#EK)jfLv_Y(5FkG4dbxo zy$&J06c75PJImLYiytscZxUu^dxfohrE_Q>u21t+8bEQoHgxw7OV=6=o=OWu#ljDV z786y{1life-8h~PK#N1BmAQp1Pj}9I!$ALICIgz<@eik!#@uPC%r!OF-(MWD&b;aa z9uMctU9RpJWBI=$lMTDYX?nd_VhxWDQ^=SQ%vPY}fk06@=iy;SucF7EvlXfpZmxzJ8>fKPD#ycF zADHVypXe+S9x7_z?1C$UB-0oQm|-a;7DG*DB`+3bOstRK9D&&SqmILY32yzBA9a-D z`@yZ0ETKe+?f&8ut|axPw@?|8!4)q4u@H3utHUwhBbglF-Na8XI-*(@-Ahh(S)H#0 zo$^pNzGS(4as`oVaEOQ3*3g}va2pVbXh~$by9Px_U+UH7HjXnb%sw#=zcQX4DaiD= zKca&^bfW44^ZPgCVip7jRBT3+St9Dsr@6xq~r_q-(VD3uG2@C}FRudQ#FbBWob?-H+S#reIzINT*Cy z9*{hk1M3x^xisC_7{*p#+CjVS?(L#sz3Tx2hm4PhsVlHE5~Nkd-cy7TLB4l7F=Q)K z@1xbIje=XOi~!dUj7)lh4Da7He$zazJGdSGq2C6NGm=SbG@cU4CmhRZ)p@!!7WyWq znU*%|)W#n{7_jDkd*QVKu91Bm@}#IxodQD8jgDa{82M-L`Mt%ta(pKC?aY##Pq(k& z{Lz?-&+G8!Bd@Dqs-gN&X-s?qe^4Znx=L|zp>hd!t+RvLr=3)Y=SM2U{A$Gdn+Rlc zLoA*lEL>++`BOYt5*0up14#+3W@?)5&bb~UKmgImmqc1J$N@lt#E@3l69f>tA`^j;Uo z)3A)fDu-Ye{)`0+$i|5z$n2;^ zza{nHU6BGOYMJHf>BG0qe0!vtP08M|%1kVm`Ecc+1tl&5*AGNI9>TvLr>AF2$EOMl zLL(u~&B(2%_Qdl%hi)xp)^L&~;w?A{lJQ)nyO)P0>tNKrbg^LN-lu1^K8n9^A1@CH zoqi|Wi6aNE#U?KfMM?A#D&itpQg!{P*VP!6l`fZb+p%lN8$g zy9cF)k4{@;&C&7s4*2MNI#+&#m(Af#NqCGQ#8i#(W5}dsD4!FL*~&V*M-h|N*>^R8 zltXFHc6){QygB+x!?miRs2DKkrdYY3njo%^s2iR@CMKT;Na^UKD~QQ*&WpP(AJxVOuSaRh-K`n^{BqT)L-t#QG37YF0OYK`cD$&w!KjG4gh)9N$_LlYbM?vT? z(qJ>_zwr{P_U$a?&^_PrefyfJ-rDHm(fqua1@pyu_49T}rQcrQeGxPf2mblf4L#~# zWq?}_5!|0QKtC;E(4~=>qFBQtF5$4GQkTW&YQsQJ6(EaEJ0huA6QMAMdjS4_oYy{t zzMXt*K#Y7 z#dNR#Ezc*R)4PtA+x2)=Zik)EXH6b!jUJ}Kv z&rc0n#4h5MV|?J_l?4?PieK3-lPdy}^h4l40>e?-mQE3b5YJRs-j{D zJ+G4ovb2*630b--Cc=Q7U#(9U4WN_09X_P(aWOuw8l<#{cT}g2HwFP{ZFY#a&$5+9 zM31Q$d1mle%c3=#n+AYqH<`4Zwp1iydH;hVy0lg|_`L4(;y>S6~U$TZWpxxE=I+sQOs%%zGw^sM$x z1xAkvA(LaV&Wtr`kHe||oUkYsH#jUMZLn5|r+<3YWAqUvH`s+I1+^;(JTqEIX(!wL z{UF8Va~|cz&WH2eW;|P zY2PV0rN11HHD|(z9l3zJRB>o1_kAQ8o7k`Wc5Lyjh&F?Z2am`fwZ$f#fY5eieV#)0 zv;0V{1jaEL(ZIUp*`x}{^#as6xJ6O~iZWRw>ckPzt!RmR%fE|B?zFN^wdm5T#`D?L^- zSx#Pcclx7;@&-a^`dbB8*tKlA5^d^RmLS-}hJR3?E_6%r?!bfV-$m1-QQ3Pr`t+pu zFvKfmv&-}GE+^B~Om|*W=oTxgsA(>0skS}pQ!`MrVG^2#QzX!s&& zp6@fTo)kQI@VWaU*@NY=Exz+9OC{nx=LR!1WWQ7IndwQR25IdC;NG1Mh0lxeC?DV`W>X{Bds?MI4lUK1_x2Kokzu z)3?$$`2AbaI&ZP+Z}XMO;)p1LGySW&e1X6|HDeayv8CmuiCNKGmAv#GVd||8%8&#V zk+8KTKid~t85$HJnXK-7co6S&;s$SfLLo*C4d#o6;L5*BJhtv?C5e)fl7QZn;$XWw zPW>@rZgYErF{N5o?ln3A4hoo8paTG4c~~U4@_0z;Wn#9fof8CaSy{!4LoBHbZWN@0 zUnrx}(w4t8n#qf%suF7Jp{Em$D}n4Zm}YS5;NTzQVzQd4X^2EAHbCBCd9LHZe1g3s z9hZR8^@#FW?y3hW_rT1`5=jSvmYvk3li{;>&p^+V&5yeN&ZxG}U@pH}|3RxocP8`t zU>Vx*{fU1rSpX}d4tWSxqXpt=IsKSYoOFWBhsA2Tf^m_~#UVv`F;yI()go#-n48OH z@cNauIB!ZBlGR%#b7`CqE?eCuanX67I4LJ8nVWStC>DHvjxZ^q((@~+nR%;!wtmO~ zlfSOmCHttBP=tfuzrRRGP+{_V!Z$E61*`8$V^x8moS|9Lm$dr6%UgghEi2_P!0@M4 z(bv@V^)n}0sjouf_Y54Qpr)nv-FSWv0FV`5MW@lF^w&}O_vXN>8*Dn;<7O1ipqiq} zV>w=5Z7k${HA^LN21iBzG>(u!U4>_;6Mk>f9EL&5nb}$Yp>5B~XM(F;oKf~|N~Hv4 zJ>xaiKsEV*?`FhRv;=LW!*fu_j6LsBNas&m zz{QaZ>7Qd#(sQIV;7c;uQH@KCd2@M9O~E!LUxH9ukW6E@6UzsWOyAJzgSDKNr@*@U4DhjRtxdpFSJei ze^9SVwd@WwRM_T-n#GKO=flHS2$!2IFKXiA9*CxmsCc+0Z>PLt{eV%ohNAHa^JoS z@C6;vIZ~sDpK)Dmfk$BK<<%L4>j44qg{)^f6wm5E{Deht2TqL_p?Q~otg%XqM)!pc z#kj-;a@joZNv~s`hvVB}2(y}8N>QbJM-0kC*k?{ZJ(bMhKRntz?>SaS3}G!S8#{qN zTx;Y0({pUCIvs`VpIMhi1ZA}z?0Nrib=;)}A4`=|nptX&4%p}{PYbKgf~5#2=y~ZR z3JXdpVT{=9m(iuk6HhcJ&jeDAxqnq`tj_uRegI4!1M`!(Tr3W=~J*>VnDS z;*+B$?w@e>(L|<@=16dLpKxgcQ-Coj6V+p|3kN`KvVtutP66{tNXP=qY?*^cQ%}uV z6$j$7`=|WYEESC`inCEy>XO0L(R?1?dv;F482QUf1fxw_>2c?@eC88ELbDyA6h;s_ zv%w`9FpK5>jroEvl~D1~N0oLz_sAdCv9MN|TgpU4eD!Ux1T{Nc5;tSmPEOB4Rg9;- z<)HVbFFeH%x+gz&!=g~unv1Y*Hfz58kY8#V%J@Au7W(ku1Sa;@TTqL&4-JQpJ<#+G zW>gctEI{G{PP2u2y1MU+-lUca;M?q?_uFX%&q-O3?*$-{8yM-nv01}}wr6mk2QAf@ zNy*n$)~7R=V^!!Iv@(^vvd^P|neUd|@X zV|q~wuXh7$wH3(F-&mXs-tb+-7p!H@+A{h23??w~mZYPna&)=BB%ftwdS|d5SYKwT z{1|^rct?~L`Xin|$?IN%^)i^NT|?PMg`%-dCXEYtFv5WAczq#iwv1Bp#C{pSoL_YM zL^VXitEg{(yUr^ScUe|aFrZErS0AZ4U}^$%bz)-!wGK=A!=f3!-6Nv8Y3$w#As1%A zdlZZbOn{7f6+H$@c;p{b89h&Vdf7KwSs5W-l{6lEN?&+eTQ_jzX;Z;h_=f`navMz~y1*Z2PbZUtZa literal 0 HcmV?d00001 diff --git a/modules/ppcp-axo/extensions.php b/modules/ppcp-axo/extensions.php index c1f7f933f..1174c6f2a 100644 --- a/modules/ppcp-axo/extensions.php +++ b/modules/ppcp-axo/extensions.php @@ -30,22 +30,49 @@ return array( $display_manager = $container->get( 'wcgateway.display-manager' ); assert( $display_manager instanceof DisplayManager ); + $module_url = $container->get( 'axo.url' ); + // Standard Payments tab fields. return $insert_after( $fields, 'vault_enabled_dcc', array( - 'axo_enabled' => array( - 'title' => __( 'Fastlane', 'woocommerce-paypal-payments' ), - 'type' => 'checkbox', - 'label' => __( 'Enable Fastlane Checkout', 'woocommerce-paypal-payments' ) - . '

' - . sprintf( - // translators: %1$s and %2$s are the opening and closing of HTML tag. - __( 'Buyers can use %1$sFastlane%2$s to make payments.', 'woocommerce-paypal-payments' ), - '', + 'axo_heading' => array( + 'heading' => __( 'Fastlane', 'woocommerce-paypal-payments' ), + 'type' => 'ppcp-heading', + 'description' => wp_kses_post( + sprintf( + // translators: %1$s and %2$s is a link tag. + __( + 'Offer an accelerated checkout experience that recognizes guest shoppers and autofills their details so they can pay in seconds.', + 'woocommerce-paypal-payments' + ), + '', '' ) + ), + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array( + 'dcc', + ), + 'gateway' => 'dcc', + ), + 'axo_enabled' => array( + 'title' => __( 'Fastlane', 'woocommerce-paypal-payments' ), + 'title_html' => sprintf( + '%s', + $module_url, + __( 'Fastlane', 'woocommerce-paypal-payments' ) + ), + 'type' => 'checkbox', + 'label' => __( 'Enable Fastlane by PayPal', 'woocommerce-paypal-payments' ) + . '

' + . __( 'Help accelerate checkout for guests with PayPal\'s autofill solution.', 'woocommerce-paypal-payments' ) . '

', 'default' => 'yes', 'screens' => array( State::STATE_ONBOARDED ), @@ -63,9 +90,28 @@ return array( ->action_visible( 'axo_payment_widget' ) ->action_class( 'axo_enabled', 'active' ) ->to_array(), + $display_manager + ->rule() + ->condition_js_variable( 'ppcpAxoShowStyles', true ) + ->action_visible( 'axo_style_root_heading' ) + ->action_visible( 'axo_style_root_bg_color' ) + ->action_visible( 'axo_style_root_error_color' ) + ->action_visible( 'axo_style_root_font_family' ) + ->action_visible( 'axo_style_root_font_size_base' ) + ->action_visible( 'axo_style_root_padding' ) + ->action_visible( 'axo_style_root_primary_color' ) + ->action_visible( 'axo_style_input_heading' ) + ->action_visible( 'axo_style_input_bg_color' ) + ->action_visible( 'axo_style_input_border_radius' ) + ->action_visible( 'axo_style_input_border_color' ) + ->action_visible( 'axo_style_root_border_width' ) + ->action_visible( 'axo_style_root_text_color_base' ) + ->action_visible( 'axo_style_root_focus_border_color' ) + ->to_array(), ) ), ), + 'classes' => array( 'ppcp-valign-label-middle', 'ppcp-align-label-center' ) ), 'axo_gateway_title' => array( 'title' => __( 'Gateway Title', 'woocommerce-paypal-payments' ), @@ -97,47 +143,249 @@ return array( 'classes' => array( 'ppcp-field-indent' ), 'class' => array(), 'input_class' => array( 'wc-enhanced-select' ), - 'default' => 'pay', + 'default' => 'render', 'options' => PropertiesDictionary::email_widget_options(), 'screens' => array( State::STATE_ONBOARDED ), 'gateway' => 'dcc', 'requirements' => array(), ), - 'axo_address_widget' => array( - 'title' => __( 'Address Widget', 'woocommerce-paypal-payments' ), + //'axo_address_widget' => array( + // 'title' => __( 'Address Widget', 'woocommerce-paypal-payments' ), + // 'type' => 'select', + // 'desc_tip' => true, + // 'description' => __( + // 'This controls if the Hosted Address Widget should be used.', + // 'woocommerce-paypal-payments' + // ), + // 'classes' => array( 'ppcp-field-indent' ), + // 'class' => array(), + // 'input_class' => array( 'wc-enhanced-select' ), + // 'default' => 'render', + // 'options' => PropertiesDictionary::address_widget_options(), + // 'screens' => array( State::STATE_ONBOARDED ), + // 'gateway' => 'dcc', + // 'requirements' => array(), + //), + //'axo_payment_widget' => array( + // 'title' => __( 'Payment Widget', 'woocommerce-paypal-payments' ), + // 'type' => 'select', + // 'desc_tip' => true, + // 'description' => __( + // 'This controls if the Hosted Payment Widget should be used.', + // 'woocommerce-paypal-payments' + // ), + // 'label' => '', + // 'input_class' => array( 'wc-enhanced-select' ), + // 'classes' => array( 'ppcp-field-indent' ), + // 'class' => array(), + // 'default' => 'render', + // 'options' => PropertiesDictionary::payment_widget_options(), + // 'screens' => array( State::STATE_ONBOARDED ), + // 'gateway' => 'dcc', + // 'requirements' => array(), + //), + 'axo_privacy' => array( + 'title' => __( 'Privacy', 'woocommerce-paypal-payments' ), 'type' => 'select', 'desc_tip' => true, 'description' => __( - 'This controls if the Hosted Address Widget should be used.', + 'This setting will control whether Fastlane branding is shown by email field.', 'woocommerce-paypal-payments' ), 'classes' => array( 'ppcp-field-indent' ), 'class' => array(), 'input_class' => array( 'wc-enhanced-select' ), - 'default' => 'pay', - 'options' => PropertiesDictionary::address_widget_options(), + 'default' => 'yes', + 'options' => PropertiesDictionary::privacy_options(), 'screens' => array( State::STATE_ONBOARDED ), 'gateway' => 'dcc', 'requirements' => array(), ), - 'axo_payment_widget' => array( - 'title' => __( 'Payment Widget', 'woocommerce-paypal-payments' ), - 'type' => 'select', - 'desc_tip' => true, - 'description' => __( - 'This controls if the Hosted Payment Widget should be used.', - 'woocommerce-paypal-payments' + 'axo_style_heading' => array( + 'heading' => __( 'Advanced Style Settings (optional)', 'woocommerce-paypal-payments' ), + 'heading_html' => sprintf( + // translators: %1$s and %2$s are the opening and closing of HTML tag. + __( + 'Advanced Style Settings (optional) %1$sSee more%2$s %3$sSee less%4$s', + 'woocommerce-paypal-payments' + ), + '', + '', + '' + ), + 'type' => 'ppcp-heading', + 'description' => wp_kses_post( + sprintf( + // translators: %1$s and %2$s is a link tag. + __( + 'Leave the default styling, or customize how Fastlane looks on your website. See PayPal\'s developer docs for info', + 'woocommerce-paypal-payments' + ), + '', + '' + ) ), - 'label' => '', - 'input_class' => array( 'wc-enhanced-select' ), 'classes' => array( 'ppcp-field-indent' ), 'class' => array(), - 'default' => 'black', - 'options' => PropertiesDictionary::payment_widget_options(), - 'screens' => array( State::STATE_ONBOARDED ), + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array( + 'dcc', + ), 'gateway' => 'dcc', - 'requirements' => array(), ), + + 'axo_style_root_heading' => array( + 'heading' => __( 'Root Settings', 'woocommerce-paypal-payments' ), + 'type' => 'ppcp-heading', + 'description' => __( + 'These apply to the overall Fastlane checkout module.', + 'woocommerce-paypal-payments' + ), + 'classes' => array( 'ppcp-field-indent' ), + 'screens' => array( State::STATE_ONBOARDED ), + 'requirements' => array( 'dcc' ), + 'gateway' => 'dcc', + ), + 'axo_style_root_bg_color' => array( + 'title' => __( 'Background Color', 'woocommerce-paypal-payments' ), + 'type' => 'text', + 'classes' => array( 'ppcp-field-indent' ), + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array(), + 'gateway' => 'dcc', + ), + 'axo_style_root_error_color' => array( + 'title' => __( 'Error Color', 'woocommerce-paypal-payments' ), + 'type' => 'text', + 'classes' => array( 'ppcp-field-indent' ), + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array(), + 'gateway' => 'dcc', + ), + 'axo_style_root_font_family' => array( + 'title' => __( 'Font Family', 'woocommerce-paypal-payments' ), + 'type' => 'text', + 'classes' => array( 'ppcp-field-indent' ), + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array(), + 'gateway' => 'dcc', + ), + 'axo_style_root_font_size_base' => array( + 'title' => __( 'Font Size Base', 'woocommerce-paypal-payments' ), + 'type' => 'text', + 'classes' => array( 'ppcp-field-indent' ), + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array(), + 'gateway' => 'dcc', + ), + 'axo_style_root_padding' => array( + 'title' => __( 'Padding', 'woocommerce-paypal-payments' ), + 'type' => 'text', + 'classes' => array( 'ppcp-field-indent' ), + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array(), + 'gateway' => 'dcc', + ), + 'axo_style_root_primary_color' => array( + 'title' => __( 'Primary Color', 'woocommerce-paypal-payments' ), + 'type' => 'text', + 'classes' => array( 'ppcp-field-indent' ), + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array(), + 'gateway' => 'dcc', + ), + + 'axo_style_input_heading' => array( + 'heading' => __( 'Input Settings', 'woocommerce-paypal-payments' ), + 'type' => 'ppcp-heading', + 'description' => __( + 'These apply to the customer input fields on your Fastlane module.', + 'woocommerce-paypal-payments' + ), + 'classes' => array( 'ppcp-field-indent' ), + 'screens' => array( State::STATE_ONBOARDED ), + 'requirements' => array( 'dcc' ), + 'gateway' => 'dcc', + ), + 'axo_style_input_bg_color' => array( + 'title' => __( 'Background Color', 'woocommerce-paypal-payments' ), + 'type' => 'text', + 'classes' => array( 'ppcp-field-indent' ), + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array(), + 'gateway' => 'dcc', + ), + 'axo_style_input_border_radius' => array( + 'title' => __( 'Border Radius', 'woocommerce-paypal-payments' ), + 'type' => 'text', + 'classes' => array( 'ppcp-field-indent' ), + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array(), + 'gateway' => 'dcc', + ), + 'axo_style_input_border_color' => array( + 'title' => __( 'Border Color', 'woocommerce-paypal-payments' ), + 'type' => 'text', + 'classes' => array( 'ppcp-field-indent' ), + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array(), + 'gateway' => 'dcc', + ), + 'axo_style_root_border_width' => array( + 'title' => __( 'Border Width', 'woocommerce-paypal-payments' ), + 'type' => 'text', + 'classes' => array( 'ppcp-field-indent' ), + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array(), + 'gateway' => 'dcc', + ), + 'axo_style_root_text_color_base' => array( + 'title' => __( 'Text Color Base', 'woocommerce-paypal-payments' ), + 'type' => 'text', + 'classes' => array( 'ppcp-field-indent' ), + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array(), + 'gateway' => 'dcc', + ), + 'axo_style_root_focus_border_color' => array( + 'title' => __( 'Focus Border Color', 'woocommerce-paypal-payments' ), + 'type' => 'text', + 'classes' => array( 'ppcp-field-indent' ), + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array(), + 'gateway' => 'dcc', + ), + ) ); }, diff --git a/modules/ppcp-axo/resources/js/AxoManager.js b/modules/ppcp-axo/resources/js/AxoManager.js index e6ebb0fdd..32af9492a 100644 --- a/modules/ppcp-axo/resources/js/AxoManager.js +++ b/modules/ppcp-axo/resources/js/AxoManager.js @@ -47,9 +47,13 @@ class AxoManager { this.billingView = new BillingView(this.el.billingAddressContainer.selector, this.el); this.cardView = new CardView(this.el.paymentContainer.selector + '-details', this.el, this); - document.testAxoStatus = (key, value) => { + document.axoDebugSetStatus = (key, value) => { this.setStatus(key, value); } + + document.axoDebugObject = (key, value) => { + console.log(this); + } } registerEventHandlers() { @@ -549,7 +553,8 @@ class AxoManager { onClickSubmitButton() { if (this.data.card) { // Ryan flow log('Ryan flow.'); - this.submit(this.data.card.getPaymentToken()); + console.log('this.data.card', this.data.card); + this.submit(this.data.card.id); } else { // Gary flow log('Gary flow.'); diff --git a/modules/ppcp-axo/resources/js/Components/FormFieldGroup.js b/modules/ppcp-axo/resources/js/Components/FormFieldGroup.js index 27beff7d6..29d1319d8 100644 --- a/modules/ppcp-axo/resources/js/Components/FormFieldGroup.js +++ b/modules/ppcp-axo/resources/js/Components/FormFieldGroup.js @@ -99,9 +99,19 @@ class FormFieldGroup { } inputValue(name) { - console.log('inputValue:name', this.fields[name].selector); + const baseSelector = this.fields[name].selector; - return document.querySelector(this.fields[name].selector).value; + const select = document.querySelector(baseSelector + ' input'); + if (select) { + return select.value; + } + + const input = document.querySelector(baseSelector + ' input'); + if (input) { + return input.value; + } + + return ''; } } diff --git a/modules/ppcp-axo/resources/js/Helper/MockData.js b/modules/ppcp-axo/resources/js/Helper/MockData.js deleted file mode 100644 index 48d89ba11..000000000 --- a/modules/ppcp-axo/resources/js/Helper/MockData.js +++ /dev/null @@ -1,32 +0,0 @@ - -class MockData { - - static cardComponent() { - return { - fields: { - phoneNumber: { - prefill: "1234567890" - }, - cardholderName: {} // optionally pass this to show the card holder name - } - } - } - - static cardComponentTokenize() { - return { - name: { - fullName: "John Doe" - }, - billingAddress: { - addressLine1: "2211 North 1st St", - adminArea1: "San Jose", - adminArea2: "CA", - postalCode: "95131", - countryCode: "US" - } - } - } - -} - -export default MockData; diff --git a/modules/ppcp-axo/services.php b/modules/ppcp-axo/services.php index 71fd62d32..f078ade25 100644 --- a/modules/ppcp-axo/services.php +++ b/modules/ppcp-axo/services.php @@ -34,15 +34,6 @@ return array( // If AXO is configured and onboarded. 'axo.available' => static function ( ContainerInterface $container ): bool { -// TODO -// if ( apply_filters( 'woocommerce_paypal_payments_googlepay_validate_product_status', true ) ) { -// $status = $container->get( 'googlepay.helpers.apm-product-status' ); -// assert( $status instanceof ApmProductStatus ); -// /** -// * If merchant isn't onboarded via /v1/customer/partner-referrals this returns false as the API call fails. -// */ -// return apply_filters( 'woocommerce_paypal_payments_googlepay_product_status', $status->is_active() ); -// } return true; }, @@ -74,6 +65,7 @@ return array( return new AxoGateway( $container->get( 'wcgateway.settings' ), $container->get( 'wcgateway.url' ), + $container->get( 'wcgateway.order-processor' ), $container->get( 'axo.card_icons' ), $container->get( 'api.endpoint.order' ), $container->get( 'api.factory.purchase-unit' ), diff --git a/modules/ppcp-axo/src/AxoModule.php b/modules/ppcp-axo/src/AxoModule.php index 847d204eb..6f8372e75 100644 --- a/modules/ppcp-axo/src/AxoModule.php +++ b/modules/ppcp-axo/src/AxoModule.php @@ -11,6 +11,7 @@ namespace WooCommerce\PayPalCommerce\Axo; use WooCommerce\PayPalCommerce\Axo\Assets\AxoManager; use WooCommerce\PayPalCommerce\Button\Assets\SmartButtonInterface; +use WooCommerce\PayPalCommerce\Onboarding\Render\OnboardingOptionsRenderer; use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider; use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface; use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface; @@ -40,6 +41,12 @@ class AxoModule implements ModuleInterface { function ( $methods ) use ( $c ): array { $gateway = $c->get( 'axo.gateway' ); + // Add the gateway in admin area. + if ( is_admin() ) { + $methods[] = $gateway; + return $methods; + } + // Check if the module is applicable, correct country, currency, ... etc. if ( ! $c->get( 'axo.eligible' ) ) { return $methods; @@ -58,6 +65,22 @@ class AxoModule implements ModuleInterface { 9 ); + add_filter( + 'ppcp_onboarding_dcc_table_rows', + function ( $rows, $renderer ): array { + if ( $renderer instanceof OnboardingOptionsRenderer ) { + $rows[] = $renderer->render_table_row( + __( 'Fastlane by PayPal', 'woocommerce-paypal-payments' ), + __( 'Yes', 'woocommerce-paypal-payments' ), + __( 'Help accelerate guest checkout with PayPal\'s autofill solution.', 'woocommerce-paypal-payments' ) + ); + } + return $rows; + }, + 10, + 2 + ); + add_action( 'init', static function () use ( $c ) { diff --git a/modules/ppcp-axo/src/Gateway/AxoGateway.php b/modules/ppcp-axo/src/Gateway/AxoGateway.php index 313322549..6ef73a28d 100644 --- a/modules/ppcp-axo/src/Gateway/AxoGateway.php +++ b/modules/ppcp-axo/src/Gateway/AxoGateway.php @@ -23,7 +23,9 @@ use WooCommerce\PayPalCommerce\Onboarding\Environment; use WooCommerce\PayPalCommerce\PPCP; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; use WooCommerce\PayPalCommerce\WcGateway\Gateway\TransactionUrlProvider; +use WooCommerce\PayPalCommerce\WcGateway\Processor\AuthorizedPaymentsProcessor; use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderMetaTrait; +use WooCommerce\PayPalCommerce\WcGateway\Processor\OrderProcessor; /** * Class AXOGateway. @@ -47,6 +49,13 @@ class AxoGateway extends WC_Payment_Gateway { */ protected $wcgateway_module_url; + /** + * The processor for orders. + * + * @var OrderProcessor + */ + protected $order_processor; + /** * The card icons. * @@ -101,6 +110,7 @@ class AxoGateway extends WC_Payment_Gateway { * * @param ContainerInterface $ppcp_settings The settings. * @param string $wcgateway_module_url The WcGateway module URL. + * @param OrderProcessor $order_processor The Order processor. * @param array $card_icons The card icons. * @param OrderEndpoint $order_endpoint The order endpoint. * @param PurchaseUnitFactory $purchase_unit_factory The purchase unit factory. @@ -112,6 +122,7 @@ class AxoGateway extends WC_Payment_Gateway { public function __construct( ContainerInterface $ppcp_settings, string $wcgateway_module_url, + OrderProcessor $order_processor, array $card_icons, OrderEndpoint $order_endpoint, PurchaseUnitFactory $purchase_unit_factory, @@ -124,10 +135,11 @@ class AxoGateway extends WC_Payment_Gateway { $this->ppcp_settings = $ppcp_settings; $this->wcgateway_module_url = $wcgateway_module_url; + $this->order_processor = $order_processor; $this->card_icons = $card_icons; $this->method_title = __( 'Fastlane Debit & Credit Cards', 'woocommerce-paypal-payments' ); - $this->method_description = __( 'Fastlane Debit & Credit Cards', 'woocommerce-paypal-payments' ); + $this->method_description = __( 'Accept credit cards with Fastlane’s latest solution.', 'woocommerce-paypal-payments' ); $is_axo_enabled = $this->ppcp_settings->has( 'axo_enabled' ) && $this->ppcp_settings->get( 'axo_enabled' ); $this->update_option( 'enabled', $is_axo_enabled ? 'yes' : 'no' ); @@ -182,6 +194,12 @@ class AxoGateway extends WC_Payment_Gateway { */ public function process_payment( $order_id ) { $wc_order = wc_get_order( $order_id ); + + $payment_method_title = __( 'Credit Card (via Fastlane by PayPal)', 'woocommerce-paypal-payments' ); + + $wc_order->set_payment_method_title( $payment_method_title ); + $wc_order->save(); + $purchase_unit = $this->purchase_unit_factory->from_wc_order( $wc_order ); $nonce = wc_clean( wp_unslash( $_POST['axo_nonce'] ?? '' ) ); @@ -212,27 +230,8 @@ class AxoGateway extends WC_Payment_Gateway { $payment_source ); - //$this->add_paypal_meta( $wc_order, $order, $this->environment ); + $this->order_processor->process_captured_and_authorized( $wc_order, $order ); - // TODO: inject dependency. - PPCP::container()->get( 'session.handler' )->replace_order( $order ); - PPCP::container()->get( 'wcgateway.order-processor' )->process( $wc_order ); - -// $payment_source = array( -// 'oxxo' => array( -// 'name' => $wc_order->get_billing_first_name() . ' ' . $wc_order->get_billing_last_name(), -// 'email' => $wc_order->get_billing_email(), -// 'country_code' => $wc_order->get_billing_country(), -// ), -// ); -// $payment_method = $this->order_endpoint->confirm_payment_source( $order->id(), $payment_source ); -// foreach ( $payment_method->links as $link ) { -// if ( $link->rel === 'payer-action' ) { -// $payer_action = $link->href; -// $wc_order->add_meta_data( 'ppcp_oxxo_payer_action', $payer_action ); -// $wc_order->save_meta_data(); -// } -// } } catch ( RuntimeException $exception ) { $error = $exception->getMessage(); if ( is_a( $exception, PayPalApiException::class ) ) { @@ -299,4 +298,28 @@ class AxoGateway extends WC_Payment_Gateway { return parent::get_transaction_url( $order ); } + + /** + * Return the gateway's title. + * + * @return string + */ + public function get_title() { + if ( is_admin() ) { + // $theorder and other things for retrieving the order or post info are not available + // in the constructor, so must do it here. + global $theorder; + if ( $theorder instanceof WC_Order ) { + if ( $theorder->get_payment_method() === self::ID ) { + $payment_method_title = $theorder->get_payment_method_title(); + if ( $payment_method_title ) { + $this->title = $payment_method_title; + } + } + } + } + + return parent::get_title(); + } + } diff --git a/modules/ppcp-axo/src/Helper/PropertiesDictionary.php b/modules/ppcp-axo/src/Helper/PropertiesDictionary.php index 714e635f5..d517959c1 100644 --- a/modules/ppcp-axo/src/Helper/PropertiesDictionary.php +++ b/modules/ppcp-axo/src/Helper/PropertiesDictionary.php @@ -15,7 +15,7 @@ namespace WooCommerce\PayPalCommerce\Axo\Helper; class PropertiesDictionary { /** - * Returns the possible list of possible email widget options. + * Returns the list of possible email widget options. * * @return array */ @@ -27,7 +27,7 @@ class PropertiesDictionary { } /** - * Returns the possible list of possible address widget options. + * Returns the list of possible address widget options. * * @return array */ @@ -39,7 +39,7 @@ class PropertiesDictionary { } /** - * Returns the possible list of possible address widget options. + * Returns the list of possible address widget options. * * @return array */ @@ -50,4 +50,16 @@ class PropertiesDictionary { ); } + /** + * Returns the list of possible privacy options. + * + * @return array + */ + public static function privacy_options(): array { + return array( + 'yes' => __( 'Yes (Recommended)', 'woocommerce-paypal-payments' ), + 'no' => __( 'No', 'woocommerce-paypal-payments' ), + ); + } + } diff --git a/modules/ppcp-googlepay/assets/images/googlepay.png b/modules/ppcp-googlepay/assets/images/googlepay.png new file mode 100644 index 0000000000000000000000000000000000000000..b264fd0ee92d8f71de18f546529f129bab9263ee GIT binary patch literal 19520 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 literal 0 HcmV?d00001 diff --git a/modules/ppcp-googlepay/extensions.php b/modules/ppcp-googlepay/extensions.php index 6a023851b..a40ba6795 100644 --- a/modules/ppcp-googlepay/extensions.php +++ b/modules/ppcp-googlepay/extensions.php @@ -39,6 +39,8 @@ return array( $display_manager = $container->get( 'wcgateway.display-manager' ); assert( $display_manager instanceof DisplayManager ); + $module_url = $container->get( 'googlepay.url' ); + // Connection tab fields. $fields = $insert_after( $fields, @@ -66,6 +68,11 @@ return array( array( 'googlepay_button_enabled' => array( 'title' => __( 'Google Pay Button', 'woocommerce-paypal-payments' ), + 'title_html' => sprintf( + '%s', + $module_url, + __( 'Google Pay', 'woocommerce-paypal-payments' ) + ), 'type' => 'checkbox', 'class' => array( 'ppcp-grayed-out-text' ), 'input_class' => array( 'ppcp-disabled-checkbox' ), @@ -93,6 +100,7 @@ return array( ) ), ), + 'classes' => array( 'ppcp-valign-label-middle', 'ppcp-align-label-center' ) ), ) ); @@ -105,6 +113,11 @@ return array( array( 'googlepay_button_enabled' => array( 'title' => __( 'Google Pay Button', 'woocommerce-paypal-payments' ), + 'title_html' => sprintf( + '%s', + $module_url, + __( 'Google Pay', 'woocommerce-paypal-payments' ) + ), 'type' => 'checkbox', 'label' => __( 'Enable Google Pay button', 'woocommerce-paypal-payments' ) . '

' @@ -134,6 +147,7 @@ return array( ) ), ), + 'classes' => array( 'ppcp-valign-label-middle', 'ppcp-align-label-center' ) ), 'googlepay_button_type' => array( 'title' => __( 'Button Label', 'woocommerce-paypal-payments' ), diff --git a/modules/ppcp-onboarding/src/Render/OnboardingOptionsRenderer.php b/modules/ppcp-onboarding/src/Render/OnboardingOptionsRenderer.php index a5d7628ec..470de9803 100644 --- a/modules/ppcp-onboarding/src/Render/OnboardingOptionsRenderer.php +++ b/modules/ppcp-onboarding/src/Render/OnboardingOptionsRenderer.php @@ -136,6 +136,9 @@ class OnboardingOptionsRenderer { __( 'For Standard payments, Casual sellers may connect their Personal PayPal account in eligible countries to sell on WooCommerce. For Advanced payments, a Business PayPal account is required.', 'woocommerce-paypal-payments' ) ), ); + + $basic_table_rows = apply_filters( 'ppcp_onboarding_basic_table_rows', $basic_table_rows ); + $items[] = '

  • Date: Wed, 10 Apr 2024 17:05:33 +0200 Subject: [PATCH 43/70] Fix phpcs --- .../Endpoint/PaymentMethodTokensEndpoint.php | 2 +- .../ppcp-button/src/Assets/SmartButton.php | 22 ++++---- .../src/SavePaymentMethodsModule.php | 4 +- .../src/Gateway/PayPalGateway.php | 54 +++++++++---------- 4 files changed, 41 insertions(+), 41 deletions(-) diff --git a/modules/ppcp-api-client/src/Endpoint/PaymentMethodTokensEndpoint.php b/modules/ppcp-api-client/src/Endpoint/PaymentMethodTokensEndpoint.php index 1b547a11e..538ca224d 100644 --- a/modules/ppcp-api-client/src/Endpoint/PaymentMethodTokensEndpoint.php +++ b/modules/ppcp-api-client/src/Endpoint/PaymentMethodTokensEndpoint.php @@ -115,7 +115,7 @@ class PaymentMethodTokensEndpoint { * @throws RuntimeException When something when wrong with the request. * @throws PayPalApiException When something when wrong setting up the token. */ - public function create_payment_token(PaymentSource $payment_source ): stdClass { + public function create_payment_token( PaymentSource $payment_source ): stdClass { $data = array( 'payment_source' => array( $payment_source->name() => $payment_source->properties(), diff --git a/modules/ppcp-button/src/Assets/SmartButton.php b/modules/ppcp-button/src/Assets/SmartButton.php index 6f73e6d88..d795d4950 100644 --- a/modules/ppcp-button/src/Assets/SmartButton.php +++ b/modules/ppcp-button/src/Assets/SmartButton.php @@ -1059,46 +1059,46 @@ document.querySelector("#payment").before(document.querySelector("#ppcp-messages 'redirect' => wc_get_checkout_url(), 'context' => $this->context(), 'ajax' => array( - 'simulate_cart' => array( + 'simulate_cart' => array( 'endpoint' => \WC_AJAX::get_endpoint( SimulateCartEndpoint::ENDPOINT ), 'nonce' => wp_create_nonce( SimulateCartEndpoint::nonce() ), ), - 'change_cart' => array( + 'change_cart' => array( 'endpoint' => \WC_AJAX::get_endpoint( ChangeCartEndpoint::ENDPOINT ), 'nonce' => wp_create_nonce( ChangeCartEndpoint::nonce() ), ), - 'create_order' => array( + 'create_order' => array( 'endpoint' => \WC_AJAX::get_endpoint( CreateOrderEndpoint::ENDPOINT ), 'nonce' => wp_create_nonce( CreateOrderEndpoint::nonce() ), ), - 'approve_order' => array( + 'approve_order' => array( 'endpoint' => \WC_AJAX::get_endpoint( ApproveOrderEndpoint::ENDPOINT ), 'nonce' => wp_create_nonce( ApproveOrderEndpoint::nonce() ), ), - 'approve_subscription' => array( + 'approve_subscription' => array( 'endpoint' => \WC_AJAX::get_endpoint( ApproveSubscriptionEndpoint::ENDPOINT ), 'nonce' => wp_create_nonce( ApproveSubscriptionEndpoint::nonce() ), ), - 'vault_paypal' => array( + 'vault_paypal' => array( 'endpoint' => \WC_AJAX::get_endpoint( StartPayPalVaultingEndpoint::ENDPOINT ), 'nonce' => wp_create_nonce( StartPayPalVaultingEndpoint::nonce() ), ), - 'save_checkout_form' => array( + 'save_checkout_form' => array( 'endpoint' => \WC_AJAX::get_endpoint( SaveCheckoutFormEndpoint::ENDPOINT ), 'nonce' => wp_create_nonce( SaveCheckoutFormEndpoint::nonce() ), ), - 'validate_checkout' => array( + 'validate_checkout' => array( 'endpoint' => \WC_AJAX::get_endpoint( ValidateCheckoutEndpoint::ENDPOINT ), 'nonce' => wp_create_nonce( ValidateCheckoutEndpoint::nonce() ), ), - 'cart_script_params' => array( + 'cart_script_params' => array( 'endpoint' => \WC_AJAX::get_endpoint( CartScriptParamsEndpoint::ENDPOINT ), ), - 'create_setup_token' => array( + 'create_setup_token' => array( 'endpoint' => \WC_AJAX::get_endpoint( CreateSetupToken::ENDPOINT ), 'nonce' => wp_create_nonce( CreateSetupToken::nonce() ), ), - 'create_payment_token' => array( + 'create_payment_token' => array( 'endpoint' => \WC_AJAX::get_endpoint( CreatePaymentToken::ENDPOINT ), 'nonce' => wp_create_nonce( CreatePaymentToken::nonce() ), ), diff --git a/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php b/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php index 8daafae4a..b667fb10b 100644 --- a/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php +++ b/modules/ppcp-save-payment-methods/src/SavePaymentMethodsModule.php @@ -317,13 +317,13 @@ class SavePaymentMethodsModule implements ModuleInterface { 'nonce' => wp_create_nonce( SubscriptionChangePaymentMethod::nonce() ), ), ), - 'labels' => array( + 'labels' => array( 'error' => array( 'generic' => __( 'Something went wrong. Please try again or choose another payment source.', 'woocommerce-paypal-payments' ), - ) + ), ), ) ); diff --git a/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php b/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php index 20b7a840d..8ac87156f 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php @@ -205,25 +205,25 @@ class PayPalGateway extends \WC_Payment_Gateway { /** * PayPalGateway constructor. * - * @param SettingsRenderer $settings_renderer The Settings Renderer. - * @param FundingSourceRenderer $funding_source_renderer The funding source renderer. - * @param OrderProcessor $order_processor The Order Processor. - * @param ContainerInterface $config The settings. - * @param SessionHandler $session_handler The Session Handler. - * @param RefundProcessor $refund_processor The Refund Processor. - * @param State $state The state. - * @param TransactionUrlProvider $transaction_url_provider Service providing transaction view URL based on order. - * @param SubscriptionHelper $subscription_helper The subscription helper. - * @param string $page_id ID of the current PPCP gateway settings page, or empty if it is not such page. - * @param Environment $environment The environment. - * @param PaymentTokenRepository $payment_token_repository The payment token repository. - * @param LoggerInterface $logger The logger. - * @param string $api_shop_country The api shop country. - * @param OrderEndpoint $order_endpoint The order endpoint. - * @param callable(string):string $paypal_checkout_url_factory The function return the PayPal checkout URL for the given order ID. - * @param string $place_order_button_text The text for the standard "Place order" button. - * @param PaymentTokensEndpoint $payment_tokens_endpoint Payment tokens endpoint. - * @param bool $vault_v3_enabled Whether Vault v3 module is enabled. + * @param SettingsRenderer $settings_renderer The Settings Renderer. + * @param FundingSourceRenderer $funding_source_renderer The funding source renderer. + * @param OrderProcessor $order_processor The Order Processor. + * @param ContainerInterface $config The settings. + * @param SessionHandler $session_handler The Session Handler. + * @param RefundProcessor $refund_processor The Refund Processor. + * @param State $state The state. + * @param TransactionUrlProvider $transaction_url_provider Service providing transaction view URL based on order. + * @param SubscriptionHelper $subscription_helper The subscription helper. + * @param string $page_id ID of the current PPCP gateway settings page, or empty if it is not such page. + * @param Environment $environment The environment. + * @param PaymentTokenRepository $payment_token_repository The payment token repository. + * @param LoggerInterface $logger The logger. + * @param string $api_shop_country The api shop country. + * @param OrderEndpoint $order_endpoint The order endpoint. + * @param callable(string):string $paypal_checkout_url_factory The function return the PayPal checkout URL for the given order ID. + * @param string $place_order_button_text The text for the standard "Place order" button. + * @param PaymentTokensEndpoint $payment_tokens_endpoint Payment tokens endpoint. + * @param bool $vault_v3_enabled Whether Vault v3 module is enabled. * @param WooCommercePaymentTokens $wc_payment_tokens WooCommerce payment tokens. */ public function __construct( @@ -266,10 +266,10 @@ class PayPalGateway extends \WC_Payment_Gateway { $this->api_shop_country = $api_shop_country; $this->paypal_checkout_url_factory = $paypal_checkout_url_factory; $this->order_button_text = $place_order_button_text; - $this->order_endpoint = $order_endpoint; - $this->payment_tokens_endpoint = $payment_tokens_endpoint; - $this->vault_v3_enabled = $vault_v3_enabled; - $this->wc_payment_tokens = $wc_payment_tokens; + $this->order_endpoint = $order_endpoint; + $this->payment_tokens_endpoint = $payment_tokens_endpoint; + $this->vault_v3_enabled = $vault_v3_enabled; + $this->wc_payment_tokens = $wc_payment_tokens; if ( $this->onboarded ) { $this->supports = array( 'refunds', 'tokenization' ); @@ -536,10 +536,10 @@ class PayPalGateway extends \WC_Payment_Gateway { && $this->is_free_trial_order( $wc_order ) && ! $this->subscription_helper->paypal_subscription_id() ) { - $ppcp_guest_payment_for_free_trial = WC()->session->get( 'ppcp_guest_payment_for_free_trial') ?? null; - if($this->vault_v3_enabled && $ppcp_guest_payment_for_free_trial) { + $ppcp_guest_payment_for_free_trial = WC()->session->get( 'ppcp_guest_payment_for_free_trial' ) ?? null; + if ( $this->vault_v3_enabled && $ppcp_guest_payment_for_free_trial ) { $customer_id = $ppcp_guest_payment_for_free_trial->customer->id ?? ''; - if($customer_id) { + if ( $customer_id ) { update_user_meta( $wc_order->get_customer_id(), '_ppcp_target_customer_id', $customer_id ); } @@ -556,7 +556,7 @@ class PayPalGateway extends \WC_Payment_Gateway { ); } - WC()->session->set( 'ppcp_guest_payment_for_free_trial', null); + WC()->session->set( 'ppcp_guest_payment_for_free_trial', null ); $wc_order->payment_complete(); return $this->handle_payment_success( $wc_order ); From 995621ba212f2784a70721f16351c4521e3f45de Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Wed, 10 Apr 2024 18:27:21 +0100 Subject: [PATCH 44/70] Fixes AXO module --- modules/ppcp-axo/extensions.php | 11 ++-- modules/ppcp-axo/resources/css/styles.scss | 4 ++ modules/ppcp-axo/resources/js/AxoManager.js | 50 ++++++++++------- .../resources/js/Components/FormFieldGroup.js | 54 +++++++++++++++---- .../resources/js/Views/BillingView.js | 26 +++++---- .../ppcp-axo/resources/js/Views/CardView.js | 28 ++++++++-- .../resources/js/Views/ShippingView.js | 16 +++--- modules/ppcp-axo/src/AxoModule.php | 7 +++ 8 files changed, 138 insertions(+), 58 deletions(-) diff --git a/modules/ppcp-axo/extensions.php b/modules/ppcp-axo/extensions.php index 1174c6f2a..b3cc8ab2b 100644 --- a/modules/ppcp-axo/extensions.php +++ b/modules/ppcp-axo/extensions.php @@ -209,9 +209,9 @@ return array( 'Advanced Style Settings (optional) %1$sSee more%2$s %3$sSee less%4$s', 'woocommerce-paypal-payments' ), - '', + '', '', - '' ), 'type' => 'ppcp-heading', @@ -219,13 +219,10 @@ return array( sprintf( // translators: %1$s and %2$s is a link tag. __( - 'Leave the default styling, or customize how Fastlane looks on your website. See PayPal\'s developer docs for info', + 'Leave the default styling, or customize how Fastlane looks on your website. %1$sSee PayPal\'s developer docs%2$s for info', 'woocommerce-paypal-payments' ), - '', + '', '' ) ), diff --git a/modules/ppcp-axo/resources/css/styles.scss b/modules/ppcp-axo/resources/css/styles.scss index 7b9c3c3e9..12c235fbd 100644 --- a/modules/ppcp-axo/resources/css/styles.scss +++ b/modules/ppcp-axo/resources/css/styles.scss @@ -34,3 +34,7 @@ .ppcp-axo-field-hidden { display: none; } + +.ppcp-axo-customer-details { + margin-bottom: 40px; +} diff --git a/modules/ppcp-axo/resources/js/AxoManager.js b/modules/ppcp-axo/resources/js/AxoManager.js index 32af9492a..9eb8ee4a3 100644 --- a/modules/ppcp-axo/resources/js/AxoManager.js +++ b/modules/ppcp-axo/resources/js/AxoManager.js @@ -51,8 +51,9 @@ class AxoManager { this.setStatus(key, value); } - document.axoDebugObject = (key, value) => { + document.axoDebugObject = () => { console.log(this); + return this; } } @@ -351,6 +352,27 @@ class AxoManager { `); } + // Watermark container + const wc = this.el.watermarkContainer; + if (!document.querySelector(wc.selector)) { + this.emailInput = document.querySelector(this.el.fieldBillingEmail.selector + ' input'); + this.emailInput.insertAdjacentHTML('afterend', ` +
    + `); + } + + // Payment container + const pc = this.el.paymentContainer; + if (!document.querySelector(pc.selector)) { + const gatewayPaymentContainer = document.querySelector('.payment_method_ppcp-axo-gateway'); + gatewayPaymentContainer.insertAdjacentHTML('beforeend', ` + + `); + } + if (this.useEmailWidget()) { // Display email widget. @@ -379,7 +401,6 @@ class AxoManager { this.initialized = true; await this.connect(); - this.insertDomElements(); this.renderWatermark(); this.watchEmail(); } @@ -395,21 +416,6 @@ class AxoManager { this.fastlane.setLocale('en_us'); } - insertDomElements() { - this.emailInput = document.querySelector(this.el.fieldBillingEmail.selector + ' input'); - this.emailInput.insertAdjacentHTML('afterend', ` -
    - `); - - const gatewayPaymentContainer = document.querySelector('.payment_method_ppcp-axo-gateway'); - gatewayPaymentContainer.insertAdjacentHTML('beforeend', ` - - `); - } - triggerGatewayChange() { this.el.gatewayRadioButton.trigger('change'); } @@ -551,14 +557,20 @@ class AxoManager { } onClickSubmitButton() { + // TODO: validate data. + if (this.data.card) { // Ryan flow log('Ryan flow.'); - console.log('this.data.card', this.data.card); + + jQuery('#ship-to-different-address-checkbox').prop('checked', 'checked'); + this.billingView.copyDataToForm(); + this.shippingView.copyDataToForm(); + this.cardView.copyDataToForm(); + this.submit(this.data.card.id); } else { // Gary flow log('Gary flow.'); - console.log('this.tokenizeData()', this.tokenizeData()); try { this.cardComponent.tokenize( diff --git a/modules/ppcp-axo/resources/js/Components/FormFieldGroup.js b/modules/ppcp-axo/resources/js/Components/FormFieldGroup.js index 29d1319d8..90d65a5f9 100644 --- a/modules/ppcp-axo/resources/js/Components/FormFieldGroup.js +++ b/modules/ppcp-axo/resources/js/Components/FormFieldGroup.js @@ -17,10 +17,17 @@ class FormFieldGroup { this.refresh(); } - getDataValue(path) { + dataValue(fieldKey) { + if (!fieldKey || !this.fields[fieldKey]) { + return ''; + } + + const path = this.fields[fieldKey].valuePath; + if (!path) { return ''; } + const value = path.split('.').reduce((acc, key) => (acc && acc[key] !== undefined) ? acc[key] : undefined, this.data); return value ? value : ''; } @@ -66,13 +73,13 @@ class FormFieldGroup { if (typeof this.template === 'function') { content.innerHTML = this.template({ - value: (valueKey) => { - return this.getDataValue(this.fields[valueKey].valuePath); + value: (fieldKey) => { + return this.dataValue(fieldKey); }, isEmpty: () => { let isEmpty = true; - Object.values(this.fields).forEach((valueField) => { - if (this.getDataValue(valueField.valuePath)) { + Object.keys(this.fields).forEach((fieldKey) => { + if (this.dataValue(fieldKey)) { isEmpty = false; return false; } @@ -98,20 +105,47 @@ class FormFieldGroup { } } - inputValue(name) { + inputElement(name) { const baseSelector = this.fields[name].selector; - const select = document.querySelector(baseSelector + ' input'); + const select = document.querySelector(baseSelector + ' select'); if (select) { - return select.value; + return select; } const input = document.querySelector(baseSelector + ' input'); if (input) { - return input.value; + return input; } - return ''; + return null; + } + + inputValue(name) { + const el = this.inputElement(name); + return el ? el.value : ''; + } + + copyDataToForm() { + + Object.keys(this.fields).forEach((fieldKey) => { + const field = this.fields[fieldKey]; + + if (!field.valuePath || !field.selector) { + return true; + } + + const inputElement = this.inputElement(fieldKey); + + if (!inputElement) { + return true; + } + + jQuery(inputElement).val( + this.dataValue(fieldKey) + ); + }); + } } diff --git a/modules/ppcp-axo/resources/js/Views/BillingView.js b/modules/ppcp-axo/resources/js/Views/BillingView.js index 72fb1f5a7..b2ec0050b 100644 --- a/modules/ppcp-axo/resources/js/Views/BillingView.js +++ b/modules/ppcp-axo/resources/js/Views/BillingView.js @@ -5,7 +5,7 @@ class BillingView { constructor(selector, elements) { this.el = elements; - this.billingFormFields = new FormFieldGroup({ + this.group = new FormFieldGroup({ baseSelector: '.woocommerce-checkout', contentSelector: selector, template: (data) => { @@ -81,42 +81,46 @@ class BillingView { 'selector': '#billing_company_field', 'valuePath': null, }, - phone: { - 'selector': '#billing_phone_field', - 'valuePath': null, - }, + // phone: { + // 'selector': '#billing_phone_field', + // 'valuePath': null, + // }, } }); } isActive() { - return this.billingFormFields.active; + return this.group.active; } activate() { - this.billingFormFields.activate(); + this.group.activate(); } deactivate() { - this.billingFormFields.deactivate(); + this.group.deactivate(); } refresh() { - this.billingFormFields.refresh(); + this.group.refresh(); } setData(data) { - this.billingFormFields.setData(data); + this.group.setData(data); } inputValue(name) { - return this.billingFormFields.inputValue(name); + return this.group.inputValue(name); } fullName() { return `${this.inputValue('firstName')} ${this.inputValue('lastName')}`.trim(); } + copyDataToForm() { + return this.group.copyDataToForm(); + } + } export default BillingView; diff --git a/modules/ppcp-axo/resources/js/Views/CardView.js b/modules/ppcp-axo/resources/js/Views/CardView.js index 67d9aa01e..c63eb3fab 100644 --- a/modules/ppcp-axo/resources/js/Views/CardView.js +++ b/modules/ppcp-axo/resources/js/Views/CardView.js @@ -6,7 +6,7 @@ class CardView { this.el = elements; this.manager = manager; - this.cardFormFields = new FormFieldGroup({ + this.group = new FormFieldGroup({ baseSelector: '.ppcp-axo-payment-container', contentSelector: selector, template: (data) => { @@ -76,19 +76,37 @@ class CardView { } activate() { - this.cardFormFields.activate(); + this.group.activate(); } deactivate() { - this.cardFormFields.deactivate(); + this.group.deactivate(); } refresh() { - this.cardFormFields.refresh(); + this.group.refresh(); } setData(data) { - this.cardFormFields.setData(data); + this.group.setData(data); + } + + copyDataToForm() { + const name = this.group.dataValue('name'); + const { firstName, lastName } = this.splitName(name); + + jQuery('#billing_first_name').val(firstName); + jQuery('#billing_last_name').val(lastName); + + return this.group.copyDataToForm(); + } + + splitName(fullName) { + let nameParts = fullName.trim().split(' '); + let firstName = nameParts[0]; + let lastName = nameParts.length > 1 ? nameParts[nameParts.length - 1] : ''; + + return { firstName, lastName }; } } diff --git a/modules/ppcp-axo/resources/js/Views/ShippingView.js b/modules/ppcp-axo/resources/js/Views/ShippingView.js index 24c95cf87..7b6da3f09 100644 --- a/modules/ppcp-axo/resources/js/Views/ShippingView.js +++ b/modules/ppcp-axo/resources/js/Views/ShippingView.js @@ -5,7 +5,7 @@ class ShippingView { constructor(selector, elements) { this.el = elements; - this.shippingFormFields = new FormFieldGroup({ + this.group = new FormFieldGroup({ baseSelector: '.woocommerce-checkout', contentSelector: selector, template: (data) => { @@ -86,23 +86,27 @@ class ShippingView { } isActive() { - return this.shippingFormFields.active; + return this.group.active; } activate() { - this.shippingFormFields.activate(); + this.group.activate(); } deactivate() { - this.shippingFormFields.deactivate(); + this.group.deactivate(); } refresh() { - this.shippingFormFields.refresh(); + this.group.refresh(); } setData(data) { - this.shippingFormFields.setData(data); + this.group.setData(data); + } + + copyDataToForm() { + return this.group.copyDataToForm(); } } diff --git a/modules/ppcp-axo/src/AxoModule.php b/modules/ppcp-axo/src/AxoModule.php index 6f8372e75..f1c07a577 100644 --- a/modules/ppcp-axo/src/AxoModule.php +++ b/modules/ppcp-axo/src/AxoModule.php @@ -127,6 +127,13 @@ class AxoModule implements ModuleInterface { } ); + add_action('wp_head', function () { + echo ' + + + '; + }); + }, 1 ); From 2f1c36af403fe18ee16bf3145f4627ac519f0bdf Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Thu, 11 Apr 2024 12:03:31 +0100 Subject: [PATCH 45/70] Fixes AXO module. --- modules/ppcp-axo/resources/js/AxoManager.js | 3 --- .../resources/js/Components/FormFieldGroup.js | 7 ++++++- .../resources/js/Views/BillingView.js | 12 +++++------ .../resources/js/Views/ShippingView.js | 20 +++++++++++++++++++ modules/ppcp-axo/services.php | 2 +- modules/ppcp-axo/src/AxoModule.php | 13 +++++------- modules/ppcp-axo/src/Helper/ApmApplies.php | 20 ++++++++++++++++--- 7 files changed, 55 insertions(+), 22 deletions(-) diff --git a/modules/ppcp-axo/resources/js/AxoManager.js b/modules/ppcp-axo/resources/js/AxoManager.js index 9eb8ee4a3..d380c2e2c 100644 --- a/modules/ppcp-axo/resources/js/AxoManager.js +++ b/modules/ppcp-axo/resources/js/AxoManager.js @@ -587,9 +587,6 @@ class AxoManager { cardComponentData() { return { fields: { - phoneNumber: { - prefill: this.billingView.inputValue('phone') - }, cardholderName: {} // optionally pass this to show the card holder name } } diff --git a/modules/ppcp-axo/resources/js/Components/FormFieldGroup.js b/modules/ppcp-axo/resources/js/Components/FormFieldGroup.js index 90d65a5f9..8fa5bb150 100644 --- a/modules/ppcp-axo/resources/js/Components/FormFieldGroup.js +++ b/modules/ppcp-axo/resources/js/Components/FormFieldGroup.js @@ -22,6 +22,10 @@ class FormFieldGroup { return ''; } + if (typeof this.fields[fieldKey].valueCallback === 'function') { + return this.fields[fieldKey].valueCallback(this.data); + } + const path = this.fields[fieldKey].valuePath; if (!path) { @@ -64,7 +68,7 @@ class FormFieldGroup { Object.keys(this.fields).forEach((key) => { const field = this.fields[key]; - if (this.active) { + if (this.active && !field.showInput) { this.hideField(field.selector); } else { this.showField(field.selector); @@ -106,6 +110,7 @@ class FormFieldGroup { } inputElement(name) { + console.log('inputElement', name); const baseSelector = this.fields[name].selector; const select = document.querySelector(baseSelector + ' select'); diff --git a/modules/ppcp-axo/resources/js/Views/BillingView.js b/modules/ppcp-axo/resources/js/Views/BillingView.js index b2ec0050b..4df27defe 100644 --- a/modules/ppcp-axo/resources/js/Views/BillingView.js +++ b/modules/ppcp-axo/resources/js/Views/BillingView.js @@ -14,6 +14,11 @@ class BillingView { return ''; } const selectElement = document.querySelector(selectSelector); + + if (!selectElement) { + return ${key}; + } + const option = selectElement.querySelector(`option[value="${key}"]`); return option ? option.textContent : key; } @@ -37,7 +42,6 @@ class BillingView {
    ${data.value('postCode')} ${data.value('city')}
    ${valueOfSelect('#billing_state', data.value('stateCode'))}
    ${valueOfSelect('#billing_country', data.value('countryCode'))}
    -
    ${data.value('phone')}
  • `; }, @@ -80,11 +84,7 @@ class BillingView { company: { 'selector': '#billing_company_field', 'valuePath': null, - }, - // phone: { - // 'selector': '#billing_phone_field', - // 'valuePath': null, - // }, + } } }); } diff --git a/modules/ppcp-axo/resources/js/Views/ShippingView.js b/modules/ppcp-axo/resources/js/Views/ShippingView.js index 7b6da3f09..bbc9a11d2 100644 --- a/modules/ppcp-axo/resources/js/Views/ShippingView.js +++ b/modules/ppcp-axo/resources/js/Views/ShippingView.js @@ -14,6 +14,11 @@ class ShippingView { return ''; } const selectElement = document.querySelector(selectSelector); + + if (!selectElement) { + return ${key}; + } + const option = selectElement.querySelector(`option[value="${key}"]`); return option ? option.textContent : key; } @@ -36,6 +41,7 @@ class ShippingView {
    ${data.value('postCode')} ${data.value('city')}
    ${valueOfSelect('#shipping_state', data.value('stateCode'))}
    ${valueOfSelect('#shipping_country', data.value('countryCode'))}
    +
    ${data.value('phone')}
    `; }, @@ -80,6 +86,20 @@ class ShippingView { shipDifferentAddress: { 'selector': '#ship-to-different-address', 'valuePath': null, + }, + phone: { + 'selector': '#billing_phone_field', // There is no shipping phone field. + 'valueCallback': function (data) { + let phone = ''; + const cc = data?.shipping?.phoneNumber?.countryCode; + const number = data?.shipping?.phoneNumber?.nationalNumber; + + if (cc) { + phone = `+${cc} `; + } + phone += number; + return phone; + } } } }); diff --git a/modules/ppcp-axo/services.php b/modules/ppcp-axo/services.php index f078ade25..54e1a8427 100644 --- a/modules/ppcp-axo/services.php +++ b/modules/ppcp-axo/services.php @@ -21,7 +21,7 @@ return array( $apm_applies = $container->get( 'axo.helpers.apm-applies' ); assert( $apm_applies instanceof ApmApplies ); - return $apm_applies->for_country_currency(); + return $apm_applies->for_country_currency() && $apm_applies->for_settings(); }, 'axo.helpers.apm-applies' => static function ( ContainerInterface $container ) : ApmApplies { diff --git a/modules/ppcp-axo/src/AxoModule.php b/modules/ppcp-axo/src/AxoModule.php index f1c07a577..dc5a94e13 100644 --- a/modules/ppcp-axo/src/AxoModule.php +++ b/modules/ppcp-axo/src/AxoModule.php @@ -35,24 +35,21 @@ class AxoModule implements ModuleInterface { * {@inheritDoc} */ public function run( ContainerInterface $c ): void { - add_filter( 'woocommerce_payment_gateways', function ( $methods ) use ( $c ): array { $gateway = $c->get( 'axo.gateway' ); - // Add the gateway in admin area. - if ( is_admin() ) { - $methods[] = $gateway; - return $methods; - } - // Check if the module is applicable, correct country, currency, ... etc. if ( ! $c->get( 'axo.eligible' ) ) { return $methods; } - // TODO: check product status eligibility. + // Add the gateway in admin area. + if ( is_admin() ) { + $methods[] = $gateway; + return $methods; + } if ( is_user_logged_in() ) { return $methods; diff --git a/modules/ppcp-axo/src/Helper/ApmApplies.php b/modules/ppcp-axo/src/Helper/ApmApplies.php index 9a3f5aaa8..f30b99c27 100644 --- a/modules/ppcp-axo/src/Helper/ApmApplies.php +++ b/modules/ppcp-axo/src/Helper/ApmApplies.php @@ -16,7 +16,7 @@ namespace WooCommerce\PayPalCommerce\Axo\Helper; class ApmApplies { /** - * The matrix which countries and currency combinations can be used for GooglePay. + * The matrix which countries and currency combinations can be used for AXO. * * @var array */ @@ -39,7 +39,7 @@ class ApmApplies { /** * DccApplies constructor. * - * @param array $allowed_country_currency_matrix The matrix which countries and currency combinations can be used for GooglePay. + * @param array $allowed_country_currency_matrix The matrix which countries and currency combinations can be used for AXO. * @param string $currency 3-letter currency code of the shop. * @param string $country 2-letter country code of the shop. */ @@ -54,7 +54,7 @@ class ApmApplies { } /** - * Returns whether GooglePay can be used in the current country and the current currency used. + * Returns whether AXO can be used in the current country and the current currency used. * * @return bool */ @@ -65,4 +65,18 @@ class ApmApplies { return in_array( $this->currency, $this->allowed_country_currency_matrix[ $this->country ], true ); } + /** + * Returns whether the settings are compatible with AXO. + * + * @return bool + */ + public function for_settings(): bool { + if ( get_option('woocommerce_ship_to_destination') === 'billing_only' ) { // Force shipping to the customer billing address + return false; + } + return true; + } + + + } From 9bf23550dda69711acbe27f6a0c7461dc58394d4 Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Thu, 11 Apr 2024 17:14:22 +0100 Subject: [PATCH 46/70] Insights preparation. --- .../resources/js/ApplepayButton.js | 2 +- modules/ppcp-axo/extensions.php | 14 ++-- modules/ppcp-axo/resources/js/AxoManager.js | 64 +++++++++++++++++-- modules/ppcp-axo/resources/js/Helper/Debug.js | 2 +- .../resources/js/Insights/PayPalInsights.js | 17 +++-- .../resources/js/Views/BillingView.js | 2 +- .../resources/js/Views/ShippingView.js | 4 +- modules/ppcp-axo/src/Assets/AxoManager.php | 9 +++ .../resources/js/GooglepayButton.js | 2 +- 9 files changed, 92 insertions(+), 24 deletions(-) diff --git a/modules/ppcp-applepay/resources/js/ApplepayButton.js b/modules/ppcp-applepay/resources/js/ApplepayButton.js index 4484bba21..11e14df6a 100644 --- a/modules/ppcp-applepay/resources/js/ApplepayButton.js +++ b/modules/ppcp-applepay/resources/js/ApplepayButton.js @@ -39,7 +39,7 @@ class ApplepayButton { this.log = function() { if ( this.buttonConfig.is_debug ) { - console.log('[ApplePayButton]', ...arguments); + //console.log('[ApplePayButton]', ...arguments); } } diff --git a/modules/ppcp-axo/extensions.php b/modules/ppcp-axo/extensions.php index b3cc8ab2b..0a709d572 100644 --- a/modules/ppcp-axo/extensions.php +++ b/modules/ppcp-axo/extensions.php @@ -88,6 +88,8 @@ return array( ->action_visible( 'axo_email_widget' ) ->action_visible( 'axo_address_widget' ) ->action_visible( 'axo_payment_widget' ) + ->action_visible( 'axo_privacy' ) + ->action_visible( 'axo_style_heading' ) ->action_class( 'axo_enabled', 'active' ) ->to_array(), $display_manager @@ -104,9 +106,9 @@ return array( ->action_visible( 'axo_style_input_bg_color' ) ->action_visible( 'axo_style_input_border_radius' ) ->action_visible( 'axo_style_input_border_color' ) - ->action_visible( 'axo_style_root_border_width' ) - ->action_visible( 'axo_style_root_text_color_base' ) - ->action_visible( 'axo_style_root_focus_border_color' ) + ->action_visible( 'axo_style_input_border_width' ) + ->action_visible( 'axo_style_input_text_color_base' ) + ->action_visible( 'axo_style_input_focus_border_color' ) ->to_array(), ) ), @@ -352,7 +354,7 @@ return array( 'requirements' => array(), 'gateway' => 'dcc', ), - 'axo_style_root_border_width' => array( + 'axo_style_input_border_width' => array( 'title' => __( 'Border Width', 'woocommerce-paypal-payments' ), 'type' => 'text', 'classes' => array( 'ppcp-field-indent' ), @@ -362,7 +364,7 @@ return array( 'requirements' => array(), 'gateway' => 'dcc', ), - 'axo_style_root_text_color_base' => array( + 'axo_style_input_text_color_base' => array( 'title' => __( 'Text Color Base', 'woocommerce-paypal-payments' ), 'type' => 'text', 'classes' => array( 'ppcp-field-indent' ), @@ -372,7 +374,7 @@ return array( 'requirements' => array(), 'gateway' => 'dcc', ), - 'axo_style_root_focus_border_color' => array( + 'axo_style_input_focus_border_color' => array( 'title' => __( 'Focus Border Color', 'woocommerce-paypal-payments' ), 'type' => 'text', 'classes' => array( 'ppcp-field-indent' ), diff --git a/modules/ppcp-axo/resources/js/AxoManager.js b/modules/ppcp-axo/resources/js/AxoManager.js index d380c2e2c..143c54707 100644 --- a/modules/ppcp-axo/resources/js/AxoManager.js +++ b/modules/ppcp-axo/resources/js/AxoManager.js @@ -4,6 +4,7 @@ import DomElementCollection from "./Components/DomElementCollection"; import ShippingView from "./Views/ShippingView"; import BillingView from "./Views/BillingView"; import CardView from "./Views/CardView"; +import PayPalInsights from "./Insights/PayPalInsights"; class AxoManager { @@ -55,10 +56,44 @@ class AxoManager { console.log(this); return this; } + + if ( + this.axoConfig?.insights?.enabled + && this.axoConfig?.insights?.client_id + && this.axoConfig?.insights?.session_id + ) { + PayPalInsights.config(this.axoConfig?.insights?.client_id, { debug: true }); + PayPalInsights.setSessionId(this.axoConfig?.insights?.session_id); + PayPalInsights.trackJsLoad(); + + if (document.querySelector('.woocommerce-checkout')) { + PayPalInsights.trackBeginCheckout({ + amount: this.axoConfig?.insights?.amount, + page_type: 'checkout', + user_data: { + country: 'US', + is_store_member: false, + } + }); + } + } } registerEventHandlers() { + jQuery(document).on('change', 'input[name=payment_method]', (ev) => { + const map = { + 'ppcp-axo-gateway': 'card', + 'ppcp-gateway': 'paypal', + } + + PayPalInsights.trackSelectPaymentMethod({ + payment_method_selected: map[ev.target.value] || 'other', + page_type: 'checkout' + }); + }); + + // Listen to Gateway Radio button changes. this.el.gatewayRadioButton.on('change', (ev) => { if (ev.target.checked) { @@ -83,7 +118,7 @@ class AxoManager { if (this.status.hasProfile) { const { selectionChanged, selectedAddress } = await this.fastlane.profile.showShippingAddressSelector(); - console.log('selectedAddress', selectedAddress); + //console.log('selectedAddress', selectedAddress); if (selectionChanged) { this.setShipping(selectedAddress); @@ -103,11 +138,11 @@ class AxoManager { this.el.changeCardLink.on('click', async () => { const response = await this.fastlane.profile.showCardSelector(); - console.log('card response', response); + //console.log('card response', response); if (response.selectionChanged) { - console.log('response.selectedCard.paymentToken', response.selectedCard.paymentToken); + //console.log('response.selectedCard.paymentToken', response.selectedCard.paymentToken); this.setCard(response.selectedCard); this.setBilling({ @@ -478,6 +513,15 @@ class AxoManager { this.data.email = this.emailInput.value; this.billingView.setData(this.data); + if (!this.fastlane.identity) { + log('Not initialized.'); + return; + } + + PayPalInsights.trackSubmitCheckoutEmail({ + page_type: 'checkout' + }); + const lookupResponse = await this.fastlane.identity.lookupCustomerByEmail(this.emailInput.value); if (lookupResponse.customerContextId) { @@ -499,7 +543,7 @@ class AxoManager { }); this.setCard(authResponse.profileData.card); - console.log('authResponse', authResponse); + //console.log('authResponse', authResponse); this.setStatus('validEmail', true); this.setStatus('hasProfile', true); @@ -522,7 +566,7 @@ class AxoManager { this.setStatus('validEmail', true); this.setStatus('hasProfile', false); - console.log('this.cardComponentData()', this.cardComponentData()); + //console.log('this.cardComponentData()', this.cardComponentData()); this.cardComponent = await this.fastlane .FastlaneCardComponent( @@ -620,6 +664,16 @@ class AxoManager { this.el.axoNonceInput.get().value = nonce; + PayPalInsights.trackEndCheckout({ + amount: this.axoConfig?.insights?.amount, + page_type: 'checkout', + payment_method_selected: 'card', + user_data: { + country: 'US', + is_store_member: false, + } + }); + this.el.defaultSubmitButton.click(); } diff --git a/modules/ppcp-axo/resources/js/Helper/Debug.js b/modules/ppcp-axo/resources/js/Helper/Debug.js index 560bb5c6a..e473d4a3b 100644 --- a/modules/ppcp-axo/resources/js/Helper/Debug.js +++ b/modules/ppcp-axo/resources/js/Helper/Debug.js @@ -1,4 +1,4 @@ export function log(...args) { - console.log('[AXO] ', ...args); + //console.log('[AXO] ', ...args); } diff --git a/modules/ppcp-axo/resources/js/Insights/PayPalInsights.js b/modules/ppcp-axo/resources/js/Insights/PayPalInsights.js index eb9b24d10..926ca9355 100644 --- a/modules/ppcp-axo/resources/js/Insights/PayPalInsights.js +++ b/modules/ppcp-axo/resources/js/Insights/PayPalInsights.js @@ -11,43 +11,46 @@ class PayPalInsights { /** * @returns {PayPalInsights} */ - static getInstance() { + static init() { if (!PayPalInsights.instance) { PayPalInsights.instance = new PayPalInsights(); } return PayPalInsights.instance; } - track(eventName, data) { + static track(eventName, data) { + PayPalInsights.init(); paypalInsight('event', eventName, data); } static config (clientId, data) { + PayPalInsights.init(); paypalInsight('config', clientId, data); } static setSessionId (sessionId) { + PayPalInsights.init(); paypalInsight('set', { session_id: sessionId }); } static trackJsLoad () { - PayPalInsights.getInstance().track('js_load', { timestamp: Date.now() }); + PayPalInsights.track('js_load', { timestamp: Date.now() }); } static trackBeginCheckout (data) { - PayPalInsights.getInstance().track('begin_checkout', data); + PayPalInsights.track('begin_checkout', data); } static trackSubmitCheckoutEmail (data) { - PayPalInsights.getInstance().track('submit_checkout_email', data); + PayPalInsights.track('submit_checkout_email', data); } static trackSelectPaymentMethod (data) { - PayPalInsights.getInstance().track('select_payment_method', data); + PayPalInsights.track('select_payment_method', data); } static trackEndCheckout (data) { - PayPalInsights.getInstance().track('end_checkout', data); + PayPalInsights.track('end_checkout', data); } } diff --git a/modules/ppcp-axo/resources/js/Views/BillingView.js b/modules/ppcp-axo/resources/js/Views/BillingView.js index 4df27defe..c024dfc38 100644 --- a/modules/ppcp-axo/resources/js/Views/BillingView.js +++ b/modules/ppcp-axo/resources/js/Views/BillingView.js @@ -16,7 +16,7 @@ class BillingView { const selectElement = document.querySelector(selectSelector); if (!selectElement) { - return ${key}; + return key; } const option = selectElement.querySelector(`option[value="${key}"]`); diff --git a/modules/ppcp-axo/resources/js/Views/ShippingView.js b/modules/ppcp-axo/resources/js/Views/ShippingView.js index bbc9a11d2..3b34c735e 100644 --- a/modules/ppcp-axo/resources/js/Views/ShippingView.js +++ b/modules/ppcp-axo/resources/js/Views/ShippingView.js @@ -16,7 +16,7 @@ class ShippingView { const selectElement = document.querySelector(selectSelector); if (!selectElement) { - return ${key}; + return key; } const option = selectElement.querySelector(`option[value="${key}"]`); @@ -88,7 +88,7 @@ class ShippingView { 'valuePath': null, }, phone: { - 'selector': '#billing_phone_field', // There is no shipping phone field. + //'selector': '#billing_phone_field', // There is no shipping phone field. 'valueCallback': function (data) { let phone = ''; const cc = data?.shipping?.phoneNumber?.countryCode; diff --git a/modules/ppcp-axo/src/Assets/AxoManager.php b/modules/ppcp-axo/src/Assets/AxoManager.php index 4a92208c6..973f0b091 100644 --- a/modules/ppcp-axo/src/Assets/AxoManager.php +++ b/modules/ppcp-axo/src/Assets/AxoManager.php @@ -160,6 +160,15 @@ class AxoManager { 'address' => $address_widget ?: 'render', 'payment' => $payment_widget ?: 'render', ), + 'insights' => array( + 'enabled' => true, + 'client_id' => ( $this->settings->has( 'client_id' ) ? $this->settings->get( 'client_id' ) : null ), + 'session_id' => substr( md5( WC()->session->get_customer_unique_id() ), 0, 16 ), + 'amount' => array( + 'currency_code' => get_woocommerce_currency(), + 'value' => WC()->cart->get_total( 'numeric' ) + ) + ) ); } diff --git a/modules/ppcp-googlepay/resources/js/GooglepayButton.js b/modules/ppcp-googlepay/resources/js/GooglepayButton.js index 3331a4d49..00b8f7f7d 100644 --- a/modules/ppcp-googlepay/resources/js/GooglepayButton.js +++ b/modules/ppcp-googlepay/resources/js/GooglepayButton.js @@ -28,7 +28,7 @@ class GooglepayButton { this.log = function() { if ( this.buttonConfig.is_debug ) { - console.log('[GooglePayButton]', ...arguments); + //console.log('[GooglePayButton]', ...arguments); } } } From 832baeaac9e50cc7917d657c43851b3fece9765e Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Thu, 11 Apr 2024 18:07:20 +0100 Subject: [PATCH 47/70] Fix lint. --- modules/ppcp-applepay/extensions.php | 4 +- modules/ppcp-axo/extensions.php | 91 ++++--------------- modules/ppcp-axo/services.php | 8 +- modules/ppcp-axo/src/Assets/AxoManager.php | 17 ++-- modules/ppcp-axo/src/AxoModule.php | 33 +++++-- modules/ppcp-axo/src/Gateway/AxoGateway.php | 3 +- modules/ppcp-axo/src/Helper/ApmApplies.php | 2 +- modules/ppcp-googlepay/extensions.php | 4 +- .../src/Render/OnboardingOptionsRenderer.php | 4 +- .../src/Settings/SettingsRenderer.php | 7 +- 10 files changed, 75 insertions(+), 98 deletions(-) diff --git a/modules/ppcp-applepay/extensions.php b/modules/ppcp-applepay/extensions.php index be21d9530..058c24904 100644 --- a/modules/ppcp-applepay/extensions.php +++ b/modules/ppcp-applepay/extensions.php @@ -129,7 +129,7 @@ return array( ) ), ), - 'classes' => array( 'ppcp-valign-label-middle', 'ppcp-align-label-center' ) + 'classes' => array( 'ppcp-valign-label-middle', 'ppcp-align-label-center' ), ), ) ); @@ -188,7 +188,7 @@ return array( ) ), ), - 'classes' => array( 'ppcp-valign-label-middle', 'ppcp-align-label-center' ) + 'classes' => array( 'ppcp-valign-label-middle', 'ppcp-align-label-center' ), ), 'applepay_button_domain_registration' => array( 'title' => __( 'Domain Registration', 'woocommerce-paypal-payments' ), diff --git a/modules/ppcp-axo/extensions.php b/modules/ppcp-axo/extensions.php index 0a709d572..f600c555f 100644 --- a/modules/ppcp-axo/extensions.php +++ b/modules/ppcp-axo/extensions.php @@ -37,7 +37,7 @@ return array( $fields, 'vault_enabled_dcc', array( - 'axo_heading' => array( + 'axo_heading' => array( 'heading' => __( 'Fastlane', 'woocommerce-paypal-payments' ), 'type' => 'ppcp-heading', 'description' => wp_kses_post( @@ -62,7 +62,7 @@ return array( ), 'gateway' => 'dcc', ), - 'axo_enabled' => array( + 'axo_enabled' => array( 'title' => __( 'Fastlane', 'woocommerce-paypal-payments' ), 'title_html' => sprintf( '%s', @@ -113,9 +113,9 @@ return array( ) ), ), - 'classes' => array( 'ppcp-valign-label-middle', 'ppcp-align-label-center' ) + 'classes' => array( 'ppcp-valign-label-middle', 'ppcp-align-label-center' ), ), - 'axo_gateway_title' => array( + 'axo_gateway_title' => array( 'title' => __( 'Gateway Title', 'woocommerce-paypal-payments' ), 'type' => 'text', 'classes' => array( 'ppcp-field-indent' ), @@ -134,59 +134,7 @@ return array( 'requirements' => array(), 'gateway' => 'dcc', ), - 'axo_email_widget' => array( - 'title' => __( 'Email Widget', 'woocommerce-paypal-payments' ), - 'type' => 'select', - 'desc_tip' => true, - 'description' => __( - 'This controls if the Hosted Email Widget should be used.', - 'woocommerce-paypal-payments' - ), - 'classes' => array( 'ppcp-field-indent' ), - 'class' => array(), - 'input_class' => array( 'wc-enhanced-select' ), - 'default' => 'render', - 'options' => PropertiesDictionary::email_widget_options(), - 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'dcc', - 'requirements' => array(), - ), - //'axo_address_widget' => array( - // 'title' => __( 'Address Widget', 'woocommerce-paypal-payments' ), - // 'type' => 'select', - // 'desc_tip' => true, - // 'description' => __( - // 'This controls if the Hosted Address Widget should be used.', - // 'woocommerce-paypal-payments' - // ), - // 'classes' => array( 'ppcp-field-indent' ), - // 'class' => array(), - // 'input_class' => array( 'wc-enhanced-select' ), - // 'default' => 'render', - // 'options' => PropertiesDictionary::address_widget_options(), - // 'screens' => array( State::STATE_ONBOARDED ), - // 'gateway' => 'dcc', - // 'requirements' => array(), - //), - //'axo_payment_widget' => array( - // 'title' => __( 'Payment Widget', 'woocommerce-paypal-payments' ), - // 'type' => 'select', - // 'desc_tip' => true, - // 'description' => __( - // 'This controls if the Hosted Payment Widget should be used.', - // 'woocommerce-paypal-payments' - // ), - // 'label' => '', - // 'input_class' => array( 'wc-enhanced-select' ), - // 'classes' => array( 'ppcp-field-indent' ), - // 'class' => array(), - // 'default' => 'render', - // 'options' => PropertiesDictionary::payment_widget_options(), - // 'screens' => array( State::STATE_ONBOARDED ), - // 'gateway' => 'dcc', - // 'requirements' => array(), - //), - 'axo_privacy' => array( + 'axo_privacy' => array( 'title' => __( 'Privacy', 'woocommerce-paypal-payments' ), 'type' => 'select', 'desc_tip' => true, @@ -203,7 +151,7 @@ return array( 'gateway' => 'dcc', 'requirements' => array(), ), - 'axo_style_heading' => array( + 'axo_style_heading' => array( 'heading' => __( 'Advanced Style Settings (optional)', 'woocommerce-paypal-payments' ), 'heading_html' => sprintf( // translators: %1$s and %2$s are the opening and closing of HTML tag. @@ -239,7 +187,7 @@ return array( 'gateway' => 'dcc', ), - 'axo_style_root_heading' => array( + 'axo_style_root_heading' => array( 'heading' => __( 'Root Settings', 'woocommerce-paypal-payments' ), 'type' => 'ppcp-heading', 'description' => __( @@ -251,7 +199,7 @@ return array( 'requirements' => array( 'dcc' ), 'gateway' => 'dcc', ), - 'axo_style_root_bg_color' => array( + 'axo_style_root_bg_color' => array( 'title' => __( 'Background Color', 'woocommerce-paypal-payments' ), 'type' => 'text', 'classes' => array( 'ppcp-field-indent' ), @@ -261,7 +209,7 @@ return array( 'requirements' => array(), 'gateway' => 'dcc', ), - 'axo_style_root_error_color' => array( + 'axo_style_root_error_color' => array( 'title' => __( 'Error Color', 'woocommerce-paypal-payments' ), 'type' => 'text', 'classes' => array( 'ppcp-field-indent' ), @@ -271,7 +219,7 @@ return array( 'requirements' => array(), 'gateway' => 'dcc', ), - 'axo_style_root_font_family' => array( + 'axo_style_root_font_family' => array( 'title' => __( 'Font Family', 'woocommerce-paypal-payments' ), 'type' => 'text', 'classes' => array( 'ppcp-field-indent' ), @@ -281,7 +229,7 @@ return array( 'requirements' => array(), 'gateway' => 'dcc', ), - 'axo_style_root_font_size_base' => array( + 'axo_style_root_font_size_base' => array( 'title' => __( 'Font Size Base', 'woocommerce-paypal-payments' ), 'type' => 'text', 'classes' => array( 'ppcp-field-indent' ), @@ -291,7 +239,7 @@ return array( 'requirements' => array(), 'gateway' => 'dcc', ), - 'axo_style_root_padding' => array( + 'axo_style_root_padding' => array( 'title' => __( 'Padding', 'woocommerce-paypal-payments' ), 'type' => 'text', 'classes' => array( 'ppcp-field-indent' ), @@ -301,7 +249,7 @@ return array( 'requirements' => array(), 'gateway' => 'dcc', ), - 'axo_style_root_primary_color' => array( + 'axo_style_root_primary_color' => array( 'title' => __( 'Primary Color', 'woocommerce-paypal-payments' ), 'type' => 'text', 'classes' => array( 'ppcp-field-indent' ), @@ -311,8 +259,7 @@ return array( 'requirements' => array(), 'gateway' => 'dcc', ), - - 'axo_style_input_heading' => array( + 'axo_style_input_heading' => array( 'heading' => __( 'Input Settings', 'woocommerce-paypal-payments' ), 'type' => 'ppcp-heading', 'description' => __( @@ -324,7 +271,7 @@ return array( 'requirements' => array( 'dcc' ), 'gateway' => 'dcc', ), - 'axo_style_input_bg_color' => array( + 'axo_style_input_bg_color' => array( 'title' => __( 'Background Color', 'woocommerce-paypal-payments' ), 'type' => 'text', 'classes' => array( 'ppcp-field-indent' ), @@ -334,7 +281,7 @@ return array( 'requirements' => array(), 'gateway' => 'dcc', ), - 'axo_style_input_border_radius' => array( + 'axo_style_input_border_radius' => array( 'title' => __( 'Border Radius', 'woocommerce-paypal-payments' ), 'type' => 'text', 'classes' => array( 'ppcp-field-indent' ), @@ -344,7 +291,7 @@ return array( 'requirements' => array(), 'gateway' => 'dcc', ), - 'axo_style_input_border_color' => array( + 'axo_style_input_border_color' => array( 'title' => __( 'Border Color', 'woocommerce-paypal-payments' ), 'type' => 'text', 'classes' => array( 'ppcp-field-indent' ), @@ -354,7 +301,7 @@ return array( 'requirements' => array(), 'gateway' => 'dcc', ), - 'axo_style_input_border_width' => array( + 'axo_style_input_border_width' => array( 'title' => __( 'Border Width', 'woocommerce-paypal-payments' ), 'type' => 'text', 'classes' => array( 'ppcp-field-indent' ), @@ -364,7 +311,7 @@ return array( 'requirements' => array(), 'gateway' => 'dcc', ), - 'axo_style_input_text_color_base' => array( + 'axo_style_input_text_color_base' => array( 'title' => __( 'Text Color Base', 'woocommerce-paypal-payments' ), 'type' => 'text', 'classes' => array( 'ppcp-field-indent' ), diff --git a/modules/ppcp-axo/services.php b/modules/ppcp-axo/services.php index 54e1a8427..2a35321c1 100644 --- a/modules/ppcp-axo/services.php +++ b/modules/ppcp-axo/services.php @@ -37,7 +37,7 @@ return array( return true; }, - 'axo.url' => static function ( ContainerInterface $container ): string { + 'axo.url' => static function ( ContainerInterface $container ): string { $path = realpath( __FILE__ ); if ( false === $path ) { return ''; @@ -48,7 +48,7 @@ return array( ); }, - 'axo.manager' => static function ( ContainerInterface $container ): AxoManager { + 'axo.manager' => static function ( ContainerInterface $container ): AxoManager { return new AxoManager( $container->get( 'axo.url' ), $container->get( 'ppcp.asset-version' ), @@ -61,7 +61,7 @@ return array( ); }, - 'axo.gateway' => static function ( ContainerInterface $container ): AxoGateway { + 'axo.gateway' => static function ( ContainerInterface $container ): AxoGateway { return new AxoGateway( $container->get( 'wcgateway.settings' ), $container->get( 'wcgateway.url' ), @@ -76,7 +76,7 @@ return array( ); }, - 'axo.card_icons' => static function ( ContainerInterface $container ): array { + 'axo.card_icons' => static function ( ContainerInterface $container ): array { return array( array( 'title' => 'Visa', diff --git a/modules/ppcp-axo/src/Assets/AxoManager.php b/modules/ppcp-axo/src/Assets/AxoManager.php index 973f0b091..0c3eacbea 100644 --- a/modules/ppcp-axo/src/Assets/AxoManager.php +++ b/modules/ppcp-axo/src/Assets/AxoManager.php @@ -155,7 +155,7 @@ class AxoManager { $payment_widget = $this->settings->has( 'axo_payment_widget' ) ? $this->settings->get( 'axo_payment_widget' ) : null; return array( - 'widgets' => array( + 'widgets' => array( 'email' => $email_widget ?: 'render', 'address' => $address_widget ?: 'render', 'payment' => $payment_widget ?: 'render', @@ -163,12 +163,17 @@ class AxoManager { 'insights' => array( 'enabled' => true, 'client_id' => ( $this->settings->has( 'client_id' ) ? $this->settings->get( 'client_id' ) : null ), - 'session_id' => substr( md5( WC()->session->get_customer_unique_id() ), 0, 16 ), + 'session_id' => + substr( + method_exists( WC()->session, 'get_customer_unique_id' ) ? md5( WC()->session->get_customer_unique_id() ) : '', + 0, + 16 + ), 'amount' => array( 'currency_code' => get_woocommerce_currency(), - 'value' => WC()->cart->get_total( 'numeric' ) - ) - ) + 'value' => WC()->cart->get_total( 'numeric' ), + ), + ), ); } @@ -187,7 +192,7 @@ class AxoManager { /** * Renders the HTML for the AXO submit button. */ - public function render_checkout_button() { + public function render_checkout_button(): void { $id = 'ppcp-axo-submit-button-container'; /** diff --git a/modules/ppcp-axo/src/AxoModule.php b/modules/ppcp-axo/src/AxoModule.php index dc5a94e13..c59424c6b 100644 --- a/modules/ppcp-axo/src/AxoModule.php +++ b/modules/ppcp-axo/src/AxoModule.php @@ -37,7 +37,16 @@ class AxoModule implements ModuleInterface { public function run( ContainerInterface $c ): void { add_filter( 'woocommerce_payment_gateways', + /** + * Param types removed to avoid third-party issues. + * + * @psalm-suppress MissingClosureParamType + */ function ( $methods ) use ( $c ): array { + if ( ! is_array( $methods ) ) { + return $methods; + } + $gateway = $c->get( 'axo.gateway' ); // Check if the module is applicable, correct country, currency, ... etc. @@ -64,8 +73,17 @@ class AxoModule implements ModuleInterface { add_filter( 'ppcp_onboarding_dcc_table_rows', + /** + * Param types removed to avoid third-party issues. + * + * @psalm-suppress MissingClosureParamType + */ function ( $rows, $renderer ): array { - if ( $renderer instanceof OnboardingOptionsRenderer ) { + if ( ! is_array( $rows ) ) { + return $rows; + } + + if ( $renderer instanceof OnboardingOptionsRenderer ) { $rows[] = $renderer->render_table_row( __( 'Fastlane by PayPal', 'woocommerce-paypal-payments' ), __( 'Yes', 'woocommerce-paypal-payments' ), @@ -124,12 +142,13 @@ class AxoModule implements ModuleInterface { } ); - add_action('wp_head', function () { - echo ' - - - '; - }); + add_action( + 'wp_head', + function () { + // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript + echo ''; + } + ); }, 1 diff --git a/modules/ppcp-axo/src/Gateway/AxoGateway.php b/modules/ppcp-axo/src/Gateway/AxoGateway.php index 6ef73a28d..870454d1f 100644 --- a/modules/ppcp-axo/src/Gateway/AxoGateway.php +++ b/modules/ppcp-axo/src/Gateway/AxoGateway.php @@ -193,7 +193,7 @@ class AxoGateway extends WC_Payment_Gateway { * @return array */ public function process_payment( $order_id ) { - $wc_order = wc_get_order( $order_id ); + $wc_order = wc_get_order( $order_id ); $payment_method_title = __( 'Credit Card (via Fastlane by PayPal)', 'woocommerce-paypal-payments' ); @@ -202,6 +202,7 @@ class AxoGateway extends WC_Payment_Gateway { $purchase_unit = $this->purchase_unit_factory->from_wc_order( $wc_order ); + // phpcs:ignore WordPress.Security.NonceVerification.Missing $nonce = wc_clean( wp_unslash( $_POST['axo_nonce'] ?? '' ) ); try { diff --git a/modules/ppcp-axo/src/Helper/ApmApplies.php b/modules/ppcp-axo/src/Helper/ApmApplies.php index f30b99c27..c5fe645a9 100644 --- a/modules/ppcp-axo/src/Helper/ApmApplies.php +++ b/modules/ppcp-axo/src/Helper/ApmApplies.php @@ -71,7 +71,7 @@ class ApmApplies { * @return bool */ public function for_settings(): bool { - if ( get_option('woocommerce_ship_to_destination') === 'billing_only' ) { // Force shipping to the customer billing address + if ( get_option( 'woocommerce_ship_to_destination' ) === 'billing_only' ) { // Force shipping to the customer billing address. return false; } return true; diff --git a/modules/ppcp-googlepay/extensions.php b/modules/ppcp-googlepay/extensions.php index a40ba6795..31b65e6f1 100644 --- a/modules/ppcp-googlepay/extensions.php +++ b/modules/ppcp-googlepay/extensions.php @@ -100,7 +100,7 @@ return array( ) ), ), - 'classes' => array( 'ppcp-valign-label-middle', 'ppcp-align-label-center' ) + 'classes' => array( 'ppcp-valign-label-middle', 'ppcp-align-label-center' ), ), ) ); @@ -147,7 +147,7 @@ return array( ) ), ), - 'classes' => array( 'ppcp-valign-label-middle', 'ppcp-align-label-center' ) + 'classes' => array( 'ppcp-valign-label-middle', 'ppcp-align-label-center' ), ), 'googlepay_button_type' => array( 'title' => __( 'Button Label', 'woocommerce-paypal-payments' ), diff --git a/modules/ppcp-onboarding/src/Render/OnboardingOptionsRenderer.php b/modules/ppcp-onboarding/src/Render/OnboardingOptionsRenderer.php index 470de9803..df99653f8 100644 --- a/modules/ppcp-onboarding/src/Render/OnboardingOptionsRenderer.php +++ b/modules/ppcp-onboarding/src/Render/OnboardingOptionsRenderer.php @@ -139,7 +139,7 @@ class OnboardingOptionsRenderer { $basic_table_rows = apply_filters( 'ppcp_onboarding_basic_table_rows', $basic_table_rows ); - $items[] = ' + $items[] = '