From 5082a0d17b3ecf3311538eb5240efdf666922d10 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 7 Jun 2023 12:50:18 +0200 Subject: [PATCH 01/56] Do not render PayPal subscription button if no plan is connected --- .../ContextBootstrap/SingleProductBootstap.js | 3 +- modules/ppcp-button/services.php | 1 - .../ppcp-button/src/Assets/SmartButton.php | 34 +-------- modules/ppcp-subscription/services.php | 2 +- .../src/Helper/SubscriptionHelper.php | 69 +++++++++++++++++++ modules/ppcp-wc-gateway/services.php | 3 +- .../src/Checkout/DisableGateways.php | 23 +++++-- 7 files changed, 94 insertions(+), 41 deletions(-) diff --git a/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js b/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js index 376962772..2237eb2b1 100644 --- a/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js +++ b/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js @@ -59,7 +59,8 @@ class SingleProductBootstap { shouldRender() { return document.querySelector('form.cart') !== null && !this.priceAmountIsZero() - && !this.isSubscriptionMode(); + && !this.isSubscriptionMode() + && PayPalCommerceGateway.subscription_plan_id !== ''; } priceAmount() { diff --git a/modules/ppcp-button/services.php b/modules/ppcp-button/services.php index 7f8c50b14..9296f1fd0 100644 --- a/modules/ppcp-button/services.php +++ b/modules/ppcp-button/services.php @@ -15,7 +15,6 @@ use WooCommerce\PayPalCommerce\Button\Helper\CheckoutFormSaver; use WooCommerce\PayPalCommerce\Button\Endpoint\SaveCheckoutFormEndpoint; use WooCommerce\PayPalCommerce\Button\Validation\CheckoutFormValidator; use WooCommerce\PayPalCommerce\Button\Endpoint\ValidateCheckoutEndpoint; -use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper; use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; use WooCommerce\PayPalCommerce\Button\Assets\DisabledSmartButton; use WooCommerce\PayPalCommerce\Button\Assets\SmartButton; diff --git a/modules/ppcp-button/src/Assets/SmartButton.php b/modules/ppcp-button/src/Assets/SmartButton.php index 7f07c332a..ff2472df2 100644 --- a/modules/ppcp-button/src/Assets/SmartButton.php +++ b/modules/ppcp-button/src/Assets/SmartButton.php @@ -850,7 +850,7 @@ class SmartButton implements SmartButtonInterface { 'endpoint' => \WC_AJAX::get_endpoint( CartScriptParamsEndpoint::ENDPOINT ), ), ), - 'subscription_plan_id' => $this->paypal_subscription_id(), + 'subscription_plan_id' => $this->subscription_helper->paypal_subscription_id(), 'enforce_vault' => $this->has_subscriptions(), 'can_save_vault_token' => $this->can_save_vault_token(), 'is_free_trial_cart' => $is_free_trial_cart, @@ -1383,38 +1383,6 @@ class SmartButton implements SmartButtonInterface { return false; } - /** - * Returns PayPal subscription plan id from WC subscription product. - * - * @return string - */ - private function paypal_subscription_id(): string { - if ( $this->subscription_helper->current_product_is_subscription() ) { - $product = wc_get_product(); - assert( $product instanceof WC_Product ); - - if ( $product->get_type() === 'subscription' && $product->meta_exists( 'ppcp_subscription_plan' ) ) { - return $product->get_meta( 'ppcp_subscription_plan' )['id']; - } - } - - $cart = WC()->cart ?? null; - if ( ! $cart || $cart->is_empty() ) { - return ''; - } - $items = $cart->get_cart_contents(); - foreach ( $items as $item ) { - $product = wc_get_product( $item['product_id'] ); - assert( $product instanceof WC_Product ); - - if ( $product->get_type() === 'subscription' && $product->meta_exists( 'ppcp_subscription_plan' ) ) { - return $product->get_meta( 'ppcp_subscription_plan' )['id']; - } - } - - return ''; - } - /** * Returns the intent. * diff --git a/modules/ppcp-subscription/services.php b/modules/ppcp-subscription/services.php index b9ce27543..1424725b2 100644 --- a/modules/ppcp-subscription/services.php +++ b/modules/ppcp-subscription/services.php @@ -15,7 +15,7 @@ use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenRepository; return array( 'subscription.helper' => static function ( ContainerInterface $container ): SubscriptionHelper { - return new SubscriptionHelper(); + return new SubscriptionHelper( $container->get( 'wcgateway.settings' ) ); }, 'subscription.renewal-handler' => static function ( ContainerInterface $container ): RenewalHandler { $logger = $container->get( 'woocommerce.logger.woocommerce' ); diff --git a/modules/ppcp-subscription/src/Helper/SubscriptionHelper.php b/modules/ppcp-subscription/src/Helper/SubscriptionHelper.php index 286fc2bc1..1e9a009b4 100644 --- a/modules/ppcp-subscription/src/Helper/SubscriptionHelper.php +++ b/modules/ppcp-subscription/src/Helper/SubscriptionHelper.php @@ -13,12 +13,30 @@ namespace WooCommerce\PayPalCommerce\Subscription\Helper; use WC_Product; use WC_Subscriptions_Product; +use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; +use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException; /** * Class SubscriptionHelper */ class SubscriptionHelper { + /** + * The settings. + * + * @var Settings + */ + private $settings; + + /** + * SubscriptionHelper constructor. + * + * @param Settings $settings The settings. + */ + public function __construct( Settings $settings ) { + $this->settings = $settings; + } + /** * Whether the current product is a subscription. * @@ -150,4 +168,55 @@ class SubscriptionHelper { return false; } + + /** + * Checks if subscription product is allowed. + * + * @return bool + * @throws NotFoundException If setting is not found. + */ + public function checkout_subscription_product_allowed(): bool { + $subscription_mode = $this->settings->has( 'subscriptions_mode' ) ? $this->settings->get( 'subscriptions_mode' ) : ''; + if ( + $subscription_mode !== 'subscriptions_api' + || ! $this->cart_contains_subscription() + || ! $this->paypal_subscription_id() + ) { + return false; + } + + return true; + } + + /** + * Returns PayPal subscription plan id from WC subscription product. + * + * @return string + */ + public function paypal_subscription_id(): string { + if ( $this->current_product_is_subscription() ) { + $product = wc_get_product(); + assert( $product instanceof WC_Product ); + + if ( $product->get_type() === 'subscription' && $product->meta_exists( 'ppcp_subscription_plan' ) ) { + return $product->get_meta( 'ppcp_subscription_plan' )['id']; + } + } + + $cart = WC()->cart ?? null; + if ( ! $cart || $cart->is_empty() ) { + return ''; + } + $items = $cart->get_cart_contents(); + foreach ( $items as $item ) { + $product = wc_get_product( $item['product_id'] ); + assert( $product instanceof WC_Product ); + + if ( $product->get_type() === 'subscription' && $product->meta_exists( 'ppcp_subscription_plan' ) ) { + return $product->get_meta( 'ppcp_subscription_plan' )['id']; + } + } + + return ''; + } } diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index 901c1f94a..9025bf9e1 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -148,7 +148,8 @@ return array( $session_handler = $container->get( 'session.handler' ); $settings = $container->get( 'wcgateway.settings' ); $settings_status = $container->get( 'wcgateway.settings.status' ); - return new DisableGateways( $session_handler, $settings, $settings_status ); + $subscription_helper = $container->get( 'subscription.helper' ); + return new DisableGateways( $session_handler, $settings, $settings_status, $subscription_helper ); }, 'wcgateway.is-wc-payments-page' => static function ( ContainerInterface $container ): bool { diff --git a/modules/ppcp-wc-gateway/src/Checkout/DisableGateways.php b/modules/ppcp-wc-gateway/src/Checkout/DisableGateways.php index bbd9b30d3..6342727a5 100644 --- a/modules/ppcp-wc-gateway/src/Checkout/DisableGateways.php +++ b/modules/ppcp-wc-gateway/src/Checkout/DisableGateways.php @@ -10,6 +10,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\WcGateway\Checkout; use WooCommerce\PayPalCommerce\Session\SessionHandler; +use WooCommerce\PayPalCommerce\Subscription\Helper\SubscriptionHelper; use WooCommerce\PayPalCommerce\WcGateway\Gateway\CardButtonGateway; use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway; use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway; @@ -42,22 +43,32 @@ class DisableGateways { */ protected $settings_status; + /** + * The subscription helper. + * + * @var SubscriptionHelper + */ + private $subscription_helper; + /** * DisableGateways constructor. * * @param SessionHandler $session_handler The Session Handler. * @param ContainerInterface $settings The Settings. * @param SettingsStatus $settings_status The Settings status helper. + * @param SubscriptionHelper $subscription_helper The subscription helper. */ public function __construct( SessionHandler $session_handler, ContainerInterface $settings, - SettingsStatus $settings_status + SettingsStatus $settings_status, + SubscriptionHelper $subscription_helper ) { - $this->session_handler = $session_handler; - $this->settings = $settings; - $this->settings_status = $settings_status; + $this->session_handler = $session_handler; + $this->settings = $settings; + $this->settings_status = $settings_status; + $this->subscription_helper = $subscription_helper; } /** @@ -113,6 +124,10 @@ class DisableGateways { return true; } + if ( is_checkout() && ! $this->subscription_helper->checkout_subscription_product_allowed() ) { + return true; + } + return false; } From 21fd894d7aa01c333a762b20b33590eb85fa0cfd Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 7 Jun 2023 14:43:32 +0200 Subject: [PATCH 02/56] Check if subscription product in cart before removing PayPal button --- modules/ppcp-subscription/src/Helper/SubscriptionHelper.php | 1 - modules/ppcp-wc-gateway/src/Checkout/DisableGateways.php | 6 +++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-subscription/src/Helper/SubscriptionHelper.php b/modules/ppcp-subscription/src/Helper/SubscriptionHelper.php index 1e9a009b4..8b3034acd 100644 --- a/modules/ppcp-subscription/src/Helper/SubscriptionHelper.php +++ b/modules/ppcp-subscription/src/Helper/SubscriptionHelper.php @@ -179,7 +179,6 @@ class SubscriptionHelper { $subscription_mode = $this->settings->has( 'subscriptions_mode' ) ? $this->settings->get( 'subscriptions_mode' ) : ''; if ( $subscription_mode !== 'subscriptions_api' - || ! $this->cart_contains_subscription() || ! $this->paypal_subscription_id() ) { return false; diff --git a/modules/ppcp-wc-gateway/src/Checkout/DisableGateways.php b/modules/ppcp-wc-gateway/src/Checkout/DisableGateways.php index 6342727a5..e5078ae3f 100644 --- a/modules/ppcp-wc-gateway/src/Checkout/DisableGateways.php +++ b/modules/ppcp-wc-gateway/src/Checkout/DisableGateways.php @@ -124,7 +124,11 @@ class DisableGateways { return true; } - if ( is_checkout() && ! $this->subscription_helper->checkout_subscription_product_allowed() ) { + if ( + is_checkout() + && $this->subscription_helper->cart_contains_subscription() + && ! $this->subscription_helper->checkout_subscription_product_allowed() + ) { return true; } From c570621b67236e745df1e0c58da4091962c91de6 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Thu, 8 Jun 2023 12:39:08 +0200 Subject: [PATCH 03/56] Add script for disabling subscription product form fields --- .../resources/js/order-edit-page.js | 2 +- modules/ppcp-subscription/.babelrc | 11 + modules/ppcp-subscription/.gitignore | 2 + modules/ppcp-subscription/package.json | 31 + .../resources/js/paypal-subscription.js | 20 + modules/ppcp-subscription/services.php | 11 + .../src/SubscriptionModule.php | 39 + modules/ppcp-subscription/webpack.config.js | 35 + modules/ppcp-subscription/yarn.lock | 2185 +++++++++++++++++ package.json | 3 + 10 files changed, 2338 insertions(+), 1 deletion(-) create mode 100644 modules/ppcp-subscription/.babelrc create mode 100644 modules/ppcp-subscription/.gitignore create mode 100644 modules/ppcp-subscription/package.json create mode 100644 modules/ppcp-subscription/resources/js/paypal-subscription.js create mode 100644 modules/ppcp-subscription/webpack.config.js create mode 100644 modules/ppcp-subscription/yarn.lock diff --git a/modules/ppcp-order-tracking/resources/js/order-edit-page.js b/modules/ppcp-order-tracking/resources/js/order-edit-page.js index a49717d8c..745ca529d 100644 --- a/modules/ppcp-order-tracking/resources/js/order-edit-page.js +++ b/modules/ppcp-order-tracking/resources/js/order-edit-page.js @@ -3,7 +3,7 @@ document.addEventListener( () => { const config = PayPalCommerceGatewayOrderTrackingInfo; if (!typeof (PayPalCommerceGatewayOrderTrackingInfo)) { - console.error('trackign cannot be set.'); + console.error('tracking cannot be set.'); return; } diff --git a/modules/ppcp-subscription/.babelrc b/modules/ppcp-subscription/.babelrc new file mode 100644 index 000000000..9e972c8b9 --- /dev/null +++ b/modules/ppcp-subscription/.babelrc @@ -0,0 +1,11 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "useBuiltIns": "usage", + "corejs": "3.25.0" + } + ] + ] +} diff --git a/modules/ppcp-subscription/.gitignore b/modules/ppcp-subscription/.gitignore new file mode 100644 index 000000000..265c2208c --- /dev/null +++ b/modules/ppcp-subscription/.gitignore @@ -0,0 +1,2 @@ +node_modules +assets diff --git a/modules/ppcp-subscription/package.json b/modules/ppcp-subscription/package.json new file mode 100644 index 000000000..9fd2f9305 --- /dev/null +++ b/modules/ppcp-subscription/package.json @@ -0,0 +1,31 @@ +{ + "name": "ppcp-subscription", + "version": "1.0.0", + "license": "GPL-3.0-or-later", + "browserslist": [ + "> 0.5%", + "Safari >= 8", + "Chrome >= 41", + "Firefox >= 43", + "Edge >= 14" + ], + "dependencies": { + "core-js": "^3.25.0" + }, + "devDependencies": { + "@babel/core": "^7.19", + "@babel/preset-env": "^7.19", + "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-subscription/resources/js/paypal-subscription.js b/modules/ppcp-subscription/resources/js/paypal-subscription.js new file mode 100644 index 000000000..3e0ee6a70 --- /dev/null +++ b/modules/ppcp-subscription/resources/js/paypal-subscription.js @@ -0,0 +1,20 @@ +document.addEventListener( + 'DOMContentLoaded', + () => { + if(PayPalCommerceGatewayPayPalSubscription.product_connected === 'yes') { + const periodInterval = document.querySelector('#_subscription_period_interval'); + periodInterval.setAttribute('disabled', 'disabled'); + + const subscriptionPeriod = document.querySelector('#_subscription_period'); + subscriptionPeriod.setAttribute('disabled', 'disabled'); + + const subscriptionLength = document.querySelector('#_subscription_length'); + subscriptionLength.setAttribute('disabled', 'disabled'); + + const subscriptionTrialLength = document.querySelector('#_subscription_trial_length'); + subscriptionTrialLength.setAttribute('disabled', 'disabled'); + + const subscriptionTrialPeriod = document.querySelector('#_subscription_trial_period'); + subscriptionTrialPeriod.setAttribute('disabled', 'disabled'); + } + }); diff --git a/modules/ppcp-subscription/services.php b/modules/ppcp-subscription/services.php index 1424725b2..d3bb5e130 100644 --- a/modules/ppcp-subscription/services.php +++ b/modules/ppcp-subscription/services.php @@ -54,4 +54,15 @@ return array( $container->get( 'woocommerce.logger.woocommerce' ) ); }, + 'subscription.module.url' => static function ( ContainerInterface $container ): string { + /** + * The path cannot be false. + * + * @psalm-suppress PossiblyFalseArgument + */ + return plugins_url( + '/modules/ppcp-subscription/', + dirname( realpath( __FILE__ ), 3 ) . '/woocommerce-paypal-payments.php' + ); + }, ); diff --git a/modules/ppcp-subscription/src/SubscriptionModule.php b/modules/ppcp-subscription/src/SubscriptionModule.php index 099ee18ff..00ae5b516 100644 --- a/modules/ppcp-subscription/src/SubscriptionModule.php +++ b/modules/ppcp-subscription/src/SubscriptionModule.php @@ -155,6 +155,45 @@ class SubscriptionModule implements ModuleInterface { if ( defined( 'PPCP_FLAG_SUBSCRIPTIONS_API' ) && PPCP_FLAG_SUBSCRIPTIONS_API ) { $this->subscriptions_api_integration( $c ); } + + add_action( + 'admin_enqueue_scripts', + function( $hook ) use ( $c ) { + $settings = $c->get( 'wcgateway.settings' ); + $subscription_mode = $settings->has( 'subscriptions_mode' ) ? $settings->get( 'subscriptions_mode' ) : ''; + if ( $hook !== 'post.php' || $subscription_mode !== 'subscriptions_api' ) { + return; + } + + $post_id = wc_clean( wp_unslash( $_GET['post'] ?? '' ) ); + $product = wc_get_product( $post_id ); + if ( ! is_a( $product, WC_Product::class ) ) { + return; + } + + $subscription_product_connected = $product->get_meta( '_ppcp_enable_subscription_product' ); + if ( $subscription_product_connected !== 'yes' ) { + return; + } + + $module_url = $c->get( 'subscription.module.url' ); + wp_enqueue_script( + 'ppcp-paypal-subscription', + untrailingslashit( $module_url ) . '/assets/js/paypal-subscription.js', + array( 'jquery' ), + $c->get( 'ppcp.asset-version' ), + true + ); + + wp_localize_script( + 'ppcp-paypal-subscription', + 'PayPalCommerceGatewayPayPalSubscription', + array( + 'product_connected' => $subscription_product_connected, + ) + ); + } + ); } /** diff --git a/modules/ppcp-subscription/webpack.config.js b/modules/ppcp-subscription/webpack.config.js new file mode 100644 index 000000000..4a5f4a0fc --- /dev/null +++ b/modules/ppcp-subscription/webpack.config.js @@ -0,0 +1,35 @@ +const path = require('path'); +const isProduction = process.env.NODE_ENV === 'production'; + +module.exports = { + devtool: isProduction ? 'source-map' : 'eval-source-map', + mode: isProduction ? 'production' : 'development', + target: 'web', + entry: { + 'paypal-subscription': path.resolve('./resources/js/paypal-subscription.js'), + }, + output: { + path: path.resolve(__dirname, 'assets/'), + filename: 'js/[name].js', + }, + module: { + rules: [{ + test: /\.js?$/, + exclude: /node_modules/, + loader: 'babel-loader', + }, + { + test: /\.scss$/, + exclude: /node_modules/, + use: [ + { + loader: 'file-loader', + options: { + name: 'css/[name].css', + } + }, + {loader:'sass-loader'} + ] + }] + } +}; diff --git a/modules/ppcp-subscription/yarn.lock b/modules/ppcp-subscription/yarn.lock new file mode 100644 index 000000000..08b8761f0 --- /dev/null +++ b/modules/ppcp-subscription/yarn.lock @@ -0,0 +1,2185 @@ +# 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.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.21.4.tgz#d0fa9e4413aca81f2b23b9442797bda1826edb39" + integrity sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g== + dependencies: + "@babel/highlight" "^7.18.6" + +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.22.0", "@babel/compat-data@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.3.tgz#cd502a6a0b6e37d7ad72ce7e71a7160a3ae36f7e" + integrity sha512-aNtko9OPOwVESUFp3MZfD8Uzxl7JzSeJpd7npIoxCasU37PFbAQRpKglkaKwlHOyeJdrREpo8TW8ldrkYWwvIQ== + +"@babel/core@^7.19": + version "7.22.1" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.1.tgz#5de51c5206f4c6f5533562838337a603c1033cfd" + integrity sha512-Hkqu7J4ynysSXxmAahpN1jjRwVJ+NdpraFLIWflgjpVob3KNyK3/tIUc7Q7szed8WMp0JNa7Qtd1E9Oo22F9gA== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.22.0" + "@babel/helper-compilation-targets" "^7.22.1" + "@babel/helper-module-transforms" "^7.22.1" + "@babel/helpers" "^7.22.0" + "@babel/parser" "^7.22.0" + "@babel/template" "^7.21.9" + "@babel/traverse" "^7.22.1" + "@babel/types" "^7.22.0" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.2" + semver "^6.3.0" + +"@babel/generator@^7.22.0", "@babel/generator@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.3.tgz#0ff675d2edb93d7596c5f6728b52615cfc0df01e" + integrity sha512-C17MW4wlk//ES/CJDL51kPNwl+qiBQyN7b9SKyVp11BLGFeSPoVaHrv+MNt8jwQFhQWowW88z1eeBx3pFz9v8A== + dependencies: + "@babel/types" "^7.22.3" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + +"@babel/helper-annotate-as-pure@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" + integrity sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-builder-binary-assignment-operator-visitor@^7.18.6": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.3.tgz#c9b83d1ba74e163e023f008a3d3204588a7ceb60" + integrity sha512-ahEoxgqNoYXm0k22TvOke48i1PkavGu0qGCmcq9ugi6gnmvKNaMjKBSrZTnWUi1CFEeNAUiVba0Wtzm03aSkJg== + dependencies: + "@babel/types" "^7.22.3" + +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.22.1": + version "7.22.1" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.1.tgz#bfcd6b7321ffebe33290d68550e2c9d7eb7c7a58" + integrity sha512-Rqx13UM3yVB5q0D/KwQ8+SPfX/+Rnsy1Lw1k/UwOC4KC6qrzIQoY3lYnBu5EHKBlEHHcj0M0W8ltPSkD8rqfsQ== + dependencies: + "@babel/compat-data" "^7.22.0" + "@babel/helper-validator-option" "^7.21.0" + browserslist "^4.21.3" + lru-cache "^5.1.1" + semver "^6.3.0" + +"@babel/helper-create-class-features-plugin@^7.21.0", "@babel/helper-create-class-features-plugin@^7.22.1": + version "7.22.1" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.1.tgz#ae3de70586cc757082ae3eba57240d42f468c41b" + integrity sha512-SowrZ9BWzYFgzUMwUmowbPSGu6CXL5MSuuCkG3bejahSpSymioPmuLdhPxNOc9MjuNGjy7M/HaXvJ8G82Lywlw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.22.1" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-member-expression-to-functions" "^7.22.0" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-replace-supers" "^7.22.1" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/helper-split-export-declaration" "^7.18.6" + semver "^6.3.0" + +"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.22.1": + version "7.22.1" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.1.tgz#a7ed9a8488b45b467fca353cd1a44dc5f0cf5c70" + integrity sha512-WWjdnfR3LPIe+0EY8td7WmjhytxXtjKAEpnAxun/hkNiyOaPlvGK+NZaBFIdi9ndYV3Gav7BpFvtUwnaJlwi1w== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + regexpu-core "^5.3.1" + semver "^6.3.0" + +"@babel/helper-define-polyfill-provider@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.0.tgz#487053f103110f25b9755c5980e031e93ced24d8" + integrity sha512-RnanLx5ETe6aybRi1cO/edaRH+bNYWaryCEmjDDYyNr4wnSzyOp8T0dWipmqVHKEY3AbVKUom50AKSlj1zmKbg== + dependencies: + "@babel/helper-compilation-targets" "^7.17.7" + "@babel/helper-plugin-utils" "^7.16.7" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + semver "^6.1.2" + +"@babel/helper-environment-visitor@^7.18.9", "@babel/helper-environment-visitor@^7.22.1": + version "7.22.1" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.1.tgz#ac3a56dbada59ed969d712cf527bd8271fe3eba8" + integrity sha512-Z2tgopurB/kTbidvzeBrc2To3PUP/9i5MUe+fU6QJCQDyPwSH2oRapkLw3KGECDYSjhQZCNxEvNvZlLw8JjGwA== + +"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0", "@babel/helper-function-name@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz#d552829b10ea9f120969304023cd0645fa00b1b4" + integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg== + dependencies: + "@babel/template" "^7.20.7" + "@babel/types" "^7.21.0" + +"@babel/helper-hoist-variables@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" + integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-member-expression-to-functions@^7.22.0": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.3.tgz#4b77a12c1b4b8e9e28736ed47d8b91f00976911f" + integrity sha512-Gl7sK04b/2WOb6OPVeNy9eFKeD3L6++CzL3ykPOWqTn08xgYYK0wz4TUh2feIImDXxcVW3/9WQ1NMKY66/jfZA== + dependencies: + "@babel/types" "^7.22.3" + +"@babel/helper-module-imports@^7.18.6", "@babel/helper-module-imports@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz#ac88b2f76093637489e718a90cec6cf8a9b029af" + integrity sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg== + dependencies: + "@babel/types" "^7.21.4" + +"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.21.5", "@babel/helper-module-transforms@^7.22.1": + version "7.22.1" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.1.tgz#e0cad47fedcf3cae83c11021696376e2d5a50c63" + integrity sha512-dxAe9E7ySDGbQdCVOY/4+UcD8M9ZFqZcZhSPsPacvCG4M+9lwtDDQfI2EoaSvmf7W/8yCBkGU0m7Pvt1ru3UZw== + dependencies: + "@babel/helper-environment-visitor" "^7.22.1" + "@babel/helper-module-imports" "^7.21.4" + "@babel/helper-simple-access" "^7.21.5" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.19.1" + "@babel/template" "^7.21.9" + "@babel/traverse" "^7.22.1" + "@babel/types" "^7.22.0" + +"@babel/helper-optimise-call-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe" + integrity sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA== + dependencies: + "@babel/types" "^7.18.6" + +"@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.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.21.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.21.5.tgz#345f2377d05a720a4e5ecfa39cbf4474a4daed56" + integrity sha512-0WDaIlXKOX/3KfBK/dwP1oQGiPh6rjMkT7HIRv7i5RR2VUMwrx5ZL0dwBkKx7+SW1zwNdgjHd34IMk5ZjTeHVg== + +"@babel/helper-remap-async-to-generator@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz#997458a0e3357080e54e1d79ec347f8a8cd28519" + integrity sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-wrap-function" "^7.18.9" + "@babel/types" "^7.18.9" + +"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.20.7", "@babel/helper-replace-supers@^7.22.1": + version "7.22.1" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.1.tgz#38cf6e56f7dc614af63a21b45565dd623f0fdc95" + integrity sha512-ut4qrkE4AuSfrwHSps51ekR1ZY/ygrP1tp0WFm8oVq6nzc/hvfV/22JylndIbsf2U2M9LOMwiSddr6y+78j+OQ== + dependencies: + "@babel/helper-environment-visitor" "^7.22.1" + "@babel/helper-member-expression-to-functions" "^7.22.0" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/template" "^7.21.9" + "@babel/traverse" "^7.22.1" + "@babel/types" "^7.22.0" + +"@babel/helper-simple-access@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.21.5.tgz#d697a7971a5c39eac32c7e63c0921c06c8a249ee" + integrity sha512-ENPDAMC1wAjR0uaCUwliBdiSl1KBJAVnMTzXqi64c2MG8MPR6ii4qf7bSXDqSFbr4W6W028/rf5ivoHop5/mkg== + dependencies: + "@babel/types" "^7.21.5" + +"@babel/helper-skip-transparent-expression-wrappers@^7.20.0": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz#fbe4c52f60518cab8140d77101f0e63a8a230684" + integrity sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg== + dependencies: + "@babel/types" "^7.20.0" + +"@babel/helper-split-export-declaration@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" + integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-string-parser@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz#2b3eea65443c6bdc31c22d037c65f6d323b6b2bd" + integrity sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w== + +"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": + version "7.19.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" + integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== + +"@babel/helper-validator-option@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" + integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== + +"@babel/helper-wrap-function@^7.18.9": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz#75e2d84d499a0ab3b31c33bcfe59d6b8a45f62e3" + integrity sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q== + dependencies: + "@babel/helper-function-name" "^7.19.0" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.20.5" + "@babel/types" "^7.20.5" + +"@babel/helpers@^7.22.0": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.3.tgz#53b74351da9684ea2f694bf0877998da26dd830e" + integrity sha512-jBJ7jWblbgr7r6wYZHMdIqKc73ycaTcCaWRq4/2LpuPHcx7xMlZvpGQkOYc9HeSjn6rcx15CPlgVcBtZ4WZJ2w== + dependencies: + "@babel/template" "^7.21.9" + "@babel/traverse" "^7.22.1" + "@babel/types" "^7.22.3" + +"@babel/highlight@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" + integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== + dependencies: + "@babel/helper-validator-identifier" "^7.18.6" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/parser@^7.21.9", "@babel/parser@^7.22.0", "@babel/parser@^7.22.4": + version "7.22.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.4.tgz#a770e98fd785c231af9d93f6459d36770993fb32" + integrity sha512-VLLsx06XkEYqBtE5YGPwfSGwfrjnyPP5oiGty3S8pQLFDFLaS8VwWSIxkTXpcvr5zeYLE6+MBNl2npl/YnfofA== + +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": + version "7.18.6" + 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.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2" + integrity sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.3.tgz#a75be1365c0c3188c51399a662168c1c98108659" + integrity sha512-6r4yRwEnorYByILoDRnEqxtojYKuiIv9FojW2E8GUKo9eWBwbKcd9IiZOZpdyXc64RmyGGyPu3/uAcrz/dq2kQ== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/plugin-transform-optional-chaining" "^7.22.3" + +"@babel/plugin-proposal-private-property-in-object@^7.21.0": + version "7.21.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz#69d597086b6760c4126525cfa154f34631ff272c" + integrity sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-create-class-features-plugin" "^7.21.0" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + +"@babel/plugin-proposal-unicode-property-regex@^7.4.4": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz#af613d2cd5e643643b65cded64207b15c85cb78e" + integrity sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@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.20.0": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz#bb50e0d4bea0957235390641209394e87bdb9cc4" + integrity sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ== + dependencies: + "@babel/helper-plugin-utils" "^7.19.0" + +"@babel/plugin-syntax-import-attributes@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.3.tgz#d7168f22b9b49a6cc1792cec78e06a18ad2e7b4b" + integrity sha512-i35jZJv6aO7hxEbIWQ41adVfOzjm9dcYDNeWlBMd8p0ZQRtNUCBrmGwZt+H5lb+oOC9a3svp956KP0oWGA1YsA== + dependencies: + "@babel/helper-plugin-utils" "^7.21.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-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.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.21.5.tgz#9bb42a53de447936a57ba256fbf537fc312b6929" + integrity sha512-wb1mhwGOCaXHDTcsRYMKF9e5bbMgqwxtqa2Y1ifH96dXJPwbuLX9qHy3clhrxVqgMz7nyNXs8VkxdH8UBcjKqA== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + +"@babel/plugin-transform-async-generator-functions@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.3.tgz#3ed99924c354fb9e80dabb2cc8d002c702e94527" + integrity sha512-36A4Aq48t66btydbZd5Fk0/xJqbpg/v4QWI4AH4cYHBXy9Mu42UOupZpebKFiCFNT9S9rJFcsld0gsv0ayLjtA== + dependencies: + "@babel/helper-environment-visitor" "^7.22.1" + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-remap-async-to-generator" "^7.18.9" + "@babel/plugin-syntax-async-generators" "^7.8.4" + +"@babel/plugin-transform-async-to-generator@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz#dfee18623c8cb31deb796aa3ca84dda9cea94354" + integrity sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q== + dependencies: + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-remap-async-to-generator" "^7.18.9" + +"@babel/plugin-transform-block-scoped-functions@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz#9187bf4ba302635b9d70d986ad70f038726216a8" + integrity sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-block-scoping@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz#e737b91037e5186ee16b76e7ae093358a5634f02" + integrity sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-transform-class-properties@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.3.tgz#3407145e513830df77f0cef828b8b231c166fe4c" + integrity sha512-mASLsd6rhOrLZ5F3WbCxkzl67mmOnqik0zrg5W6D/X0QMW7HtvnoL1dRARLKIbMP3vXwkwziuLesPqWVGIl6Bw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.22.1" + "@babel/helper-plugin-utils" "^7.21.5" + +"@babel/plugin-transform-class-static-block@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.3.tgz#e352cf33567385c731a8f21192efeba760358773" + integrity sha512-5BirgNWNOx7cwbTJCOmKFJ1pZjwk5MUfMIwiBBvsirCJMZeQgs5pk6i1OlkVg+1Vef5LfBahFOrdCnAWvkVKMw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.22.1" + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + +"@babel/plugin-transform-classes@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz#f469d0b07a4c5a7dbb21afad9e27e57b47031665" + integrity sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-compilation-targets" "^7.20.7" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-replace-supers" "^7.20.7" + "@babel/helper-split-export-declaration" "^7.18.6" + globals "^11.1.0" + +"@babel/plugin-transform-computed-properties@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.21.5.tgz#3a2d8bb771cd2ef1cd736435f6552fe502e11b44" + integrity sha512-TR653Ki3pAwxBxUe8srfF3e4Pe3FTA46uaNHYyQwIoM4oWKSoOZiDNyHJ0oIoDIUPSRQbQG7jzgVBX3FPVne1Q== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/template" "^7.20.7" + +"@babel/plugin-transform-destructuring@^7.21.3": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.21.3.tgz#73b46d0fd11cd6ef57dea8a381b1215f4959d401" + integrity sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-transform-dotall-regex@^7.18.6", "@babel/plugin-transform-dotall-regex@^7.4.4": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz#b286b3e7aae6c7b861e45bed0a2fafd6b1a4fef8" + integrity sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-duplicate-keys@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz#687f15ee3cdad6d85191eb2a372c4528eaa0ae0e" + integrity sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-dynamic-import@^7.22.1": + version "7.22.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.1.tgz#6c56afaf896a07026330cf39714532abed8d9ed1" + integrity sha512-rlhWtONnVBPdmt+jeewS0qSnMz/3yLFrqAP8hHC6EDcrYRSyuz9f9yQhHvVn2Ad6+yO9fHXac5piudeYrInxwQ== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + +"@babel/plugin-transform-exponentiation-operator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz#421c705f4521888c65e91fdd1af951bfefd4dacd" + integrity sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-export-namespace-from@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.3.tgz#9b8700aa495007d3bebac8358d1c562434b680b9" + integrity sha512-5Ti1cHLTDnt3vX61P9KZ5IG09bFXp4cDVFJIAeCZuxu9OXXJJZp5iP0n/rzM2+iAutJY+KWEyyHcRaHlpQ/P5g== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + +"@babel/plugin-transform-for-of@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.5.tgz#e890032b535f5a2e237a18535f56a9fdaa7b83fc" + integrity sha512-nYWpjKW/7j/I/mZkGVgHJXh4bA1sfdFnJoOXwJuj4m3Q2EraO/8ZyrkCau9P5tbHQk01RMSt6KYLCsW7730SXQ== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + +"@babel/plugin-transform-function-name@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz#cc354f8234e62968946c61a46d6365440fc764e0" + integrity sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ== + dependencies: + "@babel/helper-compilation-targets" "^7.18.9" + "@babel/helper-function-name" "^7.18.9" + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-json-strings@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.3.tgz#a181b8679cf7c93e9d0e3baa5b1776d65be601a9" + integrity sha512-IuvOMdeOOY2X4hRNAT6kwbePtK21BUyrAEgLKviL8pL6AEEVUVcqtRdN/HJXBLGIbt9T3ETmXRnFedRRmQNTYw== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-syntax-json-strings" "^7.8.3" + +"@babel/plugin-transform-literals@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz#72796fdbef80e56fba3c6a699d54f0de557444bc" + integrity sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-logical-assignment-operators@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.3.tgz#9e021455810f33b0baccb82fb759b194f5dc36f0" + integrity sha512-CbayIfOw4av2v/HYZEsH+Klks3NC2/MFIR3QR8gnpGNNPEaq2fdlVCRYG/paKs7/5hvBLQ+H70pGWOHtlNEWNA== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + +"@babel/plugin-transform-member-expression-literals@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz#ac9fdc1a118620ac49b7e7a5d2dc177a1bfee88e" + integrity sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-modules-amd@^7.20.11": + version "7.20.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz#3daccca8e4cc309f03c3a0c4b41dc4b26f55214a" + integrity sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g== + dependencies: + "@babel/helper-module-transforms" "^7.20.11" + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-transform-modules-commonjs@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.5.tgz#d69fb947eed51af91de82e4708f676864e5e47bc" + integrity sha512-OVryBEgKUbtqMoB7eG2rs6UFexJi6Zj6FDXx+esBLPTCxCNxAY9o+8Di7IsUGJ+AVhp5ncK0fxWUBd0/1gPhrQ== + dependencies: + "@babel/helper-module-transforms" "^7.21.5" + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-simple-access" "^7.21.5" + +"@babel/plugin-transform-modules-systemjs@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.3.tgz#cc507e03e88d87b016feaeb5dae941e6ef50d91e" + integrity sha512-V21W3bKLxO3ZjcBJZ8biSvo5gQ85uIXW2vJfh7JSWf/4SLUSr1tOoHX3ruN4+Oqa2m+BKfsxTR1I+PsvkIWvNw== + dependencies: + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-module-transforms" "^7.22.1" + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-validator-identifier" "^7.19.1" + +"@babel/plugin-transform-modules-umd@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz#81d3832d6034b75b54e62821ba58f28ed0aab4b9" + integrity sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ== + dependencies: + "@babel/helper-module-transforms" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-named-capturing-groups-regex@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.3.tgz#db6fb77e6b3b53ec3b8d370246f0b7cf67d35ab4" + integrity sha512-c6HrD/LpUdNNJsISQZpds3TXvfYIAbo+efE9aWmY/PmSRD0agrJ9cPMt4BmArwUQ7ZymEWTFjTyp+yReLJZh0Q== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.1" + "@babel/helper-plugin-utils" "^7.21.5" + +"@babel/plugin-transform-new-target@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.3.tgz#deb0377d741cbee2f45305868b9026dcd6dd96e2" + integrity sha512-5RuJdSo89wKdkRTqtM9RVVJzHum9c2s0te9rB7vZC1zKKxcioWIy+xcu4OoIAjyFZhb/bp5KkunuLin1q7Ct+w== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + +"@babel/plugin-transform-nullish-coalescing-operator@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.3.tgz#8c519f8bf5af94a9ca6f65cf422a9d3396e542b9" + integrity sha512-CpaoNp16nX7ROtLONNuCyenYdY/l7ZsR6aoVa7rW7nMWisoNoQNIH5Iay/4LDyRjKMuElMqXiBoOQCDLTMGZiw== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + +"@babel/plugin-transform-numeric-separator@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.3.tgz#02493070ca6685884b0eee705363ee4da2132ab0" + integrity sha512-+AF88fPDJrnseMh5vD9+SH6wq4ZMvpiTMHh58uLs+giMEyASFVhcT3NkoyO+NebFCNnpHJEq5AXO2txV4AGPDQ== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + +"@babel/plugin-transform-object-rest-spread@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.3.tgz#da6fba693effb8c203d8c3bdf7bf4e2567e802e9" + integrity sha512-38bzTsqMMCI46/TQnJwPPpy33EjLCc1Gsm2hRTF6zTMWnKsN61vdrpuzIEGQyKEhDSYDKyZHrrd5FMj4gcUHhw== + dependencies: + "@babel/compat-data" "^7.22.3" + "@babel/helper-compilation-targets" "^7.22.1" + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.22.3" + +"@babel/plugin-transform-object-super@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz#fb3c6ccdd15939b6ff7939944b51971ddc35912c" + integrity sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-replace-supers" "^7.18.6" + +"@babel/plugin-transform-optional-catch-binding@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.3.tgz#e971a083fc7d209d9cd18253853af1db6d8dc42f" + integrity sha512-bnDFWXFzWY0BsOyqaoSXvMQ2F35zutQipugog/rqotL2S4ciFOKlRYUu9djt4iq09oh2/34hqfRR2k1dIvuu4g== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + +"@babel/plugin-transform-optional-chaining@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.3.tgz#5fd24a4a7843b76da6aeec23c7f551da5d365290" + integrity sha512-63v3/UFFxhPKT8j8u1jTTGVyITxl7/7AfOqK8C5gz1rHURPUGe3y5mvIf68eYKGoBNahtJnTxBKug4BQOnzeJg== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + +"@babel/plugin-transform-parameters@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.3.tgz#24477acfd2fd2bc901df906c9bf17fbcfeee900d" + integrity sha512-x7QHQJHPuD9VmfpzboyGJ5aHEr9r7DsAsdxdhJiTB3J3j8dyl+NFZ+rX5Q2RWFDCs61c06qBfS4ys2QYn8UkMw== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + +"@babel/plugin-transform-private-methods@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.3.tgz#adac38020bab5047482d3297107c1f58e9c574f6" + integrity sha512-fC7jtjBPFqhqpPAE+O4LKwnLq7gGkD3ZmC2E3i4qWH34mH3gOg2Xrq5YMHUq6DM30xhqM1DNftiRaSqVjEG+ug== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.22.1" + "@babel/helper-plugin-utils" "^7.21.5" + +"@babel/plugin-transform-private-property-in-object@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.3.tgz#031621b02c7b7d95389de1a3dba2fe9e8c548e56" + integrity sha512-C7MMl4qWLpgVCbXfj3UW8rR1xeCnisQ0cU7YJHV//8oNBS0aCIVg1vFnZXxOckHhEpQyqNNkWmvSEWnMLlc+Vw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-create-class-features-plugin" "^7.22.1" + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + +"@babel/plugin-transform-property-literals@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz#e22498903a483448e94e032e9bbb9c5ccbfc93a3" + integrity sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-regenerator@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.21.5.tgz#576c62f9923f94bcb1c855adc53561fd7913724e" + integrity sha512-ZoYBKDb6LyMi5yCsByQ5jmXsHAQDDYeexT1Szvlmui+lADvfSecr5Dxd/PkrTC3pAD182Fcju1VQkB4oCp9M+w== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + regenerator-transform "^0.15.1" + +"@babel/plugin-transform-reserved-words@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz#b1abd8ebf8edaa5f7fe6bbb8d2133d23b6a6f76a" + integrity sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-shorthand-properties@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz#6d6df7983d67b195289be24909e3f12a8f664dc9" + integrity sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-spread@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz#c2d83e0b99d3bf83e07b11995ee24bf7ca09401e" + integrity sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + +"@babel/plugin-transform-sticky-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz#c6706eb2b1524028e317720339583ad0f444adcc" + integrity sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-template-literals@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz#04ec6f10acdaa81846689d63fae117dd9c243a5e" + integrity sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-typeof-symbol@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz#c8cea68263e45addcd6afc9091429f80925762c0" + integrity sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-unicode-escapes@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.21.5.tgz#1e55ed6195259b0e9061d81f5ef45a9b009fb7f2" + integrity sha512-LYm/gTOwZqsYohlvFUe/8Tujz75LqqVC2w+2qPHLR+WyWHGCZPN1KBpJCJn+4Bk4gOkQy/IXKIge6az5MqwlOg== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + +"@babel/plugin-transform-unicode-property-regex@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.3.tgz#597b6a614dc93eaae605ee293e674d79d32eb380" + integrity sha512-5ScJ+OmdX+O6HRuMGW4kv7RL9vIKdtdAj9wuWUKy1wbHY3jaM/UlyIiC1G7J6UJiiyMukjjK0QwL3P0vBd0yYg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.1" + "@babel/helper-plugin-utils" "^7.21.5" + +"@babel/plugin-transform-unicode-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz#194317225d8c201bbae103364ffe9e2cea36cdca" + integrity sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-unicode-sets-regex@^7.22.3": + version "7.22.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.3.tgz#7c14ee33fa69782b0101d0f7143d3fc73ce00700" + integrity sha512-hNufLdkF8vqywRp+P55j4FHXqAX2LRUccoZHH7AFn1pq5ZOO2ISKW9w13bFZVjBoTqeve2HOgoJCcaziJVhGNw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.1" + "@babel/helper-plugin-utils" "^7.21.5" + +"@babel/preset-env@^7.19": + version "7.22.4" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.22.4.tgz#c86a82630f0e8c61d9bb9327b7b896732028cbed" + integrity sha512-c3lHOjbwBv0TkhYCr+XCR6wKcSZ1QbQTVdSkZUaVpLv8CVWotBMArWUi5UAJrcrQaEnleVkkvaV8F/pmc/STZQ== + dependencies: + "@babel/compat-data" "^7.22.3" + "@babel/helper-compilation-targets" "^7.22.1" + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-validator-option" "^7.21.0" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.22.3" + "@babel/plugin-proposal-private-property-in-object" "^7.21.0" + "@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.20.0" + "@babel/plugin-syntax-import-attributes" "^7.22.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.21.5" + "@babel/plugin-transform-async-generator-functions" "^7.22.3" + "@babel/plugin-transform-async-to-generator" "^7.20.7" + "@babel/plugin-transform-block-scoped-functions" "^7.18.6" + "@babel/plugin-transform-block-scoping" "^7.21.0" + "@babel/plugin-transform-class-properties" "^7.22.3" + "@babel/plugin-transform-class-static-block" "^7.22.3" + "@babel/plugin-transform-classes" "^7.21.0" + "@babel/plugin-transform-computed-properties" "^7.21.5" + "@babel/plugin-transform-destructuring" "^7.21.3" + "@babel/plugin-transform-dotall-regex" "^7.18.6" + "@babel/plugin-transform-duplicate-keys" "^7.18.9" + "@babel/plugin-transform-dynamic-import" "^7.22.1" + "@babel/plugin-transform-exponentiation-operator" "^7.18.6" + "@babel/plugin-transform-export-namespace-from" "^7.22.3" + "@babel/plugin-transform-for-of" "^7.21.5" + "@babel/plugin-transform-function-name" "^7.18.9" + "@babel/plugin-transform-json-strings" "^7.22.3" + "@babel/plugin-transform-literals" "^7.18.9" + "@babel/plugin-transform-logical-assignment-operators" "^7.22.3" + "@babel/plugin-transform-member-expression-literals" "^7.18.6" + "@babel/plugin-transform-modules-amd" "^7.20.11" + "@babel/plugin-transform-modules-commonjs" "^7.21.5" + "@babel/plugin-transform-modules-systemjs" "^7.22.3" + "@babel/plugin-transform-modules-umd" "^7.18.6" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.22.3" + "@babel/plugin-transform-new-target" "^7.22.3" + "@babel/plugin-transform-nullish-coalescing-operator" "^7.22.3" + "@babel/plugin-transform-numeric-separator" "^7.22.3" + "@babel/plugin-transform-object-rest-spread" "^7.22.3" + "@babel/plugin-transform-object-super" "^7.18.6" + "@babel/plugin-transform-optional-catch-binding" "^7.22.3" + "@babel/plugin-transform-optional-chaining" "^7.22.3" + "@babel/plugin-transform-parameters" "^7.22.3" + "@babel/plugin-transform-private-methods" "^7.22.3" + "@babel/plugin-transform-private-property-in-object" "^7.22.3" + "@babel/plugin-transform-property-literals" "^7.18.6" + "@babel/plugin-transform-regenerator" "^7.21.5" + "@babel/plugin-transform-reserved-words" "^7.18.6" + "@babel/plugin-transform-shorthand-properties" "^7.18.6" + "@babel/plugin-transform-spread" "^7.20.7" + "@babel/plugin-transform-sticky-regex" "^7.18.6" + "@babel/plugin-transform-template-literals" "^7.18.9" + "@babel/plugin-transform-typeof-symbol" "^7.18.9" + "@babel/plugin-transform-unicode-escapes" "^7.21.5" + "@babel/plugin-transform-unicode-property-regex" "^7.22.3" + "@babel/plugin-transform-unicode-regex" "^7.18.6" + "@babel/plugin-transform-unicode-sets-regex" "^7.22.3" + "@babel/preset-modules" "^0.1.5" + "@babel/types" "^7.22.4" + babel-plugin-polyfill-corejs2 "^0.4.3" + babel-plugin-polyfill-corejs3 "^0.8.1" + babel-plugin-polyfill-regenerator "^0.5.0" + core-js-compat "^3.30.2" + semver "^6.3.0" + +"@babel/preset-modules@^0.1.5": + version "0.1.5" + resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.5.tgz#ef939d6e7f268827e1841638dc6ff95515e115d9" + integrity sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" + "@babel/plugin-transform-dotall-regex" "^7.4.4" + "@babel/types" "^7.4.4" + esutils "^2.0.2" + +"@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.22.3" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.3.tgz#0a7fce51d43adbf0f7b517a71f4c3aaca92ebcbb" + integrity sha512-XsDuspWKLUsxwCp6r7EhsExHtYfbe5oAGQ19kqngTdCPUoPQzOPdUbD/pB9PJiwb2ptYKQDjSJT3R6dC+EPqfQ== + dependencies: + regenerator-runtime "^0.13.11" + +"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.21.9": + version "7.21.9" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.21.9.tgz#bf8dad2859130ae46088a99c1f265394877446fb" + integrity sha512-MK0X5k8NKOuWRamiEfc3KEJiHMTkGZNUjzMipqCGDDc6ijRl/B7RGSKVGncu4Ro/HdyzzY6cmoXuKI2Gffk7vQ== + dependencies: + "@babel/code-frame" "^7.21.4" + "@babel/parser" "^7.21.9" + "@babel/types" "^7.21.5" + +"@babel/traverse@^7.20.5", "@babel/traverse@^7.22.1": + version "7.22.4" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.4.tgz#c3cf96c5c290bd13b55e29d025274057727664c0" + integrity sha512-Tn1pDsjIcI+JcLKq1AVlZEr4226gpuAQTsLMorsYg9tuS/kG7nuwwJ4AB8jfQuEgb/COBwR/DqJxmoiYFu5/rQ== + dependencies: + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.22.3" + "@babel/helper-environment-visitor" "^7.22.1" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.22.4" + "@babel/types" "^7.22.4" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.5", "@babel/types@^7.21.0", "@babel/types@^7.21.4", "@babel/types@^7.21.5", "@babel/types@^7.22.0", "@babel/types@^7.22.3", "@babel/types@^7.22.4", "@babel/types@^7.4.4": + version "7.22.4" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.4.tgz#56a2653ae7e7591365dabf20b76295410684c071" + integrity sha512-Tx9x3UBHTTsMSW85WB2kphxYQVvrZ/t1FxD88IpSgIjiUJlCm9z+xWIDwyo1vffTwSqteqyznB8ZE9vYYk16zA== + dependencies: + "@babel/helper-string-parser" "^7.21.5" + "@babel/helper-validator-identifier" "^7.19.1" + 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.0" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" + integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== + +"@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.3" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.3.tgz#8108265659d4c33e72ffe14e33d6cc5eb59f2fda" + integrity sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/sourcemap-codec@1.4.14": + version "1.4.14" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" + integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== + +"@jridgewell/sourcemap-codec@^1.4.10": + 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.9": + version "0.3.18" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" + integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== + dependencies: + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" + +"@types/eslint-scope@^3.7.3": + version "3.7.4" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.4.tgz#37fc1223f0786c39627068a12e94d6e6fc61de16" + integrity sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*": + version "8.40.0" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.40.0.tgz#ae73dc9ec5237f2794c4f79efd6a4c73b13daf23" + integrity sha512-nbq2mvc/tBrK9zQQuItvjJl++GTN5j06DaPtp3hZCpngmG6Q3xoyEmd0TwZI0gAy/G1X0zhGBbr2imsGFdFV0g== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*", "@types/estree@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194" + integrity sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA== + +"@types/json-schema@*", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8": + version "7.0.12" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb" + integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA== + +"@types/node@*": + version "20.2.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.2.5.tgz#26d295f3570323b2837d322180dfbf1ba156fefb" + integrity sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ== + +"@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== + +"@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.8.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" + integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== + +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.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.3.tgz#75044d90ba5043a5fb559ac98496f62f3eb668fd" + integrity sha512-bM3gHc337Dta490gg+/AseNB9L4YLHxq1nGKZZSHbhXv4aTYU2MD2cjza1Ru4S6975YLTaL1K8uJf6ukJhhmtw== + dependencies: + "@babel/compat-data" "^7.17.7" + "@babel/helper-define-polyfill-provider" "^0.4.0" + semver "^6.1.1" + +babel-plugin-polyfill-corejs3@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.1.tgz#39248263c38191f0d226f928d666e6db1b4b3a8a" + integrity sha512-ikFrZITKg1xH6pLND8zT14UPgjKHiGLqex7rGEZCH2EvhsneJaJPemmpQaIZV5AL03II+lXylw3UmddDK8RU5Q== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.4.0" + core-js-compat "^3.30.1" + +babel-plugin-polyfill-regenerator@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.0.tgz#e7344d88d9ef18a3c47ded99362ae4a757609380" + integrity sha512-hDJtKjMLVa7Z+LwnTCxoDLQj6wdc+B8dun7ayF2fYieI6OzfuvcLMB32ihJZ4UhCBwNYGl5bg/x/P9cMdnkc2g== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.4.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.14.5, browserslist@^4.21.3, browserslist@^4.21.5: + version "4.21.7" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.7.tgz#e2b420947e5fb0a58e8f4668ae6e23488127e551" + integrity sha512-BauCXrQ7I2ftSqd2mvKHGo85XR0u7Ru3C/Hxsy/0TkfCtjrmAbPdzLGasmoiBxplpDXlPvdjX9u7srIMfgasNA== + dependencies: + caniuse-lite "^1.0.30001489" + electron-to-chromium "^1.4.411" + node-releases "^2.0.12" + update-browserslist-db "^1.0.11" + +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.30001489: + version "1.0.30001495" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001495.tgz#64a0ccef1911a9dcff647115b4430f8eff1ef2d9" + integrity sha512-F6x5IEuigtUfU5ZMQK2jsy5JqUUlEFRVZq8bO2a+ysq5K7jD6PPc9YXZj78xDNS3uNchesp1Jw47YXEqr+Viyg== + +chalk@^2.0.0: + 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.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + 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@^1.7.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" + integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== + +core-js-compat@^3.30.1, core-js-compat@^3.30.2: + version "3.30.2" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.30.2.tgz#83f136e375babdb8c80ad3c22d67c69098c1dd8b" + integrity sha512-nriW1nuJjUgvkEjIot1Spwakz52V9YkYHZAQG6A1eCgC8AA1p0zngrQEP9R0+V6hji5XilWKG1Bd0YRppmGimA== + dependencies: + browserslist "^4.21.5" + +core-js@^3.25.0: + version "3.30.2" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.30.2.tgz#6528abfda65e5ad728143ea23f7a14f0dcf503fc" + integrity sha512-uBJiDmwqsbJCWHAwjrx3cvjbMXP7xD72Dmsn5LOJpiRmE3WbBbN5rCqQ2Qh6Ek6/eOrjlWngEynBWo4VxerQhg== + +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: + 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.411: + version "1.4.425" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.425.tgz#399df13091b836d28283a545c25c8e4d9da86da8" + integrity sha512-wv1NufHxu11zfDbY4fglYQApMswleE9FL/DSeyOyauVXDZ+Kco96JK/tPfBUaDqfRarYp2WH2hJ/5UnVywp9Jg== + +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.14.1: + version "5.14.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.14.1.tgz#de684b6803724477a4af5d74ccae5de52c25f6b3" + integrity sha512-Vklwq2vDKtl0y/vtwjSesgJ5MYS7Etuk5txS8VdKL4AOS1aUlD96zqIfsOSLQsdv3xgMRbtkWM8eG9XDfKUPow== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +envinfo@^7.7.3: + version "7.8.1" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" + integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== + +es-module-lexer@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.2.1.tgz#ba303831f63e6a394983fde2f97ad77b22324527" + integrity sha512-9978wrXM50Y4rTMmW5kXIC09ZdXQZqkE4mxhwkd8VbzsGkXGPgV4zWuqQJgCEzYngdo2dYDa0l8xhX4fkSwJSg== + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +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" + +fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +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== + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +immutable@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.0.tgz#eb1738f14ffb39fd068b1dbe1296117484dd34be" + integrity sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg== + +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.11.0: + version "2.12.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.1.tgz#0c0b6885b6f80011c71541ce15c8d66cf5a4f9fd" + integrity sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg== + dependencies: + has "^1.0.3" + +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== + +json5@^2.1.2, json5@^2.2.2: + 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.12: + version "2.0.12" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.12.tgz#35627cc224a23bfb06fb3380f2b3afaaa7eb1039" + integrity sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ== + +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" + +punycode@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" + integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== + +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.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c" + integrity sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ== + 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.13.11: + version "0.13.11" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" + integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== + +regenerator-transform@^0.15.1: + version "0.15.1" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.1.tgz#f6c4e99fc1b4591f780db2586328e4d9a9d8dc56" + integrity sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg== + 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.2" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" + integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== + dependencies: + is-core-module "^2.11.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.63.2" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.63.2.tgz#75f7d9a8e67d1d5b98a989507f4d98b6067b1f75" + integrity sha512-u56TU0AIFqMtauKl/OJ1AeFsXqRHkgO7nCWmHaDwfxDo9GUMSqBA4NEh6GMuh1CYVM7zuROYtZrHzPc2ixK+ww== + 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.1.2: + version "3.2.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.2.0.tgz#7dff4881064a4f22c09f0c6a1457feb820fd0636" + integrity sha512-0zTyLGyDJYd/MBxG1AhJkKa6fpEBds4OQO2ut0w7OYG+ZGhGea09lijvzsqegYSik88zc7cUtIlnnO+/BvD6gQ== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +serialize-javascript@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.1.tgz#b206efb27c3da0b0ab6b52f48d170b7996458e5c" + integrity sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w== + 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.7: + version "5.3.9" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz#832536999c51b46d468067f9e37662a3b96adfe1" + integrity sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA== + dependencies: + "@jridgewell/trace-mapping" "^0.3.17" + jest-worker "^27.4.5" + schema-utils "^3.1.1" + serialize-javascript "^6.0.1" + terser "^5.16.8" + +terser@^5.16.8: + version "5.17.7" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.17.7.tgz#2a8b134826fe179b711969fd9d9a0c2479b2a8c3" + integrity sha512-/bi0Zm2C6VAexlGgLlVxA0P2lru/sdLyfCVaRMfKVo9nWxbmz7f/sD8VPybPeSUJaJcwmCJis9pBIhcVcG1QcQ== + 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" + +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.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" + integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== + 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.9.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.9.0.tgz#dc160a1c4cf512ceca515cc231669e9ddb133826" + integrity sha512-6NbRQw4+Sy50vYNTw7EyOn41OZItPiXB8GNv3INSoe3PSFaHJEz3SHTrYVaRm2LilNGnFUzh0FAwqPEmU/CwDg== + dependencies: + clone-deep "^4.0.1" + wildcard "^2.0.0" + +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.86.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.86.0.tgz#b0eb81794b62aee0b7e7eb8c5073495217d9fc6d" + integrity sha512-3BOvworZ8SO/D4GVP+GoRC3fVeg5MO4vzmq8TJJEkdmopxyazGDxN8ClqN12uzrZW9Tv8EED8v5VSb6Sqyi0pg== + dependencies: + "@types/eslint-scope" "^3.7.3" + "@types/estree" "^1.0.0" + "@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.14.5" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.14.1" + 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.1.2" + tapable "^2.1.1" + terser-webpack-plugin "^5.3.7" + 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/package.json b/package.json index ccc69d50d..83284423b 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "install:modules:ppcp-wc-gateway": "cd modules/ppcp-wc-gateway && yarn install", "install:modules:ppcp-webhooks": "cd modules/ppcp-webhooks && yarn install", "install:modules:ppcp-order-tracking": "cd modules/ppcp-order-tracking && yarn install", + "install:modules:ppcp-subscription": "cd modules/ppcp-subscription && 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", @@ -20,6 +21,7 @@ "build:modules:ppcp-wc-gateway": "cd modules/ppcp-wc-gateway && yarn run build", "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-subscription": "cd modules/ppcp-subscription && 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", "build:modules:ppcp-uninstall": "cd modules/ppcp-uninstall && yarn run build", @@ -29,6 +31,7 @@ "watch:modules:ppcp-wc-gateway": "cd modules/ppcp-wc-gateway && yarn run watch", "watch:modules:ppcp-webhooks": "cd modules/ppcp-webhooks && yarn run watch", "watch:modules:ppcp-order-tracking": "cd modules/ppcp-order-tracking && yarn run watch", + "watch:modules:ppcp-subscription": "cd modules/ppcp-subscription && 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 40b00d7fb0a937625df6fb9500a366173fb7c077 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Thu, 8 Jun 2023 16:49:17 +0200 Subject: [PATCH 04/56] Display PayPal button only if cart contains only one subscription product --- .../src/Helper/SubscriptionHelper.php | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/modules/ppcp-subscription/src/Helper/SubscriptionHelper.php b/modules/ppcp-subscription/src/Helper/SubscriptionHelper.php index 8b3034acd..2250296b2 100644 --- a/modules/ppcp-subscription/src/Helper/SubscriptionHelper.php +++ b/modules/ppcp-subscription/src/Helper/SubscriptionHelper.php @@ -180,6 +180,7 @@ class SubscriptionHelper { if ( $subscription_mode !== 'subscriptions_api' || ! $this->paypal_subscription_id() + || ! $this->cart_contains_only_one_item() ) { return false; } @@ -218,4 +219,25 @@ class SubscriptionHelper { return ''; } + + /** + * Checks if cart contains only one item. + * + * @return bool + */ + public function cart_contains_only_one_item(): bool { + if ( ! $this->plugin_is_active() ) { + return false; + } + $cart = WC()->cart; + if ( ! $cart || $cart->is_empty() ) { + return false; + } + + if ( count( $cart->get_cart() ) > 1 ) { + return false; + } + + return true; + } } From d83d26c5f5405c6106581043792ac74a5f276350 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 12 Jun 2023 15:14:26 +0200 Subject: [PATCH 05/56] Remove subscriptions schedule metabox and add custom one --- .../src/FreeTrialHandlerTrait.php | 2 - .../src/SubscriptionModule.php | 41 +++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-subscription/src/FreeTrialHandlerTrait.php b/modules/ppcp-subscription/src/FreeTrialHandlerTrait.php index 8e309f4ce..cbc6e02db 100644 --- a/modules/ppcp-subscription/src/FreeTrialHandlerTrait.php +++ b/modules/ppcp-subscription/src/FreeTrialHandlerTrait.php @@ -10,8 +10,6 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Subscription; use WC_Order; -use WC_Product; -use WC_Subscription; use WC_Subscriptions_Product; use WC_Subscriptions_Synchroniser; diff --git a/modules/ppcp-subscription/src/SubscriptionModule.php b/modules/ppcp-subscription/src/SubscriptionModule.php index 00ae5b516..0686e602f 100644 --- a/modules/ppcp-subscription/src/SubscriptionModule.php +++ b/modules/ppcp-subscription/src/SubscriptionModule.php @@ -28,6 +28,7 @@ use WooCommerce\PayPalCommerce\Vendor\Interop\Container\ServiceProviderInterface use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface; use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException; +use WP_Post; /** * Class SubscriptionModule @@ -165,6 +166,7 @@ class SubscriptionModule implements ModuleInterface { return; } + //phpcs:disable WordPress.Security.NonceVerification.Recommended $post_id = wc_clean( wp_unslash( $_GET['post'] ?? '' ) ); $product = wc_get_product( $post_id ); if ( ! is_a( $product, WC_Product::class ) ) { @@ -194,6 +196,45 @@ class SubscriptionModule implements ModuleInterface { ); } ); + + add_action( + 'add_meta_boxes', + function( string $post_type, WP_Post $post ) use ( $c ) { + if ( $post_type !== 'shop_subscription' ) { + return; + } + + $subscription = wcs_get_subscription( $post->ID ); + if ( ! is_a( $subscription, WC_Subscription::class ) ) { + return; + } + + $subscription_id = $subscription->get_meta( 'ppcp_subscription' ) ?? ''; + if ( ! $subscription_id ) { + return; + } + + $screen_id = wc_get_page_screen_id( 'shop_subscription' ); + remove_meta_box( 'woocommerce-subscription-schedule', $screen_id, 'side' ); + + $environment = $c->get( 'onboarding.environment' ); + add_meta_box( + 'ppcp_paypal_subscription', + __( 'PayPal Subscription', 'woocommerce-paypal-payments' ), + function() use ( $subscription_id, $environment ) { + $host = $environment->current_environment_is( Environment::SANDBOX ) ? 'https://www.sandbox.paypal.com' : 'https://www.paypal.com'; + $url = trailingslashit( $host ) . 'billing/subscriptions/' . $subscription_id; + echo '

' . esc_html__( 'This subscription is linked to a PayPal Subscription, Cancel it to unlink.', 'woocommerce-paypal-payments' ) . '

'; + echo '

' . esc_html__( 'Subscription:', 'woocommerce-paypal-payments' ) . ' ' . esc_attr( $subscription_id ) . '

'; + }, + $post_type, + 'side' + ); + + }, + 30, + 2 + ); } /** From f9ed01e5c4af653c3cef3c9ec5a612cc2cc42801 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 13 Jun 2023 11:33:24 +0200 Subject: [PATCH 06/56] Fix single product button render conditional --- .../ContextBootstrap/SingleProductBootstap.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js b/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js index 2237eb2b1..1d14fc7e0 100644 --- a/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js +++ b/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js @@ -60,7 +60,7 @@ class SingleProductBootstap { return document.querySelector('form.cart') !== null && !this.priceAmountIsZero() && !this.isSubscriptionMode() - && PayPalCommerceGateway.subscription_plan_id !== ''; + && this.subscriptionHasPlan(); } priceAmount() { @@ -100,6 +100,18 @@ class SingleProductBootstap { || document.querySelector('.wcsatt-options-prompt-label-subscription input[type="radio"]:checked') !== null; // grouped } + subscriptionHasPlan() { + if (PayPalCommerceGateway.data_client_id.has_subscriptions) { + if (PayPalCommerceGateway.subscription_plan_id !== '') { + return true; + } + + return false; + } + + return true; + } + render() { const actionHandler = new SingleProductActionHandler( this.gateway, From 87325585f1c58c954de8b76d7311953c168a17e6 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 13 Jun 2023 11:46:56 +0200 Subject: [PATCH 07/56] Fix cart button render conditional --- .../js/modules/ContextBootstrap/CartBootstap.js | 3 ++- .../ContextBootstrap/SingleProductBootstap.js | 15 ++------------- .../resources/js/modules/Helper/Subscriptions.js | 12 ++++++++++++ 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/modules/ppcp-button/resources/js/modules/ContextBootstrap/CartBootstap.js b/modules/ppcp-button/resources/js/modules/ContextBootstrap/CartBootstap.js index 72d28932b..b22010203 100644 --- a/modules/ppcp-button/resources/js/modules/ContextBootstrap/CartBootstap.js +++ b/modules/ppcp-button/resources/js/modules/ContextBootstrap/CartBootstap.js @@ -1,5 +1,6 @@ import CartActionHandler from '../ActionHandler/CartActionHandler'; import {setVisible} from "../Helper/Hiding"; +import {subscriptionHasPlan} from "../Helper/Subscriptions"; class CartBootstrap { constructor(gateway, renderer, errorHandler) { @@ -41,7 +42,7 @@ class CartBootstrap { } shouldRender() { - return document.querySelector(this.gateway.button.wrapper) !== null; + return document.querySelector(this.gateway.button.wrapper) !== null && subscriptionHasPlan(); } render() { diff --git a/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js b/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js index 1d14fc7e0..5c625b54e 100644 --- a/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js +++ b/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js @@ -2,6 +2,7 @@ import UpdateCart from "../Helper/UpdateCart"; import SingleProductActionHandler from "../ActionHandler/SingleProductActionHandler"; import {hide, show, setVisible} from "../Helper/Hiding"; import ButtonsToggleListener from "../Helper/ButtonsToggleListener"; +import {subscriptionHasPlan} from "../Helper/Subscriptions"; class SingleProductBootstap { constructor(gateway, renderer, messages, errorHandler) { @@ -60,7 +61,7 @@ class SingleProductBootstap { return document.querySelector('form.cart') !== null && !this.priceAmountIsZero() && !this.isSubscriptionMode() - && this.subscriptionHasPlan(); + && subscriptionHasPlan(); } priceAmount() { @@ -100,18 +101,6 @@ class SingleProductBootstap { || document.querySelector('.wcsatt-options-prompt-label-subscription input[type="radio"]:checked') !== null; // grouped } - subscriptionHasPlan() { - if (PayPalCommerceGateway.data_client_id.has_subscriptions) { - if (PayPalCommerceGateway.subscription_plan_id !== '') { - return true; - } - - return false; - } - - return true; - } - render() { const actionHandler = new SingleProductActionHandler( this.gateway, diff --git a/modules/ppcp-button/resources/js/modules/Helper/Subscriptions.js b/modules/ppcp-button/resources/js/modules/Helper/Subscriptions.js index a205ac459..4fd4ae383 100644 --- a/modules/ppcp-button/resources/js/modules/Helper/Subscriptions.js +++ b/modules/ppcp-button/resources/js/modules/Helper/Subscriptions.js @@ -2,3 +2,15 @@ export const isChangePaymentPage = () => { const urlParams = new URLSearchParams(window.location.search) return urlParams.has('change_payment_method'); } + +export const subscriptionHasPlan = () => { + if (PayPalCommerceGateway.data_client_id.has_subscriptions) { + if (PayPalCommerceGateway.subscription_plan_id !== '') { + return true; + } + + return false; + } + + return true; +} From 7f25e1edd99d145a6bd14257e0b7e3cbf793908b Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 13 Jun 2023 12:17:49 +0200 Subject: [PATCH 08/56] Update subscription mode description --- modules/ppcp-wc-gateway/services.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index 9025bf9e1..74f6285b2 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -781,7 +781,14 @@ return array( ), 'paypal_saved_payments' => array( 'heading' => __( 'Saved payments', 'woocommerce-paypal-payments' ), - 'description' => __( 'PayPal can save your customers’ payment methods.', 'woocommerce-paypal-payments' ), + 'description' => sprintf( + // translators: %1$s, %2$s, %3$s and %4$s are a link tags. + __( 'PayPal can securely store your customers\' payment methods for %1$sfuture payments%2$s and %3$ssubscriptions%4$s, simplifying the checkout process and enabling recurring transactions on your website.', 'woocommerce-paypal-payments' ), + '', + '', + '', + '' + ), 'type' => 'ppcp-heading', 'screens' => array( State::STATE_START, From d9ba42e45b802298ca32deb375f6eb7518c9b3b7 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 14 Jun 2023 12:37:59 +0200 Subject: [PATCH 09/56] Add tooltip for connect PayPal checkbox --- modules/ppcp-subscription/src/SubscriptionModule.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/ppcp-subscription/src/SubscriptionModule.php b/modules/ppcp-subscription/src/SubscriptionModule.php index 0686e602f..035876380 100644 --- a/modules/ppcp-subscription/src/SubscriptionModule.php +++ b/modules/ppcp-subscription/src/SubscriptionModule.php @@ -708,7 +708,9 @@ class SubscriptionModule implements ModuleInterface { $subscription_plan_name = $product->get_meta( '_ppcp_subscription_plan_name' ); echo ''; } From 8c407546a3fc6e51f731b623c3a389fe9224d0b2 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Thu, 15 Jun 2023 15:59:16 +0200 Subject: [PATCH 11/56] Add spinner to unlink plan button --- .../resources/js/paypal-subscription.js | 28 +++++++++++++++++-- .../src/DeactivatePlanEndpoint.php | 2 +- .../src/SubscriptionModule.php | 7 +++-- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/modules/ppcp-subscription/resources/js/paypal-subscription.js b/modules/ppcp-subscription/resources/js/paypal-subscription.js index 9207e2f28..bcf41a527 100644 --- a/modules/ppcp-subscription/resources/js/paypal-subscription.js +++ b/modules/ppcp-subscription/resources/js/paypal-subscription.js @@ -18,8 +18,13 @@ document.addEventListener( subscriptionTrialPeriod.setAttribute('disabled', 'disabled'); } - document.getElementById('ppcp_unlink_sub_plan').addEventListener('click', (event)=>{ + const unlinkBtn = document.getElementById('ppcp_unlink_sub_plan'); + unlinkBtn.addEventListener('click', (event)=>{ event.preventDefault(); + unlinkBtn.disabled = true; + const spinner = document.getElementById('spinner-unlink-plan'); + spinner.style.display = 'inline-block'; + fetch(PayPalCommerceGatewayPayPalSubscription.ajax.deactivate_plan.endpoint, { method: 'POST', headers: { @@ -34,7 +39,26 @@ document.addEventListener( }).then(function (res) { return res.json(); }).then(function (data) { - console.log(data) + if (!data.success) { + unlinkBtn.disabled = false; + spinner.style.display = 'none'; + console.error(data); + throw Error(data.data.message); + } + + const enableSubscription = document.getElementById('ppcp-enable-subscription'); + const product = document.getElementById('pcpp-product'); + const plan = document.getElementById('pcpp-plan'); + enableSubscription.style.display = 'none'; + product.style.display = 'none'; + plan.style.display = 'none'; + + const enable_subscription_product = document.getElementById('ppcp_enable_subscription_product'); + enable_subscription_product.disabled = true; + + const planUnlinked = document.getElementById('pcpp-plan-unlinked'); + planUnlinked.style.display = 'block'; + }); }); }); diff --git a/modules/ppcp-subscription/src/DeactivatePlanEndpoint.php b/modules/ppcp-subscription/src/DeactivatePlanEndpoint.php index 14c5b5cbc..c2d490e81 100644 --- a/modules/ppcp-subscription/src/DeactivatePlanEndpoint.php +++ b/modules/ppcp-subscription/src/DeactivatePlanEndpoint.php @@ -70,9 +70,9 @@ class DeactivatePlanEndpoint { $product = wc_get_product( $product_id ); if ( WC_Subscriptions_Product::is_subscription( $product ) ) { $product->delete_meta_data( '_ppcp_enable_subscription_product' ); + $product->delete_meta_data( '_ppcp_subscription_plan_name' ); $product->delete_meta_data( 'ppcp_subscription_product' ); $product->delete_meta_data( 'ppcp_subscription_plan' ); - $product->delete_meta_data( 'ppcp_subscription_plan_name' ); $product->save(); } } diff --git a/modules/ppcp-subscription/src/SubscriptionModule.php b/modules/ppcp-subscription/src/SubscriptionModule.php index 65ece0859..9d690129a 100644 --- a/modules/ppcp-subscription/src/SubscriptionModule.php +++ b/modules/ppcp-subscription/src/SubscriptionModule.php @@ -729,12 +729,13 @@ class SubscriptionModule implements ModuleInterface { $subscription_plan = $product->get_meta( 'ppcp_subscription_plan' ); if ( $subscription_product && $subscription_plan ) { if ( $enable_subscription_product !== 'yes' ) { - echo '

' . esc_html__( 'Unlink PayPal Subscription Plan', 'woocommerce-paypal-payments' ) . '

'; + echo '

'; + echo ''; } $environment = $c->get( 'onboarding.environment' ); $host = $environment->current_environment_is( Environment::SANDBOX ) ? 'https://www.sandbox.paypal.com' : 'https://www.paypal.com'; - echo '

' . esc_attr( $subscription_product['id'] ) . '

'; - echo '

' . esc_attr( $subscription_plan['id'] ) . '

'; + echo '

' . esc_attr( $subscription_product['id'] ) . '

'; + echo '

' . esc_attr( $subscription_plan['id'] ) . '

'; } else { echo '

'; } From 09c7537e48f14114d6d78397a0a070dfedc5cae4 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Fri, 16 Jun 2023 16:11:45 +0200 Subject: [PATCH 12/56] Hide non updatable form fields if subscription product connected to PayPal --- .../ContextBootstrap/SingleProductBootstap.js | 3 +++ .../resources/js/paypal-subscription.js | 16 ++++++++-------- .../src/FreeTrialHandlerTrait.php | 5 ++++- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js b/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js index 5c625b54e..7ee40db54 100644 --- a/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js +++ b/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js @@ -91,6 +91,9 @@ class SingleProductBootstap { } priceAmountIsZero() { + if(subscriptionHasPlan()) { + return false; + } const price = this.priceAmount(); return !price || price === 0; } diff --git a/modules/ppcp-subscription/resources/js/paypal-subscription.js b/modules/ppcp-subscription/resources/js/paypal-subscription.js index bcf41a527..c42ca2597 100644 --- a/modules/ppcp-subscription/resources/js/paypal-subscription.js +++ b/modules/ppcp-subscription/resources/js/paypal-subscription.js @@ -8,18 +8,15 @@ document.addEventListener( const subscriptionPeriod = document.querySelector('#_subscription_period'); subscriptionPeriod.setAttribute('disabled', 'disabled'); - const subscriptionLength = document.querySelector('#_subscription_length'); - subscriptionLength.setAttribute('disabled', 'disabled'); + const subscriptionLength = document.querySelector('._subscription_length_field'); + subscriptionLength.style.display = 'none'; - const subscriptionTrialLength = document.querySelector('#_subscription_trial_length'); - subscriptionTrialLength.setAttribute('disabled', 'disabled'); - - const subscriptionTrialPeriod = document.querySelector('#_subscription_trial_period'); - subscriptionTrialPeriod.setAttribute('disabled', 'disabled'); + const subscriptionTrial = document.querySelector('._subscription_trial_length_field'); + subscriptionTrial.style.display = 'none'; } const unlinkBtn = document.getElementById('ppcp_unlink_sub_plan'); - unlinkBtn.addEventListener('click', (event)=>{ + unlinkBtn?.addEventListener('click', (event)=>{ event.preventDefault(); unlinkBtn.disabled = true; const spinner = document.getElementById('spinner-unlink-plan'); @@ -59,6 +56,9 @@ document.addEventListener( const planUnlinked = document.getElementById('pcpp-plan-unlinked'); planUnlinked.style.display = 'block'; + setTimeout(() => { + location.reload(); + }, 1000) }); }); }); diff --git a/modules/ppcp-subscription/src/FreeTrialHandlerTrait.php b/modules/ppcp-subscription/src/FreeTrialHandlerTrait.php index cbc6e02db..d528ff554 100644 --- a/modules/ppcp-subscription/src/FreeTrialHandlerTrait.php +++ b/modules/ppcp-subscription/src/FreeTrialHandlerTrait.php @@ -56,7 +56,10 @@ trait FreeTrialHandlerTrait { $product = wc_get_product(); - if ( ! $product || ! WC_Subscriptions_Product::is_subscription( $product ) ) { + if ( + ! $product || ! WC_Subscriptions_Product::is_subscription( $product ) + || $product->get_meta( '_ppcp_enable_subscription_product' ) === 'yes' + ) { return false; } From 157dd41b7cddcc811db01fe03a77119f1b72fd23 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Fri, 16 Jun 2023 16:28:15 +0200 Subject: [PATCH 13/56] Fix psalm --- .psalm/wcs.php | 9 +++++++++ .../ppcp-subscription/src/DeactivatePlanEndpoint.php | 3 ++- modules/ppcp-subscription/src/SubscriptionModule.php | 10 +++++++++- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/.psalm/wcs.php b/.psalm/wcs.php index 873fd4d50..a35088c88 100644 --- a/.psalm/wcs.php +++ b/.psalm/wcs.php @@ -2084,3 +2084,12 @@ function wcs_find_matching_line_item($order, $subscription_item, $match_type = ' function wcs_order_contains_product($order, $product) { } + +/** + * Get page ID for a specific WC resource. + * + * @param string $for Name of the resource. + * + * @return string Page ID. Empty string if resource not found. + */ +function wc_get_page_screen_id( $for ) {} diff --git a/modules/ppcp-subscription/src/DeactivatePlanEndpoint.php b/modules/ppcp-subscription/src/DeactivatePlanEndpoint.php index c2d490e81..fe10c0638 100644 --- a/modules/ppcp-subscription/src/DeactivatePlanEndpoint.php +++ b/modules/ppcp-subscription/src/DeactivatePlanEndpoint.php @@ -10,6 +10,7 @@ declare( strict_types=1 ); namespace WooCommerce\PayPalCommerce\Subscription; use Exception; +use WC_Product; use WC_Subscriptions_Product; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingPlans; use WooCommerce\PayPalCommerce\Button\Endpoint\RequestData; @@ -68,7 +69,7 @@ class DeactivatePlanEndpoint { $product_id = $data['product_id'] ?? ''; if ( $product_id ) { $product = wc_get_product( $product_id ); - if ( WC_Subscriptions_Product::is_subscription( $product ) ) { + if ( is_a( $product, WC_Product::class ) && WC_Subscriptions_Product::is_subscription( $product ) ) { $product->delete_meta_data( '_ppcp_enable_subscription_product' ); $product->delete_meta_data( '_ppcp_subscription_plan_name' ); $product->delete_meta_data( 'ppcp_subscription_product' ); diff --git a/modules/ppcp-subscription/src/SubscriptionModule.php b/modules/ppcp-subscription/src/SubscriptionModule.php index 9d690129a..7c6d71a7c 100644 --- a/modules/ppcp-subscription/src/SubscriptionModule.php +++ b/modules/ppcp-subscription/src/SubscriptionModule.php @@ -160,7 +160,15 @@ class SubscriptionModule implements ModuleInterface { add_action( 'admin_enqueue_scripts', + /** + * Param types removed to avoid third-party issues. + * + * @psalm-suppress MissingClosureParamType + */ function( $hook ) use ( $c ) { + if ( ! is_string( $hook ) ) { + return; + } $settings = $c->get( 'wcgateway.settings' ); $subscription_mode = $settings->has( 'subscriptions_mode' ) ? $settings->get( 'subscriptions_mode' ) : ''; if ( $hook !== 'post.php' || $subscription_mode !== 'subscriptions_api' ) { @@ -183,7 +191,7 @@ class SubscriptionModule implements ModuleInterface { true ); - $plan = $product->get_meta( 'ppcp_subscription_plan' ) ?? null; + $plan = $product->get_meta( 'ppcp_subscription_plan' ) ?? array(); $plan_id = $plan['id'] ?? ''; wp_localize_script( 'ppcp-paypal-subscription', From 06bfe33db260098cf46d788e02663120a3c5ba42 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 21 Jun 2023 15:42:18 +0200 Subject: [PATCH 14/56] Working on subscriptions (WIP) --- .../js/modules/Helper/Subscriptions.js | 2 +- .../resources/js/paypal-subscription.js | 2 + .../src/Helper/SubscriptionHelper.php | 4 +- .../src/SubscriptionModule.php | 213 +++++++++++++----- .../src/Checkout/DisableGateways.php | 3 +- 5 files changed, 165 insertions(+), 59 deletions(-) diff --git a/modules/ppcp-button/resources/js/modules/Helper/Subscriptions.js b/modules/ppcp-button/resources/js/modules/Helper/Subscriptions.js index 4fd4ae383..bac46fbad 100644 --- a/modules/ppcp-button/resources/js/modules/Helper/Subscriptions.js +++ b/modules/ppcp-button/resources/js/modules/Helper/Subscriptions.js @@ -4,7 +4,7 @@ export const isChangePaymentPage = () => { } export const subscriptionHasPlan = () => { - if (PayPalCommerceGateway.data_client_id.has_subscriptions) { + if (PayPalCommerceGateway.data_client_id.paypal_subscriptions_enabled && PayPalCommerceGateway.data_client_id.has_subscriptions) { if (PayPalCommerceGateway.subscription_plan_id !== '') { return true; } diff --git a/modules/ppcp-subscription/resources/js/paypal-subscription.js b/modules/ppcp-subscription/resources/js/paypal-subscription.js index c42ca2597..ae71a1b8e 100644 --- a/modules/ppcp-subscription/resources/js/paypal-subscription.js +++ b/modules/ppcp-subscription/resources/js/paypal-subscription.js @@ -15,6 +15,8 @@ document.addEventListener( subscriptionTrial.style.display = 'none'; } + console.log('testing') + const unlinkBtn = document.getElementById('ppcp_unlink_sub_plan'); unlinkBtn?.addEventListener('click', (event)=>{ event.preventDefault(); diff --git a/modules/ppcp-subscription/src/Helper/SubscriptionHelper.php b/modules/ppcp-subscription/src/Helper/SubscriptionHelper.php index 2250296b2..6bd1e40fe 100644 --- a/modules/ppcp-subscription/src/Helper/SubscriptionHelper.php +++ b/modules/ppcp-subscription/src/Helper/SubscriptionHelper.php @@ -176,10 +176,8 @@ class SubscriptionHelper { * @throws NotFoundException If setting is not found. */ public function checkout_subscription_product_allowed(): bool { - $subscription_mode = $this->settings->has( 'subscriptions_mode' ) ? $this->settings->get( 'subscriptions_mode' ) : ''; if ( - $subscription_mode !== 'subscriptions_api' - || ! $this->paypal_subscription_id() + ! $this->paypal_subscription_id() || ! $this->cart_contains_only_one_item() ) { return false; diff --git a/modules/ppcp-subscription/src/SubscriptionModule.php b/modules/ppcp-subscription/src/SubscriptionModule.php index 7c6d71a7c..c9ba156fc 100644 --- a/modules/ppcp-subscription/src/SubscriptionModule.php +++ b/modules/ppcp-subscription/src/SubscriptionModule.php @@ -11,6 +11,7 @@ namespace WooCommerce\PayPalCommerce\Subscription; use Exception; use WC_Product; +use WC_Product_Subscription_Variation; use WC_Subscriptions_Product; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingSubscriptions; use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException; @@ -178,7 +179,7 @@ class SubscriptionModule implements ModuleInterface { //phpcs:disable WordPress.Security.NonceVerification.Recommended $post_id = wc_clean( wp_unslash( $_GET['post'] ?? '' ) ); $product = wc_get_product( $post_id ); - if ( ! is_a( $product, WC_Product::class ) || ! WC_Subscriptions_Product::is_subscription( $product ) ) { + if ( ! ( is_a( $product, WC_Product::class) || is_a( $product, WC_Product_Subscription_Variation::class) ) || ! WC_Subscriptions_Product::is_subscription( $product ) ) { return; } @@ -463,40 +464,28 @@ class SubscriptionModule implements ModuleInterface { return; } - $enable_subscription_product = wc_clean( wp_unslash( $_POST['_ppcp_enable_subscription_product'] ?? '' ) ); - $product->update_meta_data( '_ppcp_enable_subscription_product', $enable_subscription_product ); - $product->save(); - - if ( $product->get_type() === 'subscription' && $enable_subscription_product === 'yes' ) { - $subscriptions_api_handler = $c->get( 'subscription.api-handler' ); - assert( $subscriptions_api_handler instanceof SubscriptionsApiHandler ); - - if ( $product->meta_exists( 'ppcp_subscription_product' ) && $product->meta_exists( 'ppcp_subscription_plan' ) ) { - $subscriptions_api_handler->update_product( $product ); - $subscriptions_api_handler->update_plan( $product ); - return; - } - - if ( ! $product->meta_exists( 'ppcp_subscription_product' ) ) { - $subscriptions_api_handler->create_product( $product ); - } - - if ( $product->meta_exists( 'ppcp_subscription_product' ) && ! $product->meta_exists( 'ppcp_subscription_plan' ) ) { - $subscription_plan_name = wc_clean( wp_unslash( $_POST['_ppcp_subscription_plan_name'] ?? '' ) ); - if ( ! is_string( $subscription_plan_name ) ) { - return; - } - - $product->update_meta_data( '_ppcp_subscription_plan_name', $subscription_plan_name ); - $product->save(); - - $subscriptions_api_handler->create_plan( $subscription_plan_name, $product ); - } - } + $subscriptions_api_handler = $c->get('subscription.api-handler'); + assert($subscriptions_api_handler instanceof SubscriptionsApiHandler); + $this->update_subscription_product_meta($product, $subscriptions_api_handler); }, 12 ); + add_action('woocommerce_save_product_variation', function($variation_id) use($c){ + if ( ! WC_Subscriptions_Product::is_subscription( $variation_id ) || empty( $_POST['_wcsnonce_save_variations'] ) || ! wp_verify_nonce( $_POST['_wcsnonce_save_variations'], 'wcs_subscription_variations' ) ) { + return; + } + + $product = wc_get_product( $variation_id ); + if ( ! is_a( $product, WC_Product_Subscription_Variation::class ) ) { + return; + } + + $subscriptions_api_handler = $c->get('subscription.api-handler'); + assert($subscriptions_api_handler instanceof SubscriptionsApiHandler); + $this->update_subscription_product_meta($product, $subscriptions_api_handler); + }); + add_action( 'woocommerce_process_shop_subscription_meta', /** @@ -705,6 +694,32 @@ class SubscriptionModule implements ModuleInterface { } ); + add_action( + 'woocommerce_variation_options_pricing', + function( $loop, $variation_data, $variation ) use ( $c ) { + $settings = $c->get( 'wcgateway.settings' ); + assert( $settings instanceof Settings ); + + try { + $subscriptions_mode = $settings->get( 'subscriptions_mode' ); + if ( $subscriptions_mode === 'subscriptions_api' ) { + $product = wc_get_product( $variation->ID ); + if ( ! is_a( $product, WC_Product_Subscription_Variation::class ) ) { + return; + } + + $environment = $c->get( 'onboarding.environment' ); + $this->render_paypal_subscription_fields( $product, $environment ); + + } + } catch ( NotFoundException $exception ) { + return; + } + }, + 10, + 3 + ); + add_action( 'woocommerce_product_options_general_product_data', function() use ( $c ) { @@ -725,29 +740,8 @@ class SubscriptionModule implements ModuleInterface { return; } - $enable_subscription_product = $product->get_meta( '_ppcp_enable_subscription_product' ); - $subscription_plan_name = $product->get_meta( '_ppcp_subscription_plan_name' ); - - echo ''; + $environment = $c->get( 'onboarding.environment' ); + $this->render_paypal_subscription_fields( $product, $environment ); } } catch ( NotFoundException $exception ) { return; @@ -832,4 +826,115 @@ class SubscriptionModule implements ModuleInterface { } ); } + + /** + * Render PayPal Subscriptions fields. + * + * @param WC_Product $product WC Product. + * @param Environment $environment The environment. + * @return void + */ + private function render_paypal_subscription_fields( WC_Product $product, Environment $environment ): void { + $enable_subscription_product = $product->get_meta( '_ppcp_enable_subscription_product' ); + $subscription_plan_name = $product->get_meta( '_ppcp_subscription_plan_name' ); + + //echo ''; + } + + /** + * Updates subscription product meta. + * + * @param WC_Product $product + * @param SubscriptionsApiHandler $subscriptions_api_handler + * @return void + */ + private function update_subscription_product_meta(WC_Product $product, SubscriptionsApiHandler $subscriptions_api_handler): void + { + $enable_subscription_product = wc_clean(wp_unslash($_POST['_ppcp_enable_subscription_product'] ?? '')); + $product->update_meta_data('_ppcp_enable_subscription_product', $enable_subscription_product); + $product->save(); + + if (($product->get_type() === 'subscription' || $product->get_type() === 'subscription_variation') && $enable_subscription_product === 'yes') { + if ($product->meta_exists('ppcp_subscription_product') && $product->meta_exists('ppcp_subscription_plan')) { + $subscriptions_api_handler->update_product($product); + $subscriptions_api_handler->update_plan($product); + return; + } + + if (!$product->meta_exists('ppcp_subscription_product')) { + $subscriptions_api_handler->create_product($product); + } + + if ($product->meta_exists('ppcp_subscription_product') && !$product->meta_exists('ppcp_subscription_plan')) { + $subscription_plan_name = wc_clean(wp_unslash($_POST['_ppcp_subscription_plan_name'] ?? '')); + if (!is_string($subscription_plan_name)) { + return; + } + + $product->update_meta_data('_ppcp_subscription_plan_name', $subscription_plan_name); + $product->save(); + + $subscriptions_api_handler->create_plan($subscription_plan_name, $product); + } + } + } } diff --git a/modules/ppcp-wc-gateway/src/Checkout/DisableGateways.php b/modules/ppcp-wc-gateway/src/Checkout/DisableGateways.php index e5078ae3f..d2f07d7f1 100644 --- a/modules/ppcp-wc-gateway/src/Checkout/DisableGateways.php +++ b/modules/ppcp-wc-gateway/src/Checkout/DisableGateways.php @@ -124,8 +124,9 @@ class DisableGateways { return true; } + $subscription_mode = $this->settings->has( 'subscriptions_mode' ) ? $this->settings->get( 'subscriptions_mode' ) : ''; if ( - is_checkout() + (is_checkout() && $subscription_mode === 'subscriptions_api') && $this->subscription_helper->cart_contains_subscription() && ! $this->subscription_helper->checkout_subscription_product_allowed() ) { From c8e40b56c473b49e46fca840af42e9081dbcd581 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 28 Jun 2023 17:17:22 +0200 Subject: [PATCH 15/56] Add support for variable subscriptions (WIP) --- .../SingleProductActionHandler.js | 23 ++++++++++++-- .../ContextBootstrap/SingleProductBootstap.js | 21 ++++++++++++- .../js/modules/Helper/Subscriptions.js | 1 + .../ppcp-button/src/Assets/SmartButton.php | 1 + .../src/Helper/SubscriptionHelper.php | 31 +++++++++++++++++++ .../src/SubscriptionsApiHandler.php | 2 +- 6 files changed, 75 insertions(+), 4 deletions(-) diff --git a/modules/ppcp-button/resources/js/modules/ActionHandler/SingleProductActionHandler.js b/modules/ppcp-button/resources/js/modules/ActionHandler/SingleProductActionHandler.js index 3c3371542..152ba64f0 100644 --- a/modules/ppcp-button/resources/js/modules/ActionHandler/SingleProductActionHandler.js +++ b/modules/ppcp-button/resources/js/modules/ActionHandler/SingleProductActionHandler.js @@ -18,11 +18,30 @@ class SingleProductActionHandler { this.errorHandler = errorHandler; } - subscriptionsConfiguration() { + getPlanIdFromVariation = (variation) => { + let subscription_plan = ''; + PayPalCommerceGateway.variable_paypal_subscription_variations.forEach((element) => { + let obj = {}; + variation.forEach(({name, value}) => { + Object.assign(obj, {[name.replace('attribute_', '')]: value}); + }) + + if(JSON.stringify(obj) === JSON.stringify(element.attributes) && element.subscription_plan !== '') { + subscription_plan = element.subscription_plan; + } + }); + + return subscription_plan; + } + + subscriptionsConfiguration(selected_variation = null) { + const subscription_plan = selected_variation !== null ? this.getPlanIdFromVariation(selected_variation) : this.config.subscription_plan_id + console.log(subscription_plan) + return { createSubscription: (data, actions) => { return actions.subscription.create({ - 'plan_id': this.config.subscription_plan_id + 'plan_id': subscription_plan }); }, onApprove: (data, actions) => { diff --git a/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js b/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js index 7ee40db54..25b71b1ab 100644 --- a/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js +++ b/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js @@ -13,6 +13,25 @@ class SingleProductBootstap { this.mutationObserver = new MutationObserver(this.handleChange.bind(this)); } + variations() { + if (!this.hasVariations()) { + return null; + } + + const attributes = [...document.querySelector('form.cart')?.querySelectorAll("[name^='attribute_']")].map( + (element) => { + return { + value: element.value, + name: element.name + } + } + ); + return attributes; + } + + hasVariations() { + return document.querySelector('form.cart')?.classList.contains('variations_form'); + } handleChange() { const shouldRender = this.shouldRender(); @@ -119,7 +138,7 @@ class SingleProductBootstap { PayPalCommerceGateway.data_client_id.has_subscriptions && PayPalCommerceGateway.data_client_id.paypal_subscriptions_enabled ) { - this.renderer.render(actionHandler.subscriptionsConfiguration()); + this.renderer.render(actionHandler.subscriptionsConfiguration(this.variations())); return; } diff --git a/modules/ppcp-button/resources/js/modules/Helper/Subscriptions.js b/modules/ppcp-button/resources/js/modules/Helper/Subscriptions.js index bac46fbad..89ef910fe 100644 --- a/modules/ppcp-button/resources/js/modules/Helper/Subscriptions.js +++ b/modules/ppcp-button/resources/js/modules/Helper/Subscriptions.js @@ -4,6 +4,7 @@ export const isChangePaymentPage = () => { } export const subscriptionHasPlan = () => { + return true; if (PayPalCommerceGateway.data_client_id.paypal_subscriptions_enabled && PayPalCommerceGateway.data_client_id.has_subscriptions) { if (PayPalCommerceGateway.subscription_plan_id !== '') { return true; diff --git a/modules/ppcp-button/src/Assets/SmartButton.php b/modules/ppcp-button/src/Assets/SmartButton.php index ff2472df2..ad0170fb5 100644 --- a/modules/ppcp-button/src/Assets/SmartButton.php +++ b/modules/ppcp-button/src/Assets/SmartButton.php @@ -851,6 +851,7 @@ class SmartButton implements SmartButtonInterface { ), ), 'subscription_plan_id' => $this->subscription_helper->paypal_subscription_id(), + 'variable_paypal_subscription_variations' => $this->subscription_helper->variable_paypal_subscription_variations(), 'enforce_vault' => $this->has_subscriptions(), 'can_save_vault_token' => $this->can_save_vault_token(), 'is_free_trial_cart' => $is_free_trial_cart, diff --git a/modules/ppcp-subscription/src/Helper/SubscriptionHelper.php b/modules/ppcp-subscription/src/Helper/SubscriptionHelper.php index 6bd1e40fe..619fbc658 100644 --- a/modules/ppcp-subscription/src/Helper/SubscriptionHelper.php +++ b/modules/ppcp-subscription/src/Helper/SubscriptionHelper.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Subscription\Helper; use WC_Product; +use WC_Product_Subscription_Variation; use WC_Subscriptions_Product; use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException; @@ -218,6 +219,36 @@ class SubscriptionHelper { return ''; } + public function variable_paypal_subscription_variations(): array { + $variations = array(); + if ( ! $this->current_product_is_subscription() ) { + return $variations; + } + + $product = wc_get_product(); + assert( $product instanceof WC_Product ); + if ( $product->get_type() !== 'variable-subscription' ) { + return $variations; + } + + $variation_ids = $product->get_children(); + foreach ($variation_ids as $id) { + $product = wc_get_product($id); + if(! is_a($product, WC_Product_Subscription_Variation::class )) { + continue; + } + + $subscription_plan = $product->get_meta('ppcp_subscription_plan') ?? ''; + $variations[] = array( + 'id' => $product->get_id(), + 'attributes' => $product->get_attributes(), + 'subscription_plan' => $subscription_plan['id'] ?? '', + ); + } + + return $variations; + } + /** * Checks if cart contains only one item. * diff --git a/modules/ppcp-subscription/src/SubscriptionsApiHandler.php b/modules/ppcp-subscription/src/SubscriptionsApiHandler.php index bdfa9f9b5..569bbf5a5 100644 --- a/modules/ppcp-subscription/src/SubscriptionsApiHandler.php +++ b/modules/ppcp-subscription/src/SubscriptionsApiHandler.php @@ -265,7 +265,7 @@ class SubscriptionsApiHandler { 'REGULAR', array( 'fixed_price' => array( - 'value' => $product->get_meta( '_subscription_price' ), + 'value' => $product->get_meta( '_subscription_price' ) ?: $product->get_price(), 'currency_code' => $this->currency, ), ), From 59afa81160c9ca74e8d36ffb4409cf058d0cb5e2 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Thu, 13 Jul 2023 16:22:57 +0200 Subject: [PATCH 16/56] Introduce paypal-js loading wrapper to be able to reload script --- modules/ppcp-button/package.json | 5 +- .../SingleProductActionHandler.js | 3 +- .../modules/ContextBootstrap/CartBootstap.js | 3 +- .../ContextBootstrap/SingleProductBootstap.js | 22 +++++--- .../js/modules/Helper/ScriptLoading.js | 7 +++ .../js/modules/Helper/Subscriptions.js | 12 ----- .../ppcp-button/src/Assets/SmartButton.php | 52 ++++++++++--------- modules/ppcp-button/yarn.lock | 12 +++++ .../resources/js/paypal-subscription.js | 2 - .../src/Checkout/DisableGateways.php | 9 ---- 10 files changed, 65 insertions(+), 62 deletions(-) diff --git a/modules/ppcp-button/package.json b/modules/ppcp-button/package.json index 61e31d70f..77828e436 100644 --- a/modules/ppcp-button/package.json +++ b/modules/ppcp-button/package.json @@ -11,9 +11,10 @@ "Edge >= 14" ], "dependencies": { + "@paypal/paypal-js": "^6.0.0", "core-js": "^3.25.0", - "formdata-polyfill": "^4.0.10", - "deepmerge": "^4.2.2" + "deepmerge": "^4.2.2", + "formdata-polyfill": "^4.0.10" }, "devDependencies": { "@babel/core": "^7.19", diff --git a/modules/ppcp-button/resources/js/modules/ActionHandler/SingleProductActionHandler.js b/modules/ppcp-button/resources/js/modules/ActionHandler/SingleProductActionHandler.js index 123381912..1aa3e47bd 100644 --- a/modules/ppcp-button/resources/js/modules/ActionHandler/SingleProductActionHandler.js +++ b/modules/ppcp-button/resources/js/modules/ActionHandler/SingleProductActionHandler.js @@ -35,7 +35,6 @@ class SingleProductActionHandler { subscriptionsConfiguration(selected_variation = null) { const subscription_plan = selected_variation !== null ? this.getPlanIdFromVariation(selected_variation) : this.config.subscription_plan_id - console.log(subscription_plan) return { createSubscription: (data, actions) => { @@ -56,7 +55,7 @@ class SingleProductActionHandler { return res.json(); }).then(() => { const id = document.querySelector('[name="add-to-cart"]').value; - const products = [new Product(id, 1, null)]; + const products = [new Product(id, 1, [])]; fetch(this.config.ajax.change_cart.endpoint, { method: 'POST', diff --git a/modules/ppcp-button/resources/js/modules/ContextBootstrap/CartBootstap.js b/modules/ppcp-button/resources/js/modules/ContextBootstrap/CartBootstap.js index 6e65e9be8..504fc4c42 100644 --- a/modules/ppcp-button/resources/js/modules/ContextBootstrap/CartBootstap.js +++ b/modules/ppcp-button/resources/js/modules/ContextBootstrap/CartBootstap.js @@ -1,7 +1,6 @@ import CartActionHandler from '../ActionHandler/CartActionHandler'; import BootstrapHelper from "../Helper/BootstrapHelper"; import {setVisible} from "../Helper/Hiding"; -import {subscriptionHasPlan} from "../Helper/Subscriptions"; class CartBootstrap { constructor(gateway, renderer, errorHandler) { @@ -53,7 +52,7 @@ class CartBootstrap { } shouldRender() { - return document.querySelector(this.gateway.button.wrapper) !== null && subscriptionHasPlan(); + return document.querySelector(this.gateway.button.wrapper) !== null; } shouldEnable() { diff --git a/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js b/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js index b3d3e2359..61f167d31 100644 --- a/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js +++ b/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js @@ -2,7 +2,7 @@ import UpdateCart from "../Helper/UpdateCart"; import SingleProductActionHandler from "../ActionHandler/SingleProductActionHandler"; import {hide, show} from "../Helper/Hiding"; import BootstrapHelper from "../Helper/BootstrapHelper"; -import {subscriptionHasPlan} from "../Helper/Subscriptions"; +import {loadPaypalJsScript} from "../Helper/ScriptLoading"; class SingleProductBootstap { constructor(gateway, renderer, messages, errorHandler) { @@ -80,8 +80,7 @@ class SingleProductBootstap { shouldRender() { return this.form() !== null - && !this.isWcsattSubscriptionMode() - && subscriptionHasPlan(); + && !this.isWcsattSubscriptionMode(); } shouldEnable() { @@ -120,10 +119,6 @@ class SingleProductBootstap { } priceAmountIsZero() { - if(subscriptionHasPlan()) { - return false; - } - const price = this.priceAmount(); return !price || price === 0; } @@ -168,7 +163,18 @@ class SingleProductBootstap { PayPalCommerceGateway.data_client_id.has_subscriptions && PayPalCommerceGateway.data_client_id.paypal_subscriptions_enabled ) { - this.renderer.render(actionHandler.subscriptionsConfiguration(this.variations())); + const buttonWrapper = document.getElementById('ppc-button-ppcp-gateway'); + buttonWrapper.innerHTML = ''; + loadPaypalJsScript( + { + clientId: PayPalCommerceGateway.client_id, + currency: PayPalCommerceGateway.currency, + intent: 'subscription', + vault: true + }, + actionHandler.subscriptionsConfiguration(this.variations()), + this.gateway.button.wrapper + ); return; } diff --git a/modules/ppcp-button/resources/js/modules/Helper/ScriptLoading.js b/modules/ppcp-button/resources/js/modules/Helper/ScriptLoading.js index c5742ab19..5925d6263 100644 --- a/modules/ppcp-button/resources/js/modules/Helper/ScriptLoading.js +++ b/modules/ppcp-button/resources/js/modules/Helper/ScriptLoading.js @@ -1,4 +1,5 @@ import dataClientIdAttributeHandler from "../DataClientIdAttributeHandler"; +import {loadScript} from "@paypal/paypal-js"; export const loadPaypalScript = (config, onLoaded) => { if (typeof paypal !== 'undefined') { @@ -22,3 +23,9 @@ export const loadPaypalScript = (config, onLoaded) => { document.body.appendChild(script); } + +export const loadPaypalJsScript = (options, buttons, container) => { + loadScript(options).then((paypal) => { + paypal.Buttons(buttons).render(container); + }); +} diff --git a/modules/ppcp-button/resources/js/modules/Helper/Subscriptions.js b/modules/ppcp-button/resources/js/modules/Helper/Subscriptions.js index bac46fbad..a205ac459 100644 --- a/modules/ppcp-button/resources/js/modules/Helper/Subscriptions.js +++ b/modules/ppcp-button/resources/js/modules/Helper/Subscriptions.js @@ -2,15 +2,3 @@ export const isChangePaymentPage = () => { const urlParams = new URLSearchParams(window.location.search) return urlParams.has('change_payment_method'); } - -export const subscriptionHasPlan = () => { - if (PayPalCommerceGateway.data_client_id.paypal_subscriptions_enabled && PayPalCommerceGateway.data_client_id.has_subscriptions) { - if (PayPalCommerceGateway.subscription_plan_id !== '') { - return true; - } - - return false; - } - - return true; -} diff --git a/modules/ppcp-button/src/Assets/SmartButton.php b/modules/ppcp-button/src/Assets/SmartButton.php index d86798eea..9b7f20acc 100644 --- a/modules/ppcp-button/src/Assets/SmartButton.php +++ b/modules/ppcp-button/src/Assets/SmartButton.php @@ -813,10 +813,12 @@ class SmartButton implements SmartButtonInterface { $this->request_data->enqueue_nonce_fix(); $localize = array( - 'url' => add_query_arg( $url_params, 'https://www.paypal.com/sdk/js' ), - 'url_params' => $url_params, - 'script_attributes' => $this->attributes(), - 'data_client_id' => array( + 'url' => add_query_arg( $url_params, 'https://www.paypal.com/sdk/js' ), + 'url_params' => $url_params, + 'script_attributes' => $this->attributes(), + 'client_id' => $this->client_id, + 'currency' => $this->currency, + 'data_client_id' => array( 'set_attribute' => ( is_checkout() && $this->dcc_is_enabled() ) || $this->can_save_vault_token(), 'endpoint' => \WC_AJAX::get_endpoint( DataClientIdEndpoint::ENDPOINT ), 'nonce' => wp_create_nonce( DataClientIdEndpoint::nonce() ), @@ -824,9 +826,9 @@ class SmartButton implements SmartButtonInterface { 'has_subscriptions' => $this->has_subscriptions(), 'paypal_subscriptions_enabled' => $this->paypal_subscriptions_enabled(), ), - 'redirect' => wc_get_checkout_url(), - 'context' => $this->context(), - 'ajax' => array( + 'redirect' => wc_get_checkout_url(), + 'context' => $this->context(), + 'ajax' => array( 'change_cart' => array( 'endpoint' => \WC_AJAX::get_endpoint( ChangeCartEndpoint::ENDPOINT ), 'nonce' => wp_create_nonce( ChangeCartEndpoint::nonce() ), @@ -859,15 +861,15 @@ class SmartButton implements SmartButtonInterface { 'endpoint' => \WC_AJAX::get_endpoint( CartScriptParamsEndpoint::ENDPOINT ), ), ), - 'subscription_plan_id' => $this->subscription_helper->paypal_subscription_id(), + 'subscription_plan_id' => $this->subscription_helper->paypal_subscription_id(), 'variable_paypal_subscription_variations' => $this->subscription_helper->variable_paypal_subscription_variations(), - 'enforce_vault' => $this->has_subscriptions(), - 'can_save_vault_token' => $this->can_save_vault_token(), - 'is_free_trial_cart' => $is_free_trial_cart, - 'vaulted_paypal_email' => ( is_checkout() && $is_free_trial_cart ) ? $this->get_vaulted_paypal_email() : '', - 'bn_codes' => $this->bn_codes(), - 'payer' => $this->payerData(), - 'button' => array( + 'enforce_vault' => $this->has_subscriptions(), + 'can_save_vault_token' => $this->can_save_vault_token(), + 'is_free_trial_cart' => $is_free_trial_cart, + 'vaulted_paypal_email' => ( is_checkout() && $is_free_trial_cart ) ? $this->get_vaulted_paypal_email() : '', + 'bn_codes' => $this->bn_codes(), + 'payer' => $this->payerData(), + 'button' => array( 'wrapper' => '#ppc-button-' . PayPalGateway::ID, 'is_disabled' => $this->is_button_disabled(), 'mini_cart_wrapper' => '#ppc-button-minicart', @@ -889,7 +891,7 @@ class SmartButton implements SmartButtonInterface { 'tagline' => $this->style_for_context( 'tagline', $this->context() ), ), ), - 'separate_buttons' => array( + 'separate_buttons' => array( 'card' => array( 'id' => CardButtonGateway::ID, 'wrapper' => '#ppc-button-' . CardButtonGateway::ID, @@ -899,7 +901,7 @@ class SmartButton implements SmartButtonInterface { ), ), ), - 'hosted_fields' => array( + 'hosted_fields' => array( 'wrapper' => '#ppcp-hosted-fields', 'labels' => array( 'credit_card_number' => '', @@ -922,8 +924,8 @@ class SmartButton implements SmartButtonInterface { 'valid_cards' => $this->dcc_applies->valid_cards(), 'contingency' => $this->get_3ds_contingency(), ), - 'messages' => $this->message_values(), - 'labels' => array( + 'messages' => $this->message_values(), + 'labels' => array( 'error' => array( 'generic' => __( 'Something went wrong. Please try again or choose another payment source.', @@ -950,12 +952,12 @@ class SmartButton implements SmartButtonInterface { // phpcs:ignore WordPress.WP.I18n 'shipping_field' => _x( 'Shipping %s', 'checkout-validation', 'woocommerce' ), ), - 'order_id' => 'pay-now' === $this->context() ? $this->get_order_pay_id() : 0, - 'single_product_buttons_enabled' => $this->settings_status->is_smart_button_enabled_for_location( 'product' ), - 'mini_cart_buttons_enabled' => $this->settings_status->is_smart_button_enabled_for_location( 'mini-cart' ), - 'basic_checkout_validation_enabled' => $this->basic_checkout_validation_enabled, - 'early_checkout_validation_enabled' => $this->early_validation_enabled, - 'funding_sources_without_redirect' => $this->funding_sources_without_redirect, + 'order_id' => 'pay-now' === $this->context() ? $this->get_order_pay_id() : 0, + 'single_product_buttons_enabled' => $this->settings_status->is_smart_button_enabled_for_location( 'product' ), + 'mini_cart_buttons_enabled' => $this->settings_status->is_smart_button_enabled_for_location( 'mini-cart' ), + 'basic_checkout_validation_enabled' => $this->basic_checkout_validation_enabled, + 'early_checkout_validation_enabled' => $this->early_validation_enabled, + 'funding_sources_without_redirect' => $this->funding_sources_without_redirect, ); if ( $this->style_for_context( 'layout', 'mini-cart' ) !== 'horizontal' ) { diff --git a/modules/ppcp-button/yarn.lock b/modules/ppcp-button/yarn.lock index e76c992e1..9d5ebef6e 100644 --- a/modules/ppcp-button/yarn.lock +++ b/modules/ppcp-button/yarn.lock @@ -956,6 +956,13 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" +"@paypal/paypal-js@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@paypal/paypal-js/-/paypal-js-6.0.0.tgz#a5a9556af29e4a0124049bf9a093606f52b8a951" + integrity sha512-FYzjYby9F7tgg4tUxYNseZ6vkeDJcdcjoULsyNhfrWZZjicDpdj5932fZlyUlQXDSR9KlhjXH6H4nPIJ0Lq0Kw== + dependencies: + promise-polyfill "^8.3.0" + "@types/eslint-scope@^3.7.3": version "3.7.4" resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.4.tgz#37fc1223f0786c39627068a12e94d6e6fc61de16" @@ -1858,6 +1865,11 @@ pkg-dir@^4.1.0, pkg-dir@^4.2.0: 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.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" diff --git a/modules/ppcp-subscription/resources/js/paypal-subscription.js b/modules/ppcp-subscription/resources/js/paypal-subscription.js index ae71a1b8e..c42ca2597 100644 --- a/modules/ppcp-subscription/resources/js/paypal-subscription.js +++ b/modules/ppcp-subscription/resources/js/paypal-subscription.js @@ -15,8 +15,6 @@ document.addEventListener( subscriptionTrial.style.display = 'none'; } - console.log('testing') - const unlinkBtn = document.getElementById('ppcp_unlink_sub_plan'); unlinkBtn?.addEventListener('click', (event)=>{ event.preventDefault(); diff --git a/modules/ppcp-wc-gateway/src/Checkout/DisableGateways.php b/modules/ppcp-wc-gateway/src/Checkout/DisableGateways.php index 380f64818..6afa24f31 100644 --- a/modules/ppcp-wc-gateway/src/Checkout/DisableGateways.php +++ b/modules/ppcp-wc-gateway/src/Checkout/DisableGateways.php @@ -129,15 +129,6 @@ class DisableGateways { return true; } - $subscription_mode = $this->settings->has( 'subscriptions_mode' ) ? $this->settings->get( 'subscriptions_mode' ) : ''; - if ( - (is_checkout() && $subscription_mode === 'subscriptions_api') - && $this->subscription_helper->cart_contains_subscription() - && ! $this->subscription_helper->checkout_subscription_product_allowed() - ) { - return true; - } - return false; } From 92a59346a77df258b6d0f0b64aabeb3ec7d2943b Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 17 Jul 2023 11:49:51 +0200 Subject: [PATCH 17/56] Add subscription fields for variable subscription (WIP) --- .../src/SubscriptionModule.php | 127 ++++++++++-------- 1 file changed, 68 insertions(+), 59 deletions(-) diff --git a/modules/ppcp-subscription/src/SubscriptionModule.php b/modules/ppcp-subscription/src/SubscriptionModule.php index c9ba156fc..d7e39bf7c 100644 --- a/modules/ppcp-subscription/src/SubscriptionModule.php +++ b/modules/ppcp-subscription/src/SubscriptionModule.php @@ -12,6 +12,7 @@ namespace WooCommerce\PayPalCommerce\Subscription; use Exception; use WC_Product; use WC_Product_Subscription_Variation; +use WC_Product_Variable_Subscription; use WC_Subscriptions_Product; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingSubscriptions; use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException; @@ -694,61 +695,6 @@ class SubscriptionModule implements ModuleInterface { } ); - add_action( - 'woocommerce_variation_options_pricing', - function( $loop, $variation_data, $variation ) use ( $c ) { - $settings = $c->get( 'wcgateway.settings' ); - assert( $settings instanceof Settings ); - - try { - $subscriptions_mode = $settings->get( 'subscriptions_mode' ); - if ( $subscriptions_mode === 'subscriptions_api' ) { - $product = wc_get_product( $variation->ID ); - if ( ! is_a( $product, WC_Product_Subscription_Variation::class ) ) { - return; - } - - $environment = $c->get( 'onboarding.environment' ); - $this->render_paypal_subscription_fields( $product, $environment ); - - } - } catch ( NotFoundException $exception ) { - return; - } - }, - 10, - 3 - ); - - add_action( - 'woocommerce_product_options_general_product_data', - function() use ( $c ) { - $settings = $c->get( 'wcgateway.settings' ); - assert( $settings instanceof Settings ); - - try { - $subscriptions_mode = $settings->get( 'subscriptions_mode' ); - if ( $subscriptions_mode === 'subscriptions_api' ) { - /** - * Needed for getting global post object. - * - * @psalm-suppress InvalidGlobal - */ - global $post; - $product = wc_get_product( $post->ID ); - if ( ! is_a( $product, WC_Product::class ) ) { - return; - } - - $environment = $c->get( 'onboarding.environment' ); - $this->render_paypal_subscription_fields( $product, $environment ); - } - } catch ( NotFoundException $exception ) { - return; - } - } - ); - add_filter( 'woocommerce_order_data_store_cpt_get_orders_query', /** @@ -825,6 +771,64 @@ class SubscriptionModule implements ModuleInterface { } } ); + + add_action( + 'woocommerce_product_options_general_product_data', + function() use ( $c ) { + $settings = $c->get( 'wcgateway.settings' ); + assert( $settings instanceof Settings ); + + try { + $subscriptions_mode = $settings->get( 'subscriptions_mode' ); + if ( $subscriptions_mode === 'subscriptions_api' ) { + /** + * Needed for getting global post object. + * + * @psalm-suppress InvalidGlobal + */ + global $post; + $product = wc_get_product( $post->ID ); + if ( ! is_a( $product, WC_Product::class ) ) { + return; + } + + $environment = $c->get( 'onboarding.environment' ); + echo ''; + + } + } catch ( NotFoundException $exception ) { + return; + } + } + ); + + add_action( + 'woocommerce_variation_options_pricing', + function( $loop, $variation_data, $variation ) use ( $c ) { + $settings = $c->get( 'wcgateway.settings' ); + assert( $settings instanceof Settings ); + + try { + $subscriptions_mode = $settings->get( 'subscriptions_mode' ); + if ( $subscriptions_mode === 'subscriptions_api' ) { + $product = wc_get_product( $variation->ID ); + if ( ! is_a( $product, WC_Product_Subscription_Variation::class ) ) { + return; + } + + $environment = $c->get( 'onboarding.environment' ); + $this->render_paypal_subscription_fields( $product, $environment ); + + } + } catch ( NotFoundException $exception ) { + return; + } + }, + 10, + 3 + ); } /** @@ -835,11 +839,17 @@ class SubscriptionModule implements ModuleInterface { * @return void */ private function render_paypal_subscription_fields( WC_Product $product, Environment $environment ): void { + if(is_a($product, WC_Product_Variable_Subscription::class)) { + $variations = $product->get_available_variations(); + foreach ($variations as $variation) { + $a = 1; + // $variation['variation_id'] + } + } + $enable_subscription_product = $product->get_meta( '_ppcp_enable_subscription_product' ); $subscription_plan_name = $product->get_meta( '_ppcp_subscription_plan_name' ); - //echo ''; } /** From b066fc7ccf752eb15d5a0dac736a492d0073ac57 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 17 Jul 2023 15:13:25 +0200 Subject: [PATCH 18/56] Add subscription support for variable subscriptions --- .../resources/js/paypal-subscription.js | 111 +++++++------- .../src/SubscriptionModule.php | 136 ++++++++++-------- 2 files changed, 137 insertions(+), 110 deletions(-) diff --git a/modules/ppcp-subscription/resources/js/paypal-subscription.js b/modules/ppcp-subscription/resources/js/paypal-subscription.js index c42ca2597..a00bf57a0 100644 --- a/modules/ppcp-subscription/resources/js/paypal-subscription.js +++ b/modules/ppcp-subscription/resources/js/paypal-subscription.js @@ -1,64 +1,73 @@ document.addEventListener( 'DOMContentLoaded', () => { - if(PayPalCommerceGatewayPayPalSubscription.product_connected === 'yes') { - const periodInterval = document.querySelector('#_subscription_period_interval'); - periodInterval.setAttribute('disabled', 'disabled'); + const setupProducts = () => { + PayPalCommerceGatewayPayPalSubscriptionProducts?.forEach((product) => { + if(product.product_connected === 'yes') { + const periodInterval = document.querySelector('#_subscription_period_interval'); + periodInterval.setAttribute('disabled', 'disabled'); - const subscriptionPeriod = document.querySelector('#_subscription_period'); - subscriptionPeriod.setAttribute('disabled', 'disabled'); + const subscriptionPeriod = document.querySelector('#_subscription_period'); + subscriptionPeriod.setAttribute('disabled', 'disabled'); - const subscriptionLength = document.querySelector('._subscription_length_field'); - subscriptionLength.style.display = 'none'; + const subscriptionLength = document.querySelector('._subscription_length_field'); + subscriptionLength.style.display = 'none'; - const subscriptionTrial = document.querySelector('._subscription_trial_length_field'); - subscriptionTrial.style.display = 'none'; - } - - const unlinkBtn = document.getElementById('ppcp_unlink_sub_plan'); - unlinkBtn?.addEventListener('click', (event)=>{ - event.preventDefault(); - unlinkBtn.disabled = true; - const spinner = document.getElementById('spinner-unlink-plan'); - spinner.style.display = 'inline-block'; - - fetch(PayPalCommerceGatewayPayPalSubscription.ajax.deactivate_plan.endpoint, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - credentials: 'same-origin', - body: JSON.stringify({ - nonce: PayPalCommerceGatewayPayPalSubscription.ajax.deactivate_plan.nonce, - plan_id: PayPalCommerceGatewayPayPalSubscription.ajax.deactivate_plan.plan_id, - product_id: PayPalCommerceGatewayPayPalSubscription.ajax.deactivate_plan.product_id - }) - }).then(function (res) { - return res.json(); - }).then(function (data) { - if (!data.success) { - unlinkBtn.disabled = false; - spinner.style.display = 'none'; - console.error(data); - throw Error(data.data.message); + const subscriptionTrial = document.querySelector('._subscription_trial_length_field'); + subscriptionTrial.style.display = 'none'; } - const enableSubscription = document.getElementById('ppcp-enable-subscription'); - const product = document.getElementById('pcpp-product'); - const plan = document.getElementById('pcpp-plan'); - enableSubscription.style.display = 'none'; - product.style.display = 'none'; - plan.style.display = 'none'; + const unlinkBtn = document.getElementById(`ppcp-unlink-sub-plan-${product.ajax.deactivate_plan.product_id}`); + unlinkBtn?.addEventListener('click', (event)=>{ + event.preventDefault(); + unlinkBtn.disabled = true; + const spinner = document.getElementById('spinner-unlink-plan'); + spinner.style.display = 'inline-block'; - const enable_subscription_product = document.getElementById('ppcp_enable_subscription_product'); - enable_subscription_product.disabled = true; + fetch(product.ajax.deactivate_plan.endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + credentials: 'same-origin', + body: JSON.stringify({ + nonce: product.ajax.deactivate_plan.nonce, + plan_id: product.ajax.deactivate_plan.plan_id, + product_id: product.ajax.deactivate_plan.product_id + }) + }).then(function (res) { + return res.json(); + }).then(function (data) { + if (!data.success) { + unlinkBtn.disabled = false; + spinner.style.display = 'none'; + console.error(data); + throw Error(data.data.message); + } - const planUnlinked = document.getElementById('pcpp-plan-unlinked'); - planUnlinked.style.display = 'block'; + const enableSubscription = document.getElementById('ppcp-enable-subscription'); + const product = document.getElementById('pcpp-product'); + const plan = document.getElementById('pcpp-plan'); + enableSubscription.style.display = 'none'; + product.style.display = 'none'; + plan.style.display = 'none'; - setTimeout(() => { - location.reload(); - }, 1000) - }); + const enable_subscription_product = document.getElementById('ppcp_enable_subscription_product'); + enable_subscription_product.disabled = true; + + const planUnlinked = document.getElementById('pcpp-plan-unlinked'); + planUnlinked.style.display = 'block'; + + setTimeout(() => { + location.reload(); + }, 1000) + }); + }); + }) + } + + setupProducts(); + jQuery( '#woocommerce-product-data' ).on('woocommerce_variations_loaded', () => { + setupProducts(); }); }); diff --git a/modules/ppcp-subscription/src/SubscriptionModule.php b/modules/ppcp-subscription/src/SubscriptionModule.php index d7e39bf7c..5b2978e35 100644 --- a/modules/ppcp-subscription/src/SubscriptionModule.php +++ b/modules/ppcp-subscription/src/SubscriptionModule.php @@ -180,7 +180,7 @@ class SubscriptionModule implements ModuleInterface { //phpcs:disable WordPress.Security.NonceVerification.Recommended $post_id = wc_clean( wp_unslash( $_GET['post'] ?? '' ) ); $product = wc_get_product( $post_id ); - if ( ! ( is_a( $product, WC_Product::class) || is_a( $product, WC_Product_Subscription_Variation::class) ) || ! WC_Subscriptions_Product::is_subscription( $product ) ) { + if ( ! ( is_a( $product, WC_Product::class ) || is_a( $product, WC_Product_Subscription_Variation::class ) ) || ! WC_Subscriptions_Product::is_subscription( $product ) ) { return; } @@ -193,22 +193,20 @@ class SubscriptionModule implements ModuleInterface { true ); - $plan = $product->get_meta( 'ppcp_subscription_plan' ) ?? array(); - $plan_id = $plan['id'] ?? ''; + $products = array( $this->set_product_config( $product ) ); + if ( $product->get_type() === 'variable-subscription' ) { + $products = array(); + $available_variations = $product->get_available_variations(); + foreach ( $available_variations as $variation ) { + $variation = wc_get_product_object( 'variation', $variation['variation_id'] ); + $products[] = $this->set_product_config( $variation ); + } + } + wp_localize_script( 'ppcp-paypal-subscription', - 'PayPalCommerceGatewayPayPalSubscription', - array( - 'product_connected' => $product->get_meta( '_ppcp_enable_subscription_product' ) ?? '', - 'ajax' => array( - 'deactivate_plan' => array( - 'endpoint' => \WC_AJAX::get_endpoint( DeactivatePlanEndpoint::ENDPOINT ), - 'nonce' => wp_create_nonce( DeactivatePlanEndpoint::ENDPOINT ), - 'plan_id' => $plan_id, - 'product_id' => $product->get_id(), - ), - ), - ) + 'PayPalCommerceGatewayPayPalSubscriptionProducts', + $products ); } ); @@ -465,27 +463,31 @@ class SubscriptionModule implements ModuleInterface { return; } - $subscriptions_api_handler = $c->get('subscription.api-handler'); - assert($subscriptions_api_handler instanceof SubscriptionsApiHandler); - $this->update_subscription_product_meta($product, $subscriptions_api_handler); + $subscriptions_api_handler = $c->get( 'subscription.api-handler' ); + assert( $subscriptions_api_handler instanceof SubscriptionsApiHandler ); + $this->update_subscription_product_meta( $product, $subscriptions_api_handler ); }, 12 ); - add_action('woocommerce_save_product_variation', function($variation_id) use($c){ - if ( ! WC_Subscriptions_Product::is_subscription( $variation_id ) || empty( $_POST['_wcsnonce_save_variations'] ) || ! wp_verify_nonce( $_POST['_wcsnonce_save_variations'], 'wcs_subscription_variations' ) ) { - return; - } + add_action( + 'woocommerce_save_product_variation', + function( $variation_id ) use ( $c ) { + $wcsnonce_save_variations = wc_clean( wp_unslash( $_POST['_wcsnonce_save_variations'] ?? '' ) ); + if ( ! WC_Subscriptions_Product::is_subscription( $variation_id ) || empty( $wcsnonce_save_variations ) || ! wp_verify_nonce( $wcsnonce_save_variations, 'wcs_subscription_variations' ) ) { + return; + } - $product = wc_get_product( $variation_id ); - if ( ! is_a( $product, WC_Product_Subscription_Variation::class ) ) { - return; - } + $product = wc_get_product( $variation_id ); + if ( ! is_a( $product, WC_Product_Subscription_Variation::class ) ) { + return; + } - $subscriptions_api_handler = $c->get('subscription.api-handler'); - assert($subscriptions_api_handler instanceof SubscriptionsApiHandler); - $this->update_subscription_product_meta($product, $subscriptions_api_handler); - }); + $subscriptions_api_handler = $c->get( 'subscription.api-handler' ); + assert( $subscriptions_api_handler instanceof SubscriptionsApiHandler ); + $this->update_subscription_product_meta( $product, $subscriptions_api_handler ); + } + ); add_action( 'woocommerce_process_shop_subscription_meta', @@ -839,16 +841,7 @@ class SubscriptionModule implements ModuleInterface { * @return void */ private function render_paypal_subscription_fields( WC_Product $product, Environment $environment ): void { - if(is_a($product, WC_Product_Variable_Subscription::class)) { - $variations = $product->get_available_variations(); - foreach ($variations as $variation) { - $a = 1; - // $variation['variation_id'] - } - } - $enable_subscription_product = $product->get_meta( '_ppcp_enable_subscription_product' ); - $subscription_plan_name = $product->get_meta( '_ppcp_subscription_plan_name' ); echo '

'; echo sprintf( @@ -868,14 +861,15 @@ class SubscriptionModule implements ModuleInterface { echo wc_help_tip( esc_html__( 'Create a subscription product and plan to bill customers at regular intervals. Be aware that certain subscription settings cannot be modified once the PayPal Subscription is linked to this product. Unlink the product to edit disabled fields.', 'woocommerce-paypal-payments' ) ); echo '

'; - $subscription_product = $product->get_meta( 'ppcp_subscription_product' ); - $subscription_plan = $product->get_meta( 'ppcp_subscription_plan' ); + $subscription_product = $product->get_meta( 'ppcp_subscription_product' ); + $subscription_plan = $product->get_meta( 'ppcp_subscription_plan' ); + $subscription_plan_name = $product->get_meta( '_ppcp_subscription_plan_name' ); if ( $subscription_product && $subscription_plan ) { if ( $enable_subscription_product !== 'yes' ) { echo sprintf( // translators: %1$s and %2$s are button and wrapper html tags. esc_html__( '%1$sUnlink PayPal Subscription Plan%2$s', 'woocommerce-paypal-payments' ), - '

' ); echo sprintf( @@ -912,38 +906,62 @@ class SubscriptionModule implements ModuleInterface { /** * Updates subscription product meta. * - * @param WC_Product $product - * @param SubscriptionsApiHandler $subscriptions_api_handler + * @param WC_Product $product The product. + * @param SubscriptionsApiHandler $subscriptions_api_handler The subscription api handler. * @return void */ - private function update_subscription_product_meta(WC_Product $product, SubscriptionsApiHandler $subscriptions_api_handler): void - { - $enable_subscription_product = wc_clean(wp_unslash($_POST['_ppcp_enable_subscription_product'] ?? '')); - $product->update_meta_data('_ppcp_enable_subscription_product', $enable_subscription_product); + private function update_subscription_product_meta( WC_Product $product, SubscriptionsApiHandler $subscriptions_api_handler ): void { + // phpcs:ignore WordPress.Security.NonceVerification + $enable_subscription_product = wc_clean( wp_unslash( $_POST['_ppcp_enable_subscription_product'] ?? '' ) ); + $product->update_meta_data( '_ppcp_enable_subscription_product', $enable_subscription_product ); $product->save(); - if (($product->get_type() === 'subscription' || $product->get_type() === 'subscription_variation') && $enable_subscription_product === 'yes') { - if ($product->meta_exists('ppcp_subscription_product') && $product->meta_exists('ppcp_subscription_plan')) { - $subscriptions_api_handler->update_product($product); - $subscriptions_api_handler->update_plan($product); + if ( ( $product->get_type() === 'subscription' || $product->get_type() === 'subscription_variation' ) && $enable_subscription_product === 'yes' ) { + if ( $product->meta_exists( 'ppcp_subscription_product' ) && $product->meta_exists( 'ppcp_subscription_plan' ) ) { + $subscriptions_api_handler->update_product( $product ); + $subscriptions_api_handler->update_plan( $product ); return; } - if (!$product->meta_exists('ppcp_subscription_product')) { - $subscriptions_api_handler->create_product($product); + if ( ! $product->meta_exists( 'ppcp_subscription_product' ) ) { + $subscriptions_api_handler->create_product( $product ); } - if ($product->meta_exists('ppcp_subscription_product') && !$product->meta_exists('ppcp_subscription_plan')) { - $subscription_plan_name = wc_clean(wp_unslash($_POST['_ppcp_subscription_plan_name'] ?? '')); - if (!is_string($subscription_plan_name)) { + if ( $product->meta_exists( 'ppcp_subscription_product' ) && ! $product->meta_exists( 'ppcp_subscription_plan' ) ) { + // phpcs:ignore WordPress.Security.NonceVerification + $subscription_plan_name = wc_clean( wp_unslash( $_POST['_ppcp_subscription_plan_name'] ?? '' ) ); + if ( ! is_string( $subscription_plan_name ) ) { return; } - $product->update_meta_data('_ppcp_subscription_plan_name', $subscription_plan_name); + $product->update_meta_data( '_ppcp_subscription_plan_name', $subscription_plan_name ); $product->save(); - $subscriptions_api_handler->create_plan($subscription_plan_name, $product); + $subscriptions_api_handler->create_plan( $subscription_plan_name, $product ); } } } + + /** + * Returns subscription product configuration. + * + * @param WC_Product $product The product. + * @return array + */ + private function set_product_config( WC_Product $product ): array { + $plan = $product->get_meta( 'ppcp_subscription_plan' ) ?? array(); + $plan_id = $plan['id'] ?? ''; + + return array( + 'product_connected' => $product->get_meta( '_ppcp_enable_subscription_product' ) ?? '', + 'ajax' => array( + 'deactivate_plan' => array( + 'endpoint' => \WC_AJAX::get_endpoint( DeactivatePlanEndpoint::ENDPOINT ), + 'nonce' => wp_create_nonce( DeactivatePlanEndpoint::ENDPOINT ), + 'plan_id' => $plan_id, + 'product_id' => $product->get_id(), + ), + ), + ); + } } From 4ed7742d9acb02407ef8d8d0ad0ac8163e5a4b1a Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 18 Jul 2023 17:17:22 +0200 Subject: [PATCH 19/56] Disable variable subscription fields (WIP) --- .../resources/js/paypal-subscription.js | 41 ++++++++++++------- .../src/SubscriptionModule.php | 4 +- 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/modules/ppcp-subscription/resources/js/paypal-subscription.js b/modules/ppcp-subscription/resources/js/paypal-subscription.js index a00bf57a0..427fdda96 100644 --- a/modules/ppcp-subscription/resources/js/paypal-subscription.js +++ b/modules/ppcp-subscription/resources/js/paypal-subscription.js @@ -1,23 +1,36 @@ document.addEventListener( 'DOMContentLoaded', () => { + const variations = document.querySelector('.woocommerce_variations'); + const disableFields = (productId) => { + if(variations) { + const children = variations.children; + for(let i=0; i < children.length; i++) { + const variableId = children[i].querySelector('h3').getElementsByClassName('variable_post_id')[0].value + if( parseInt(variableId) === productId ) { + children[i].querySelector('.woocommerce_variable_attributes').getElementsByClassName('wc_input_subscription_period_interval')[0].setAttribute('disabled', 'disabled'); + } + + const periodInterval = document.querySelector('#_subscription_period_interval'); + periodInterval.setAttribute('disabled', 'disabled'); + + const subscriptionPeriod = document.querySelector('#_subscription_period'); + subscriptionPeriod.setAttribute('disabled', 'disabled'); + + const subscriptionLength = document.querySelector('._subscription_length_field'); + subscriptionLength.style.display = 'none'; + + const subscriptionTrial = document.querySelector('._subscription_trial_length_field'); + subscriptionTrial.style.display = 'none'; + } + const setupProducts = () => { PayPalCommerceGatewayPayPalSubscriptionProducts?.forEach((product) => { if(product.product_connected === 'yes') { - const periodInterval = document.querySelector('#_subscription_period_interval'); - periodInterval.setAttribute('disabled', 'disabled'); - - const subscriptionPeriod = document.querySelector('#_subscription_period'); - subscriptionPeriod.setAttribute('disabled', 'disabled'); - - const subscriptionLength = document.querySelector('._subscription_length_field'); - subscriptionLength.style.display = 'none'; - - const subscriptionTrial = document.querySelector('._subscription_trial_length_field'); - subscriptionTrial.style.display = 'none'; + disableFields(product.product_id); } - const unlinkBtn = document.getElementById(`ppcp-unlink-sub-plan-${product.ajax.deactivate_plan.product_id}`); + const unlinkBtn = document.getElementById(`ppcp-unlink-sub-plan-${product.product_id}`); unlinkBtn?.addEventListener('click', (event)=>{ event.preventDefault(); unlinkBtn.disabled = true; @@ -32,8 +45,8 @@ document.addEventListener( credentials: 'same-origin', body: JSON.stringify({ nonce: product.ajax.deactivate_plan.nonce, - plan_id: product.ajax.deactivate_plan.plan_id, - product_id: product.ajax.deactivate_plan.product_id + plan_id: product.plan_id, + product_id: product.product_id }) }).then(function (res) { return res.json(); diff --git a/modules/ppcp-subscription/src/SubscriptionModule.php b/modules/ppcp-subscription/src/SubscriptionModule.php index 5b2978e35..1ed3d75e5 100644 --- a/modules/ppcp-subscription/src/SubscriptionModule.php +++ b/modules/ppcp-subscription/src/SubscriptionModule.php @@ -954,12 +954,12 @@ class SubscriptionModule implements ModuleInterface { return array( 'product_connected' => $product->get_meta( '_ppcp_enable_subscription_product' ) ?? '', + 'plan_id' => $plan_id, + 'product_id' => $product->get_id(), 'ajax' => array( 'deactivate_plan' => array( 'endpoint' => \WC_AJAX::get_endpoint( DeactivatePlanEndpoint::ENDPOINT ), 'nonce' => wp_create_nonce( DeactivatePlanEndpoint::ENDPOINT ), - 'plan_id' => $plan_id, - 'product_id' => $product->get_id(), ), ), ); From 2273a87659a6a5474227e6e32a3c24fc333c0024 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 18 Jul 2023 17:25:43 +0200 Subject: [PATCH 20/56] Disable variable subscription fields (WIP) --- .../ppcp-subscription/resources/js/paypal-subscription.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-subscription/resources/js/paypal-subscription.js b/modules/ppcp-subscription/resources/js/paypal-subscription.js index 427fdda96..99c1cd630 100644 --- a/modules/ppcp-subscription/resources/js/paypal-subscription.js +++ b/modules/ppcp-subscription/resources/js/paypal-subscription.js @@ -7,8 +7,12 @@ document.addEventListener( const children = variations.children; for(let i=0; i < children.length; i++) { const variableId = children[i].querySelector('h3').getElementsByClassName('variable_post_id')[0].value - if( parseInt(variableId) === productId ) { - children[i].querySelector('.woocommerce_variable_attributes').getElementsByClassName('wc_input_subscription_period_interval')[0].setAttribute('disabled', 'disabled'); + if (parseInt(variableId) === productId) { + children[i].querySelector('.woocommerce_variable_attributes') + .getElementsByClassName('wc_input_subscription_period_interval')[0] + .setAttribute('disabled', 'disabled'); + } + } } const periodInterval = document.querySelector('#_subscription_period_interval'); From 9ce1f80e90351b32409675f32b2a3cf5b6faad60 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 19 Jul 2023 12:03:16 +0200 Subject: [PATCH 21/56] Adjust form fields display --- .../resources/js/paypal-subscription.js | 12 ++++++++++++ .../ppcp-subscription/src/SubscriptionModule.php | 15 ++++++++------- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/modules/ppcp-subscription/resources/js/paypal-subscription.js b/modules/ppcp-subscription/resources/js/paypal-subscription.js index 99c1cd630..7d848091f 100644 --- a/modules/ppcp-subscription/resources/js/paypal-subscription.js +++ b/modules/ppcp-subscription/resources/js/paypal-subscription.js @@ -11,6 +11,18 @@ document.addEventListener( children[i].querySelector('.woocommerce_variable_attributes') .getElementsByClassName('wc_input_subscription_period_interval')[0] .setAttribute('disabled', 'disabled'); + children[i].querySelector('.woocommerce_variable_attributes') + .getElementsByClassName('wc_input_subscription_period')[0] + .setAttribute('disabled', 'disabled'); + children[i].querySelector('.woocommerce_variable_attributes') + .getElementsByClassName('wc_input_subscription_trial_length')[0] + .setAttribute('disabled', 'disabled'); + children[i].querySelector('.woocommerce_variable_attributes') + .getElementsByClassName('wc_input_subscription_trial_period')[0] + .setAttribute('disabled', 'disabled'); + children[i].querySelector('.woocommerce_variable_attributes') + .getElementsByClassName('wc_input_subscription_length')[0] + .setAttribute('disabled', 'disabled'); } } } diff --git a/modules/ppcp-subscription/src/SubscriptionModule.php b/modules/ppcp-subscription/src/SubscriptionModule.php index 1ed3d75e5..d2077db06 100644 --- a/modules/ppcp-subscription/src/SubscriptionModule.php +++ b/modules/ppcp-subscription/src/SubscriptionModule.php @@ -842,12 +842,13 @@ class SubscriptionModule implements ModuleInterface { */ private function render_paypal_subscription_fields( WC_Product $product, Environment $environment ): void { $enable_subscription_product = $product->get_meta( '_ppcp_enable_subscription_product' ); + $style = $product->get_type() === 'subscription_variation' ? 'float:left; width:150px;' : ''; echo '

'; echo sprintf( // translators: %1$s and %2$s are label open and close tags. esc_html__( '%1$sConnect to PayPal%2$s', 'woocommerce-paypal-payments' ), - '

' . esc_attr( $subscription_product['id'] ) . '

' ); echo sprintf( // translators: %1$s and %2$s are wrapper html tags. esc_html__( '%1$sPlan%2$s', 'woocommerce-paypal-payments' ), - '

' . esc_attr( $subscription_plan['id'] ) . '

' ); } else { @@ -954,12 +955,12 @@ class SubscriptionModule implements ModuleInterface { return array( 'product_connected' => $product->get_meta( '_ppcp_enable_subscription_product' ) ?? '', - 'plan_id' => $plan_id, - 'product_id' => $product->get_id(), + 'plan_id' => $plan_id, + 'product_id' => $product->get_id(), 'ajax' => array( 'deactivate_plan' => array( - 'endpoint' => \WC_AJAX::get_endpoint( DeactivatePlanEndpoint::ENDPOINT ), - 'nonce' => wp_create_nonce( DeactivatePlanEndpoint::ENDPOINT ), + 'endpoint' => \WC_AJAX::get_endpoint( DeactivatePlanEndpoint::ENDPOINT ), + 'nonce' => wp_create_nonce( DeactivatePlanEndpoint::ENDPOINT ), ), ), ); From c636192e9842fc53f24969b7da3ad4d8851a724b Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Thu, 20 Jul 2023 12:27:14 +0200 Subject: [PATCH 22/56] Do not render buttons on single product page if there is not subscription plan --- .../SingleProductActionHandler.js | 20 +------------------ .../ContextBootstrap/SingleProductBootstap.js | 11 +++++++++- .../js/modules/Helper/Subscriptions.js | 16 +++++++++++++++ 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/modules/ppcp-button/resources/js/modules/ActionHandler/SingleProductActionHandler.js b/modules/ppcp-button/resources/js/modules/ActionHandler/SingleProductActionHandler.js index ca913baf5..fb002ebdf 100644 --- a/modules/ppcp-button/resources/js/modules/ActionHandler/SingleProductActionHandler.js +++ b/modules/ppcp-button/resources/js/modules/ActionHandler/SingleProductActionHandler.js @@ -21,25 +21,7 @@ class SingleProductActionHandler { this.cartHelper = null; } - getPlanIdFromVariation = (variation) => { - let subscription_plan = ''; - PayPalCommerceGateway.variable_paypal_subscription_variations.forEach((element) => { - let obj = {}; - variation.forEach(({name, value}) => { - Object.assign(obj, {[name.replace('attribute_', '')]: value}); - }) - - if(JSON.stringify(obj) === JSON.stringify(element.attributes) && element.subscription_plan !== '') { - subscription_plan = element.subscription_plan; - } - }); - - return subscription_plan; - } - - subscriptionsConfiguration(selected_variation = null) { - const subscription_plan = selected_variation !== null ? this.getPlanIdFromVariation(selected_variation) : this.config.subscription_plan_id - + subscriptionsConfiguration(subscription_plan) { return { createSubscription: (data, actions) => { return actions.subscription.create({ diff --git a/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js b/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js index 61f167d31..f76ba9c91 100644 --- a/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js +++ b/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js @@ -3,6 +3,7 @@ import SingleProductActionHandler from "../ActionHandler/SingleProductActionHand import {hide, show} from "../Helper/Hiding"; import BootstrapHelper from "../Helper/BootstrapHelper"; import {loadPaypalJsScript} from "../Helper/ScriptLoading"; +import {getPlanIdFromVariation} from "../Helper/Subscriptions" class SingleProductBootstap { constructor(gateway, renderer, messages, errorHandler) { @@ -165,6 +166,14 @@ class SingleProductBootstap { ) { const buttonWrapper = document.getElementById('ppc-button-ppcp-gateway'); buttonWrapper.innerHTML = ''; + + const subscription_plan = this.variations() !== null + ? getPlanIdFromVariation(this.variations()) + : PayPalCommerceGateway.subscription_plan_id + if(!subscription_plan) { + return; + } + loadPaypalJsScript( { clientId: PayPalCommerceGateway.client_id, @@ -172,7 +181,7 @@ class SingleProductBootstap { intent: 'subscription', vault: true }, - actionHandler.subscriptionsConfiguration(this.variations()), + actionHandler.subscriptionsConfiguration(subscription_plan), this.gateway.button.wrapper ); return; diff --git a/modules/ppcp-button/resources/js/modules/Helper/Subscriptions.js b/modules/ppcp-button/resources/js/modules/Helper/Subscriptions.js index a205ac459..4366fd9d2 100644 --- a/modules/ppcp-button/resources/js/modules/Helper/Subscriptions.js +++ b/modules/ppcp-button/resources/js/modules/Helper/Subscriptions.js @@ -2,3 +2,19 @@ export const isChangePaymentPage = () => { const urlParams = new URLSearchParams(window.location.search) return urlParams.has('change_payment_method'); } + +export const getPlanIdFromVariation = (variation) => { + let subscription_plan = ''; + PayPalCommerceGateway.variable_paypal_subscription_variations.forEach((element) => { + let obj = {}; + variation.forEach(({name, value}) => { + Object.assign(obj, {[name.replace('attribute_', '')]: value}); + }) + + if(JSON.stringify(obj) === JSON.stringify(element.attributes) && element.subscription_plan !== '') { + subscription_plan = element.subscription_plan; + } + }); + + return subscription_plan; +} From c13ac3ab733dca374d60f9196f669962186f4381 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Thu, 20 Jul 2023 14:29:37 +0200 Subject: [PATCH 23/56] Disable buttons in cart and checkout pages when no subscription plan exists --- .../resources/js/modules/ContextBootstrap/CartBootstap.js | 6 ++++++ .../js/modules/ContextBootstrap/CheckoutBootstap.js | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/modules/ppcp-button/resources/js/modules/ContextBootstrap/CartBootstap.js b/modules/ppcp-button/resources/js/modules/ContextBootstrap/CartBootstap.js index 5aadb7f61..126c28d08 100644 --- a/modules/ppcp-button/resources/js/modules/ContextBootstrap/CartBootstap.js +++ b/modules/ppcp-button/resources/js/modules/ContextBootstrap/CartBootstap.js @@ -77,6 +77,12 @@ class CartBootstrap { && PayPalCommerceGateway.data_client_id.paypal_subscriptions_enabled ) { this.renderer.render(actionHandler.subscriptionsConfiguration()); + + if(!PayPalCommerceGateway.subscription_plan_id) { + this.gateway.button.is_disabled = true; + this.handleButtonStatus(); + } + return; } diff --git a/modules/ppcp-button/resources/js/modules/ContextBootstrap/CheckoutBootstap.js b/modules/ppcp-button/resources/js/modules/ContextBootstrap/CheckoutBootstap.js index db1cba84f..ab75d1c16 100644 --- a/modules/ppcp-button/resources/js/modules/ContextBootstrap/CheckoutBootstap.js +++ b/modules/ppcp-button/resources/js/modules/ContextBootstrap/CheckoutBootstap.js @@ -107,6 +107,12 @@ class CheckoutBootstap { && PayPalCommerceGateway.data_client_id.paypal_subscriptions_enabled ) { this.renderer.render(actionHandler.subscriptionsConfiguration(), {}, actionHandler.configuration()); + + if(!PayPalCommerceGateway.subscription_plan_id) { + this.gateway.button.is_disabled = true; + this.handleButtonStatus(); + } + return; } From 8835f4b2a9ce9ad95f7cabb721cedb02ac9b9666 Mon Sep 17 00:00:00 2001 From: Alex P Date: Fri, 21 Jul 2023 19:35:13 +0300 Subject: [PATCH 24/56] Always save form in checkout --- modules/ppcp-button/resources/js/button.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-button/resources/js/button.js b/modules/ppcp-button/resources/js/button.js index 790030920..2eff9210a 100644 --- a/modules/ppcp-button/resources/js/button.js +++ b/modules/ppcp-button/resources/js/button.js @@ -123,7 +123,7 @@ const bootstrap = () => { return actions.reject(); } - if (context === 'checkout' && !PayPalCommerceGateway.funding_sources_without_redirect.includes(data.fundingSource)) { + if (context === 'checkout') { try { await formSaver.save(form); } catch (error) { From e678967c556890afe5f20fcf379c215af8a90d50 Mon Sep 17 00:00:00 2001 From: Alex P Date: Fri, 21 Jul 2023 19:35:56 +0300 Subject: [PATCH 25/56] Do not create wc order in webhook when no form data --- .../ppcp-webhooks/src/Handler/CheckoutOrderApproved.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/modules/ppcp-webhooks/src/Handler/CheckoutOrderApproved.php b/modules/ppcp-webhooks/src/Handler/CheckoutOrderApproved.php index 60b51b513..7b631ef22 100644 --- a/modules/ppcp-webhooks/src/Handler/CheckoutOrderApproved.php +++ b/modules/ppcp-webhooks/src/Handler/CheckoutOrderApproved.php @@ -159,6 +159,14 @@ class CheckoutOrderApproved implements RequestHandler { WC()->cart->calculate_shipping(); $form = $this->session_handler->checkout_form(); + if ( ! $form ) { + return $this->failure_response( + sprintf( + 'Failed to create WC order in webhook event %s, checkout data not found.', + $request['id'] ?: '' + ) + ); + } $checkout = new WC_Checkout(); $wc_order_id = $checkout->create_order( $form ); From 0cbc9996846187a150b68adfc6f99820a99d8d48 Mon Sep 17 00:00:00 2001 From: Alex P Date: Fri, 21 Jul 2023 19:37:34 +0300 Subject: [PATCH 26/56] Allow to disable order creation in webhook via filter --- modules/ppcp-webhooks/src/Handler/CheckoutOrderApproved.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/ppcp-webhooks/src/Handler/CheckoutOrderApproved.php b/modules/ppcp-webhooks/src/Handler/CheckoutOrderApproved.php index 7b631ef22..9a266d41b 100644 --- a/modules/ppcp-webhooks/src/Handler/CheckoutOrderApproved.php +++ b/modules/ppcp-webhooks/src/Handler/CheckoutOrderApproved.php @@ -145,6 +145,10 @@ class CheckoutOrderApproved implements RequestHandler { return $this->success_response(); } + if ( ! (bool) apply_filters( 'woocommerce_paypal_payments_order_approved_webhook_can_create_wc_order', true ) ) { + return $this->success_response(); + } + $wc_session = new WC_Session_Handler(); $session_data = $wc_session->get_session( $customer_id ); From 59a68fb604ad22adb232e01a1d4a291b7cffeb69 Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Mon, 24 Jul 2023 17:09:45 +0100 Subject: [PATCH 27/56] Add sanitize woocommerce_widget_cart_is_hidden filter for consistent values. Remove context restrictions to mini-cart paypal buttons. --- modules/ppcp-button/resources/js/button.js | 17 +++++++------ .../ppcp-button/src/Assets/SmartButton.php | 24 +++++++++++++++++++ 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/modules/ppcp-button/resources/js/button.js b/modules/ppcp-button/resources/js/button.js index 790030920..97fa91656 100644 --- a/modules/ppcp-button/resources/js/button.js +++ b/modules/ppcp-button/resources/js/button.js @@ -137,16 +137,15 @@ const bootstrap = () => { }; const renderer = new Renderer(creditCardRenderer, PayPalCommerceGateway, onSmartButtonClick, onSmartButtonsInit); const messageRenderer = new MessageRenderer(PayPalCommerceGateway.messages); - if (context === 'mini-cart' || context === 'product') { - if (PayPalCommerceGateway.mini_cart_buttons_enabled === '1') { - const miniCartBootstrap = new MiniCartBootstap( - PayPalCommerceGateway, - renderer, - errorHandler, - ); - miniCartBootstrap.init(); - } + if (PayPalCommerceGateway.mini_cart_buttons_enabled === '1') { + const miniCartBootstrap = new MiniCartBootstap( + PayPalCommerceGateway, + renderer, + errorHandler, + ); + + miniCartBootstrap.init(); } if (context === 'product' && PayPalCommerceGateway.single_product_buttons_enabled === '1') { diff --git a/modules/ppcp-button/src/Assets/SmartButton.php b/modules/ppcp-button/src/Assets/SmartButton.php index fded20a67..3e7af313b 100644 --- a/modules/ppcp-button/src/Assets/SmartButton.php +++ b/modules/ppcp-button/src/Assets/SmartButton.php @@ -372,6 +372,8 @@ class SmartButton implements SmartButtonInterface { ); } + $this->sanitize_woocommerce_filters(); + return true; } @@ -1522,4 +1524,26 @@ class SmartButton implements SmartButtonInterface { return absint( $wp->query_vars['order-pay'] ); } + + /** + * Sanitize woocommerce filter on unexpected states. + * + * @return void + */ + private function sanitize_woocommerce_filters(): void { + + // Sometimes external plugins like "woocommerce-one-page-checkout" set the $value to null. + // Here we also disable the mini-cart on cart-block and checkout-block pages where our buttons aren't supported yet. + add_filter( 'woocommerce_widget_cart_is_hidden', function ($value) { + if (null === $value) { + if ( is_product() ) { + return false; + } + return in_array($this->context(), array('cart', 'checkout', 'cart-block', 'checkout-block')); + } + return in_array($this->context(), array('cart-block', 'checkout-block')) ? true : $value; + }, 11); + + } + } From d2fc0d38286d5a9d92fe8a47e884e7b5f6cbd717 Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Mon, 24 Jul 2023 17:24:34 +0100 Subject: [PATCH 28/56] Fix lint --- .../ppcp-button/src/Assets/SmartButton.php | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/modules/ppcp-button/src/Assets/SmartButton.php b/modules/ppcp-button/src/Assets/SmartButton.php index 3e7af313b..e0a42c7c9 100644 --- a/modules/ppcp-button/src/Assets/SmartButton.php +++ b/modules/ppcp-button/src/Assets/SmartButton.php @@ -1532,17 +1532,25 @@ class SmartButton implements SmartButtonInterface { */ private function sanitize_woocommerce_filters(): void { - // Sometimes external plugins like "woocommerce-one-page-checkout" set the $value to null. - // Here we also disable the mini-cart on cart-block and checkout-block pages where our buttons aren't supported yet. - add_filter( 'woocommerce_widget_cart_is_hidden', function ($value) { - if (null === $value) { - if ( is_product() ) { - return false; + add_filter( + 'woocommerce_widget_cart_is_hidden', + /** + * Sometimes external plugins like "woocommerce-one-page-checkout" set the $value to null, handle that case here. + * Here we also disable the mini-cart on cart-block and checkout-block pages where our buttons aren't supported yet. + * + * @psalm-suppress MissingClosureParamType + */ + function ( $value ) { + if ( null === $value ) { + if ( is_product() ) { + return false; + } + return in_array( $this->context(), array( 'cart', 'checkout', 'cart-block', 'checkout-block' ), true ); } - return in_array($this->context(), array('cart', 'checkout', 'cart-block', 'checkout-block')); - } - return in_array($this->context(), array('cart-block', 'checkout-block')) ? true : $value; - }, 11); + return in_array( $this->context(), array( 'cart-block', 'checkout-block' ), true ) ? true : $value; + }, + 11 + ); } From 77db626a92f541b519d248752e8a2fd9ec714a17 Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Wed, 26 Jul 2023 09:43:06 +0100 Subject: [PATCH 29/56] Add discount and also handling, insurance, shipping_discount amounts to PUI total roundings --- .../src/Endpoint/PayUponInvoiceOrderEndpoint.php | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/modules/ppcp-api-client/src/Endpoint/PayUponInvoiceOrderEndpoint.php b/modules/ppcp-api-client/src/Endpoint/PayUponInvoiceOrderEndpoint.php index 97b40a73d..285ebda70 100644 --- a/modules/ppcp-api-client/src/Endpoint/PayUponInvoiceOrderEndpoint.php +++ b/modules/ppcp-api-client/src/Endpoint/PayUponInvoiceOrderEndpoint.php @@ -233,9 +233,15 @@ class PayUponInvoiceOrderEndpoint { * @return array */ private function ensure_taxes( WC_Order $wc_order, array $data ): array { - $tax_total = $data['purchase_units'][0]['amount']['breakdown']['tax_total']['value']; - $item_total = $data['purchase_units'][0]['amount']['breakdown']['item_total']['value']; - $shipping = $data['purchase_units'][0]['amount']['breakdown']['shipping']['value']; + $tax_total = $data['purchase_units'][0]['amount']['breakdown']['tax_total']['value']; + $item_total = $data['purchase_units'][0]['amount']['breakdown']['item_total']['value']; + $shipping = $data['purchase_units'][0]['amount']['breakdown']['shipping']['value']; + + $handling = isset( $data['purchase_units'][0]['amount']['breakdown']['handling'] ) ? $data['purchase_units'][0]['amount']['breakdown']['handling']['value'] : 0; + $insurance = isset( $data['purchase_units'][0]['amount']['breakdown']['insurance'] ) ? $data['purchase_units'][0]['amount']['breakdown']['insurance']['value'] : 0; + $shipping_discount = isset( $data['purchase_units'][0]['amount']['breakdown']['shipping_discount'] ) ? $data['purchase_units'][0]['amount']['breakdown']['shipping_discount']['value'] : 0; + $discount = isset( $data['purchase_units'][0]['amount']['breakdown']['discount'] ) ? $data['purchase_units'][0]['amount']['breakdown']['discount']['value'] : 0; + $order_tax_total = $wc_order->get_total_tax(); $tax_rate = round( ( $order_tax_total / $item_total ) * 100, 1 ); @@ -263,7 +269,7 @@ class PayUponInvoiceOrderEndpoint { ); $total_amount = $data['purchase_units'][0]['amount']['value']; - $breakdown_total = $item_total + $tax_total + $shipping; + $breakdown_total = $item_total + $tax_total + $shipping + $handling + $insurance - $shipping_discount - $discount; $diff = round( $total_amount - $breakdown_total, 2 ); if ( $diff === -0.01 || $diff === 0.01 ) { $data['purchase_units'][0]['amount']['value'] = number_format( $breakdown_total, 2, '.', '' ); From 686884027634d160b0195efbac0dc7fbc45fc552 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 26 Jul 2023 11:08:09 +0200 Subject: [PATCH 30/56] Add wc rest api library for testing --- .env.e2e.example | 3 + package.json | 3 +- tests/playwright/subscriptions-api.spec.js | 15 +- yarn.lock | 155 +++++++++++++++++++++ 4 files changed, 170 insertions(+), 6 deletions(-) diff --git a/.env.e2e.example b/.env.e2e.example index 98ad27c23..57d94d433 100644 --- a/.env.e2e.example +++ b/.env.e2e.example @@ -3,6 +3,9 @@ PPCP_E2E_WP_DIR=${ROOT_DIR}/.ddev/wordpress BASEURL="https://woocommerce-paypal-payments.ddev.site" AUTHORIZATION="Bearer ABC123" +WC_CONSUMER_KEY="ck_abc123", +WC_CONSUMER_SECRET="cs_def456" + CHECKOUT_URL="/checkout" CHECKOUT_PAGE_ID=7 CART_URL="/cart" diff --git a/package.json b/package.json index 2fd22648b..c3e27161e 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,7 @@ "npm-run-all": "^4.1.5" }, "devDependencies": { - "@playwright/test": "^1.31.1" + "@playwright/test": "^1.31.1", + "@woocommerce/woocommerce-rest-api": "^1.0.1" } } diff --git a/tests/playwright/subscriptions-api.spec.js b/tests/playwright/subscriptions-api.spec.js index 9d5bce889..2b8845dfa 100644 --- a/tests/playwright/subscriptions-api.spec.js +++ b/tests/playwright/subscriptions-api.spec.js @@ -1,4 +1,6 @@ const {test, expect} = require('@playwright/test'); +const wcApi = require('@woocommerce/woocommerce-rest-api').default; + const {loginAsAdmin, loginAsCustomer} = require('./utils/user'); const {openPaypalPopup, loginIntoPaypal, completePaypalPayment} = require("./utils/paypal-popup"); const {fillCheckoutForm, expectOrderReceivedPage} = require("./utils/checkout"); @@ -7,6 +9,9 @@ const { SUBSCRIPTION_URL, CHECKOUT_URL, CART_URL, + BASEURL, + WC_CONSUMER_KEY, + WC_CONSUMER_SECRET, } = process.env; async function purchaseSubscriptionFromCart(page) { @@ -18,7 +23,7 @@ async function purchaseSubscriptionFromCart(page) { const popup = await openPaypalPopup(page); await loginIntoPaypal(popup); - await popup.getByText('Continue', { exact: true }).click(); + await popup.getByText('Continue', {exact: true}).click(); await popup.locator('#confirmButtonTop').click(); await fillCheckoutForm(page); @@ -92,7 +97,7 @@ test.describe.serial('Subscriptions Merchant', () => { await loginAsAdmin(page); await page.goto('/wp-admin/edit.php?post_type=product'); - await page.getByRole('link', { name: productTitle, exact: true }).click(); + await page.getByRole('link', {name: productTitle, exact: true}).click(); await page.fill('#title', `Updated ${productTitle}`); await page.fill('#_subscription_price', '20'); @@ -209,7 +214,7 @@ test.describe('Subscriber purchase a Subscription', () => { const popup = await openPaypalPopup(page); await loginIntoPaypal(popup); - await popup.getByText('Continue', { exact: true }).click(); + await popup.getByText('Continue', {exact: true}).click(); await Promise.all([ page.waitForNavigation(), @@ -226,7 +231,7 @@ test.describe('Subscriber purchase a Subscription', () => { const popup = await openPaypalPopup(page); await loginIntoPaypal(popup); - await popup.getByText('Continue', { exact: true }).click(); + await popup.getByText('Continue', {exact: true}).click(); await popup.locator('#confirmButtonTop').click(); await fillCheckoutForm(page); @@ -308,4 +313,4 @@ test.describe('Subscriber my account actions', () => { details = await subscription.json(); await expect(details.status).toBe('CANCELLED'); }); -}) ; +}); diff --git a/yarn.lock b/yarn.lock index 89ead66f1..0b3914349 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17,6 +17,16 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.14.1.tgz#90dad8476f1e42797c49d6f8b69aaf9f876fc69f" integrity sha512-QH+37Qds3E0eDlReeboBxfHbX9omAcBCXEzswCu6jySP642jiM3cYSIkU/REqwhCUqXdonHFuBfJDiAJxMNhaQ== +"@woocommerce/woocommerce-rest-api@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@woocommerce/woocommerce-rest-api/-/woocommerce-rest-api-1.0.1.tgz#112b998e8e3758203a71d5e3e0b24efcb1cff842" + integrity sha512-YBk3EEYE0zax/egx6Rhpbu6hcCFyZpYQrjH9JO4NUGU3n3T0W9Edn7oAUbjL/c7Oezcg+UaQluCaKjY/B3zwxg== + dependencies: + axios "^0.19.0" + create-hmac "^1.1.7" + oauth-1.0a "^2.2.6" + url-parse "^1.4.7" + ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -24,6 +34,13 @@ ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" +axios@^0.19.0: + version "0.19.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27" + integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA== + dependencies: + follow-redirects "1.5.10" + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -54,6 +71,14 @@ chalk@^2.4.1: escape-string-regexp "^1.0.5" supports-color "^5.3.0" +cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -71,6 +96,29 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== +create-hash@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" @@ -82,6 +130,13 @@ cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" +debug@=3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + define-properties@^1.1.3, define-properties@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" @@ -146,6 +201,13 @@ escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== +follow-redirects@1.5.10: + version "1.5.10" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a" + integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ== + dependencies: + debug "=3.1.0" + fsevents@2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" @@ -229,11 +291,25 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +hash-base@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== + dependencies: + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + hosted-git-info@^2.1.4: version "2.8.9" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== +inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + internal-slot@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" @@ -350,6 +426,15 @@ load-json-file@^4.0.0: pify "^3.0.0" strip-bom "^3.0.0" +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + memorystream@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" @@ -362,6 +447,11 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -392,6 +482,11 @@ npm-run-all@^4.1.5: shell-quote "^1.6.1" string.prototype.padend "^3.0.0" +oauth-1.0a@^2.2.6: + version "2.2.6" + resolved "https://registry.yarnpkg.com/oauth-1.0a/-/oauth-1.0a-2.2.6.tgz#eadbccdb3bceea412d24586e6f39b2b412f0e491" + integrity sha512-6bkxv3N4Gu5lty4viIcIAnq5GbxECviMBeKR3WX/q87SPQ8E8aursPZUtsXDnxCs787af09WPRBLqYrf/lwoYQ== + object-inspect@^1.12.2, object-inspect@^1.9.0: version "1.12.2" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" @@ -452,6 +547,11 @@ playwright-core@1.31.1: resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.31.1.tgz#4deeebbb8fb73b512593fe24bea206d8fd85ff7f" integrity sha512-JTyX4kV3/LXsvpHkLzL2I36aCdml4zeE35x+G5aPc4bkLsiRiQshU5lWeVpHFAuC8xAcbI6FDcw/8z3q2xtJSQ== +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + read-pkg@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" @@ -461,6 +561,15 @@ read-pkg@^3.0.0: normalize-package-data "^2.3.2" path-type "^3.0.0" +readable-stream@^3.6.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + regexp.prototype.flags@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" @@ -470,6 +579,11 @@ regexp.prototype.flags@^1.4.3: define-properties "^1.1.3" functions-have-names "^1.2.2" +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + resolve@^1.10.0: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" @@ -479,6 +593,19 @@ resolve@^1.10.0: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.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== + 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" @@ -493,6 +620,14 @@ safe-regex-test@^1.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -572,6 +707,13 @@ string.prototype.trimstart@^1.0.5: define-properties "^1.1.4" es-abstract "^1.19.5" +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -599,6 +741,19 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" +url-parse@^1.4.7: + version "1.5.10" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + validate-npm-package-license@^3.0.1: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" From 11adb21c21a2944f47e474fefc870b7df4ff60d0 Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Wed, 26 Jul 2023 10:15:08 +0100 Subject: [PATCH 31/56] Fix e2e testing --- .github/workflows/e2e.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index c6496f58a..3731a1de4 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -15,7 +15,7 @@ jobs: - uses: satackey/action-docker-layer-caching@v0.0.11 continue-on-error: true - - uses: jonaseberle/github-action-setup-ddev@v1 + - uses: ddev/github-action-setup-ddev@v1 with: autostart: false From e81ed024bb0e5818c86c498de5792b2a9e58cc2f Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Wed, 26 Jul 2023 10:31:39 +0100 Subject: [PATCH 32/56] Fix e2e testing --- .github/workflows/e2e.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index c6496f58a..3731a1de4 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -15,7 +15,7 @@ jobs: - uses: satackey/action-docker-layer-caching@v0.0.11 continue-on-error: true - - uses: jonaseberle/github-action-setup-ddev@v1 + - uses: ddev/github-action-setup-ddev@v1 with: autostart: false From b07619c2725b8ae20dca8920b37b50b00ed42b4e Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Thu, 27 Jul 2023 12:55:01 +0200 Subject: [PATCH 33/56] Add tests for subscription buttons display (WIP) --- .env.e2e.example | 3 - playwright.config.js | 2 +- tests/playwright/subscriptions-api.spec.js | 67 ++++++++++++++++++++-- tests/playwright/utils/product.js | 59 +++++++++++++++++++ 4 files changed, 123 insertions(+), 8 deletions(-) create mode 100644 tests/playwright/utils/product.js diff --git a/.env.e2e.example b/.env.e2e.example index 57d94d433..98ad27c23 100644 --- a/.env.e2e.example +++ b/.env.e2e.example @@ -3,9 +3,6 @@ PPCP_E2E_WP_DIR=${ROOT_DIR}/.ddev/wordpress BASEURL="https://woocommerce-paypal-payments.ddev.site" AUTHORIZATION="Bearer ABC123" -WC_CONSUMER_KEY="ck_abc123", -WC_CONSUMER_SECRET="cs_def456" - CHECKOUT_URL="/checkout" CHECKOUT_PAGE_ID=7 CART_URL="/cart" diff --git a/playwright.config.js b/playwright.config.js index 6ade8826a..1a1663f2e 100644 --- a/playwright.config.js +++ b/playwright.config.js @@ -2,7 +2,7 @@ require('dotenv').config({ path: '.env.e2e' }); const config = { testDir: './tests/playwright', - timeout: 60000, + timeout: 30000, use: { baseURL: process.env.BASEURL, ignoreHTTPSErrors: true, diff --git a/tests/playwright/subscriptions-api.spec.js b/tests/playwright/subscriptions-api.spec.js index 2b8845dfa..d1970e328 100644 --- a/tests/playwright/subscriptions-api.spec.js +++ b/tests/playwright/subscriptions-api.spec.js @@ -1,17 +1,14 @@ const {test, expect} = require('@playwright/test'); -const wcApi = require('@woocommerce/woocommerce-rest-api').default; const {loginAsAdmin, loginAsCustomer} = require('./utils/user'); const {openPaypalPopup, loginIntoPaypal, completePaypalPayment} = require("./utils/paypal-popup"); const {fillCheckoutForm, expectOrderReceivedPage} = require("./utils/checkout"); +const {createProduct, deleteProduct, updateProduct, updateProductUi} = require("./utils/product"); const { AUTHORIZATION, SUBSCRIPTION_URL, CHECKOUT_URL, CART_URL, - BASEURL, - WC_CONSUMER_KEY, - WC_CONSUMER_SECRET, } = process.env; async function purchaseSubscriptionFromCart(page) { @@ -314,3 +311,65 @@ test.describe('Subscriber my account actions', () => { await expect(details.status).toBe('CANCELLED'); }); }); + +test('Disable buttons if no plan connected', async ({page}) => { + const data = { + name: 'Subscription', + type: 'subscription', + meta_data: [ + { + key: '_subscription_price', + value: '10' + } + ] + } + const productId = await createProduct(data) + + // for some reason product meta is not updated in frontend, + // so we need to manually update the product + await updateProductUi(productId, page); + + await page.goto('/product/subscription') + await expect(page.locator('#ppc-button-ppcp-gateway')).not.toBeVisible(); + + await page.locator('.single_add_to_cart_button').click(); + await page.goto('/cart'); + await expect(page.locator('#ppc-button-ppcp-gateway')).toBeVisible(); + await expect(page.locator('#ppc-button-ppcp-gateway')).toHaveCSS('cursor', 'not-allowed') + + await page.goto('/checkout'); + await expect(page.locator('#ppc-button-ppcp-gateway')).toBeVisible(); + await expect(page.locator('#ppc-button-ppcp-gateway')).toHaveCSS('cursor', 'not-allowed') + + await deleteProduct(productId) +}) + +test('Enable buttons if plan connected', async ({page}) => { + const data = { + name: 'Subscription', + type: 'subscription', + meta_data: [ + { + key: '_subscription_price', + value: '10' + } + ] + } + const productId = await createProduct(data) + + await loginAsAdmin(page); + await page.goto(`/wp-admin/post.php?post=${productId}&action=edit`) + await page.locator('#ppcp_enable_subscription_product').check(); + await page.locator('#ppcp_subscription_plan_name').fill('Plan name'); + await page.locator('#publish').click(); + await expect(page.getByText('Product updated.')).toBeVisible(); + + await page.goto('/product/subscription') + await expect(page.locator('#ppc-button-ppcp-gateway')).toBeVisible(); + + await page.getByText('Sign up now').click(); + await page.goto('/cart'); + await expect(page.locator('#ppc-button-ppcp-gateway')).toBeVisible(); + + await deleteProduct(productId) +}) diff --git a/tests/playwright/utils/product.js b/tests/playwright/utils/product.js new file mode 100644 index 000000000..65fbdbe2e --- /dev/null +++ b/tests/playwright/utils/product.js @@ -0,0 +1,59 @@ +import {loginAsAdmin} from "./user"; +import {expect} from "@playwright/test"; + +const wcApi = require('@woocommerce/woocommerce-rest-api').default; +const { + BASEURL, + WP_MERCHANT_USER, + WP_MERCHANT_PASSWORD, +} = process.env; + +const wc = () => { + return new wcApi({ + url: BASEURL, + consumerKey: WP_MERCHANT_USER, + consumerSecret: WP_MERCHANT_PASSWORD, + version: 'wc/v3', + }); +} + +export const createProduct = async (data) => { + const api = wc(); + + return await api.post('products', data) + .then((response) => { + return response.data.id + }).catch((error) => { + console.log(error) + }) +} + +export const updateProduct = async (data, id) => { + const api = wc(); + + return await api.put(`products/${id}`, data) + .then((response) => { + return response.data.id + }).catch((error) => { + console.log(error) + }) +} + +export const deleteProduct = async (id) => { + const api = wc(); + + return await api.delete(`products/${id}`) + .then((response) => { + return response.data.id + }).catch((error) => { + console.log(error) + }) +} + +export const updateProductUi = async (id, page) => { + await loginAsAdmin(page); + await page.goto(`/wp-admin/post.php?post=${id}&action=edit`) + await page.locator('#publish').click(); + await expect(page.getByText('Product updated.')).toBeVisible(); +} + From 65c50e0888139b66bb33db355dc1d6830f642801 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Thu, 27 Jul 2023 16:07:43 +0200 Subject: [PATCH 34/56] Remove parenthesis in test name othewise does not work when run test individually --- tests/playwright/place-order.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/playwright/place-order.spec.js b/tests/playwright/place-order.spec.js index c0228c351..4a9f7523f 100644 --- a/tests/playwright/place-order.spec.js +++ b/tests/playwright/place-order.spec.js @@ -65,7 +65,7 @@ test.describe('Classic checkout', () => { await expectOrderReceivedPage(page); }); - test('Advanced Credit and Debit Card (ACDC) place order from Checkout page', async ({page}) => { + test('Advanced Credit and Debit Card place order from Checkout page', async ({page}) => { await page.goto(PRODUCT_URL); await page.locator('.single_add_to_cart_button').click(); From 14f8701c9644dd0ab95a3640f4516440f86b2407 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Fri, 28 Jul 2023 15:34:34 +0200 Subject: [PATCH 35/56] Add ddev playwright addon --- .DS_Store | Bin 0 -> 8196 bytes .ddev/addon-metadata/playwright/manifest.yaml | 14 ++++ .ddev/commands/playwright/playwright | 14 ++++ .ddev/commands/playwright/playwright-install | 17 ++++ .ddev/docker-compose.playwright.yaml | 38 +++++++++ .ddev/playwright-build/Dockerfile | 57 +++++++++++++ .ddev/playwright-build/entrypoint.sh | 18 ++++ .ddev/playwright-build/kasmvnc.yaml | 14 ++++ .ddev/playwright-build/xstartup | 32 ++++++++ .github/workflows/playwright.yml | 19 +++++ package.json | 3 - playwright.config.js | 12 --- tests/.DS_Store | Bin 0 -> 6148 bytes tests/playwright/.DS_Store | Bin 0 -> 6148 bytes tests/playwright/.env.example | 3 + tests/playwright/.gitignore | 8 +- tests/playwright/README.md | 40 --------- tests/playwright/package.json | 7 ++ tests/playwright/playwright-report/index.html | 62 ++++++++++++++ tests/playwright/playwright.config.js | 77 ++++++++++++++++++ tests/playwright/tests/example.spec.js | 9 ++ .../{ => tests}/place-order.spec.js | 0 .../{ => tests}/subscriptions-api.spec.js | 0 .../playwright/{ => tests}/utils/checkout.js | 0 .../{ => tests}/utils/paypal-popup.js | 0 tests/playwright/{ => tests}/utils/server.js | 0 tests/playwright/{ => tests}/utils/user.js | 0 yarn.lock | 25 ------ 28 files changed, 386 insertions(+), 83 deletions(-) create mode 100644 .DS_Store create mode 100644 .ddev/addon-metadata/playwright/manifest.yaml create mode 100755 .ddev/commands/playwright/playwright create mode 100755 .ddev/commands/playwright/playwright-install create mode 100644 .ddev/docker-compose.playwright.yaml create mode 100644 .ddev/playwright-build/Dockerfile create mode 100755 .ddev/playwright-build/entrypoint.sh create mode 100644 .ddev/playwright-build/kasmvnc.yaml create mode 100755 .ddev/playwright-build/xstartup create mode 100644 .github/workflows/playwright.yml delete mode 100644 playwright.config.js create mode 100644 tests/.DS_Store create mode 100644 tests/playwright/.DS_Store create mode 100644 tests/playwright/.env.example delete mode 100644 tests/playwright/README.md create mode 100644 tests/playwright/package.json create mode 100644 tests/playwright/playwright-report/index.html create mode 100644 tests/playwright/playwright.config.js create mode 100644 tests/playwright/tests/example.spec.js rename tests/playwright/{ => tests}/place-order.spec.js (100%) rename tests/playwright/{ => tests}/subscriptions-api.spec.js (100%) rename tests/playwright/{ => tests}/utils/checkout.js (100%) rename tests/playwright/{ => tests}/utils/paypal-popup.js (100%) rename tests/playwright/{ => tests}/utils/server.js (100%) rename tests/playwright/{ => tests}/utils/user.js (100%) diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..ae33a69cba05b4c4756af5612a9193641449a61b GIT binary patch literal 8196 zcmeHM!EVz)5S?vW;xx31P!YW#EOBi^fr_f)lBVsU5^!h`8~_D7HmRw*j^d;#s)}+2 z{()cM$e-{T+&RIUwHtYzMC}QIx*P4zdOdGu_U){lO^8U;``&$`Wg@arSyswuCKSHU zb*#*!Bex+1@D#P&FmiEK8Phcbh5^HXVZbn87%&X{8w}u`&BdDX-cRnD)-YfgxRMO; z^T9@CSxe5`74JkAi{uLU2CSD(<;&v+M{jSrDxQjU0s`)yRRAUikL%09`#|{ zr#Pq0d}X!Ex;ExKfn~*27Ihu`vJan{I$rlCm?<6C!PG3j`Vll&IC1C?xO6d14#DiT8zXW`U$Uh+J zE-bVWKU?8Ulk`rFsCnpdFyES;8?Y0@PKfANu{RuCadro=!Yry;!ZP5-rg{&e9Pxr>GYlwNw*GHTlbUDHaJ5aIxF~fEN@pY-CTPEf-t2GB( z$ZNE@+*Yhj(R2rKC;h$fyW0taxVN_UL(CLr=dKm4qE)uuG!CLcqv!Pcaoy>@;;ZLT z6uReq$9)m_gJxxEJCZ#okbYMI0?&ua>z9G_qCq|COD|T~MsmX{S*2#>*6HcS#`=o= zaQ$q>KHYqQx$Kb?>On#}77sQ1ZfJxzMh{6DK*h z;pmF*OJ1hQk*Ji(T3<@Hwg)?$JTX2^wb|Q{obsIXW6Q46=w~L4=3%FUC|YWRR1p`p zygoCPR9Q?3S`NHQ#5(!W&08{y7%hzHT!w){qO@Fw+@1dW|7A=W9m9ZO;6G(R%InY)J(r&cpS5k&x2RlLZ> /etc/sudoers.d/pwuser \ + && chmod 0440 /etc/sudoers.d/pwuser + +# CAROOT for `mkcert` to use, has the CA config +ENV CAROOT=/mnt/ddev-global-cache/mkcert + +# Install the correct architecture binary of `mkcert` +RUN export TARGETPLATFORM=linux/$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/') && mkdir -p /usr/local/bin && curl --fail -JL -s -o /usr/local/bin/mkcert "https://dl.filippo.io/mkcert/latest?for=${TARGETPLATFORM}" +RUN chmod +x /usr/local/bin/mkcert + + +# Install a window manager. +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + --mount=type=cache,target=/var/lib/apt,sharing=locked \ + apt-get update \ + && apt-get install -y icewm xauth + +# Install kasmvnc for remote access. +RUN /bin/bash -c 'if [ $(arch) == "aarch64" ]; then KASM_ARCH=arm64; else KASM_ARCH=amd64; fi; wget https://github.com/kasmtech/KasmVNC/releases/download/v1.1.0/kasmvncserver_bullseye_1.1.0_${KASM_ARCH}.deb' +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ + --mount=type=cache,target=/var/lib/apt,sharing=locked \ + apt-get install -y ./kasmvncserver*.deb + +# We're done with apt so disable caching again for the final image. +RUN mv /etc/apt/docker-clean-disabled /etc/apt/apt.conf.d/docker-clean + +# prepare KasmVNC +RUN sudo -u pwuser mkdir /home/pwuser/.vnc +COPY kasmvnc.yaml xstartup /home/pwuser/.vnc/ +RUN chown pwuser:pwuser /home/pwuser/.vnc/* +RUN sudo -u pwuser touch /home/pwuser/.vnc/.de-was-selected +RUN sudo -u pwuser /bin/bash -c 'echo -e "secret\nsecret\n" | kasmvncpasswd -wo -u pwuser' # We actually disable auth, but KASM complains without it + + +COPY entrypoint.sh /root/entrypoint.sh +ENTRYPOINT "/root/entrypoint.sh" diff --git a/.ddev/playwright-build/entrypoint.sh b/.ddev/playwright-build/entrypoint.sh new file mode 100755 index 000000000..1ff25d127 --- /dev/null +++ b/.ddev/playwright-build/entrypoint.sh @@ -0,0 +1,18 @@ +#!/bin/bash +#ddev-generated +# Remove the line above if you don't want this file to be overwritten when you run +# ddev get julienloizelet/ddev-playwright +# +# This file comes from https://github.com/julienloizelet/ddev-playwright +# + +# Change pwuser IDs to the host IDs supplied by DDEV +usermod -u ${DDEV_UID} pwuser +groupmod -g ${DDEV_GID} pwuser +usermod -a -G ssl-cert pwuser + +# Install DDEV certificate +mkcert -install + +# Run CMD from parameters as pwuser +sudo -u pwuser vncserver -fg -disableBasicAuth diff --git a/.ddev/playwright-build/kasmvnc.yaml b/.ddev/playwright-build/kasmvnc.yaml new file mode 100644 index 000000000..c42c849b2 --- /dev/null +++ b/.ddev/playwright-build/kasmvnc.yaml @@ -0,0 +1,14 @@ +#ddev-generated +# Remove the line above if you don't want this file to be overwritten when you run +# ddev get julienloizelet/ddev-playwright +# +# This file comes from https://github.com/julienloizelet/ddev-playwright +# +logging: + log_writer_name: all + log_dest: syslog + level: 100 + +network: + ssl: + require_ssl: false diff --git a/.ddev/playwright-build/xstartup b/.ddev/playwright-build/xstartup new file mode 100755 index 000000000..7d5896d3f --- /dev/null +++ b/.ddev/playwright-build/xstartup @@ -0,0 +1,32 @@ +#!/bin/sh +#ddev-generated +# Remove the line above if you don't want this file to be overwritten when you run +# ddev get julienloizelet/ddev-playwright +# +# This file comes from https://github.com/julienloizelet/ddev-playwright +# + +export DISPLAY=:1 + +unset SESSION_MANAGER +unset DBUS_SESSION_BUS_ADDRESS +OS=`uname -s` +if [ $OS = 'Linux' ]; then + case "$WINDOWMANAGER" in + *gnome*) + if [ -e /etc/SuSE-release ]; then + PATH=$PATH:/opt/gnome/bin + export PATH + fi + ;; + esac +fi +if [ -x /etc/X11/xinit/xinitrc ]; then + exec /etc/X11/xinit/xinitrc +fi +if [ -f /etc/X11/xinit/xinitrc ]; then + exec sh /etc/X11/xinit/xinitrc +fi +[ -r $HOME/.Xresources ] && xrdb $HOME/.Xresources +xterm -geometry 80x24+10+10 -ls -title "$VNCDESKTOP Desktop" & +icewm-session diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 000000000..b99d2bde0 --- /dev/null +++ b/.github/workflows/playwright.yml @@ -0,0 +1,19 @@ +name: Run Playwright tests via DDEV +on: + push: + workflow_dispatch: +jobs: + ddev-playwright: + uses: inpsyde/reusable-workflows/.github/workflows/ddev-playwright.yml@feature/ddev-playwright + secrets: + COMPOSER_AUTH_JSON: ${{ secrets.PACKAGIST_AUTH_JSON }} + NGROK_AUTH_TOKEN: ${{ secrets.NGROK_AUTH_TOKEN }} + ENV_VARS: >- + [ + {"name": "SOME_USERNAME", "value": "user1"}, + {"name": "SOME_PASSWORD", "value": "pass123"} + ] + with: + DDEV_ORCHESTRATE_CMD: ddev orchestrate + PLAYWRIGHT_INSTALL_CMD: ddev playwright-install + PLAYWRIGHT_RUN_CMD: ddev playwright test diff --git a/package.json b/package.json index 47a99da9c..ef26f7239 100644 --- a/package.json +++ b/package.json @@ -69,8 +69,5 @@ "dependencies": { "dotenv": "^16.0.3", "npm-run-all": "^4.1.5" - }, - "devDependencies": { - "@playwright/test": "^1.31.1" } } diff --git a/playwright.config.js b/playwright.config.js deleted file mode 100644 index 6ade8826a..000000000 --- a/playwright.config.js +++ /dev/null @@ -1,12 +0,0 @@ -require('dotenv').config({ path: '.env.e2e' }); - -const config = { - testDir: './tests/playwright', - timeout: 60000, - use: { - baseURL: process.env.BASEURL, - ignoreHTTPSErrors: true, - }, -}; - -module.exports = config; diff --git a/tests/.DS_Store b/tests/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..e422aec7e0c3c1f17716971aa45cb3c5acff6acf GIT binary patch literal 6148 zcmeHKL2DC16n>MW&Bjv5L8-?DuR+r`Sc;c5R?wTc+8$Kmu36f}b*E%EE+qu^s{R;{ z{*(Tfp7eV&Q!!};p&+!p2XDTadGDKLzD;KK5s?~B$D2f5BJ$vjjSiYijN|MVtl?%> zfr{7AAL2~M81Zsq*Fgb(cTGxYi<0Nr!uxxS$o-$j%tqSuf}X)&Q%)(BOWEI-vTq?X zqliwZq6zX3>1dgw&i;iHSH-dWRsD#Mv$QDt{ZrLwwpLc#UfWyq-i1fH3d^XR7Q<-r zhDWco&f@ca6z``=HR|4eqVqCJ^JHQO={P~k+k-S8>uRW{`M5CL&<4DY*BNy;X0x50 zLC@bF%zOT9cdOs?AMOt3^Nx4t-h)RkM<26Kx&C70A%(wyv2};%@D-g)<@g*<@=WK4 zh%s-F4?dhRa9CS3 zM+X{H0svc>Hio|W_y_tt0Sp}07BK@;mI|~~d$MvB zigKlg@=ZFaz@oIGfGDu2K+_JpeE&cFd;Pyil94DN3fw3KRBJEX8)8fDZoRNMzH5E> sdpH~C)fRtK&@opra``I03pa*&pF6<7VQmpJF!>NLGDsr|{8t5j0R%dKlK=n! literal 0 HcmV?d00001 diff --git a/tests/playwright/.DS_Store b/tests/playwright/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..81d5f9bac321197b0280e33b830e20a21af42971 GIT binary patch literal 6148 zcmeHKO>fgc5S>jE>Zl;)P*g6JR_Zk>0V1l3iwWhx6{GghR>4k8Yihhv>|j+ys`au?e!9o=uL*th+0JC!8O*JXl^lmUi*^OT%-XM zrbb976w@|+rhF-z1An6edh8z3h(a3B8QS0Drzm@uszH>h2swI!yoPW8Foqt{I(R8K z=Ty>|5^ztcz~~PMT>4}pW-i>Q^X*2R23Rp>OhNf#rr5kYGb;0uySJ-olqPx6>3kQp zm3rgBs<-N`dB?%2DuW^{CV4L$AL^qIN~O`fA4dC0T=rY*Z&X%+_M7iGvlq@b6qaS3a|qIz5@FD zAmJK)hm}S1=|E$)0KhuJ#?aTlikc%G`VK3LI0F-w3ba(=wiv?F5tlB{cUW1pbP{g! zA>5UP+o1?^b<~$8orG_ZTULM-SX5xe9=7!Q|Mu?v|00QxSOHexzfwTd_kz719?7k( yOAn{lS_%IM*QWC-i@zx7n57uGdMSPaH-@;>9iZ>9vWN(be*}yS+^_ + + + + + + Playwright Test Report + + + + +
+ + + + \ No newline at end of file diff --git a/tests/playwright/playwright.config.js b/tests/playwright/playwright.config.js new file mode 100644 index 000000000..d7e8f4928 --- /dev/null +++ b/tests/playwright/playwright.config.js @@ -0,0 +1,77 @@ +// @ts-check +const { defineConfig, devices } = require('@playwright/test'); + +require('dotenv').config({ path: '.env' }); + +/** + * @see https://playwright.dev/docs/test-configuration + */ +module.exports = defineConfig({ + testDir: './tests', + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: [ + [process.env.CI ? 'github' : 'list'], + ['html', {open: 'never'}], + ], + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Base URL to use in actions like `await page.goto('/')`. */ + baseURL: process.env.BASEURL, + ignoreHTTPSErrors: true, + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + + // { + // name: 'firefox', + // use: { ...devices['Desktop Firefox'] }, + // }, + // + // { + // name: 'webkit', + // use: { ...devices['Desktop Safari'] }, + // }, + + /* Test against mobile viewports. */ + // { + // name: 'Mobile Chrome', + // use: { ...devices['Pixel 5'] }, + // }, + // { + // name: 'Mobile Safari', + // use: { ...devices['iPhone 12'] }, + // }, + + /* Test against branded browsers. */ + // { + // name: 'Microsoft Edge', + // use: { ...devices['Desktop Edge'], channel: 'msedge' }, + // }, + // { + // name: 'Google Chrome', + // use: { ..devices['Desktop Chrome'], channel: 'chrome' }, + // }, + ], + + /* Run your local dev server before starting the tests */ + // webServer: { + // command: 'npm run start', + // url: 'http://127.0.0.1:3000', + // reuseExistingServer: !process.env.CI, + // }, +}); diff --git a/tests/playwright/tests/example.spec.js b/tests/playwright/tests/example.spec.js new file mode 100644 index 000000000..757852e0e --- /dev/null +++ b/tests/playwright/tests/example.spec.js @@ -0,0 +1,9 @@ +const {test, expect} = require('@playwright/test'); + +const { + BASEURL, +} = process.env; + +test('Visit home page', async ({page}) => { + await page.goto(BASEURL); +}); diff --git a/tests/playwright/place-order.spec.js b/tests/playwright/tests/place-order.spec.js similarity index 100% rename from tests/playwright/place-order.spec.js rename to tests/playwright/tests/place-order.spec.js diff --git a/tests/playwright/subscriptions-api.spec.js b/tests/playwright/tests/subscriptions-api.spec.js similarity index 100% rename from tests/playwright/subscriptions-api.spec.js rename to tests/playwright/tests/subscriptions-api.spec.js diff --git a/tests/playwright/utils/checkout.js b/tests/playwright/tests/utils/checkout.js similarity index 100% rename from tests/playwright/utils/checkout.js rename to tests/playwright/tests/utils/checkout.js diff --git a/tests/playwright/utils/paypal-popup.js b/tests/playwright/tests/utils/paypal-popup.js similarity index 100% rename from tests/playwright/utils/paypal-popup.js rename to tests/playwright/tests/utils/paypal-popup.js diff --git a/tests/playwright/utils/server.js b/tests/playwright/tests/utils/server.js similarity index 100% rename from tests/playwright/utils/server.js rename to tests/playwright/tests/utils/server.js diff --git a/tests/playwright/utils/user.js b/tests/playwright/tests/utils/user.js similarity index 100% rename from tests/playwright/utils/user.js rename to tests/playwright/tests/utils/user.js diff --git a/yarn.lock b/yarn.lock index 89ead66f1..9316efe59 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,21 +2,6 @@ # yarn lockfile v1 -"@playwright/test@^1.31.1": - version "1.31.1" - resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.31.1.tgz#39d6873dc46af135f12451d79707db7d1357455d" - integrity sha512-IsytVZ+0QLDh1Hj83XatGp/GsI1CDJWbyDaBGbainsh0p2zC7F4toUocqowmjS6sQff2NGT3D9WbDj/3K2CJiA== - dependencies: - "@types/node" "*" - playwright-core "1.31.1" - optionalDependencies: - fsevents "2.3.2" - -"@types/node@*": - version "18.14.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.14.1.tgz#90dad8476f1e42797c49d6f8b69aaf9f876fc69f" - integrity sha512-QH+37Qds3E0eDlReeboBxfHbX9omAcBCXEzswCu6jySP642jiM3cYSIkU/REqwhCUqXdonHFuBfJDiAJxMNhaQ== - ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -146,11 +131,6 @@ escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== -fsevents@2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== - function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -447,11 +427,6 @@ pify@^3.0.0: resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg== -playwright-core@1.31.1: - version "1.31.1" - resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.31.1.tgz#4deeebbb8fb73b512593fe24bea206d8fd85ff7f" - integrity sha512-JTyX4kV3/LXsvpHkLzL2I36aCdml4zeE35x+G5aPc4bkLsiRiQshU5lWeVpHFAuC8xAcbI6FDcw/8z3q2xtJSQ== - read-pkg@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" From f8bb9cc1a80fc757d32cc8371e45b84a4051aee5 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Fri, 28 Jul 2023 16:26:04 +0200 Subject: [PATCH 36/56] Remove .DS_Store --- .DS_Store | Bin 8196 -> 0 bytes .gitignore | 1 + tests/playwright/playwright-report/index.html | 2 +- 3 files changed, 2 insertions(+), 1 deletion(-) delete mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index ae33a69cba05b4c4756af5612a9193641449a61b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8196 zcmeHM!EVz)5S?vW;xx31P!YW#EOBi^fr_f)lBVsU5^!h`8~_D7HmRw*j^d;#s)}+2 z{()cM$e-{T+&RIUwHtYzMC}QIx*P4zdOdGu_U){lO^8U;``&$`Wg@arSyswuCKSHU zb*#*!Bex+1@D#P&FmiEK8Phcbh5^HXVZbn87%&X{8w}u`&BdDX-cRnD)-YfgxRMO; z^T9@CSxe5`74JkAi{uLU2CSD(<;&v+M{jSrDxQjU0s`)yRRAUikL%09`#|{ zr#Pq0d}X!Ex;ExKfn~*27Ihu`vJan{I$rlCm?<6C!PG3j`Vll&IC1C?xO6d14#DiT8zXW`U$Uh+J zE-bVWKU?8Ulk`rFsCnpdFyES;8?Y0@PKfANu{RuCadro=!Yry;!ZP5-rg{&e9Pxr>GYlwNw*GHTlbUDHaJ5aIxF~fEN@pY-CTPEf-t2GB( z$ZNE@+*Yhj(R2rKC;h$fyW0taxVN_UL(CLr=dKm4qE)uuG!CLcqv!Pcaoy>@;;ZLT z6uReq$9)m_gJxxEJCZ#okbYMI0?&ua>z9G_qCq|COD|T~MsmX{S*2#>*6HcS#`=o= zaQ$q>KHYqQx$Kb?>On#}77sQ1ZfJxzMh{6DK*h z;pmF*OJ1hQk*Ji(T3<@Hwg)?$JTX2^wb|Q{obsIXW6Q46=w~L4=3%FUC|YWRR1p`p zygoCPR9Q?3S`NHQ#5(!W&08{y7%hzHT!w){qO@Fw+@1dW|7A=W9m9ZO;6G(R%InY)J(r&cpS5k&x2RlLZ \ No newline at end of file +window.playwrightReportBase64 = "data:application/zip;base64,UEsDBBQAAAgIAAZy/FZUq2m5iAIAAHsIAAAZAAAAZGQ5YjJlZGZmZTliNzUwMTA0MGMuanNvbqVVXW+bMBT9K8gvTSWgtqHhY9qkdpq0SdM0bd0e1maSYy4NLWBkjNoqy38fpmQQRhqyoTxc5/iee44/rtcoTlL4EKEQRVGwpBDFMQRL7xwT7GKOzAb/xDKoZ8Ajy4oU7LIAbt+VNaigVCUKr9dNtJfGcti8Hiw9n3NCzucRJZjq9ESlmvh7UibKWIkMjILdQo0UUtwBV21hvpIiS6qsBlLBmUpEjsJ1I21UVprkNeCZiIu0yuq5ZGOiqJJtpkN910Qsz4Vq/tEOFiYSleKiNVpTKYi0EKZWz7CEskpbt0OuUjGprpImmWLqWNizqH9F3JDMQ+zbc4/8QJpCyScUYp0ARbtw7RpcQiwkGO+FuNcWDjO6mrETQgN3jDZOHlUlITSWUjyUIKdQ+8GAmtIx6pbx6qkAO2VVzldTyAM8JCcd+UJvWZUrvWP92BwxxEWu4FFNqBkQuluTkBf82Dk8vD2Ce7AP3r+5ac/9wXLU2S3nnr9gpbWhHX2eyj+w47qH/ezxpi3Zt0KJ2UqpogzPzsiSY8snFrao/tn5rRT3ViwBbFYUp1P0eYPj41CsNzNPigJqAeimwpgsr3FmtFFQh4Zn/GqHTpAZulfNtmOanQzaz0k3dRs5mdnL34bzjJVPOe8hs7Um2JwaXeLrNz18/UfeTT6u1N9Rqr9etQeWqB6qa3WF7A5pFr1DLi++vvv25WOHn3bYq7/M7tdm7GjbhmSbSJ71dt/PFqA0O6JKsFNlc0Ar2jmeR7wOfu91mG/GT/BFrEBOa8mE2uS5yXdnM3ixIU+58Q3roHnh8Qv5Hy3yiCqDGKQUsp1Tl1BV2dz7smzeTqYU46sM8ubVXOgEcY9CJSvYLDa/AVBLAwQUAAAICAAGcvxWgWBpcTYBAACHAgAACwAAAHJlcG9ydC5qc29utVLBTsMwDP2VyecyNW23bv0DLpwQHNAOXuLRrElTNY4EmvrvJF3FQMCR5GLnOe89O7mAJUaFjNBcACUHNM9u7Gj00IgM2DGaR20Jmkps63W1EXlcZbXL91MGJ20oFr5c5uheQQNK7Y8FqdOJ9sd6k4u8yiVcKx8w8QC9oR0Mrf1Acn32EWTyfKVJ0Z80dyVuY3Ksd1IKsdmqQuRFuq7ZJOIn7TWvWmdpNeArRWQY3ZkkL8KyHZ3VwUbAOImsXZ+6TtZ+tWV0H4E6A+lMsLFWxJZVGJebZbGrMsC+jzNKJ6mDQwYusHRLo5GKSSUjyO0Cd9DwGCiDkXwwS9/IjLK11M/5YYo7Ax95fXI4v8L8Hp+UKQn9Lc3jiA1273PkOz0My+lN77vzKSl8mU/SvU3oP9V/fqTpA1BLAQI/AxQAAAgIAAZy/FZUq2m5iAIAAHsIAAAZAAAAAAAAAAAAAAC0gQAAAABkZDliMmVkZmZlOWI3NTAxMDQwYy5qc29uUEsBAj8DFAAACAgABnL8VoFgaXE2AQAAhwIAAAsAAAAAAAAAAAAAALSBvwIAAHJlcG9ydC5qc29uUEsFBgAAAAACAAIAgAAAAB4EAAAAAA=="; \ No newline at end of file From f9e6a34e87d0b452438d636fa1e993ca541b7e9d Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Fri, 28 Jul 2023 16:34:36 +0200 Subject: [PATCH 37/56] Remove .DS_Store --- .gitignore | 1 + tests/.DS_Store | Bin 6148 -> 0 bytes tests/playwright/.DS_Store | Bin 6148 -> 0 bytes 3 files changed, 1 insertion(+) delete mode 100644 tests/.DS_Store delete mode 100644 tests/playwright/.DS_Store diff --git a/.gitignore b/.gitignore index ca2c44035..ff5c7fa98 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ modules/ppcp-wc-gateway/assets/css .env.e2e auth.json .DS_Store +tests/.DS_Store diff --git a/tests/.DS_Store b/tests/.DS_Store deleted file mode 100644 index e422aec7e0c3c1f17716971aa45cb3c5acff6acf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKL2DC16n>MW&Bjv5L8-?DuR+r`Sc;c5R?wTc+8$Kmu36f}b*E%EE+qu^s{R;{ z{*(Tfp7eV&Q!!};p&+!p2XDTadGDKLzD;KK5s?~B$D2f5BJ$vjjSiYijN|MVtl?%> zfr{7AAL2~M81Zsq*Fgb(cTGxYi<0Nr!uxxS$o-$j%tqSuf}X)&Q%)(BOWEI-vTq?X zqliwZq6zX3>1dgw&i;iHSH-dWRsD#Mv$QDt{ZrLwwpLc#UfWyq-i1fH3d^XR7Q<-r zhDWco&f@ca6z``=HR|4eqVqCJ^JHQO={P~k+k-S8>uRW{`M5CL&<4DY*BNy;X0x50 zLC@bF%zOT9cdOs?AMOt3^Nx4t-h)RkM<26Kx&C70A%(wyv2};%@D-g)<@g*<@=WK4 zh%s-F4?dhRa9CS3 zM+X{H0svc>Hio|W_y_tt0Sp}07BK@;mI|~~d$MvB zigKlg@=ZFaz@oIGfGDu2K+_JpeE&cFd;Pyil94DN3fw3KRBJEX8)8fDZoRNMzH5E> sdpH~C)fRtK&@opra``I03pa*&pF6<7VQmpJF!>NLGDsr|{8t5j0R%dKlK=n! diff --git a/tests/playwright/.DS_Store b/tests/playwright/.DS_Store deleted file mode 100644 index 81d5f9bac321197b0280e33b830e20a21af42971..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKO>fgc5S>jE>Zl;)P*g6JR_Zk>0V1l3iwWhx6{GghR>4k8Yihhv>|j+ys`au?e!9o=uL*th+0JC!8O*JXl^lmUi*^OT%-XM zrbb976w@|+rhF-z1An6edh8z3h(a3B8QS0Drzm@uszH>h2swI!yoPW8Foqt{I(R8K z=Ty>|5^ztcz~~PMT>4}pW-i>Q^X*2R23Rp>OhNf#rr5kYGb;0uySJ-olqPx6>3kQp zm3rgBs<-N`dB?%2DuW^{CV4L$AL^qIN~O`fA4dC0T=rY*Z&X%+_M7iGvlq@b6qaS3a|qIz5@FD zAmJK)hm}S1=|E$)0KhuJ#?aTlikc%G`VK3LI0F-w3ba(=wiv?F5tlB{cUW1pbP{g! zA>5UP+o1?^b<~$8orG_ZTULM-SX5xe9=7!Q|Mu?v|00QxSOHexzfwTd_kz719?7k( yOAn{lS_%IM*QWC-i@zx7n57uGdMSPaH-@;>9iZ>9vWN(be*}yS+^_ Date: Fri, 28 Jul 2023 16:37:44 +0100 Subject: [PATCH 38/56] Refactor hosted fields for early card detection --- .../js/modules/Renderer/CreditCardRenderer.js | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/modules/ppcp-button/resources/js/modules/Renderer/CreditCardRenderer.js b/modules/ppcp-button/resources/js/modules/Renderer/CreditCardRenderer.js index fcd20a609..9cd6e345c 100644 --- a/modules/ppcp-button/resources/js/modules/Renderer/CreditCardRenderer.js +++ b/modules/ppcp-button/resources/js/modules/Renderer/CreditCardRenderer.js @@ -122,24 +122,17 @@ class CreditCardRenderer { const className = this._cardNumberFiledCLassNameByCardType(event.cards[0].type); this._recreateElementClassAttribute(cardNumber, cardNumberField.className); - if (event.fields.number.isValid) { + if (event.cards.length === 1) { cardNumber.classList.add(className); } }) hostedFields.on('validityChange', (event) => { - const formValid = Object.keys(event.fields).every(function (key) { + this.formValid = Object.keys(event.fields).every(function (key) { return event.fields[key].isValid; }); - - const className = event.cards.length ? this._cardNumberFiledCLassNameByCardType(event.cards[0].type) : ''; - event.fields.number.isValid - ? cardNumber.classList.add(className) - : this._recreateElementClassAttribute(cardNumber, cardNumberField.className); - - this.formValid = formValid; - }); hostedFields.on('empty', (event) => { + this._recreateElementClassAttribute(cardNumber, cardNumberField.className); this.emptyFields.add(event.emittedBy); }); hostedFields.on('notEmpty', (event) => { From e459c9926dee2d1b462f7551d175b78f48ccfa13 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 31 Jul 2023 12:20:30 +0200 Subject: [PATCH 39/56] Remove playwright github action for now --- .github/workflows/playwright.yml | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 .github/workflows/playwright.yml diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml deleted file mode 100644 index b99d2bde0..000000000 --- a/.github/workflows/playwright.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Run Playwright tests via DDEV -on: - push: - workflow_dispatch: -jobs: - ddev-playwright: - uses: inpsyde/reusable-workflows/.github/workflows/ddev-playwright.yml@feature/ddev-playwright - secrets: - COMPOSER_AUTH_JSON: ${{ secrets.PACKAGIST_AUTH_JSON }} - NGROK_AUTH_TOKEN: ${{ secrets.NGROK_AUTH_TOKEN }} - ENV_VARS: >- - [ - {"name": "SOME_USERNAME", "value": "user1"}, - {"name": "SOME_PASSWORD", "value": "pass123"} - ] - with: - DDEV_ORCHESTRATE_CMD: ddev orchestrate - PLAYWRIGHT_INSTALL_CMD: ddev playwright-install - PLAYWRIGHT_RUN_CMD: ddev playwright test From 9bf4422fa825a8a2513bbdb3494d038d60f98087 Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Mon, 31 Jul 2023 11:29:02 +0100 Subject: [PATCH 40/56] Rename tests/playwright to tests/Playwright --- tests/{playwright => Playwright}/.env.example | 0 tests/{playwright => Playwright}/.gitignore | 0 tests/{playwright => Playwright}/package.json | 0 tests/{playwright => Playwright}/playwright-report/index.html | 0 tests/{playwright => Playwright}/playwright.config.js | 0 tests/{playwright => Playwright}/tests/example.spec.js | 0 tests/{playwright => Playwright}/tests/place-order.spec.js | 0 tests/{playwright => Playwright}/tests/subscriptions-api.spec.js | 0 tests/{playwright => Playwright}/tests/utils/checkout.js | 0 tests/{playwright => Playwright}/tests/utils/paypal-popup.js | 0 tests/{playwright => Playwright}/tests/utils/server.js | 0 tests/{playwright => Playwright}/tests/utils/user.js | 0 12 files changed, 0 insertions(+), 0 deletions(-) rename tests/{playwright => Playwright}/.env.example (100%) rename tests/{playwright => Playwright}/.gitignore (100%) rename tests/{playwright => Playwright}/package.json (100%) rename tests/{playwright => Playwright}/playwright-report/index.html (100%) rename tests/{playwright => Playwright}/playwright.config.js (100%) rename tests/{playwright => Playwright}/tests/example.spec.js (100%) rename tests/{playwright => Playwright}/tests/place-order.spec.js (100%) rename tests/{playwright => Playwright}/tests/subscriptions-api.spec.js (100%) rename tests/{playwright => Playwright}/tests/utils/checkout.js (100%) rename tests/{playwright => Playwright}/tests/utils/paypal-popup.js (100%) rename tests/{playwright => Playwright}/tests/utils/server.js (100%) rename tests/{playwright => Playwright}/tests/utils/user.js (100%) diff --git a/tests/playwright/.env.example b/tests/Playwright/.env.example similarity index 100% rename from tests/playwright/.env.example rename to tests/Playwright/.env.example diff --git a/tests/playwright/.gitignore b/tests/Playwright/.gitignore similarity index 100% rename from tests/playwright/.gitignore rename to tests/Playwright/.gitignore diff --git a/tests/playwright/package.json b/tests/Playwright/package.json similarity index 100% rename from tests/playwright/package.json rename to tests/Playwright/package.json diff --git a/tests/playwright/playwright-report/index.html b/tests/Playwright/playwright-report/index.html similarity index 100% rename from tests/playwright/playwright-report/index.html rename to tests/Playwright/playwright-report/index.html diff --git a/tests/playwright/playwright.config.js b/tests/Playwright/playwright.config.js similarity index 100% rename from tests/playwright/playwright.config.js rename to tests/Playwright/playwright.config.js diff --git a/tests/playwright/tests/example.spec.js b/tests/Playwright/tests/example.spec.js similarity index 100% rename from tests/playwright/tests/example.spec.js rename to tests/Playwright/tests/example.spec.js diff --git a/tests/playwright/tests/place-order.spec.js b/tests/Playwright/tests/place-order.spec.js similarity index 100% rename from tests/playwright/tests/place-order.spec.js rename to tests/Playwright/tests/place-order.spec.js diff --git a/tests/playwright/tests/subscriptions-api.spec.js b/tests/Playwright/tests/subscriptions-api.spec.js similarity index 100% rename from tests/playwright/tests/subscriptions-api.spec.js rename to tests/Playwright/tests/subscriptions-api.spec.js diff --git a/tests/playwright/tests/utils/checkout.js b/tests/Playwright/tests/utils/checkout.js similarity index 100% rename from tests/playwright/tests/utils/checkout.js rename to tests/Playwright/tests/utils/checkout.js diff --git a/tests/playwright/tests/utils/paypal-popup.js b/tests/Playwright/tests/utils/paypal-popup.js similarity index 100% rename from tests/playwright/tests/utils/paypal-popup.js rename to tests/Playwright/tests/utils/paypal-popup.js diff --git a/tests/playwright/tests/utils/server.js b/tests/Playwright/tests/utils/server.js similarity index 100% rename from tests/playwright/tests/utils/server.js rename to tests/Playwright/tests/utils/server.js diff --git a/tests/playwright/tests/utils/user.js b/tests/Playwright/tests/utils/user.js similarity index 100% rename from tests/playwright/tests/utils/user.js rename to tests/Playwright/tests/utils/user.js From c035cdaf3e6a07b5cf28cfa42fdc7da3318f29f5 Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Mon, 31 Jul 2023 18:20:39 +0100 Subject: [PATCH 41/56] Add previous token validation for OnboardingUrl Add retries on listen_for_merchant_id() to wait for client_id and signature --- modules/ppcp-onboarding/services.php | 6 +- .../src/Helper/OnboardingUrl.php | 100 ++++++++++++++++-- .../src/Render/OnboardingRenderer.php | 19 +++- modules/ppcp-wc-gateway/services.php | 4 +- .../src/Settings/SettingsListener.php | 69 +++++++++++- psalm-baseline.xml | 3 +- 6 files changed, 184 insertions(+), 17 deletions(-) diff --git a/modules/ppcp-onboarding/services.php b/modules/ppcp-onboarding/services.php index 61c231f94..06f2d27a7 100644 --- a/modules/ppcp-onboarding/services.php +++ b/modules/ppcp-onboarding/services.php @@ -229,13 +229,15 @@ return array( $partner_referrals_sandbox = $container->get( 'api.endpoint.partner-referrals-sandbox' ); $partner_referrals_data = $container->get( 'api.repository.partner-referrals-data' ); $settings = $container->get( 'wcgateway.settings' ); - $signup_link_cache = $container->get( 'onboarding.signup-link-cache' ); + $signup_link_cache = $container->get( 'onboarding.signup-link-cache' ); + $logger = $container->get( 'woocommerce.logger.woocommerce' ); return new OnboardingRenderer( $settings, $partner_referrals, $partner_referrals_sandbox, $partner_referrals_data, - $signup_link_cache + $signup_link_cache, + $logger ); }, 'onboarding.render-options' => static function ( ContainerInterface $container ) : OnboardingOptionsRenderer { diff --git a/modules/ppcp-onboarding/src/Helper/OnboardingUrl.php b/modules/ppcp-onboarding/src/Helper/OnboardingUrl.php index 3365882ad..881d4295f 100644 --- a/modules/ppcp-onboarding/src/Helper/OnboardingUrl.php +++ b/modules/ppcp-onboarding/src/Helper/OnboardingUrl.php @@ -66,6 +66,13 @@ class OnboardingUrl { */ private $cache_ttl = 3 * MONTH_IN_SECONDS; + /** + * The TTL for the previous token cache. + * + * @var int + */ + private $previous_cache_ttl = 60; + /** * The constructor * @@ -84,20 +91,19 @@ class OnboardingUrl { } /** - * Validates the token, if it's valid then delete it. - * If it's invalid don't delete it, to prevent malicious requests from invalidating the token. + * Instances the object with a $token. * * @param Cache $cache The cache object where the URL is stored. - * @param string $onboarding_token The token to validate. + * @param string $token The token to validate. * @param int $user_id User ID to associate the link with. - * @return bool + * @return false|self */ - public static function validate_token_and_delete( Cache $cache, string $onboarding_token, int $user_id ): bool { - if ( ! $onboarding_token ) { + public static function make_from_token( Cache $cache, string $token, int $user_id ) { + if ( ! $token ) { return false; } - $token_data = json_decode( UrlHelper::url_safe_base64_decode( $onboarding_token ) ?: '', true ); + $token_data = json_decode( UrlHelper::url_safe_base64_decode( $token ) ?: '', true ); if ( ! $token_data ) { return false; @@ -111,20 +117,57 @@ class OnboardingUrl { return false; } - $onboarding_url = new self( $cache, $token_data['k'], $token_data['u'] ); + return new self( $cache, $token_data['k'], $token_data['u'] ); + } + + /** + * Validates the token, if it's valid then delete it. + * If it's invalid don't delete it, to prevent malicious requests from invalidating the token. + * + * @param Cache $cache The cache object where the URL is stored. + * @param string $token The token to validate. + * @param int $user_id User ID to associate the link with. + * @return bool + */ + public static function validate_token_and_delete( Cache $cache, string $token, int $user_id ): bool { + $onboarding_url = self::make_from_token( $cache, $token, $user_id ); + + if ( $onboarding_url === false ) { + return false; + } if ( ! $onboarding_url->load() ) { return false; } - if ( ( $onboarding_url->token() ?: '' ) !== $onboarding_token ) { + if ( ( $onboarding_url->token() ?: '' ) !== $token ) { return false; } + $onboarding_url->replace_previous_token( $token ); $onboarding_url->delete(); return true; } + /** + * Validates the token against the previous token. + * Useful to don't throw errors on burst calls to endpoints. + * + * @param Cache $cache The cache object where the URL is stored. + * @param string $token The token to validate. + * @param int $user_id User ID to associate the link with. + * @return bool + */ + public static function validate_previous_token( Cache $cache, string $token, int $user_id ): bool { + $onboarding_url = self::make_from_token( $cache, $token, $user_id ); + + if ( $onboarding_url === false ) { + return false; + } + + return $onboarding_url->check_previous_token( $token ); + } + /** * Load cached data if is valid and initialize object. * @@ -315,4 +358,43 @@ class OnboardingUrl { return implode( '_', array( $this->cache_key_prefix, $this->user_id ) ); } + /** + * Returns the compiled cache key of the previous token + * + * @return string + */ + private function previous_cache_key(): string { + return $this->cache_key() . '_previous'; + } + + /** + * Checks it the previous token matches the token provided. + * + * @param string $previous_token The previous token. + * @return bool + */ + private function check_previous_token( string $previous_token ): bool { + if ( ! $this->cache->has( $this->previous_cache_key() ) ) { + return false; + } + + $cached_token = $this->cache->get( $this->previous_cache_key() ); + + return $cached_token === $previous_token; + } + + /** + * Replaces the previous token. + * + * @param string $previous_token The previous token. + * @return void + */ + private function replace_previous_token( string $previous_token ): void { + $this->cache->set( + $this->previous_cache_key(), + $previous_token, + $this->previous_cache_ttl + ); + } + } diff --git a/modules/ppcp-onboarding/src/Render/OnboardingRenderer.php b/modules/ppcp-onboarding/src/Render/OnboardingRenderer.php index 34215a2a0..081f1599b 100644 --- a/modules/ppcp-onboarding/src/Render/OnboardingRenderer.php +++ b/modules/ppcp-onboarding/src/Render/OnboardingRenderer.php @@ -9,12 +9,14 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\Onboarding\Render; +use Psr\Log\LoggerInterface; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PartnerReferrals; use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache; use WooCommerce\PayPalCommerce\ApiClient\Repository\PartnerReferralsData; use WooCommerce\PayPalCommerce\Onboarding\Helper\OnboardingUrl; use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; +use WooCommerce\WooCommerce\Logging\Logger\NullLogger; /** * Class OnboardingRenderer @@ -56,6 +58,13 @@ class OnboardingRenderer { */ protected $cache; + /** + * The logger + * + * @var LoggerInterface + */ + private $logger; + /** * OnboardingRenderer constructor. * @@ -64,19 +73,22 @@ class OnboardingRenderer { * @param PartnerReferrals $sandbox_partner_referrals The PartnerReferrals for sandbox. * @param PartnerReferralsData $partner_referrals_data The default partner referrals data. * @param Cache $cache The cache. + * @param ?LoggerInterface $logger The logger. */ public function __construct( Settings $settings, PartnerReferrals $production_partner_referrals, PartnerReferrals $sandbox_partner_referrals, PartnerReferralsData $partner_referrals_data, - Cache $cache + Cache $cache, + LoggerInterface $logger = null ) { $this->settings = $settings; $this->production_partner_referrals = $production_partner_referrals; $this->sandbox_partner_referrals = $sandbox_partner_referrals; $this->partner_referrals_data = $partner_referrals_data; $this->cache = $cache; + $this->logger = $logger ?: new NullLogger(); } /** @@ -102,9 +114,12 @@ class OnboardingRenderer { $onboarding_url = new OnboardingUrl( $this->cache, $cache_key, get_current_user_id() ); if ( $onboarding_url->load() ) { + $this->logger->debug( 'Loaded onbording URL from cache: ' . $cache_key ); return $onboarding_url->get() ?: ''; } + $this->logger->info( 'Generating onboarding URL for: ' . $cache_key ); + $onboarding_url->init(); $data = $this->partner_referrals_data @@ -116,6 +131,8 @@ class OnboardingRenderer { $onboarding_url->set( $url ); $onboarding_url->persist(); + $this->logger->info( 'Persisted onboarding URL for: ' . $cache_key ); + return $url; } diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index 26d866066..6be410b8d 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -292,6 +292,7 @@ return array( $signup_link_ids = $container->get( 'onboarding.signup-link-ids' ); $pui_status_cache = $container->get( 'pui.status-cache' ); $dcc_status_cache = $container->get( 'dcc.status-cache' ); + $logger = $container->get( 'woocommerce.logger.woocommerce' ); return new SettingsListener( $settings, $fields, @@ -304,7 +305,8 @@ return array( $signup_link_ids, $pui_status_cache, $dcc_status_cache, - $container->get( 'http.redirector' ) + $container->get( 'http.redirector' ), + $logger ); }, 'wcgateway.order-processor' => static function ( ContainerInterface $container ): OrderProcessor { diff --git a/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php b/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php index 15110b650..60d07be4c 100644 --- a/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php +++ b/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php @@ -9,6 +9,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\WcGateway\Settings; +use Psr\Log\LoggerInterface; use WooCommerce\PayPalCommerce\AdminNotices\Entity\Message; use WooCommerce\PayPalCommerce\AdminNotices\Repository\Repository; use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer; @@ -23,6 +24,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Helper\DCCProductStatus; use WooCommerce\PayPalCommerce\WcGateway\Helper\PayUponInvoiceProductStatus; use WooCommerce\PayPalCommerce\Webhooks\WebhookRegistrar; use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException; +use WooCommerce\WooCommerce\Logging\Logger\NullLogger; /** * Class SettingsListener @@ -122,6 +124,27 @@ class SettingsListener { */ protected $redirector; + /** + * The logger. + * + * @var LoggerInterface + */ + private $logger; + + /** + * Max onboarding URL retries. + * + * @var int + */ + private $onboarding_max_retries = 3; + + /** + * Delay between onboarding URL retries. + * + * @var int + */ + private $onboarding_retry_delay = 2; + /** * SettingsListener constructor. * @@ -137,6 +160,7 @@ class SettingsListener { * @param Cache $pui_status_cache The PUI status cache. * @param Cache $dcc_status_cache The DCC status cache. * @param RedirectorInterface $redirector The HTTP redirector. + * @param ?LoggerInterface $logger The logger. */ public function __construct( Settings $settings, @@ -150,7 +174,8 @@ class SettingsListener { array $signup_link_ids, Cache $pui_status_cache, Cache $dcc_status_cache, - RedirectorInterface $redirector + RedirectorInterface $redirector, + LoggerInterface $logger = null ) { $this->settings = $settings; @@ -165,6 +190,7 @@ class SettingsListener { $this->pui_status_cache = $pui_status_cache; $this->dcc_status_cache = $dcc_status_cache; $this->redirector = $redirector; + $this->logger = $logger ?: new NullLogger(); } /** @@ -187,16 +213,48 @@ class SettingsListener { $merchant_id = sanitize_text_field( wp_unslash( $_GET['merchantIdInPayPal'] ) ); $merchant_email = sanitize_text_field( wp_unslash( $_GET['merchantId'] ) ); $onboarding_token = sanitize_text_field( wp_unslash( $_GET['ppcpToken'] ) ); + $retry_count = isset( $_GET['ppcpRetry'] ) ? ( (int) sanitize_text_field( wp_unslash( $_GET['ppcpRetry'] ) ) ) : 0; // phpcs:enable WordPress.Security.NonceVerification.Missing // phpcs:enable WordPress.Security.NonceVerification.Recommended $this->settings->set( 'merchant_id', $merchant_id ); $this->settings->set( 'merchant_email', $merchant_email ); - if ( ! OnboardingUrl::validate_token_and_delete( $this->signup_link_cache, $onboarding_token, get_current_user_id() ) ) { - $this->onboarding_redirect( false ); + // If no client_id is present we will try to wait for PayPal to invoke LoginSellerEndpoint. + if ( ! $this->settings->has( 'client_id' ) || ! $this->settings->get( 'client_id' ) ) { + + // Try at most {onboarding_max_retries} times ({onboarding_retry_delay} seconds delay). Then give up and just fill the merchant fields like before. + if ( $retry_count < $this->onboarding_max_retries ) { + + if ( $this->onboarding_retry_delay > 0 ) { + sleep( $this->onboarding_retry_delay ); + } + + $retry_count++; + $this->logger->info( 'Retrying onboarding return URL, retry nr: ' . ( (string) $retry_count ) ); + $redirect_url = add_query_arg( 'ppcpRetry', $retry_count ); + $this->redirector->redirect( $redirect_url ); + } } + // Process token validation. + $onboarding_token_sample = ( (string) substr( $onboarding_token, 0, 4 ) ) . '...' . ( (string) substr( $onboarding_token, -4 ) ); + $this->logger->debug( 'Validating onboarding ppcpToken: ' . $onboarding_token_sample ); + + if ( ! OnboardingUrl::validate_token_and_delete( $this->signup_link_cache, $onboarding_token, get_current_user_id() ) ) { + if ( OnboardingUrl::validate_previous_token( $this->signup_link_cache, $onboarding_token, get_current_user_id() ) ) { + // It's a valid token used previously, don't do anything but silently redirect. + $this->logger->info( 'Validated previous token, silently redirecting: ' . $onboarding_token_sample ); + $this->onboarding_redirect(); + } else { + $this->logger->error( 'Failed to validate onboarding ppcpToken: ' . $onboarding_token_sample ); + $this->onboarding_redirect( false ); + } + } + + $this->logger->info( 'Validated onboarding ppcpToken: ' . $onboarding_token_sample ); + + // Save the merchant data. $is_sandbox = $this->settings->has( 'sandbox_on' ) && $this->settings->get( 'sandbox_on' ); if ( $is_sandbox ) { $this->settings->set( 'merchant_id_sandbox', $merchant_id ); @@ -212,6 +270,7 @@ class SettingsListener { */ do_action( 'woocommerce_paypal_payments_onboarding_before_redirect' ); + // If after all the retry redirects there still isn't a valid client_id then just send an error. if ( ! $this->settings->has( 'client_id' ) || ! $this->settings->get( 'client_id' ) ) { $this->onboarding_redirect( false ); } @@ -230,6 +289,10 @@ class SettingsListener { if ( ! $success ) { $redirect_url = add_query_arg( 'ppcp-onboarding-error', '1', $redirect_url ); + $this->logger->info( 'Redirect ERROR: ' . $redirect_url ); + } else { + $redirect_url = remove_query_arg( 'ppcp-onboarding-error', $redirect_url ); + $this->logger->info( 'Redirect OK: ' . $redirect_url ); } $this->redirector->redirect( $redirect_url ); diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 42ce967ca..fb66e57af 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -685,10 +685,11 @@ listen_for_merchant_id listen_for_vaulting_enabled - + wp_unslash( $_GET['merchantId'] ) wp_unslash( $_GET['merchantIdInPayPal'] ) wp_unslash( $_GET['ppcpToken'] ) + wp_unslash( $_GET['ppcpRetry'] ) wp_unslash( $_POST['ppcp-nonce'] ) From 39a4da6d525c0ecc213cc430dbed70959d85f23c Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Tue, 1 Aug 2023 08:19:40 +0100 Subject: [PATCH 42/56] Change onboarding parameters --- modules/ppcp-wc-gateway/src/Settings/SettingsListener.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php b/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php index 60d07be4c..b1d057537 100644 --- a/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php +++ b/modules/ppcp-wc-gateway/src/Settings/SettingsListener.php @@ -136,7 +136,7 @@ class SettingsListener { * * @var int */ - private $onboarding_max_retries = 3; + private $onboarding_max_retries = 5; /** * Delay between onboarding URL retries. @@ -238,7 +238,7 @@ class SettingsListener { } // Process token validation. - $onboarding_token_sample = ( (string) substr( $onboarding_token, 0, 4 ) ) . '...' . ( (string) substr( $onboarding_token, -4 ) ); + $onboarding_token_sample = ( (string) substr( $onboarding_token, 0, 2 ) ) . '...' . ( (string) substr( $onboarding_token, -6 ) ); $this->logger->debug( 'Validating onboarding ppcpToken: ' . $onboarding_token_sample ); if ( ! OnboardingUrl::validate_token_and_delete( $this->signup_link_cache, $onboarding_token, get_current_user_id() ) ) { From f1bc00e3b97b1fa47e2d561a9636a04895581ed9 Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Tue, 1 Aug 2023 08:55:14 +0100 Subject: [PATCH 43/56] Fix tests --- tests/PHPUnit/Onboarding/Helper/OnboardingUrlTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/PHPUnit/Onboarding/Helper/OnboardingUrlTest.php b/tests/PHPUnit/Onboarding/Helper/OnboardingUrlTest.php index 5dadd2c79..db50265d5 100644 --- a/tests/PHPUnit/Onboarding/Helper/OnboardingUrlTest.php +++ b/tests/PHPUnit/Onboarding/Helper/OnboardingUrlTest.php @@ -58,6 +58,7 @@ class OnboardingUrlTest extends TestCase // Expectations $this->cache->shouldReceive('has')->once()->andReturn(true); $this->cache->shouldReceive('get')->once()->andReturn($cacheData); + $this->cache->shouldReceive('set')->once(); $this->cache->shouldReceive('delete')->once(); $this->assertTrue( From 246fb86c650bf5e77efb1f6eec31ee645db29678 Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Wed, 2 Aug 2023 08:33:09 +0100 Subject: [PATCH 44/56] Changed the woocommerce_paypal_payments_order_approved_webhook_can_create_wc_order filter default value to false --- modules/ppcp-webhooks/src/Handler/CheckoutOrderApproved.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-webhooks/src/Handler/CheckoutOrderApproved.php b/modules/ppcp-webhooks/src/Handler/CheckoutOrderApproved.php index 9a266d41b..177bda0ad 100644 --- a/modules/ppcp-webhooks/src/Handler/CheckoutOrderApproved.php +++ b/modules/ppcp-webhooks/src/Handler/CheckoutOrderApproved.php @@ -145,7 +145,7 @@ class CheckoutOrderApproved implements RequestHandler { return $this->success_response(); } - if ( ! (bool) apply_filters( 'woocommerce_paypal_payments_order_approved_webhook_can_create_wc_order', true ) ) { + if ( ! (bool) apply_filters( 'woocommerce_paypal_payments_order_approved_webhook_can_create_wc_order', false ) ) { return $this->success_response(); } From eb7cd764f90266d57bdb69f8fe13688b2139defe Mon Sep 17 00:00:00 2001 From: Pedro Silva Date: Wed, 2 Aug 2023 08:42:44 +0100 Subject: [PATCH 45/56] Fix ddev --- .github/workflows/e2e.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index c6496f58a..3731a1de4 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -15,7 +15,7 @@ jobs: - uses: satackey/action-docker-layer-caching@v0.0.11 continue-on-error: true - - uses: jonaseberle/github-action-setup-ddev@v1 + - uses: ddev/github-action-setup-ddev@v1 with: autostart: false From e60a7ea41ae91d5158fdb4c311c54b790d6f2a62 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Thu, 3 Aug 2023 16:13:30 +0200 Subject: [PATCH 46/56] Add readme and ignore playwright-report content --- tests/Playwright/.gitignore | 1 + tests/Playwright/README.md | 15 +++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 tests/Playwright/README.md diff --git a/tests/Playwright/.gitignore b/tests/Playwright/.gitignore index d6ff675a6..802710f25 100644 --- a/tests/Playwright/.gitignore +++ b/tests/Playwright/.gitignore @@ -3,3 +3,4 @@ pw-browsers test-results .env yarn.lock +playwright-report/* diff --git a/tests/Playwright/README.md b/tests/Playwright/README.md new file mode 100644 index 000000000..e8724a857 --- /dev/null +++ b/tests/Playwright/README.md @@ -0,0 +1,15 @@ +## ddev-playwright addon +https://github.com/julienloizelet/ddev-playwright + +### Install +``` +$ ddev restart +$ ddev playwright-install +``` + +### Usage +https://github.com/julienloizelet/ddev-playwright#basic-usage +``` +$ ddev playwright test +``` + From b77579bc214cc5702ed24d9510e7c26b577e5d6d Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Thu, 3 Aug 2023 16:15:25 +0200 Subject: [PATCH 47/56] Update `.env.example` contents --- tests/Playwright/.env.example | 37 +++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/tests/Playwright/.env.example b/tests/Playwright/.env.example index 6a5244acb..3de8add8c 100644 --- a/tests/Playwright/.env.example +++ b/tests/Playwright/.env.example @@ -1,3 +1,36 @@ -BASEURL="https://ddev-playwright-test.ddev.site" +BASEURL="https://woocommerce-paypal-payments.ddev.site" +AUTHORIZATION="Bearer ABC123" -PAGE_URL="/" +CHECKOUT_URL="/checkout" +CHECKOUT_PAGE_ID=7 +CART_URL="/cart" +BLOCK_CHECKOUT_URL="/checkout-block" +BLOCK_CHECKOUT_PAGE_ID=22 +BLOCK_CART_URL="/cart-block" + +PRODUCT_URL="/product/prod" +PRODUCT_ID=123 + +SUBSCRIPTION_URL="/product/sub" + +APM_ID="sofort" + +WP_MERCHANT_USER="admin" +WP_MERCHANT_PASSWORD="admin" + +WP_CUSTOMER_USER="customer" +WP_CUSTOMER_PASSWORD="password" + +CUSTOMER_EMAIL="customer@example.com" +CUSTOMER_PASSWORD="password" +CUSTOMER_FIRST_NAME="John" +CUSTOMER_LAST_NAME="Doe" +CUSTOMER_COUNTRY="DE" +CUSTOMER_ADDRESS="street 1" +CUSTOMER_POSTCODE="12345" +CUSTOMER_CITY="city" +CUSTOMER_PHONE="1234567890" + +CREDIT_CARD_NUMBER="1234567890" +CREDIT_CARD_EXPIRATION="01/2042" +CREDIT_CARD_CVV="123" From 83617d9108c08738c886d746566fadca5f1fc6eb Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Thu, 3 Aug 2023 16:16:31 +0200 Subject: [PATCH 48/56] Remove example test file --- tests/Playwright/tests/example.spec.js | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 tests/Playwright/tests/example.spec.js diff --git a/tests/Playwright/tests/example.spec.js b/tests/Playwright/tests/example.spec.js deleted file mode 100644 index 757852e0e..000000000 --- a/tests/Playwright/tests/example.spec.js +++ /dev/null @@ -1,9 +0,0 @@ -const {test, expect} = require('@playwright/test'); - -const { - BASEURL, -} = process.env; - -test('Visit home page', async ({page}) => { - await page.goto(BASEURL); -}); From dc8836d7de732c15a1eb298a3346eeea677461b0 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Thu, 3 Aug 2023 16:33:32 +0200 Subject: [PATCH 49/56] Remove index.html from playwright-report --- tests/Playwright/playwright-report/index.html | 62 ------------------- 1 file changed, 62 deletions(-) delete mode 100644 tests/Playwright/playwright-report/index.html diff --git a/tests/Playwright/playwright-report/index.html b/tests/Playwright/playwright-report/index.html deleted file mode 100644 index 56faa308a..000000000 --- a/tests/Playwright/playwright-report/index.html +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - - - Playwright Test Report - - - - -
- - - - \ No newline at end of file From d10e1c975abf7394fa12f09fd2c15b4bbefbe39e Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Thu, 3 Aug 2023 16:36:47 +0200 Subject: [PATCH 50/56] Update readme --- tests/Playwright/README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/Playwright/README.md b/tests/Playwright/README.md index e8724a857..d837443ce 100644 --- a/tests/Playwright/README.md +++ b/tests/Playwright/README.md @@ -13,3 +13,10 @@ https://github.com/julienloizelet/ddev-playwright#basic-usage $ ddev playwright test ``` +### Known issues +It does not open browser in macOS, to make it work use `npx`: +``` +$ cd tests/Playwright +$ npx playwright test +``` + From 59b583b7aae267cdb9adc7bb08ab55cdcc5e051e Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 7 Aug 2023 15:27:17 +0200 Subject: [PATCH 51/56] Add products utility test --- tests/Playwright/package.json | 5 +- tests/Playwright/playwright.config.js | 1 + .../tests/subscriptions-api.spec.js | 114 +++++++++--------- tests/Playwright/tests/utils/products.js | 59 +++++++++ 4 files changed, 122 insertions(+), 57 deletions(-) create mode 100644 tests/Playwright/tests/utils/products.js diff --git a/tests/Playwright/package.json b/tests/Playwright/package.json index 7b12a8f11..e2e3e6465 100644 --- a/tests/Playwright/package.json +++ b/tests/Playwright/package.json @@ -1,7 +1,8 @@ { "license": "MIT", "dependencies": { - "@playwright/test": "^1.34.2", - "dotenv": "^16.0.3" + "@playwright/test": "^1.34.2", + "@woocommerce/woocommerce-rest-api": "^1.0.1", + "dotenv": "^16.0.3" } } diff --git a/tests/Playwright/playwright.config.js b/tests/Playwright/playwright.config.js index d7e8f4928..dbe3173da 100644 --- a/tests/Playwright/playwright.config.js +++ b/tests/Playwright/playwright.config.js @@ -7,6 +7,7 @@ require('dotenv').config({ path: '.env' }); * @see https://playwright.dev/docs/test-configuration */ module.exports = defineConfig({ + timeout: 60000, testDir: './tests', /* Run tests in files in parallel */ fullyParallel: true, diff --git a/tests/Playwright/tests/subscriptions-api.spec.js b/tests/Playwright/tests/subscriptions-api.spec.js index d1970e328..fe79ccad9 100644 --- a/tests/Playwright/tests/subscriptions-api.spec.js +++ b/tests/Playwright/tests/subscriptions-api.spec.js @@ -3,7 +3,7 @@ const {test, expect} = require('@playwright/test'); const {loginAsAdmin, loginAsCustomer} = require('./utils/user'); const {openPaypalPopup, loginIntoPaypal, completePaypalPayment} = require("./utils/paypal-popup"); const {fillCheckoutForm, expectOrderReceivedPage} = require("./utils/checkout"); -const {createProduct, deleteProduct, updateProduct, updateProductUi} = require("./utils/product"); +const {createProduct, deleteProduct, updateProduct, updateProductUi} = require("./utils/products"); const { AUTHORIZATION, SUBSCRIPTION_URL, @@ -312,64 +312,68 @@ test.describe('Subscriber my account actions', () => { }); }); -test('Disable buttons if no plan connected', async ({page}) => { - const data = { - name: 'Subscription', - type: 'subscription', - meta_data: [ - { - key: '_subscription_price', - value: '10' - } - ] - } - const productId = await createProduct(data) +test.describe('Plan connected display buttons', () => { + test('Disable buttons if no plan connected', async ({page}) => { + const data = { + name: (Math.random() + 1).toString(36).substring(7), + type: 'subscription', + meta_data: [ + { + key: '_subscription_price', + value: '10' + } + ] + } + const productId = await createProduct(data) - // for some reason product meta is not updated in frontend, - // so we need to manually update the product - await updateProductUi(productId, page); + // for some reason product meta is not updated in frontend, + // so we need to manually update the product + await updateProductUi(productId, page); - await page.goto('/product/subscription') - await expect(page.locator('#ppc-button-ppcp-gateway')).not.toBeVisible(); + await page.goto(`/product/?p=${productId}`) + await expect(page.locator('#ppc-button-ppcp-gateway')).not.toBeVisible(); - await page.locator('.single_add_to_cart_button').click(); - await page.goto('/cart'); - await expect(page.locator('#ppc-button-ppcp-gateway')).toBeVisible(); - await expect(page.locator('#ppc-button-ppcp-gateway')).toHaveCSS('cursor', 'not-allowed') + await page.locator('.single_add_to_cart_button').click(); + await page.goto('/cart'); + await expect(page.locator('#ppc-button-ppcp-gateway')).toBeVisible(); + await expect(page.locator('#ppc-button-ppcp-gateway')).toHaveCSS('cursor', 'not-allowed') - await page.goto('/checkout'); - await expect(page.locator('#ppc-button-ppcp-gateway')).toBeVisible(); - await expect(page.locator('#ppc-button-ppcp-gateway')).toHaveCSS('cursor', 'not-allowed') + await page.goto('/checkout'); + await expect(page.locator('#ppc-button-ppcp-gateway')).toBeVisible(); + await expect(page.locator('#ppc-button-ppcp-gateway')).toHaveCSS('cursor', 'not-allowed') - await deleteProduct(productId) + await deleteProduct(productId) + }) + + test('Enable buttons if plan connected', async ({page}) => { + const data = { + name: (Math.random() + 1).toString(36).substring(7), + type: 'subscription', + meta_data: [ + { + key: '_subscription_price', + value: '10' + } + ] + } + const productId = await createProduct(data) + + await loginAsAdmin(page); + await page.goto(`/wp-admin/post.php?post=${productId}&action=edit`) + await page.locator('#ppcp_enable_subscription_product').check(); + await page.locator('#ppcp_subscription_plan_name').fill('Plan name'); + await page.locator('#publish').click(); + await expect(page.getByText('Product updated.')).toBeVisible(); + + await page.goto(`/product/?p=${productId}`) + await expect(page.locator('#ppc-button-ppcp-gateway')).toBeVisible(); + + //await page.getByText('Sign up now').click(); + await page.locator('.single_add_to_cart_button').click(); + await page.goto('/cart'); + await expect(page.locator('#ppc-button-ppcp-gateway')).toBeVisible(); + + await deleteProduct(productId) + }) }) -test('Enable buttons if plan connected', async ({page}) => { - const data = { - name: 'Subscription', - type: 'subscription', - meta_data: [ - { - key: '_subscription_price', - value: '10' - } - ] - } - const productId = await createProduct(data) - - await loginAsAdmin(page); - await page.goto(`/wp-admin/post.php?post=${productId}&action=edit`) - await page.locator('#ppcp_enable_subscription_product').check(); - await page.locator('#ppcp_subscription_plan_name').fill('Plan name'); - await page.locator('#publish').click(); - await expect(page.getByText('Product updated.')).toBeVisible(); - - await page.goto('/product/subscription') - await expect(page.locator('#ppc-button-ppcp-gateway')).toBeVisible(); - - await page.getByText('Sign up now').click(); - await page.goto('/cart'); - await expect(page.locator('#ppc-button-ppcp-gateway')).toBeVisible(); - - await deleteProduct(productId) -}) diff --git a/tests/Playwright/tests/utils/products.js b/tests/Playwright/tests/utils/products.js new file mode 100644 index 000000000..65fbdbe2e --- /dev/null +++ b/tests/Playwright/tests/utils/products.js @@ -0,0 +1,59 @@ +import {loginAsAdmin} from "./user"; +import {expect} from "@playwright/test"; + +const wcApi = require('@woocommerce/woocommerce-rest-api').default; +const { + BASEURL, + WP_MERCHANT_USER, + WP_MERCHANT_PASSWORD, +} = process.env; + +const wc = () => { + return new wcApi({ + url: BASEURL, + consumerKey: WP_MERCHANT_USER, + consumerSecret: WP_MERCHANT_PASSWORD, + version: 'wc/v3', + }); +} + +export const createProduct = async (data) => { + const api = wc(); + + return await api.post('products', data) + .then((response) => { + return response.data.id + }).catch((error) => { + console.log(error) + }) +} + +export const updateProduct = async (data, id) => { + const api = wc(); + + return await api.put(`products/${id}`, data) + .then((response) => { + return response.data.id + }).catch((error) => { + console.log(error) + }) +} + +export const deleteProduct = async (id) => { + const api = wc(); + + return await api.delete(`products/${id}`) + .then((response) => { + return response.data.id + }).catch((error) => { + console.log(error) + }) +} + +export const updateProductUi = async (id, page) => { + await loginAsAdmin(page); + await page.goto(`/wp-admin/post.php?post=${id}&action=edit`) + await page.locator('#publish').click(); + await expect(page.getByText('Product updated.')).toBeVisible(); +} + From cb19889728c79e64dd590df3f48a54e1312ecb4a Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 7 Aug 2023 16:53:44 +0200 Subject: [PATCH 52/56] Add subscription path to build assest scripts --- package.json | 3 +++ tests/Playwright/tests/subscriptions-api.spec.js | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index ef26f7239..19c9fee2b 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "install:modules:ppcp-wc-gateway": "cd modules/ppcp-wc-gateway && yarn install", "install:modules:ppcp-webhooks": "cd modules/ppcp-webhooks && yarn install", "install:modules:ppcp-order-tracking": "cd modules/ppcp-order-tracking && yarn install", + "install:modules:ppcp-subscription": "cd modules/ppcp-subscription && 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", @@ -20,6 +21,7 @@ "build:modules:ppcp-wc-gateway": "cd modules/ppcp-wc-gateway && yarn run build", "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-subscription": "cd modules/ppcp-subscription && 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", "build:modules:ppcp-uninstall": "cd modules/ppcp-uninstall && yarn run build", @@ -29,6 +31,7 @@ "watch:modules:ppcp-wc-gateway": "cd modules/ppcp-wc-gateway && yarn run watch", "watch:modules:ppcp-webhooks": "cd modules/ppcp-webhooks && yarn run watch", "watch:modules:ppcp-order-tracking": "cd modules/ppcp-order-tracking && yarn run watch", + "watch:modules:ppcp-subscription": "cd modules/ppcp-subscription && 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", diff --git a/tests/Playwright/tests/subscriptions-api.spec.js b/tests/Playwright/tests/subscriptions-api.spec.js index fe79ccad9..d575ed8f7 100644 --- a/tests/Playwright/tests/subscriptions-api.spec.js +++ b/tests/Playwright/tests/subscriptions-api.spec.js @@ -368,7 +368,6 @@ test.describe('Plan connected display buttons', () => { await page.goto(`/product/?p=${productId}`) await expect(page.locator('#ppc-button-ppcp-gateway')).toBeVisible(); - //await page.getByText('Sign up now').click(); await page.locator('.single_add_to_cart_button').click(); await page.goto('/cart'); await expect(page.locator('#ppc-button-ppcp-gateway')).toBeVisible(); From be8015b8ddc2fcf0d436bef0532d10534f94c35c Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Mon, 7 Aug 2023 18:53:01 +0200 Subject: [PATCH 53/56] Add variations get subscription products --- .../js/modules/ActionHandler/SingleProductActionHandler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ppcp-button/resources/js/modules/ActionHandler/SingleProductActionHandler.js b/modules/ppcp-button/resources/js/modules/ActionHandler/SingleProductActionHandler.js index b751740ef..68bf43e81 100644 --- a/modules/ppcp-button/resources/js/modules/ActionHandler/SingleProductActionHandler.js +++ b/modules/ppcp-button/resources/js/modules/ActionHandler/SingleProductActionHandler.js @@ -73,7 +73,7 @@ class SingleProductActionHandler { getSubscriptionProducts() { const id = document.querySelector('[name="add-to-cart"]').value; - return [new Product(id, 1, [])]; + return [new Product(id, 1, this.variations())]; } configuration() From 02e35bbe9d8bdd1f7b64abf772ee8e77bf433a07 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 8 Aug 2023 12:10:31 +0200 Subject: [PATCH 54/56] Prevent PayPal subscription buttons to be rendered multiple times --- .../js/modules/ContextBootstrap/SingleProductBootstap.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js b/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js index cc6407ccf..54f3f931f 100644 --- a/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js +++ b/modules/ppcp-button/resources/js/modules/ContextBootstrap/SingleProductBootstap.js @@ -22,6 +22,8 @@ class SingleProductBootstap { this.renderer.onButtonsInit(this.gateway.button.wrapper, () => { this.handleChange(); }, true); + + this.subscriptionButtonsLoaded = false } form() { @@ -29,6 +31,8 @@ class SingleProductBootstap { } handleChange() { + this.subscriptionButtonsLoaded = false + if (!this.shouldRender()) { this.renderer.disableSmartButtons(this.gateway.button.wrapper); hide(this.gateway.button.wrapper, this.formSelector); @@ -187,6 +191,7 @@ class SingleProductBootstap { return; } + if(this.subscriptionButtonsLoaded) return loadPaypalJsScript( { clientId: PayPalCommerceGateway.client_id, @@ -197,6 +202,8 @@ class SingleProductBootstap { actionHandler.subscriptionsConfiguration(subscription_plan), this.gateway.button.wrapper ); + + this.subscriptionButtonsLoaded = true return; } From 4d8302be453314a0a1d0184320792218148adab0 Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Tue, 8 Aug 2023 12:53:14 +0200 Subject: [PATCH 55/56] Fix psalm --- .psalm/wcs.php | 14 +++++++++++ .../src/Helper/SubscriptionHelper.php | 19 ++++++++------ .../src/SubscriptionModule.php | 25 +++++++++++++++++-- 3 files changed, 49 insertions(+), 9 deletions(-) diff --git a/.psalm/wcs.php b/.psalm/wcs.php index a35088c88..cc33e0103 100644 --- a/.psalm/wcs.php +++ b/.psalm/wcs.php @@ -2093,3 +2093,17 @@ function wcs_order_contains_product($order, $product) * @return string Page ID. Empty string if resource not found. */ function wc_get_page_screen_id( $for ) {} + +/** + * Subscription Product Variation Class + * + * The subscription product variation class extends the WC_Product_Variation product class + * to create subscription product variations. + * + * @class WC_Product_Subscription + * @package WooCommerce Subscriptions + * @category Class + * @since 1.0.0 - Migrated from WooCommerce Subscriptions v1.3 + * + */ +class WC_Product_Subscription_Variation extends WC_Product_Variation {} diff --git a/modules/ppcp-subscription/src/Helper/SubscriptionHelper.php b/modules/ppcp-subscription/src/Helper/SubscriptionHelper.php index 619fbc658..635e66cc1 100644 --- a/modules/ppcp-subscription/src/Helper/SubscriptionHelper.php +++ b/modules/ppcp-subscription/src/Helper/SubscriptionHelper.php @@ -219,6 +219,11 @@ class SubscriptionHelper { return ''; } + /** + * Returns variations for variable PayPal subscription product. + * + * @return array + */ public function variable_paypal_subscription_variations(): array { $variations = array(); if ( ! $this->current_product_is_subscription() ) { @@ -232,16 +237,16 @@ class SubscriptionHelper { } $variation_ids = $product->get_children(); - foreach ($variation_ids as $id) { - $product = wc_get_product($id); - if(! is_a($product, WC_Product_Subscription_Variation::class )) { + foreach ( $variation_ids as $id ) { + $product = wc_get_product( $id ); + if ( ! is_a( $product, WC_Product_Subscription_Variation::class ) ) { continue; } - $subscription_plan = $product->get_meta('ppcp_subscription_plan') ?? ''; - $variations[] = array( - 'id' => $product->get_id(), - 'attributes' => $product->get_attributes(), + $subscription_plan = $product->get_meta( 'ppcp_subscription_plan' ) ?? array(); + $variations[] = array( + 'id' => $product->get_id(), + 'attributes' => $product->get_attributes(), 'subscription_plan' => $subscription_plan['id'] ?? '', ); } diff --git a/modules/ppcp-subscription/src/SubscriptionModule.php b/modules/ppcp-subscription/src/SubscriptionModule.php index d2077db06..5fe6f1dc1 100644 --- a/modules/ppcp-subscription/src/SubscriptionModule.php +++ b/modules/ppcp-subscription/src/SubscriptionModule.php @@ -12,6 +12,7 @@ namespace WooCommerce\PayPalCommerce\Subscription; use Exception; use WC_Product; use WC_Product_Subscription_Variation; +use WC_Product_Variable; use WC_Product_Variable_Subscription; use WC_Subscriptions_Product; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingSubscriptions; @@ -195,9 +196,15 @@ class SubscriptionModule implements ModuleInterface { $products = array( $this->set_product_config( $product ) ); if ( $product->get_type() === 'variable-subscription' ) { - $products = array(); + $products = array(); + assert( $product instanceof WC_Product_Variable ); $available_variations = $product->get_available_variations(); foreach ( $available_variations as $variation ) { + /** + * The method is defined in WooCommerce. + * + * @psalm-suppress UndefinedMethod + */ $variation = wc_get_product_object( 'variation', $variation['variation_id'] ); $products[] = $this->set_product_config( $variation ); } @@ -472,9 +479,18 @@ class SubscriptionModule implements ModuleInterface { add_action( 'woocommerce_save_product_variation', + /** + * Param types removed to avoid third-party issues. + * + * @psalm-suppress MissingClosureParamType + */ function( $variation_id ) use ( $c ) { $wcsnonce_save_variations = wc_clean( wp_unslash( $_POST['_wcsnonce_save_variations'] ?? '' ) ); - if ( ! WC_Subscriptions_Product::is_subscription( $variation_id ) || empty( $wcsnonce_save_variations ) || ! wp_verify_nonce( $wcsnonce_save_variations, 'wcs_subscription_variations' ) ) { + if ( + ! WC_Subscriptions_Product::is_subscription( $variation_id ) + || ! is_string( $wcsnonce_save_variations ) + || ! wp_verify_nonce( $wcsnonce_save_variations, 'wcs_subscription_variations' ) + ) { return; } @@ -808,6 +824,11 @@ class SubscriptionModule implements ModuleInterface { add_action( 'woocommerce_variation_options_pricing', + /** + * Param types removed to avoid third-party issues. + * + * @psalm-suppress MissingClosureParamType + */ function( $loop, $variation_data, $variation ) use ( $c ) { $settings = $c->get( 'wcgateway.settings' ); assert( $settings instanceof Settings ); From 4260dce97b3244093d9091761f26a0fbd5462d6a Mon Sep 17 00:00:00 2001 From: Emili Castells Guasch Date: Wed, 9 Aug 2023 15:38:46 +0200 Subject: [PATCH 56/56] Enable buttons when cart contains only one subscription product --- .../resources/js/modules/ContextBootstrap/CartBootstap.js | 2 +- .../js/modules/ContextBootstrap/CheckoutBootstap.js | 2 +- modules/ppcp-button/src/Assets/SmartButton.php | 7 ++++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/modules/ppcp-button/resources/js/modules/ContextBootstrap/CartBootstap.js b/modules/ppcp-button/resources/js/modules/ContextBootstrap/CartBootstap.js index ad4547819..1deaafd54 100644 --- a/modules/ppcp-button/resources/js/modules/ContextBootstrap/CartBootstap.js +++ b/modules/ppcp-button/resources/js/modules/ContextBootstrap/CartBootstap.js @@ -87,7 +87,7 @@ class CartBootstrap { ) { this.renderer.render(actionHandler.subscriptionsConfiguration()); - if(!PayPalCommerceGateway.subscription_plan_id) { + if(!PayPalCommerceGateway.subscription_product_allowed) { this.gateway.button.is_disabled = true; this.handleButtonStatus(); } diff --git a/modules/ppcp-button/resources/js/modules/ContextBootstrap/CheckoutBootstap.js b/modules/ppcp-button/resources/js/modules/ContextBootstrap/CheckoutBootstap.js index 5d770b91e..eb9aa3885 100644 --- a/modules/ppcp-button/resources/js/modules/ContextBootstrap/CheckoutBootstap.js +++ b/modules/ppcp-button/resources/js/modules/ContextBootstrap/CheckoutBootstap.js @@ -107,7 +107,7 @@ class CheckoutBootstap { ) { this.renderer.render(actionHandler.subscriptionsConfiguration(), {}, actionHandler.configuration()); - if(!PayPalCommerceGateway.subscription_plan_id) { + if(!PayPalCommerceGateway.subscription_product_allowed) { this.gateway.button.is_disabled = true; this.handleButtonStatus(); } diff --git a/modules/ppcp-button/src/Assets/SmartButton.php b/modules/ppcp-button/src/Assets/SmartButton.php index 1227e233a..fa7c4e23f 100644 --- a/modules/ppcp-button/src/Assets/SmartButton.php +++ b/modules/ppcp-button/src/Assets/SmartButton.php @@ -828,9 +828,9 @@ class SmartButton implements SmartButtonInterface { 'has_subscriptions' => $this->has_subscriptions(), 'paypal_subscriptions_enabled' => $this->paypal_subscriptions_enabled(), ), - 'redirect' => wc_get_checkout_url(), - 'context' => $this->context(), - 'ajax' => array( + 'redirect' => wc_get_checkout_url(), + 'context' => $this->context(), + 'ajax' => array( 'simulate_cart' => array( 'endpoint' => \WC_AJAX::get_endpoint( SimulateCartEndpoint::ENDPOINT ), 'nonce' => wp_create_nonce( SimulateCartEndpoint::nonce() ), @@ -869,6 +869,7 @@ class SmartButton implements SmartButtonInterface { ), 'subscription_plan_id' => $this->subscription_helper->paypal_subscription_id(), 'variable_paypal_subscription_variations' => $this->subscription_helper->variable_paypal_subscription_variations(), + 'subscription_product_allowed' => $this->subscription_helper->checkout_subscription_product_allowed(), 'enforce_vault' => $this->has_subscriptions(), 'can_save_vault_token' => $this->can_save_vault_token(), 'is_free_trial_cart' => $is_free_trial_cart,