diff --git a/modules.php b/modules.php index c4065ea78..06a9f482d 100644 --- a/modules.php +++ b/modules.php @@ -81,5 +81,12 @@ return function ( string $root_dir ): iterable { $modules[] = ( require "$modules_dir/ppcp-paylater-configurator/module.php" )(); } + if ( apply_filters( + 'woocommerce.feature-flags.woocommerce_paypal_payments.axo_enabled', + getenv( 'PCP_AXO_ENABLED' ) === '1' + ) ) { + $modules[] = ( require "$modules_dir/ppcp-axo/module.php" )(); + } + return $modules; }; diff --git a/modules/ppcp-api-client/services.php b/modules/ppcp-api-client/services.php index 225530ec2..f91e24119 100644 --- a/modules/ppcp-api-client/services.php +++ b/modules/ppcp-api-client/services.php @@ -9,6 +9,7 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\ApiClient; +use WooCommerce\PayPalCommerce\ApiClient\Authentication\SdkClientToken; use WooCommerce\PayPalCommerce\ApiClient\Authentication\UserIdToken; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentMethodTokensEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentTokensEndpoint; @@ -1633,4 +1634,11 @@ return array( $container->get( 'woocommerce.logger.woocommerce' ) ); }, + 'api.sdk-client-token' => static function( ContainerInterface $container ): SdkClientToken { + return new SdkClientToken( + $container->get( 'api.host' ), + $container->get( 'api.bearer' ), + $container->get( 'woocommerce.logger.woocommerce' ) + ); + }, ); diff --git a/modules/ppcp-api-client/src/Authentication/SdkClientToken.php b/modules/ppcp-api-client/src/Authentication/SdkClientToken.php new file mode 100644 index 000000000..6a708e1c1 --- /dev/null +++ b/modules/ppcp-api-client/src/Authentication/SdkClientToken.php @@ -0,0 +1,109 @@ +host = $host; + $this->bearer = $bearer; + $this->logger = $logger; + } + + /** + * Returns `sdk_client_token` which uniquely identifies the payer. + * + * @param string $target_customer_id Vaulted customer id. + * + * @return string + * + * @throws PayPalApiException If the request fails. + * @throws RuntimeException If something unexpected happens. + */ + public function sdk_client_token( string $target_customer_id = '' ): string { + $bearer = $this->bearer->bearer(); + + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + $domain = wp_unslash( $_SERVER['HTTP_HOST'] ?? '' ); + + $url = trailingslashit( $this->host ) . 'v1/oauth2/token?grant_type=client_credentials&response_type=client_token&intent=sdk_init&domains[]=*.' . $domain; + + if ( $target_customer_id ) { + $url = add_query_arg( + array( + 'target_customer_id' => $target_customer_id, + ), + $url + ); + } + + $args = array( + 'method' => 'POST', + 'headers' => array( + 'Authorization' => 'Bearer ' . $bearer->token(), + 'Content-Type' => 'application/x-www-form-urlencoded', + ), + ); + + $response = $this->request( $url, $args ); + if ( $response instanceof WP_Error ) { + throw new RuntimeException( $response->get_error_message() ); + } + + $json = json_decode( $response['body'] ); + $status_code = (int) wp_remote_retrieve_response_code( $response ); + if ( 200 !== $status_code ) { + throw new PayPalApiException( $json, $status_code ); + } + + return $json->access_token; + } +} diff --git a/modules/ppcp-api-client/src/Endpoint/PaymentMethodTokensEndpoint.php b/modules/ppcp-api-client/src/Endpoint/PaymentMethodTokensEndpoint.php index ab8d5473b..538ca224d 100644 --- a/modules/ppcp-api-client/src/Endpoint/PaymentMethodTokensEndpoint.php +++ b/modules/ppcp-api-client/src/Endpoint/PaymentMethodTokensEndpoint.php @@ -115,7 +115,7 @@ class PaymentMethodTokensEndpoint { * @throws RuntimeException When something when wrong with the request. * @throws PayPalApiException When something when wrong setting up the token. */ - public function payment_tokens( PaymentSource $payment_source ): stdClass { + public function create_payment_token( PaymentSource $payment_source ): stdClass { $data = array( 'payment_source' => array( $payment_source->name() => $payment_source->properties(), diff --git a/modules/ppcp-api-client/src/Exception/PayPalApiException.php b/modules/ppcp-api-client/src/Exception/PayPalApiException.php index 36e6c1e57..9db633729 100644 --- a/modules/ppcp-api-client/src/Exception/PayPalApiException.php +++ b/modules/ppcp-api-client/src/Exception/PayPalApiException.php @@ -9,6 +9,8 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\ApiClient\Exception; +use stdClass; + /** * Class PayPalApiException */ @@ -17,7 +19,7 @@ class PayPalApiException extends RuntimeException { /** * The JSON response object of PayPal. * - * @var \stdClass + * @var stdClass */ private $response; @@ -31,10 +33,10 @@ class PayPalApiException extends RuntimeException { /** * PayPalApiException constructor. * - * @param \stdClass|null $response The JSON object. - * @param int $status_code The HTTP status code. + * @param stdClass|null $response The JSON object. + * @param int $status_code The HTTP status code. */ - public function __construct( \stdClass $response = null, int $status_code = 0 ) { + public function __construct( stdClass $response = null, int $status_code = 0 ) { if ( is_null( $response ) ) { $response = new \stdClass(); } @@ -65,7 +67,7 @@ class PayPalApiException extends RuntimeException { */ $this->response = $response; $this->status_code = $status_code; - $message = $response->message; + $message = $this->get_customer_friendly_message( $response ); if ( $response->name ) { $message = '[' . $response->name . '] ' . $message; } @@ -141,4 +143,40 @@ class PayPalApiException extends RuntimeException { return $details; } + + /** + * Returns a friendly message if the error detail is known. + * + * @param stdClass $json The response. + * @return string + */ + public function get_customer_friendly_message( stdClass $json ): string { + if ( empty( $json->details ) ) { + return $json->message; + } + $improved_keys_messages = array( + 'PAYMENT_DENIED' => __( 'PayPal rejected the payment. Please reach out to the PayPal support for more information.', 'woocommerce-paypal-payments' ), + 'TRANSACTION_REFUSED' => __( 'The transaction has been refused by the payment processor. Please reach out to the PayPal support for more information.', 'woocommerce-paypal-payments' ), + 'DUPLICATE_INVOICE_ID' => __( 'The transaction has been refused because the Invoice ID already exists. Please create a new order or reach out to the store owner.', 'woocommerce-paypal-payments' ), + 'PAYER_CANNOT_PAY' => __( 'There was a problem processing this transaction. Please reach out to the store owner.', 'woocommerce-paypal-payments' ), + 'PAYEE_ACCOUNT_RESTRICTED' => __( 'There was a problem processing this transaction. Please reach out to the store owner.', 'woocommerce-paypal-payments' ), + 'AGREEMENT_ALREADY_CANCELLED' => __( 'The requested agreement is already canceled. Please reach out to the PayPal support for more information.', 'woocommerce-paypal-payments' ), + ); + $improved_errors = array_filter( + array_keys( $improved_keys_messages ), + function ( $key ) use ( $json ): bool { + foreach ( $json->details as $detail ) { + if ( isset( $detail->issue ) && $detail->issue === $key ) { + return true; + } + } + return false; + } + ); + if ( $improved_errors ) { + $improved_errors = array_values( $improved_errors ); + return $improved_keys_messages[ $improved_errors[0] ]; + } + return $json->message; + } } diff --git a/modules/ppcp-api-client/src/Repository/ApplicationContextRepository.php b/modules/ppcp-api-client/src/Repository/ApplicationContextRepository.php index 244b955a0..8a10aca9d 100644 --- a/modules/ppcp-api-client/src/Repository/ApplicationContextRepository.php +++ b/modules/ppcp-api-client/src/Repository/ApplicationContextRepository.php @@ -54,7 +54,7 @@ class ApplicationContextRepository { $payment_preference = $this->settings->has( 'payee_preferred' ) && $this->settings->get( 'payee_preferred' ) ? ApplicationContext::PAYMENT_METHOD_IMMEDIATE_PAYMENT_REQUIRED : ApplicationContext::PAYMENT_METHOD_UNRESTRICTED; $context = new ApplicationContext( - network_home_url( \WC_AJAX::get_endpoint( ReturnUrlEndpoint::ENDPOINT ) ), + home_url( \WC_AJAX::get_endpoint( ReturnUrlEndpoint::ENDPOINT ) ), (string) wc_get_checkout_url(), (string) $brand_name, $locale, diff --git a/modules/ppcp-applepay/assets/images/applepay.png b/modules/ppcp-applepay/assets/images/applepay.png new file mode 100644 index 000000000..d46807d77 Binary files /dev/null and b/modules/ppcp-applepay/assets/images/applepay.png differ diff --git a/modules/ppcp-applepay/extensions.php b/modules/ppcp-applepay/extensions.php index 302095e3a..058c24904 100644 --- a/modules/ppcp-applepay/extensions.php +++ b/modules/ppcp-applepay/extensions.php @@ -68,6 +68,8 @@ return array( $device_eligibility_notes = __( 'The Apple Pay button will be visible both in previews and below the PayPal buttons in the shop.', 'woocommerce-paypal-payments' ); } + $module_url = $container->get( 'applepay.url' ); + // Connection tab fields. $fields = $insert_after( $fields, @@ -91,10 +93,15 @@ return array( $connection_link = ''; return $insert_after( $fields, - 'allow_card_button_gateway', + 'digital_wallet_heading', array( 'applepay_button_enabled' => array( 'title' => __( 'Apple Pay Button', 'woocommerce-paypal-payments' ), + 'title_html' => sprintf( + '%s', + $module_url, + __( 'Apple Pay', 'woocommerce-paypal-payments' ) + ), 'type' => 'checkbox', 'class' => array( 'ppcp-grayed-out-text' ), 'input_class' => array( 'ppcp-disabled-checkbox' ), @@ -109,7 +116,7 @@ return array( . '

', 'default' => 'yes', 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', + 'gateway' => 'dcc', 'requirements' => array(), 'custom_attributes' => array( 'data-ppcp-display' => wp_json_encode( @@ -122,6 +129,7 @@ return array( ) ), ), + 'classes' => array( 'ppcp-valign-label-middle', 'ppcp-align-label-center' ), ), ) ); @@ -129,10 +137,25 @@ return array( return $insert_after( $fields, - 'allow_card_button_gateway', + 'digital_wallet_heading', array( + 'spacer' => array( + 'title' => '', + 'type' => 'ppcp-text', + 'text' => '', + 'class' => array(), + 'classes' => array( 'ppcp-active-spacer' ), + 'screens' => array( State::STATE_ONBOARDED ), + 'gateway' => 'dcc', + 'requirements' => array(), + ), 'applepay_button_enabled' => array( 'title' => __( 'Apple Pay Button', 'woocommerce-paypal-payments' ), + 'title_html' => sprintf( + '%s', + $module_url, + __( 'Apple Pay', 'woocommerce-paypal-payments' ) + ), 'type' => 'checkbox', 'label' => __( 'Enable Apple Pay button', 'woocommerce-paypal-payments' ) . '

' @@ -145,7 +168,7 @@ return array( . '

', 'default' => 'yes', 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', + 'gateway' => 'dcc', 'requirements' => array(), 'custom_attributes' => array( 'data-ppcp-display' => wp_json_encode( @@ -160,10 +183,12 @@ return array( ->action_visible( 'applepay_button_type' ) ->action_visible( 'applepay_button_language' ) ->action_visible( 'applepay_checkout_data_mode' ) + ->action_class( 'applepay_button_enabled', 'active' ) ->to_array(), ) ), ), + 'classes' => array( 'ppcp-valign-label-middle', 'ppcp-align-label-center' ), ), 'applepay_button_domain_registration' => array( 'title' => __( 'Domain Registration', 'woocommerce-paypal-payments' ), @@ -183,7 +208,7 @@ return array( 'classes' => array( 'ppcp-field-indent' ), 'class' => array(), 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', + 'gateway' => 'dcc', 'requirements' => array(), ), 'applepay_button_domain_validation' => array( @@ -206,7 +231,7 @@ return array( 'classes' => array( 'ppcp-field-indent' ), 'class' => array(), 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', + 'gateway' => 'dcc', 'requirements' => array(), ), 'applepay_button_device_eligibility' => array( @@ -224,7 +249,7 @@ return array( 'classes' => array( 'ppcp-field-indent' ), 'class' => array(), 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', + 'gateway' => 'dcc', 'requirements' => array(), ), 'applepay_button_type' => array( @@ -241,7 +266,7 @@ return array( 'default' => 'pay', 'options' => PropertiesDictionary::button_types(), 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', + 'gateway' => 'dcc', 'requirements' => array(), ), 'applepay_button_color' => array( @@ -259,7 +284,7 @@ return array( 'default' => 'black', 'options' => PropertiesDictionary::button_colors(), 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', + 'gateway' => 'dcc', 'requirements' => array(), ), 'applepay_button_language' => array( @@ -276,7 +301,7 @@ return array( 'default' => 'en', 'options' => PropertiesDictionary::button_languages(), 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', + 'gateway' => 'dcc', 'requirements' => array(), ), 'applepay_checkout_data_mode' => array( @@ -290,7 +315,7 @@ return array( 'default' => PropertiesDictionary::BILLING_DATA_MODE_DEFAULT, 'options' => PropertiesDictionary::billing_data_modes(), 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', + 'gateway' => 'dcc', 'requirements' => array(), ), ) diff --git a/modules/ppcp-applepay/resources/js/ApplepayButton.js b/modules/ppcp-applepay/resources/js/ApplepayButton.js index 3526a0f03..c90f54932 100644 --- a/modules/ppcp-applepay/resources/js/ApplepayButton.js +++ b/modules/ppcp-applepay/resources/js/ApplepayButton.js @@ -39,7 +39,7 @@ class ApplepayButton { this.log = function() { if ( this.buttonConfig.is_debug ) { - console.log('[ApplePayButton]', ...arguments); + //console.log('[ApplePayButton]', ...arguments); } } diff --git a/modules/ppcp-axo/.babelrc b/modules/ppcp-axo/.babelrc new file mode 100644 index 000000000..822778e6c --- /dev/null +++ b/modules/ppcp-axo/.babelrc @@ -0,0 +1,14 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "useBuiltIns": "usage", + "corejs": "3.25.0" + } + ], + [ + "@babel/preset-react" + ] + ] +} diff --git a/modules/ppcp-axo/.gitignore b/modules/ppcp-axo/.gitignore new file mode 100644 index 000000000..0bd2b9f58 --- /dev/null +++ b/modules/ppcp-axo/.gitignore @@ -0,0 +1,3 @@ +node_modules +assets/js +assets/css diff --git a/modules/ppcp-axo/assets/images/fastlane.png b/modules/ppcp-axo/assets/images/fastlane.png new file mode 100644 index 000000000..f09040a03 Binary files /dev/null and b/modules/ppcp-axo/assets/images/fastlane.png differ diff --git a/modules/ppcp-axo/composer.json b/modules/ppcp-axo/composer.json new file mode 100644 index 000000000..352c30523 --- /dev/null +++ b/modules/ppcp-axo/composer.json @@ -0,0 +1,17 @@ +{ + "name": "woocommerce/ppcp-axo", + "type": "dhii-mod", + "description": "Axo module for PPCP", + "license": "GPL-2.0", + "require": { + "php": "^7.2 | ^8.0", + "dhii/module-interface": "^0.3.0-alpha1" + }, + "autoload": { + "psr-4": { + "WooCommerce\\PayPalCommerce\\Axo\\": "src" + } + }, + "minimum-stability": "dev", + "prefer-stable": true +} diff --git a/modules/ppcp-axo/extensions.php b/modules/ppcp-axo/extensions.php new file mode 100644 index 000000000..93e36e31c --- /dev/null +++ b/modules/ppcp-axo/extensions.php @@ -0,0 +1,336 @@ + function ( ContainerInterface $container, array $fields ): array { + + $insert_after = function( array $array, string $key, array $new ): array { + $keys = array_keys( $array ); + $index = array_search( $key, $keys, true ); + $pos = false === $index ? count( $array ) : $index + 1; + + return array_merge( array_slice( $array, 0, $pos ), $new, array_slice( $array, $pos ) ); + }; + + $display_manager = $container->get( 'wcgateway.display-manager' ); + assert( $display_manager instanceof DisplayManager ); + + $module_url = $container->get( 'axo.url' ); + + // Standard Payments tab fields. + return $insert_after( + $fields, + 'vault_enabled_dcc', + array( + 'axo_heading' => array( + 'heading' => __( 'Fastlane', 'woocommerce-paypal-payments' ), + 'type' => 'ppcp-heading', + 'description' => wp_kses_post( + sprintf( + // translators: %1$s and %2$s is a link tag. + __( + 'Offer an accelerated checkout experience that recognizes guest shoppers and autofills their details so they can pay in seconds.', + 'woocommerce-paypal-payments' + ), + '
', + '' + ) + ), + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array( + 'dcc', + ), + 'gateway' => 'dcc', + ), + 'axo_enabled' => array( + 'title' => __( 'Fastlane', 'woocommerce-paypal-payments' ), + 'title_html' => sprintf( + '%s', + $module_url, + __( 'Fastlane', 'woocommerce-paypal-payments' ) + ), + 'type' => 'checkbox', + 'label' => __( 'Enable Fastlane by PayPal', 'woocommerce-paypal-payments' ) + . '

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

', + 'default' => 'yes', + 'screens' => array( State::STATE_ONBOARDED ), + 'gateway' => 'dcc', + 'requirements' => array(), + 'custom_attributes' => array( + 'data-ppcp-display' => wp_json_encode( + array( + $display_manager + ->rule() + ->condition_element( 'axo_enabled', '1' ) + ->action_visible( 'axo_gateway_title' ) + ->action_visible( 'axo_privacy' ) + ->action_visible( 'axo_style_heading' ) + ->action_class( 'axo_enabled', 'active' ) + ->to_array(), + $display_manager + ->rule() + ->condition_element( 'axo_enabled', '1' ) + ->condition_js_variable( 'ppcpAxoShowStyles', true ) + ->action_visible( 'axo_style_root_heading' ) + ->action_visible( 'axo_style_root_bg_color' ) + ->action_visible( 'axo_style_root_error_color' ) + ->action_visible( 'axo_style_root_font_family' ) + ->action_visible( 'axo_style_root_font_size_base' ) + ->action_visible( 'axo_style_root_padding' ) + ->action_visible( 'axo_style_root_primary_color' ) + ->action_visible( 'axo_style_input_heading' ) + ->action_visible( 'axo_style_input_bg_color' ) + ->action_visible( 'axo_style_input_border_radius' ) + ->action_visible( 'axo_style_input_border_color' ) + ->action_visible( 'axo_style_input_border_width' ) + ->action_visible( 'axo_style_input_text_color_base' ) + ->action_visible( 'axo_style_input_focus_border_color' ) + ->to_array(), + ) + ), + ), + 'classes' => array( 'ppcp-valign-label-middle', 'ppcp-align-label-center' ), + ), + 'axo_gateway_title' => array( + 'title' => __( 'Gateway Title', 'woocommerce-paypal-payments' ), + 'type' => 'text', + 'classes' => array( 'ppcp-field-indent' ), + 'desc_tip' => true, + 'description' => __( + 'This controls the title of the Fastlane gateway the user sees on checkout.', + 'woocommerce-paypal-payments' + ), + 'default' => __( + 'Fastlane Debit & Credit Cards', + 'woocommerce-paypal-payments' + ), + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array(), + 'gateway' => 'dcc', + ), + 'axo_privacy' => array( + 'title' => __( 'Privacy', 'woocommerce-paypal-payments' ), + 'type' => 'select', + 'desc_tip' => true, + 'description' => __( + 'This setting will control whether Fastlane branding is shown by email field.', + 'woocommerce-paypal-payments' + ), + 'classes' => array( 'ppcp-field-indent' ), + 'class' => array(), + 'input_class' => array( 'wc-enhanced-select' ), + 'default' => 'yes', + 'options' => PropertiesDictionary::privacy_options(), + 'screens' => array( State::STATE_ONBOARDED ), + 'gateway' => 'dcc', + 'requirements' => array(), + ), + 'axo_style_heading' => array( + 'heading' => __( 'Advanced Style Settings (optional)', 'woocommerce-paypal-payments' ), + 'heading_html' => sprintf( + // translators: %1$s and %2$s are the opening and closing of HTML tag. + __( + 'Advanced Style Settings (optional) %1$sSee more%2$s %3$sSee less%4$s', + 'woocommerce-paypal-payments' + ), + '', + '', + '' + ), + 'type' => 'ppcp-heading', + 'description' => wp_kses_post( + sprintf( + // translators: %1$s and %2$s is a link tag. + __( + 'Leave the default styling, or customize how Fastlane looks on your website. %1$sSee PayPal\'s developer docs%2$s for info', + 'woocommerce-paypal-payments' + ), + '', + '' + ) + ), + 'classes' => array( 'ppcp-field-indent' ), + 'class' => array(), + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array( + 'dcc', + ), + 'gateway' => 'dcc', + ), + + 'axo_style_root_heading' => array( + 'heading' => __( 'Root Settings', 'woocommerce-paypal-payments' ), + 'type' => 'ppcp-heading', + 'description' => __( + 'These apply to the overall Fastlane checkout module.', + 'woocommerce-paypal-payments' + ), + 'classes' => array( 'ppcp-field-indent' ), + 'screens' => array( State::STATE_ONBOARDED ), + 'requirements' => array( 'dcc' ), + 'gateway' => 'dcc', + ), + 'axo_style_root_bg_color' => array( + 'title' => __( 'Background Color', 'woocommerce-paypal-payments' ), + 'type' => 'text', + 'classes' => array( 'ppcp-field-indent' ), + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array(), + 'gateway' => 'dcc', + ), + 'axo_style_root_error_color' => array( + 'title' => __( 'Error Color', 'woocommerce-paypal-payments' ), + 'type' => 'text', + 'classes' => array( 'ppcp-field-indent' ), + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array(), + 'gateway' => 'dcc', + ), + 'axo_style_root_font_family' => array( + 'title' => __( 'Font Family', 'woocommerce-paypal-payments' ), + 'type' => 'text', + 'classes' => array( 'ppcp-field-indent' ), + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array(), + 'gateway' => 'dcc', + ), + 'axo_style_root_font_size_base' => array( + 'title' => __( 'Font Size Base', 'woocommerce-paypal-payments' ), + 'type' => 'text', + 'classes' => array( 'ppcp-field-indent' ), + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array(), + 'gateway' => 'dcc', + ), + 'axo_style_root_padding' => array( + 'title' => __( 'Padding', 'woocommerce-paypal-payments' ), + 'type' => 'text', + 'classes' => array( 'ppcp-field-indent' ), + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array(), + 'gateway' => 'dcc', + ), + 'axo_style_root_primary_color' => array( + 'title' => __( 'Primary Color', 'woocommerce-paypal-payments' ), + 'type' => 'text', + 'classes' => array( 'ppcp-field-indent' ), + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array(), + 'gateway' => 'dcc', + ), + 'axo_style_input_heading' => array( + 'heading' => __( 'Input Settings', 'woocommerce-paypal-payments' ), + 'type' => 'ppcp-heading', + 'description' => __( + 'These apply to the customer input fields on your Fastlane module.', + 'woocommerce-paypal-payments' + ), + 'classes' => array( 'ppcp-field-indent' ), + 'screens' => array( State::STATE_ONBOARDED ), + 'requirements' => array( 'dcc' ), + 'gateway' => 'dcc', + ), + 'axo_style_input_bg_color' => array( + 'title' => __( 'Background Color', 'woocommerce-paypal-payments' ), + 'type' => 'text', + 'classes' => array( 'ppcp-field-indent' ), + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array(), + 'gateway' => 'dcc', + ), + 'axo_style_input_border_radius' => array( + 'title' => __( 'Border Radius', 'woocommerce-paypal-payments' ), + 'type' => 'text', + 'classes' => array( 'ppcp-field-indent' ), + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array(), + 'gateway' => 'dcc', + ), + 'axo_style_input_border_color' => array( + 'title' => __( 'Border Color', 'woocommerce-paypal-payments' ), + 'type' => 'text', + 'classes' => array( 'ppcp-field-indent' ), + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array(), + 'gateway' => 'dcc', + ), + 'axo_style_input_border_width' => array( + 'title' => __( 'Border Width', 'woocommerce-paypal-payments' ), + 'type' => 'text', + 'classes' => array( 'ppcp-field-indent' ), + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array(), + 'gateway' => 'dcc', + ), + 'axo_style_input_text_color_base' => array( + 'title' => __( 'Text Color Base', 'woocommerce-paypal-payments' ), + 'type' => 'text', + 'classes' => array( 'ppcp-field-indent' ), + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array(), + 'gateway' => 'dcc', + ), + 'axo_style_input_focus_border_color' => array( + 'title' => __( 'Focus Border Color', 'woocommerce-paypal-payments' ), + 'type' => 'text', + 'classes' => array( 'ppcp-field-indent' ), + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array(), + 'gateway' => 'dcc', + ), + + ) + ); + }, + +); diff --git a/modules/ppcp-axo/module.php b/modules/ppcp-axo/module.php new file mode 100644 index 000000000..8e0a3064f --- /dev/null +++ b/modules/ppcp-axo/module.php @@ -0,0 +1,16 @@ + 0.5%", + "Safari >= 8", + "Chrome >= 41", + "Firefox >= 43", + "Edge >= 14" + ], + "dependencies": { + "@paypal/paypal-js": "^6.0.0", + "core-js": "^3.25.0" + }, + "devDependencies": { + "@babel/core": "^7.19", + "@babel/preset-env": "^7.19", + "@babel/preset-react": "^7.18.6", + "@woocommerce/dependency-extraction-webpack-plugin": "^2.2.0", + "babel-loader": "^8.2", + "cross-env": "^7.0.3", + "file-loader": "^6.2.0", + "sass": "^1.42.1", + "sass-loader": "^12.1.0", + "webpack": "^5.76", + "webpack-cli": "^4.10" + }, + "scripts": { + "build": "cross-env BABEL_ENV=default NODE_ENV=production webpack", + "watch": "cross-env BABEL_ENV=default NODE_ENV=production webpack --watch", + "dev": "cross-env BABEL_ENV=default webpack --watch" + } +} diff --git a/modules/ppcp-axo/resources/css/styles.scss b/modules/ppcp-axo/resources/css/styles.scss new file mode 100644 index 000000000..12c235fbd --- /dev/null +++ b/modules/ppcp-axo/resources/css/styles.scss @@ -0,0 +1,40 @@ + +.ppcp-axo-card-icons { + padding: 4px 0 16px 25px; + + .ppcp-card-icon { + float: left !important; + } +} + +.ppcp-axo-watermark-container { + max-width: 200px; + margin-top: 10px; +} + +.ppcp-axo-payment-container { + padding: 1rem 0; + background-color: #ffffff; + + &.hidden { + display: none; + } +} + +.ppcp-axo-email-widget { + border: 1px solid #cccccc; + background-color: #eeeeee; + padding: 0.5rem 1rem; + margin-bottom: 1rem; + text-align: center; + font-weight: bold; + color: #000000; +} + +.ppcp-axo-field-hidden { + display: none; +} + +.ppcp-axo-customer-details { + margin-bottom: 40px; +} diff --git a/modules/ppcp-axo/resources/js/AxoManager.js b/modules/ppcp-axo/resources/js/AxoManager.js new file mode 100644 index 000000000..140dea944 --- /dev/null +++ b/modules/ppcp-axo/resources/js/AxoManager.js @@ -0,0 +1,733 @@ +import Fastlane from "./Connection/Fastlane"; +import {log} from "./Helper/Debug"; +import DomElementCollection from "./Components/DomElementCollection"; +import ShippingView from "./Views/ShippingView"; +import BillingView from "./Views/BillingView"; +import CardView from "./Views/CardView"; +import PayPalInsights from "./Insights/PayPalInsights"; +import {disable,enable} from "../../../ppcp-button/resources/js/modules/Helper/ButtonDisabler"; + +class AxoManager { + + constructor(axoConfig, ppcpConfig) { + this.axoConfig = axoConfig; + this.ppcpConfig = ppcpConfig; + + this.initialized = false; + this.fastlane = new Fastlane(); + this.$ = jQuery; + + this.hideGatewaySelection = false; + + this.status = { + active: false, + validEmail: false, + hasProfile: false, + useEmailWidget: this.useEmailWidget() + }; + + this.data = { + email: null, + billing: null, + shipping: null, + card: null, + }; + + this.el = new DomElementCollection(); + + this.styles = { + root: { + backgroundColorPrimary: '#ffffff' + } + }; + + this.locale = 'en_us'; + + this.registerEventHandlers(); + + this.shippingView = new ShippingView(this.el.shippingAddressContainer.selector, this.el); + this.billingView = new BillingView(this.el.billingAddressContainer.selector, this.el); + this.cardView = new CardView(this.el.paymentContainer.selector + '-details', this.el, this); + + document.axoDebugSetStatus = (key, value) => { + this.setStatus(key, value); + } + + document.axoDebugObject = () => { + console.log(this); + return this; + } + + if ( + this.axoConfig?.insights?.enabled + && this.axoConfig?.insights?.client_id + && this.axoConfig?.insights?.session_id + ) { + PayPalInsights.config(this.axoConfig?.insights?.client_id, { debug: true }); + PayPalInsights.setSessionId(this.axoConfig?.insights?.session_id); + PayPalInsights.trackJsLoad(); + + if (document.querySelector('.woocommerce-checkout')) { + PayPalInsights.trackBeginCheckout({ + amount: this.axoConfig?.insights?.amount, + page_type: 'checkout', + user_data: { + country: 'US', + is_store_member: false, + } + }); + } + } + + this.triggerGatewayChange(); + } + + registerEventHandlers() { + + this.$(document).on('change', 'input[name=payment_method]', (ev) => { + const map = { + 'ppcp-axo-gateway': 'card', + 'ppcp-gateway': 'paypal', + } + + PayPalInsights.trackSelectPaymentMethod({ + payment_method_selected: map[ev.target.value] || 'other', + page_type: 'checkout' + }); + }); + + + // Listen to Gateway Radio button changes. + this.el.gatewayRadioButton.on('change', (ev) => { + if (ev.target.checked) { + this.activateAxo(); + } else { + this.deactivateAxo(); + } + }); + + this.$(document).on('updated_checkout payment_method_selected', () => { + this.triggerGatewayChange(); + }); + + // On checkout form submitted. + this.el.submitButton.on('click', () => { + this.onClickSubmitButton(); + return false; + }) + + // Click change shipping address link. + this.el.changeShippingAddressLink.on('click', async () => { + if (this.status.hasProfile) { + const { selectionChanged, selectedAddress } = await this.fastlane.profile.showShippingAddressSelector(); + + if (selectionChanged) { + this.setShipping(selectedAddress); + this.shippingView.refresh(); + } + } + }); + + // Click change billing address link. + this.el.changeBillingAddressLink.on('click', async () => { + if (this.status.hasProfile) { + this.el.changeCardLink.trigger('click'); + } + }); + + // Click change card link. + this.el.changeCardLink.on('click', async () => { + const response = await this.fastlane.profile.showCardSelector(); + + if (response.selectionChanged) { + this.setCard(response.selectedCard); + this.setBilling({ + address: response.selectedCard.paymentSource.card.billingAddress + }); + } + }); + + // Cancel "continuation" mode. + this.el.showGatewaySelectionLink.on('click', async () => { + this.hideGatewaySelection = false; + this.$('.wc_payment_methods label').show(); + this.cardView.refresh(); + }); + + } + + rerender() { + /** + * active | 0 1 1 1 + * validEmail | * 0 1 1 + * hasProfile | * * 0 1 + * -------------------------------- + * defaultSubmitButton | 1 0 0 0 + * defaultEmailField | 1 0 0 0 + * defaultFormFields | 1 0 1 0 + * extraFormFields | 0 0 0 1 + * axoEmailField | 0 1 0 0 + * axoProfileViews | 0 0 0 1 + * axoPaymentContainer | 0 0 1 1 + * axoSubmitButton | 0 0 1 1 + */ + const scenario = this.identifyScenario( + this.status.active, + this.status.validEmail, + this.status.hasProfile + ); + + log('Scenario', scenario); + + // Reset some elements to a default status. + this.el.watermarkContainer.hide(); + + if (scenario.defaultSubmitButton) { + this.el.defaultSubmitButton.show(); + } else { + this.el.defaultSubmitButton.hide(); + } + + if (scenario.defaultEmailField) { + this.el.fieldBillingEmail.show(); + } else { + this.el.fieldBillingEmail.hide(); + } + + if (scenario.defaultFormFields) { + this.el.customerDetails.show(); + } else { + this.el.customerDetails.hide(); + } + + if (scenario.extraFormFields) { + this.el.customerDetails.show(); + // Hiding of unwanted will be handled by the axoProfileViews handler. + } + + if (scenario.axoEmailField) { + this.showAxoEmailField(); + this.el.watermarkContainer.show(); + + // Move watermark to after email. + this.$(this.el.fieldBillingEmail.selector).append( + this.$(this.el.watermarkContainer.selector) + ); + + } else { + this.el.emailWidgetContainer.hide(); + if (!scenario.defaultEmailField) { + this.el.fieldBillingEmail.hide(); + } + } + + if (scenario.axoProfileViews) { + this.shippingView.activate(); + this.billingView.activate(); + this.cardView.activate(); + + // Move watermark to after shipping. + this.$(this.el.shippingAddressContainer.selector).after( + this.$(this.el.watermarkContainer.selector) + ); + + this.el.watermarkContainer.show(); + + } else { + this.shippingView.deactivate(); + this.billingView.deactivate(); + this.cardView.deactivate(); + } + + if (scenario.axoPaymentContainer) { + this.el.paymentContainer.show(); + } else { + this.el.paymentContainer.hide(); + } + + if (scenario.axoSubmitButton) { + this.el.submitButtonContainer.show(); + } else { + this.el.submitButtonContainer.hide(); + } + + this.ensureBillingFieldsConsistency(); + this.ensureShippingFieldsConsistency(); + } + + identifyScenario(active, validEmail, hasProfile) { + let response = { + defaultSubmitButton: false, + defaultEmailField: false, + defaultFormFields: false, + extraFormFields: false, + axoEmailField: false, + axoProfileViews: false, + axoPaymentContainer: false, + axoSubmitButton: false, + } + + if (active && validEmail && hasProfile) { + response.extraFormFields = true; + response.axoProfileViews = true; + response.axoPaymentContainer = true; + response.axoSubmitButton = true; + return response; + } + if (active && validEmail && !hasProfile) { + response.defaultFormFields = true; + response.axoEmailField = true; + response.axoPaymentContainer = true; + response.axoSubmitButton = true; + return response; + } + if (active && !validEmail) { + response.axoEmailField = true; + return response; + } + if (!active) { + response.defaultSubmitButton = true; + response.defaultEmailField = true; + response.defaultFormFields = true; + return response; + } + throw new Error('Invalid scenario.'); + } + + ensureBillingFieldsConsistency() { + const $billingFields = this.$('.woocommerce-billing-fields .form-row:visible'); + const $billingHeaders = this.$('.woocommerce-billing-fields h3'); + if (this.billingView.isActive()) { + if ($billingFields.length) { + $billingHeaders.show(); + } else { + $billingHeaders.hide(); + } + } else { + $billingHeaders.show(); + } + } + + ensureShippingFieldsConsistency() { + const $shippingFields = this.$('.woocommerce-shipping-fields .form-row:visible'); + const $shippingHeaders = this.$('.woocommerce-shipping-fields h3'); + if (this.shippingView.isActive()) { + if ($shippingFields.length) { + $shippingHeaders.show(); + } else { + $shippingHeaders.hide(); + } + } else { + $shippingHeaders.show(); + } + } + + showAxoEmailField() { + if (this.status.useEmailWidget) { + this.el.emailWidgetContainer.show(); + this.el.fieldBillingEmail.hide(); + } else { + this.el.emailWidgetContainer.hide(); + this.el.fieldBillingEmail.show(); + } + } + + setStatus(key, value) { + this.status[key] = value; + + log('Status updated', JSON.parse(JSON.stringify(this.status))); + + this.rerender(); + } + + activateAxo() { + this.initPlacements(); + this.initFastlane(); + this.setStatus('active', true); + + const emailInput = document.querySelector(this.el.fieldBillingEmail.selector + ' input'); + if (emailInput && this.lastEmailCheckedIdentity !== emailInput.value) { + this.onChangeEmail(); + } + } + + deactivateAxo() { + this.setStatus('active', false); + } + + initPlacements() { + const wrapper = this.el.axoCustomerDetails; + + // Customer details container. + if (!document.querySelector(wrapper.selector)) { + document.querySelector(wrapper.anchorSelector).insertAdjacentHTML('afterbegin', ` +
+ `); + } + + const wrapperElement = document.querySelector(wrapper.selector); + + // Billing view container. + const bc = this.el.billingAddressContainer; + if (!document.querySelector(bc.selector)) { + wrapperElement.insertAdjacentHTML('beforeend', ` +
+ `); + } + + // Shipping view container. + const sc = this.el.shippingAddressContainer; + if (!document.querySelector(sc.selector)) { + wrapperElement.insertAdjacentHTML('beforeend', ` +
+ `); + } + + // Watermark container + const wc = this.el.watermarkContainer; + if (!document.querySelector(wc.selector)) { + this.emailInput = document.querySelector(this.el.fieldBillingEmail.selector + ' input'); + this.emailInput.insertAdjacentHTML('afterend', ` +
+ `); + } + + // Payment container + const pc = this.el.paymentContainer; + if (!document.querySelector(pc.selector)) { + const gatewayPaymentContainer = document.querySelector('.payment_method_ppcp-axo-gateway'); + gatewayPaymentContainer.insertAdjacentHTML('beforeend', ` + + `); + } + + if (this.useEmailWidget()) { + + // Display email widget. + const ec = this.el.emailWidgetContainer; + if (!document.querySelector(ec.selector)) { + wrapperElement.insertAdjacentHTML('afterbegin', ` +
+ --- EMAIL WIDGET PLACEHOLDER --- +
+ `); + } + + } else { + + // Move email to the AXO container. + let emailRow = document.querySelector(this.el.fieldBillingEmail.selector); + wrapperElement.prepend(emailRow); + emailRow.querySelector('input').focus(); + } + } + + async initFastlane() { + if (this.initialized) { + return; + } + this.initialized = true; + + await this.connect(); + this.renderWatermark(); + this.watchEmail(); + } + + async connect() { + if (this.axoConfig.environment.is_sandbox) { + window.localStorage.setItem('axoEnv', 'sandbox'); + } + + await this.fastlane.connect({ + locale: this.locale, + styles: this.styles + }); + + this.fastlane.setLocale('en_us'); + } + + triggerGatewayChange() { + this.el.gatewayRadioButton.trigger('change'); + } + + async renderWatermark() { + (await this.fastlane.FastlaneWatermarkComponent({ + includeAdditionalInfo: true + })).render(this.el.watermarkContainer.selector); + } + + watchEmail() { + + if (this.useEmailWidget()) { + + // TODO + + } else { + + this.emailInput = document.querySelector(this.el.fieldBillingEmail.selector + ' input'); + this.emailInput.addEventListener('change', async ()=> { + this.onChangeEmail(); + }); + + if (this.emailInput.value) { + this.onChangeEmail(); + } + } + } + + async onChangeEmail () { + this.clearData(); + + if (!this.status.active) { + log('Email checking skipped, AXO not active.'); + return; + } + + if (!this.emailInput) { + log('Email field not initialized.'); + return; + } + + log('Email changed: ' + (this.emailInput ? this.emailInput.value : '')); + + this.$(this.el.paymentContainer.selector + '-detail').html(''); + this.$(this.el.paymentContainer.selector + '-form').html(''); + + this.setStatus('validEmail', false); + this.setStatus('hasProfile', false); + + this.hideGatewaySelection = false; + + this.lastEmailCheckedIdentity = this.emailInput.value; + + if (!this.emailInput.value || !this.emailInput.checkValidity()) { + log('The email address is not valid.'); + return; + } + + this.data.email = this.emailInput.value; + this.billingView.setData(this.data); + + if (!this.fastlane.identity) { + log('Not initialized.'); + return; + } + + PayPalInsights.trackSubmitCheckoutEmail({ + page_type: 'checkout' + }); + + const lookupResponse = await this.fastlane.identity.lookupCustomerByEmail(this.emailInput.value); + + if (lookupResponse.customerContextId) { + // Email is associated with a Connect profile or a PayPal member. + // Authenticate the customer to get access to their profile. + log('Email is associated with a Connect profile or a PayPal member'); + + const authResponse = await this.fastlane.identity.triggerAuthenticationFlow(lookupResponse.customerContextId); + + log('AuthResponse', authResponse); + + if (authResponse.authenticationState === 'succeeded') { + log(JSON.stringify(authResponse)); + + // Add addresses + this.setShipping(authResponse.profileData.shippingAddress); + this.setBilling({ + address: authResponse.profileData.card.paymentSource.card.billingAddress + }); + this.setCard(authResponse.profileData.card); + + this.setStatus('validEmail', true); + this.setStatus('hasProfile', true); + + this.hideGatewaySelection = true; + this.$('.wc_payment_methods label').hide(); + + this.rerender(); + + } else { + // authentication failed or canceled by the customer + log("Authentication Failed") + } + + } else { + // No profile found with this email address. + // This is a guest customer. + log('No profile found with this email address.'); + + this.setStatus('validEmail', true); + this.setStatus('hasProfile', false); + + this.cardComponent = (await this.fastlane.FastlaneCardComponent( + this.cardComponentData() + )).render(this.el.paymentContainer.selector + '-form'); + } + } + + clearData() { + this.data = { + email: null, + billing: null, + shipping: null, + card: null, + }; + } + + setShipping(shipping) { + this.data.shipping = shipping; + this.shippingView.setData(this.data); + } + + setBilling(billing) { + this.data.billing = billing; + this.billingView.setData(this.data); + } + + setCard(card) { + this.data.card = card; + this.cardView.setData(this.data); + } + + onClickSubmitButton() { + // TODO: validate data. + + if (this.data.card) { // Ryan flow + log('Ryan flow.'); + + this.$('#ship-to-different-address-checkbox').prop('checked', 'checked'); + + let data = {}; + this.billingView.toSubmitData(data); + this.shippingView.toSubmitData(data); + this.cardView.toSubmitData(data); + + this.submit(this.data.card.id, data); + + } else { // Gary flow + log('Gary flow.'); + + try { + this.cardComponent.getPaymentToken( + this.tokenizeData() + ).then((response) => { + this.submit(response.id); + }); + } catch (e) { + log('Error tokenizing.'); + alert('Error tokenizing data.'); + } + } + } + + cardComponentData() { + return { + fields: { + cardholderName: {} // optionally pass this to show the card holder name + } + } + } + + tokenizeData() { + return { + name: { + fullName: this.billingView.fullName() + }, + billingAddress: { + addressLine1: this.billingView.inputValue('street1'), + addressLine2: this.billingView.inputValue('street2'), + adminArea1: this.billingView.inputValue('city'), + adminArea2: this.billingView.inputValue('stateCode'), + postalCode: this.billingView.inputValue('postCode'), + countryCode: this.billingView.inputValue('countryCode'), + } + } + } + + submit(nonce, data) { + // Send the nonce and previously captured device data to server to complete checkout + if (!this.el.axoNonceInput.get()) { + this.$('form.woocommerce-checkout').append(``); + } + + this.el.axoNonceInput.get().value = nonce; + + PayPalInsights.trackEndCheckout({ + amount: this.axoConfig?.insights?.amount, + page_type: 'checkout', + payment_method_selected: 'card', + user_data: { + country: 'US', + is_store_member: false, + } + }); + + if (data) { + + // Ryan flow. + const form = document.querySelector('form.woocommerce-checkout'); + const formData = new FormData(form); + + this.showLoading(); + + // Fill in form data. + Object.keys(data).forEach((key) => { + formData.set(key, data[key]); + }); + + fetch(wc_checkout_params.checkout_url, { // TODO: maybe create a new endpoint to process_payment. + method: "POST", + body: formData + }) + .then(response => response.json()) + .then(responseData => { + if (responseData.result === 'failure') { + if (responseData.messages) { + const $notices = this.$('.woocommerce-notices-wrapper').eq(0); + $notices.html(responseData.messages); + this.$('html, body').animate({ + scrollTop: $notices.offset().top + }, 500); + } + console.error('Failure:', responseData); + this.hideLoading(); + return; + } + if (responseData.redirect) { + window.location.href = responseData.redirect; + } + }) + .catch(error => { + console.error('Error:', error); + this.hideLoading(); + }); + + } else { + // Gary flow. + this.el.defaultSubmitButton.click(); + } + + } + + showLoading() { + const submitContainerSelector = '.woocommerce-checkout-payment'; + jQuery('form.woocommerce-checkout').append('
'); + disable(submitContainerSelector); + } + + hideLoading() { + const submitContainerSelector = '.woocommerce-checkout-payment'; + jQuery('form.woocommerce-checkout .blockOverlay').remove(); + enable(submitContainerSelector); + } + + useEmailWidget() { + return this.axoConfig?.widgets?.email === 'use_widget'; + } + +} + +export default AxoManager; diff --git a/modules/ppcp-axo/resources/js/Components/DomElement.js b/modules/ppcp-axo/resources/js/Components/DomElement.js new file mode 100644 index 000000000..8e789d485 --- /dev/null +++ b/modules/ppcp-axo/resources/js/Components/DomElement.js @@ -0,0 +1,41 @@ +import {setVisible} from "../../../../ppcp-button/resources/js/modules/Helper/Hiding"; + +class DomElement { + + constructor(config) { + this.$ = jQuery; + this.config = config; + this.selector = this.config.selector; + this.id = this.config.id || null; + this.className = this.config.className || null; + this.attributes = this.config.attributes || null; + this.anchorSelector = this.config.anchorSelector || null; + } + + trigger(action) { + this.$(this.selector).trigger(action); + } + + on(action, callable) { + this.$(document).on(action, this.selector, callable); + } + + hide() { + this.$(this.selector).hide(); + } + + show() { + this.$(this.selector).show(); + } + + click() { + this.get().click(); + } + + get() { + return document.querySelector(this.selector); + } + +} + +export default DomElement; diff --git a/modules/ppcp-axo/resources/js/Components/DomElementCollection.js b/modules/ppcp-axo/resources/js/Components/DomElementCollection.js new file mode 100644 index 000000000..fc44a2823 --- /dev/null +++ b/modules/ppcp-axo/resources/js/Components/DomElementCollection.js @@ -0,0 +1,95 @@ +import DomElement from "./DomElement"; + +class DomElementCollection { + + constructor() { + this.gatewayRadioButton = new DomElement({ + selector: '#payment_method_ppcp-axo-gateway', + }); + + this.defaultSubmitButton = new DomElement({ + selector: '#place_order', + }); + + this.paymentContainer = new DomElement({ + id: 'ppcp-axo-payment-container', + selector: '#ppcp-axo-payment-container', + className: 'ppcp-axo-payment-container' + }); + + this.watermarkContainer = new DomElement({ + id: 'ppcp-axo-watermark-container', + selector: '#ppcp-axo-watermark-container', + className: 'ppcp-axo-watermark-container' + }); + + this.customerDetails = new DomElement({ + selector: '#customer_details > *:not(#ppcp-axo-customer-details)' + }); + + this.axoCustomerDetails = new DomElement({ + id: 'ppcp-axo-customer-details', + selector: '#ppcp-axo-customer-details', + className: 'ppcp-axo-customer-details', + anchorSelector: '#customer_details' + }); + + this.emailWidgetContainer = new DomElement({ + id: 'ppcp-axo-email-widget', + selector: '#ppcp-axo-email-widget', + className: 'ppcp-axo-email-widget' + }); + + this.shippingAddressContainer = new DomElement({ + id: 'ppcp-axo-shipping-address-container', + selector: '#ppcp-axo-shipping-address-container', + className: 'ppcp-axo-shipping-address-container' + }); + + this.billingAddressContainer = new DomElement({ + id: 'ppcp-axo-billing-address-container', + selector: '#ppcp-axo-billing-address-container', + className: 'ppcp-axo-billing-address-container' + }); + + this.fieldBillingEmail = new DomElement({ + selector: '#billing_email_field' + }); + + this.submitButtonContainer = new DomElement({ + selector: '#ppcp-axo-submit-button-container', + }); + + this.submitButton = new DomElement({ + selector: '#ppcp-axo-submit-button-container button' + }); + + this.changeShippingAddressLink = new DomElement({ + selector: '*[data-ppcp-axo-change-shipping-address]', + attributes: 'data-ppcp-axo-change-shipping-address', + }); + + this.changeBillingAddressLink = new DomElement({ + selector: '*[data-ppcp-axo-change-billing-address]', + attributes: 'data-ppcp-axo-change-billing-address', + }); + + this.changeCardLink = new DomElement({ + selector: '*[data-ppcp-axo-change-card]', + attributes: 'data-ppcp-axo-change-card', + }); + + this.showGatewaySelectionLink = new DomElement({ + selector: '*[data-ppcp-axo-show-gateway-selection]', + attributes: 'data-ppcp-axo-show-gateway-selection', + }); + + this.axoNonceInput = new DomElement({ + id: 'ppcp-axo-nonce', + selector: '#ppcp-axo-nonce', + }); + + } +} + +export default DomElementCollection; diff --git a/modules/ppcp-axo/resources/js/Components/FormFieldGroup.js b/modules/ppcp-axo/resources/js/Components/FormFieldGroup.js new file mode 100644 index 000000000..272df1288 --- /dev/null +++ b/modules/ppcp-axo/resources/js/Components/FormFieldGroup.js @@ -0,0 +1,153 @@ + +class FormFieldGroup { + + constructor(config) { + this.data = {}; + + this.baseSelector = config.baseSelector; + this.contentSelector = config.contentSelector; + this.fields = config.fields || {}; + this.template = config.template; + + this.active = false; + } + + setData(data) { + this.data = data; + this.refresh(); + } + + dataValue(fieldKey) { + if (!fieldKey || !this.fields[fieldKey]) { + return ''; + } + + if (typeof this.fields[fieldKey].valueCallback === 'function') { + return this.fields[fieldKey].valueCallback(this.data); + } + + const path = this.fields[fieldKey].valuePath; + + if (!path) { + return ''; + } + + const value = path.split('.').reduce((acc, key) => (acc && acc[key] !== undefined) ? acc[key] : undefined, this.data); + return value ? value : ''; + } + + activate() { + this.active = true; + this.refresh(); + } + + deactivate() { + this.active = false; + this.refresh(); + } + + toggle() { + this.active ? this.deactivate() : this.activate(); + } + + refresh() { + let content = document.querySelector(this.contentSelector); + + if (!content) { + return; + } + + content.innerHTML = ''; + + if (!this.active) { + this.hideField(this.contentSelector); + } else { + this.showField(this.contentSelector); + } + + Object.keys(this.fields).forEach((key) => { + const field = this.fields[key]; + + if (this.active && !field.showInput) { + this.hideField(field.selector); + } else { + this.showField(field.selector); + } + }); + + if (typeof this.template === 'function') { + content.innerHTML = this.template({ + value: (fieldKey) => { + return this.dataValue(fieldKey); + }, + isEmpty: () => { + let isEmpty = true; + Object.keys(this.fields).forEach((fieldKey) => { + if (this.dataValue(fieldKey)) { + isEmpty = false; + return false; + } + }); + return isEmpty; + } + }); + } + + } + + showField(selector) { + const field = document.querySelector(this.baseSelector + ' ' + selector); + if (field) { + field.classList.remove('ppcp-axo-field-hidden'); + } + } + + hideField(selector) { + const field = document.querySelector(this.baseSelector + ' ' + selector); + if (field) { + field.classList.add('ppcp-axo-field-hidden'); + } + } + + inputElement(name) { + const baseSelector = this.fields[name].selector; + + const select = document.querySelector(baseSelector + ' select'); + if (select) { + return select; + } + + const input = document.querySelector(baseSelector + ' input'); + if (input) { + return input; + } + + return null; + } + + inputValue(name) { + const el = this.inputElement(name); + return el ? el.value : ''; + } + + toSubmitData(data) { + Object.keys(this.fields).forEach((fieldKey) => { + const field = this.fields[fieldKey]; + + if (!field.valuePath || !field.selector) { + return true; + } + + const inputElement = this.inputElement(fieldKey); + + if (!inputElement) { + return true; + } + + data[inputElement.name] = this.dataValue(fieldKey); + }); + } + +} + +export default FormFieldGroup; diff --git a/modules/ppcp-axo/resources/js/Connection/Fastlane.js b/modules/ppcp-axo/resources/js/Connection/Fastlane.js new file mode 100644 index 000000000..9f2993081 --- /dev/null +++ b/modules/ppcp-axo/resources/js/Connection/Fastlane.js @@ -0,0 +1,41 @@ + +class Fastlane { + + construct() { + this.connection = null; + this.identity = null; + this.profile = null; + this.FastlaneCardComponent = null; + this.FastlanePaymentComponent = null; + this.FastlaneWatermarkComponent = null; + } + + connect(config) { + return new Promise((resolve, reject) => { + window.paypal.Fastlane(config) + .then((result) => { + this.init(result); + resolve(); + }) + .catch((error) => { + reject(); + }); + }); + } + + init(connection) { + this.connection = connection; + this.identity = this.connection.identity; + this.profile = this.connection.profile; + this.FastlaneCardComponent = this.connection.FastlaneCardComponent; + this.FastlanePaymentComponent = this.connection.FastlanePaymentComponent; + this.FastlaneWatermarkComponent = this.connection.FastlaneWatermarkComponent + } + + setLocale(locale) { + this.connection.setLocale(locale); + } + +} + +export default Fastlane; diff --git a/modules/ppcp-axo/resources/js/Helper/Debug.js b/modules/ppcp-axo/resources/js/Helper/Debug.js new file mode 100644 index 000000000..e473d4a3b --- /dev/null +++ b/modules/ppcp-axo/resources/js/Helper/Debug.js @@ -0,0 +1,4 @@ + +export function log(...args) { + //console.log('[AXO] ', ...args); +} diff --git a/modules/ppcp-axo/resources/js/Insights/PayPalInsights.js b/modules/ppcp-axo/resources/js/Insights/PayPalInsights.js new file mode 100644 index 000000000..926ca9355 --- /dev/null +++ b/modules/ppcp-axo/resources/js/Insights/PayPalInsights.js @@ -0,0 +1,58 @@ + +class PayPalInsights { + + constructor() { + window.paypalInsightDataLayer = window.paypalInsightDataLayer || []; + document.paypalInsight = () => { + paypalInsightDataLayer.push(arguments); + } + } + + /** + * @returns {PayPalInsights} + */ + static init() { + if (!PayPalInsights.instance) { + PayPalInsights.instance = new PayPalInsights(); + } + return PayPalInsights.instance; + } + + static track(eventName, data) { + PayPalInsights.init(); + paypalInsight('event', eventName, data); + } + + static config (clientId, data) { + PayPalInsights.init(); + paypalInsight('config', clientId, data); + } + + static setSessionId (sessionId) { + PayPalInsights.init(); + paypalInsight('set', { session_id: sessionId }); + } + + static trackJsLoad () { + PayPalInsights.track('js_load', { timestamp: Date.now() }); + } + + static trackBeginCheckout (data) { + PayPalInsights.track('begin_checkout', data); + } + + static trackSubmitCheckoutEmail (data) { + PayPalInsights.track('submit_checkout_email', data); + } + + static trackSelectPaymentMethod (data) { + PayPalInsights.track('select_payment_method', data); + } + + static trackEndCheckout (data) { + PayPalInsights.track('end_checkout', data); + } + +} + +export default PayPalInsights; diff --git a/modules/ppcp-axo/resources/js/Views/BillingView.js b/modules/ppcp-axo/resources/js/Views/BillingView.js new file mode 100644 index 000000000..c0f1f0f26 --- /dev/null +++ b/modules/ppcp-axo/resources/js/Views/BillingView.js @@ -0,0 +1,126 @@ +import FormFieldGroup from "../Components/FormFieldGroup"; + +class BillingView { + + constructor(selector, elements) { + this.el = elements; + + this.group = new FormFieldGroup({ + baseSelector: '.woocommerce-checkout', + contentSelector: selector, + template: (data) => { + const valueOfSelect = (selectSelector, key) => { + if (!key) { + return ''; + } + const selectElement = document.querySelector(selectSelector); + + if (!selectElement) { + return key; + } + + const option = selectElement.querySelector(`option[value="${key}"]`); + return option ? option.textContent : key; + } + + if (data.isEmpty()) { + return ` +
+

Billing Edit

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

Billing Edit

+
${data.value('email')}
+
${data.value('company')}
+
${data.value('firstName')} ${data.value('lastName')}
+
${data.value('street1')}
+
${data.value('street2')}
+
${data.value('postCode')} ${data.value('city')}
+
${valueOfSelect('#billing_state', data.value('stateCode'))}
+
${valueOfSelect('#billing_country', data.value('countryCode'))}
+
+ `; + }, + fields: { + email: { + 'valuePath': 'email', + }, + firstName: { + 'selector': '#billing_first_name_field', + 'valuePath': null + }, + lastName: { + 'selector': '#billing_last_name_field', + 'valuePath': null + }, + street1: { + 'selector': '#billing_address_1_field', + 'valuePath': 'billing.address.addressLine1', + }, + street2: { + 'selector': '#billing_address_2_field', + 'valuePath': null + }, + postCode: { + 'selector': '#billing_postcode_field', + 'valuePath': 'billing.address.postalCode', + }, + city: { + 'selector': '#billing_city_field', + 'valuePath': 'billing.address.adminArea2', + }, + stateCode: { + 'selector': '#billing_state_field', + 'valuePath': 'billing.address.adminArea1', + }, + countryCode: { + 'selector': '#billing_country_field', + 'valuePath': 'billing.address.countryCode', + }, + company: { + 'selector': '#billing_company_field', + 'valuePath': null, + } + } + }); + } + + isActive() { + return this.group.active; + } + + activate() { + this.group.activate(); + } + + deactivate() { + this.group.deactivate(); + } + + refresh() { + this.group.refresh(); + } + + setData(data) { + this.group.setData(data); + } + + inputValue(name) { + return this.group.inputValue(name); + } + + fullName() { + return `${this.inputValue('firstName')} ${this.inputValue('lastName')}`.trim(); + } + + toSubmitData(data) { + return this.group.toSubmitData(data); + } + +} + +export default BillingView; diff --git a/modules/ppcp-axo/resources/js/Views/CardView.js b/modules/ppcp-axo/resources/js/Views/CardView.js new file mode 100644 index 000000000..a2a1b4a86 --- /dev/null +++ b/modules/ppcp-axo/resources/js/Views/CardView.js @@ -0,0 +1,114 @@ +import FormFieldGroup from "../Components/FormFieldGroup"; + +class CardView { + + constructor(selector, elements, manager) { + this.el = elements; + this.manager = manager; + + this.group = new FormFieldGroup({ + baseSelector: '.ppcp-axo-payment-container', + contentSelector: selector, + template: (data) => { + const selectOtherPaymentMethod = () => { + if (!this.manager.hideGatewaySelection) { + return ''; + } + return `

Select other payment method

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

Add card details

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

Card Details Edit

+
+
+ ${data.value('brand')} +
+
${data.value('lastDigits') ? '**** **** **** ' + data.value('lastDigits'): ''}
+
${expiry[1]}/${expiry[0]}
+
${data.value('name')}
+
+ ${selectOtherPaymentMethod()} +
+ `; + }, + fields: { + brand: { + 'valuePath': 'card.paymentSource.card.brand', + }, + expiry: { + 'valuePath': 'card.paymentSource.card.expiry', + }, + lastDigits: { + 'valuePath': 'card.paymentSource.card.lastDigits', + }, + name: { + 'valuePath': 'card.paymentSource.card.name', + }, + } + }); + } + + activate() { + this.group.activate(); + } + + deactivate() { + this.group.deactivate(); + } + + refresh() { + this.group.refresh(); + } + + setData(data) { + this.group.setData(data); + } + + toSubmitData(data) { + const name = this.group.dataValue('name'); + const { firstName, lastName } = this.splitName(name); + + data['billing_first_name'] = firstName; + data['billing_last_name'] = lastName ? lastName : firstName; + + return this.group.toSubmitData(data); + } + + splitName(fullName) { + let nameParts = fullName.trim().split(' '); + let firstName = nameParts[0]; + let lastName = nameParts.length > 1 ? nameParts[nameParts.length - 1] : ''; + + return { firstName, lastName }; + } + +} + +export default CardView; diff --git a/modules/ppcp-axo/resources/js/Views/ShippingView.js b/modules/ppcp-axo/resources/js/Views/ShippingView.js new file mode 100644 index 000000000..5da86176e --- /dev/null +++ b/modules/ppcp-axo/resources/js/Views/ShippingView.js @@ -0,0 +1,134 @@ +import FormFieldGroup from "../Components/FormFieldGroup"; + +class ShippingView { + + constructor(selector, elements) { + this.el = elements; + + this.group = new FormFieldGroup({ + baseSelector: '.woocommerce-checkout', + contentSelector: selector, + template: (data) => { + const valueOfSelect = (selectSelector, key) => { + if (!key) { + return ''; + } + const selectElement = document.querySelector(selectSelector); + + if (!selectElement) { + return key; + } + + const option = selectElement.querySelector(`option[value="${key}"]`); + return option ? option.textContent : key; + } + + if (data.isEmpty()) { + return ` +
+

Shipping Edit

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

Shipping Edit

+
${data.value('company')}
+
${data.value('firstName')} ${data.value('lastName')}
+
${data.value('street1')}
+
${data.value('street2')}
+
${data.value('postCode')} ${data.value('city')}
+
${valueOfSelect('#shipping_state', data.value('stateCode'))}
+
${valueOfSelect('#shipping_country', data.value('countryCode'))}
+
${data.value('phone')}
+
+ `; + }, + fields: { + firstName: { + 'key': 'firstName', + 'selector': '#shipping_first_name_field', + 'valuePath': 'shipping.name.firstName', + }, + lastName: { + 'selector': '#shipping_last_name_field', + 'valuePath': 'shipping.name.lastName', + }, + street1: { + 'selector': '#shipping_address_1_field', + 'valuePath': 'shipping.address.addressLine1', + }, + street2: { + 'selector': '#shipping_address_2_field', + 'valuePath': null + }, + postCode: { + 'selector': '#shipping_postcode_field', + 'valuePath': 'shipping.address.postalCode', + }, + city: { + 'selector': '#shipping_city_field', + 'valuePath': 'shipping.address.adminArea2', + }, + stateCode: { + 'selector': '#shipping_state_field', + 'valuePath': 'shipping.address.adminArea1', + }, + countryCode: { + 'selector': '#shipping_country_field', + 'valuePath': 'shipping.address.countryCode', + }, + company: { + 'selector': '#shipping_company_field', + 'valuePath': null, + }, + shipDifferentAddress: { + 'selector': '#ship-to-different-address', + 'valuePath': null, + }, + phone: { + //'selector': '#billing_phone_field', // There is no shipping phone field. + 'valueCallback': function (data) { + let phone = ''; + const cc = data?.shipping?.phoneNumber?.countryCode; + const number = data?.shipping?.phoneNumber?.nationalNumber; + + if (cc) { + phone = `+${cc} `; + } + phone += number; + return phone; + } + } + } + }); + } + + isActive() { + return this.group.active; + } + + activate() { + this.group.activate(); + } + + deactivate() { + this.group.deactivate(); + } + + refresh() { + this.group.refresh(); + } + + setData(data) { + this.group.setData(data); + } + + toSubmitData(data) { + return this.group.toSubmitData(data); + } + +} + +export default ShippingView; diff --git a/modules/ppcp-axo/resources/js/boot.js b/modules/ppcp-axo/resources/js/boot.js new file mode 100644 index 000000000..6b8b35893 --- /dev/null +++ b/modules/ppcp-axo/resources/js/boot.js @@ -0,0 +1,33 @@ +import AxoManager from "./AxoManager"; +import {loadPaypalScript} from "../../../ppcp-button/resources/js/modules/Helper/ScriptLoading"; + +(function ({ + axoConfig, + ppcpConfig, + jQuery +}) { + + const bootstrap = () => { + new AxoManager(axoConfig, ppcpConfig); + } + + document.addEventListener( + 'DOMContentLoaded', + () => { + if (!typeof (PayPalCommerceGateway)) { + console.error('AXO could not be configured.'); + return; + } + + // Load PayPal + loadPaypalScript(ppcpConfig, () => { + bootstrap(); + }); + }, + ); + +})({ + axoConfig: window.wc_ppcp_axo, + ppcpConfig: window.PayPalCommerceGateway, + jQuery: window.jQuery +}); diff --git a/modules/ppcp-axo/services.php b/modules/ppcp-axo/services.php new file mode 100644 index 000000000..2a35321c1 --- /dev/null +++ b/modules/ppcp-axo/services.php @@ -0,0 +1,117 @@ + static function ( ContainerInterface $container ): bool { + $apm_applies = $container->get( 'axo.helpers.apm-applies' ); + assert( $apm_applies instanceof ApmApplies ); + + return $apm_applies->for_country_currency() && $apm_applies->for_settings(); + }, + + 'axo.helpers.apm-applies' => static function ( ContainerInterface $container ) : ApmApplies { + return new ApmApplies( + $container->get( 'axo.supported-country-currency-matrix' ), + $container->get( 'api.shop.currency' ), + $container->get( 'api.shop.country' ) + ); + }, + + // If AXO is configured and onboarded. + 'axo.available' => static function ( ContainerInterface $container ): bool { + return true; + }, + + 'axo.url' => static function ( ContainerInterface $container ): string { + $path = realpath( __FILE__ ); + if ( false === $path ) { + return ''; + } + return plugins_url( + '/modules/ppcp-axo/', + dirname( $path, 3 ) . '/woocommerce-paypal-payments.php' + ); + }, + + 'axo.manager' => static function ( ContainerInterface $container ): AxoManager { + return new AxoManager( + $container->get( 'axo.url' ), + $container->get( 'ppcp.asset-version' ), + $container->get( 'session.handler' ), + $container->get( 'wcgateway.settings' ), + $container->get( 'onboarding.environment' ), + $container->get( 'wcgateway.settings.status' ), + $container->get( 'api.shop.currency' ), + $container->get( 'woocommerce.logger.woocommerce' ) + ); + }, + + 'axo.gateway' => static function ( ContainerInterface $container ): AxoGateway { + return new AxoGateway( + $container->get( 'wcgateway.settings' ), + $container->get( 'wcgateway.url' ), + $container->get( 'wcgateway.order-processor' ), + $container->get( 'axo.card_icons' ), + $container->get( 'api.endpoint.order' ), + $container->get( 'api.factory.purchase-unit' ), + $container->get( 'api.factory.shipping-preference' ), + $container->get( 'wcgateway.transaction-url-provider' ), + $container->get( 'onboarding.environment' ), + $container->get( 'woocommerce.logger.woocommerce' ) + ); + }, + + 'axo.card_icons' => static function ( ContainerInterface $container ): array { + return array( + array( + 'title' => 'Visa', + 'file' => 'visa-dark.svg', + ), + array( + 'title' => 'MasterCard', + 'file' => 'mastercard-dark.svg', + ), + array( + 'title' => 'American Express', + 'file' => 'amex.svg', + ), + array( + 'title' => 'Discover', + 'file' => 'discover.svg', + ), + ); + }, + + /** + * The matrix which countries and currency combinations can be used for AXO. + */ + 'axo.supported-country-currency-matrix' => static function ( ContainerInterface $container ) : array { + /** + * Returns which countries and currency combinations can be used for AXO. + */ + return apply_filters( + 'woocommerce_paypal_payments_axo_supported_country_currency_matrix', + array( + 'US' => array( + 'USD', + ), + ) + ); + }, + +); diff --git a/modules/ppcp-axo/src/Assets/AxoManager.php b/modules/ppcp-axo/src/Assets/AxoManager.php new file mode 100644 index 000000000..adb5543c2 --- /dev/null +++ b/modules/ppcp-axo/src/Assets/AxoManager.php @@ -0,0 +1,210 @@ +module_url = $module_url; + $this->version = $version; + $this->session_handler = $session_handler; + $this->settings = $settings; + $this->environment = $environment; + $this->settings_status = $settings_status; + $this->currency = $currency; + $this->logger = $logger; + } + + /** + * Enqueues scripts/styles. + * + * @return void + */ + public function enqueue() { + + // Register styles. + wp_register_style( + 'wc-ppcp-axo', + untrailingslashit( $this->module_url ) . '/assets/css/styles.css', + array(), + $this->version + ); + wp_enqueue_style( 'wc-ppcp-axo' ); + + // Register scripts. + wp_register_script( + 'wc-ppcp-axo', + untrailingslashit( $this->module_url ) . '/assets/js/boot.js', + array(), + $this->version, + true + ); + wp_enqueue_script( 'wc-ppcp-axo' ); + + wp_localize_script( + 'wc-ppcp-axo', + 'wc_ppcp_axo', + $this->script_data() + ); + } + + /** + * The configuration for AXO. + * + * @return array + */ + private function script_data() { + return array( + 'environment' => array( + 'is_sandbox' => $this->environment->current_environment() === 'sandbox', + ), + 'widgets' => array( + 'email' => 'render', + ), + 'insights' => array( + 'enabled' => true, + 'client_id' => ( $this->settings->has( 'client_id' ) ? $this->settings->get( 'client_id' ) : null ), + 'session_id' => + substr( + method_exists( WC()->session, 'get_customer_unique_id' ) ? md5( WC()->session->get_customer_unique_id() ) : '', + 0, + 16 + ), + 'amount' => array( + 'currency_code' => get_woocommerce_currency(), + 'value' => WC()->cart->get_total( 'numeric' ), + ), + ), + ); + } + + /** + * Returns the action name that PayPal AXO button will use for rendering on the checkout page. + * + * @return string + */ + public function checkout_button_renderer_hook(): string { + /** + * The filter returning the action name that PayPal AXO button will use for rendering on the checkout page. + */ + return (string) apply_filters( 'woocommerce_paypal_payments_checkout_axo_renderer_hook', 'woocommerce_review_order_after_submit' ); + } + + /** + * Renders the HTML for the AXO submit button. + */ + public function render_checkout_button(): void { + $id = 'ppcp-axo-submit-button-container'; + + /** + * The WC filter returning the WC order button text. + * phpcs:disable WordPress.WP.I18n.TextDomainMismatch + */ + $label = apply_filters( 'woocommerce_order_button_text', __( 'Place order', 'woocommerce' ) ); + + printf( + '', + esc_attr( $id ), + esc_html( $label ) + ); + } + +} diff --git a/modules/ppcp-axo/src/AxoModule.php b/modules/ppcp-axo/src/AxoModule.php new file mode 100644 index 000000000..6680c9fd7 --- /dev/null +++ b/modules/ppcp-axo/src/AxoModule.php @@ -0,0 +1,224 @@ +get( 'axo.gateway' ); + + // Check if the module is applicable, correct country, currency, ... etc. + if ( ! $c->get( 'axo.eligible' ) ) { + return $methods; + } + + // Add the gateway in admin area. + if ( is_admin() ) { + $methods[] = $gateway; + return $methods; + } + + if ( is_user_logged_in() ) { + return $methods; + } + + $methods[] = $gateway; + return $methods; + }, + 1, + 9 + ); + + add_action( + 'init', + static function () use ( $c, $module ) { + + // Check if the module is applicable, correct country, currency, ... etc. + if ( ! $c->get( 'axo.eligible' ) ) { + return; + } + + $manager = $c->get( 'axo.manager' ); + assert( $manager instanceof AxoManager ); + + // Enqueue frontend scripts. + add_action( + 'wp_enqueue_scripts', + static function () use ( $c, $manager ) { + $smart_button = $c->get( 'button.smart-button' ); + assert( $smart_button instanceof SmartButtonInterface ); + + if ( $smart_button->should_load_ppcp_script() ) { + $manager->enqueue(); + } + } + ); + + // Render submit button. + add_action( + $manager->checkout_button_renderer_hook(), + static function () use ( $c, $manager ) { + $manager->render_checkout_button(); + } + ); + + /** + * Param types removed to avoid third-party issues. + * + * @psalm-suppress MissingClosureParamType + */ + add_filter( + 'woocommerce_paypal_payments_sdk_components_hook', + function( $components ) { + $components[] = 'fastlane'; + return $components; + } + ); + + add_action( + 'wp_head', + function () { + // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript + echo ''; + } + ); + + add_filter( + 'woocommerce_paypal_payments_localized_script_data', + function( array $localized_script_data ) use ( $c, $module ) { + $api = $c->get( 'api.sdk-client-token' ); + assert( $api instanceof SdkClientToken ); + + $logger = $c->get( 'woocommerce.logger.woocommerce' ); + assert( $logger instanceof LoggerInterface ); + + return $module->add_sdk_client_token_to_script_data( $api, $logger, $localized_script_data ); + } + ); + + add_filter( + 'ppcp_onboarding_dcc_table_rows', + /** + * Param types removed to avoid third-party issues. + * + * @psalm-suppress MissingClosureParamType + */ + function ( $rows, $renderer ): array { + if ( ! is_array( $rows ) ) { + return $rows; + } + + if ( $renderer instanceof OnboardingOptionsRenderer ) { + $rows[] = $renderer->render_table_row( + __( 'Fastlane by PayPal', 'woocommerce-paypal-payments' ), + __( 'Yes', 'woocommerce-paypal-payments' ), + __( 'Help accelerate guest checkout with PayPal\'s autofill solution.', 'woocommerce-paypal-payments' ) + ); + } + return $rows; + }, + 10, + 2 + ); + + }, + 1 + ); + + } + + /** + * Adds id token to localized script data. + * + * @param SdkClientToken $api User id token api. + * @param LoggerInterface $logger The logger. + * @param array $localized_script_data The localized script data. + * @return array + */ + private function add_sdk_client_token_to_script_data( + SdkClientToken $api, + LoggerInterface $logger, + array $localized_script_data + ): array { + try { + $target_customer_id = ''; + if ( is_user_logged_in() ) { + $target_customer_id = get_user_meta( get_current_user_id(), '_ppcp_target_customer_id', true ); + if ( ! $target_customer_id ) { + $target_customer_id = get_user_meta( get_current_user_id(), 'ppcp_customer_id', true ); + } + } + + $sdk_client_token = $api->sdk_client_token( $target_customer_id ); + $localized_script_data['axo'] = array( + 'sdk_client_token' => $sdk_client_token, + ); + + } catch ( RuntimeException $exception ) { + $error = $exception->getMessage(); + if ( is_a( $exception, PayPalApiException::class ) ) { + $error = $exception->get_details( $error ); + } + + $logger->error( $error ); + } + + return $localized_script_data; + } + + /** + * Returns the key for the module. + * + * @return string|void + */ + public function getKey() { + } +} diff --git a/modules/ppcp-axo/src/Gateway/AxoGateway.php b/modules/ppcp-axo/src/Gateway/AxoGateway.php new file mode 100644 index 000000000..356c35d79 --- /dev/null +++ b/modules/ppcp-axo/src/Gateway/AxoGateway.php @@ -0,0 +1,324 @@ +id = self::ID; + + $this->ppcp_settings = $ppcp_settings; + $this->wcgateway_module_url = $wcgateway_module_url; + $this->order_processor = $order_processor; + $this->card_icons = $card_icons; + + $this->method_title = __( 'Fastlane Debit & Credit Cards', 'woocommerce-paypal-payments' ); + $this->method_description = __( 'Accept credit cards with Fastlane’s latest solution.', 'woocommerce-paypal-payments' ); + + $is_axo_enabled = $this->ppcp_settings->has( 'axo_enabled' ) && $this->ppcp_settings->get( 'axo_enabled' ); + $this->update_option( 'enabled', $is_axo_enabled ? 'yes' : 'no' ); + + $this->title = $this->ppcp_settings->has( 'axo_gateway_title' ) + ? $this->ppcp_settings->get( 'axo_gateway_title' ) + : $this->get_option( 'title', $this->method_title ); + + $this->description = $this->get_option( 'description', '' ); + + $this->init_form_fields(); + $this->init_settings(); + + add_action( + 'woocommerce_update_options_payment_gateways_' . $this->id, + array( + $this, + 'process_admin_options', + ) + ); + + $this->order_endpoint = $order_endpoint; + $this->purchase_unit_factory = $purchase_unit_factory; + $this->shipping_preference_factory = $shipping_preference_factory; + $this->logger = $logger; + + $this->transaction_url_provider = $transaction_url_provider; + $this->environment = $environment; + } + + /** + * Initialize the form fields. + */ + public function init_form_fields() { + $this->form_fields = array( + 'enabled' => array( + 'title' => __( 'Enable/Disable', 'woocommerce-paypal-payments' ), + 'type' => 'checkbox', + 'label' => __( 'AXO', 'woocommerce-paypal-payments' ), + 'default' => 'no', + 'desc_tip' => true, + 'description' => __( 'Enable/Disable AXO payment gateway.', 'woocommerce-paypal-payments' ), + ), + ); + } + + /** + * Processes the order. + * + * @param int $order_id The WC order ID. + * @return array + */ + public function process_payment( $order_id ) { + $wc_order = wc_get_order( $order_id ); + + $payment_method_title = __( 'Credit Card (via Fastlane by PayPal)', 'woocommerce-paypal-payments' ); + + $wc_order->set_payment_method_title( $payment_method_title ); + $wc_order->save(); + + $purchase_unit = $this->purchase_unit_factory->from_wc_order( $wc_order ); + + // phpcs:ignore WordPress.Security.NonceVerification.Missing + $nonce = wc_clean( wp_unslash( $_POST['axo_nonce'] ?? '' ) ); + + try { + $shipping_preference = $this->shipping_preference_factory->from_state( + $purchase_unit, + 'checkout' + ); + + $payment_source_properties = new \stdClass(); + $payment_source_properties->single_use_token = $nonce; + + $payment_source = new PaymentSource( + 'card', + $payment_source_properties + ); + + $order = $this->order_endpoint->create( + array( $purchase_unit ), + $shipping_preference, + null, + null, + '', + ApplicationContext::USER_ACTION_CONTINUE, + '', + array(), + $payment_source + ); + + $this->order_processor->process_captured_and_authorized( $wc_order, $order ); + + } catch ( RuntimeException $exception ) { + $error = $exception->getMessage(); + if ( is_a( $exception, PayPalApiException::class ) ) { + $error = $exception->get_details( $error ); + } + + $this->logger->error( $error ); + wc_add_notice( $error, 'error' ); + + $wc_order->update_status( + 'failed', + $error + ); + + return array( + 'result' => 'failure', + 'redirect' => wc_get_checkout_url(), + ); + } + + WC()->cart->empty_cart(); + + $result = array( + 'result' => 'success', + 'redirect' => $this->get_return_url( $wc_order ), + ); + + return $result; + } + + /** + * Returns the icons of the gateway. + * + * @return string + */ + public function get_icon() { + $icon = parent::get_icon(); + + if ( empty( $this->card_icons ) ) { + return $icon; + } + + $images = array(); + foreach ( $this->card_icons as $card ) { + $images[] = ' '; + } + + return '
' . implode( '', $images ) . '
'; + } + + /** + * Return transaction url for this gateway and given order. + * + * @param WC_Order $order WC order to get transaction url by. + * + * @return string + */ + public function get_transaction_url( $order ): string { + $this->view_transaction_url = $this->transaction_url_provider->get_transaction_url_base( $order ); + + return parent::get_transaction_url( $order ); + } + + /** + * Return the gateway's title. + * + * @return string + */ + public function get_title() { + if ( is_admin() ) { + // $theorder and other things for retrieving the order or post info are not available + // in the constructor, so must do it here. + global $theorder; + if ( $theorder instanceof WC_Order ) { + if ( $theorder->get_payment_method() === self::ID ) { + $payment_method_title = $theorder->get_payment_method_title(); + if ( $payment_method_title ) { + $this->title = $payment_method_title; + } + } + } + } + + return parent::get_title(); + } + +} diff --git a/modules/ppcp-axo/src/Helper/ApmApplies.php b/modules/ppcp-axo/src/Helper/ApmApplies.php new file mode 100644 index 000000000..c5fe645a9 --- /dev/null +++ b/modules/ppcp-axo/src/Helper/ApmApplies.php @@ -0,0 +1,82 @@ +allowed_country_currency_matrix = $allowed_country_currency_matrix; + $this->currency = $currency; + $this->country = $country; + } + + /** + * Returns whether AXO can be used in the current country and the current currency used. + * + * @return bool + */ + public function for_country_currency(): bool { + if ( ! in_array( $this->country, array_keys( $this->allowed_country_currency_matrix ), true ) ) { + return false; + } + return in_array( $this->currency, $this->allowed_country_currency_matrix[ $this->country ], true ); + } + + /** + * Returns whether the settings are compatible with AXO. + * + * @return bool + */ + public function for_settings(): bool { + if ( get_option( 'woocommerce_ship_to_destination' ) === 'billing_only' ) { // Force shipping to the customer billing address. + return false; + } + return true; + } + + + +} diff --git a/modules/ppcp-axo/src/Helper/PropertiesDictionary.php b/modules/ppcp-axo/src/Helper/PropertiesDictionary.php new file mode 100644 index 000000000..024c3649c --- /dev/null +++ b/modules/ppcp-axo/src/Helper/PropertiesDictionary.php @@ -0,0 +1,29 @@ + __( 'Yes (Recommended)', 'woocommerce-paypal-payments' ), + 'no' => __( 'No', 'woocommerce-paypal-payments' ), + ); + } + +} diff --git a/modules/ppcp-axo/webpack.config.js b/modules/ppcp-axo/webpack.config.js new file mode 100644 index 000000000..95c7f0fc6 --- /dev/null +++ b/modules/ppcp-axo/webpack.config.js @@ -0,0 +1,39 @@ +const path = require('path'); +const isProduction = process.env.NODE_ENV === 'production'; + +const DependencyExtractionWebpackPlugin = require( '@woocommerce/dependency-extraction-webpack-plugin' ); + +module.exports = { + devtool: isProduction ? 'source-map' : 'eval-source-map', + mode: isProduction ? 'production' : 'development', + target: 'web', + plugins: [ new DependencyExtractionWebpackPlugin() ], + entry: { + 'boot': path.resolve('./resources/js/boot.js'), + 'styles': path.resolve('./resources/css/styles.scss') + }, + output: { + path: path.resolve(__dirname, 'assets/'), + 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-axo/yarn.lock b/modules/ppcp-axo/yarn.lock new file mode 100644 index 000000000..b3f03c73d --- /dev/null +++ b/modules/ppcp-axo/yarn.lock @@ -0,0 +1,2213 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@ampproject/remapping@^2.1.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" + integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== + dependencies: + "@jridgewell/gen-mapping" "^0.1.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@babel/code-frame@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" + integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== + dependencies: + "@babel/highlight" "^7.18.6" + +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.0", "@babel/compat-data@^7.20.1": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.5.tgz#86f172690b093373a933223b4745deeb6049e733" + integrity sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g== + +"@babel/core@^7.19": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.5.tgz#45e2114dc6cd4ab167f81daf7820e8fa1250d113" + integrity sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ== + dependencies: + "@ampproject/remapping" "^2.1.0" + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.20.5" + "@babel/helper-compilation-targets" "^7.20.0" + "@babel/helper-module-transforms" "^7.20.2" + "@babel/helpers" "^7.20.5" + "@babel/parser" "^7.20.5" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.20.5" + "@babel/types" "^7.20.5" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.1" + semver "^6.3.0" + +"@babel/generator@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.5.tgz#cb25abee3178adf58d6814b68517c62bdbfdda95" + integrity sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA== + dependencies: + "@babel/types" "^7.20.5" + "@jridgewell/gen-mapping" "^0.3.2" + 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.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz#acd4edfd7a566d1d51ea975dff38fd52906981bb" + integrity sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw== + dependencies: + "@babel/helper-explode-assignable-expression" "^7.18.6" + "@babel/types" "^7.18.9" + +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.0": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz#6bf5374d424e1b3922822f1d9bdaa43b1a139d0a" + integrity sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ== + dependencies: + "@babel/compat-data" "^7.20.0" + "@babel/helper-validator-option" "^7.18.6" + browserslist "^4.21.3" + semver "^6.3.0" + +"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.5.tgz#327154eedfb12e977baa4ecc72e5806720a85a06" + integrity sha512-3RCdA/EmEaikrhayahwToF0fpweU/8o2p8vhc1c/1kftHOdTKuC65kik/TLc+qfbS8JKw4qqJbne4ovICDhmww== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" + "@babel/helper-member-expression-to-functions" "^7.18.9" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-replace-supers" "^7.19.1" + "@babel/helper-split-export-declaration" "^7.18.6" + +"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.20.5.tgz#5ea79b59962a09ec2acf20a963a01ab4d076ccca" + integrity sha512-m68B1lkg3XDGX5yCvGO0kPx3v9WIYLnzjKfPcQiwntEQa5ZeRkPmo2X/ISJc8qxWGfwUr+kvZAeEzAwLec2r2w== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + regexpu-core "^5.2.1" + +"@babel/helper-define-polyfill-provider@^0.3.3": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz#8612e55be5d51f0cd1f36b4a5a83924e89884b7a" + integrity sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww== + 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": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" + integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== + +"@babel/helper-explode-assignable-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz#41f8228ef0a6f1a036b8dfdfec7ce94f9a6bc096" + integrity sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" + integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== + dependencies: + "@babel/template" "^7.18.10" + "@babel/types" "^7.19.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.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz#1531661e8375af843ad37ac692c132841e2fd815" + integrity sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg== + dependencies: + "@babel/types" "^7.18.9" + +"@babel/helper-module-imports@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" + integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.19.6", "@babel/helper-module-transforms@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.20.2.tgz#ac53da669501edd37e658602a21ba14c08748712" + integrity sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-simple-access" "^7.20.2" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.19.1" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.20.1" + "@babel/types" "^7.20.2" + +"@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.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" + integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== + +"@babel/helper-remap-async-to-generator@^7.18.6", "@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.19.1": + version "7.19.1" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.19.1.tgz#e1592a9b4b368aa6bdb8784a711e0bcbf0612b78" + integrity sha512-T7ahH7wV0Hfs46SFh5Jz3s0B6+o8g3c+7TMxu7xKfmHikg7EAZ3I2Qk9LFhjxXq8sL7UkP5JflezNwoZa8WvWw== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-member-expression-to-functions" "^7.18.9" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/traverse" "^7.19.1" + "@babel/types" "^7.19.0" + +"@babel/helper-simple-access@^7.19.4", "@babel/helper-simple-access@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz#0ab452687fe0c2cfb1e2b9e0015de07fc2d62dd9" + integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== + dependencies: + "@babel/types" "^7.20.2" + +"@babel/helper-skip-transparent-expression-wrappers@^7.18.9": + 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.19.4": + version "7.19.4" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" + integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== + +"@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.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" + integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== + +"@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.20.5": + version "7.20.6" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.6.tgz#e64778046b70e04779dfbdf924e7ebb45992c763" + integrity sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w== + dependencies: + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.20.5" + "@babel/types" "^7.20.5" + +"@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.18.10", "@babel/parser@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.5.tgz#7f3c7335fe417665d929f34ae5dceae4c04015e8" + integrity sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA== + +"@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.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz#a11af19aa373d68d561f08e0a57242350ed0ec50" + integrity sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" + "@babel/plugin-proposal-optional-chaining" "^7.18.9" + +"@babel/plugin-proposal-async-generator-functions@^7.20.1": + version "7.20.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.1.tgz#352f02baa5d69f4e7529bdac39aaa02d41146af9" + integrity sha512-Gh5rchzSwE4kC+o/6T8waD0WHEQIsDmjltY8WnWRXHUdH8axZhuH86Ov9M72YhJfDrZseQwuuWaaIT/TmePp3g== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-remap-async-to-generator" "^7.18.9" + "@babel/plugin-syntax-async-generators" "^7.8.4" + +"@babel/plugin-proposal-class-properties@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3" + integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-proposal-class-static-block@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz#8aa81d403ab72d3962fc06c26e222dacfc9b9020" + integrity sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + +"@babel/plugin-proposal-dynamic-import@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz#72bcf8d408799f547d759298c3c27c7e7faa4d94" + integrity sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + +"@babel/plugin-proposal-export-namespace-from@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz#5f7313ab348cdb19d590145f9247540e94761203" + integrity sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + +"@babel/plugin-proposal-json-strings@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz#7e8788c1811c393aff762817e7dbf1ebd0c05f0b" + integrity sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-json-strings" "^7.8.3" + +"@babel/plugin-proposal-logical-assignment-operators@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz#8148cbb350483bf6220af06fa6db3690e14b2e23" + integrity sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + +"@babel/plugin-proposal-nullish-coalescing-operator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz#fdd940a99a740e577d6c753ab6fbb43fdb9467e1" + integrity sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + +"@babel/plugin-proposal-numeric-separator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz#899b14fbafe87f053d2c5ff05b36029c62e13c75" + integrity sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + +"@babel/plugin-proposal-object-rest-spread@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.2.tgz#a556f59d555f06961df1e572bb5eca864c84022d" + integrity sha512-Ks6uej9WFK+fvIMesSqbAto5dD8Dz4VuuFvGJFKgIGSkJuRGcrwGECPA1fDgQK3/DbExBJpEkTeYeB8geIFCSQ== + dependencies: + "@babel/compat-data" "^7.20.1" + "@babel/helper-compilation-targets" "^7.20.0" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.20.1" + +"@babel/plugin-proposal-optional-catch-binding@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz#f9400d0e6a3ea93ba9ef70b09e72dd6da638a2cb" + integrity sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + +"@babel/plugin-proposal-optional-chaining@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz#e8e8fe0723f2563960e4bf5e9690933691915993" + integrity sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + +"@babel/plugin-proposal-private-methods@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz#5209de7d213457548a98436fa2882f52f4be6bea" + integrity sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-proposal-private-property-in-object@^7.18.6": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.20.5.tgz#309c7668f2263f1c711aa399b5a9a6291eef6135" + integrity sha512-Vq7b9dUA12ByzB4EjQTPo25sFhY+08pQDBSZRtUAkj7lb7jahaHR5igera16QZ+3my1nYR4dKsNdYj5IjPHilQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-create-class-features-plugin" "^7.20.5" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + +"@babel/plugin-proposal-unicode-property-regex@^7.18.6", "@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-json-strings@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-jsx@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz#a8feef63b010150abd97f1649ec296e849943ca0" + integrity sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@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-transform-arrow-functions@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz#19063fcf8771ec7b31d742339dac62433d0611fe" + integrity sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-async-to-generator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz#ccda3d1ab9d5ced5265fdb13f1882d5476c71615" + integrity sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag== + dependencies: + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-remap-async-to-generator" "^7.18.6" + +"@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.20.2": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.5.tgz#401215f9dc13dc5262940e2e527c9536b3d7f237" + integrity sha512-WvpEIW9Cbj9ApF3yJCjIEEf1EiNJLtXagOrL5LNWEZOo3jv8pmPoYTSNJQvqej8OavVlgOoOPw6/htGZro6IkA== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-transform-classes@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.2.tgz#c0033cf1916ccf78202d04be4281d161f6709bb2" + integrity sha512-9rbPp0lCVVoagvtEyQKSo5L8oo0nQS/iif+lwlAz29MccX2642vWDlSZK+2T2buxbopotId2ld7zZAzRfz9j1g== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-compilation-targets" "^7.20.0" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-replace-supers" "^7.19.1" + "@babel/helper-split-export-declaration" "^7.18.6" + globals "^11.1.0" + +"@babel/plugin-transform-computed-properties@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz#2357a8224d402dad623caf6259b611e56aec746e" + integrity sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-destructuring@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.2.tgz#c23741cfa44ddd35f5e53896e88c75331b8b2792" + integrity sha512-mENM+ZHrvEgxLTBXUiQ621rRXZes3KWUv6NdQlrnr1TkWVw+hUjQBZuP2X32qKlrlG2BzgR95gkuCRSkJl8vIw== + 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-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-for-of@^7.18.8": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz#6ef8a50b244eb6a0bdbad0c7c61877e4e30097c1" + integrity sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@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-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-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.19.6": + version "7.19.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.19.6.tgz#aca391801ae55d19c4d8d2ebfeaa33df5f2a2cbd" + integrity sha512-uG3od2mXvAtIFQIh0xrpLH6r5fpSQN04gIVovl+ODLdUMANokxQLZnPBHcjmv3GxRjnqwLuHvppjjcelqUFZvg== + dependencies: + "@babel/helper-module-transforms" "^7.19.6" + "@babel/helper-plugin-utils" "^7.19.0" + +"@babel/plugin-transform-modules-commonjs@^7.19.6": + version "7.19.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.19.6.tgz#25b32feef24df8038fc1ec56038917eacb0b730c" + integrity sha512-8PIa1ym4XRTKuSsOUXqDG0YaOlEuTVvHMe5JCfgBMOtHvJKw/4NGovEGN33viISshG/rZNVrACiBmPQLvWN8xQ== + dependencies: + "@babel/helper-module-transforms" "^7.19.6" + "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-simple-access" "^7.19.4" + +"@babel/plugin-transform-modules-systemjs@^7.19.6": + version "7.19.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.6.tgz#59e2a84064b5736a4471b1aa7b13d4431d327e0d" + integrity sha512-fqGLBepcc3kErfR9R3DnVpURmckXP7gj7bAlrTQyBxrigFqszZCkFkcoxzCp2v32XmwXLvbw+8Yq9/b+QqksjQ== + dependencies: + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-module-transforms" "^7.19.6" + "@babel/helper-plugin-utils" "^7.19.0" + "@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.19.1": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz#626298dd62ea51d452c3be58b285d23195ba69a8" + integrity sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.20.5" + "@babel/helper-plugin-utils" "^7.20.2" + +"@babel/plugin-transform-new-target@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz#d128f376ae200477f37c4ddfcc722a8a1b3246a8" + integrity sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@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-parameters@^7.20.1": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.5.tgz#f8f9186c681d10c3de7620c916156d893c8a019e" + integrity sha512-h7plkOmcndIUWXZFLgpbrh2+fXAi47zcUX7IrOQuZdLD0I0KvjJ6cvo3BEcAOsDOcZhVKGJqv07mkSqK0y2isQ== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + +"@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-react-display-name@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz#8b1125f919ef36ebdfff061d664e266c666b9415" + integrity sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-react-jsx-development@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz#dbe5c972811e49c7405b630e4d0d2e1380c0ddc5" + integrity sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA== + dependencies: + "@babel/plugin-transform-react-jsx" "^7.18.6" + +"@babel/plugin-transform-react-jsx@^7.18.6": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.19.0.tgz#b3cbb7c3a00b92ec8ae1027910e331ba5c500eb9" + integrity sha512-UVEvX3tXie3Szm3emi1+G63jyw1w5IcMY0FSKM+CRnKRI5Mr1YbCNgsSTwoTwKphQEG9P+QqmuRFneJPZuHNhg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-plugin-utils" "^7.19.0" + "@babel/plugin-syntax-jsx" "^7.18.6" + "@babel/types" "^7.19.0" + +"@babel/plugin-transform-react-pure-annotations@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz#561af267f19f3e5d59291f9950fd7b9663d0d844" + integrity sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-regenerator@^7.18.6": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz#57cda588c7ffb7f4f8483cc83bdcea02a907f04d" + integrity sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + 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.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.19.0.tgz#dd60b4620c2fec806d60cfaae364ec2188d593b6" + integrity sha512-RsuMk7j6n+r752EtzyScnWkQyuJdli6LdO5Klv8Yx0OfPVTcQkIUfS8clx5e9yHXzlnhOZF3CbQ8C2uP5j074w== + dependencies: + "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" + +"@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.18.10": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz#1ecfb0eda83d09bbcb77c09970c2dd55832aa246" + integrity sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@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/preset-env@^7.19": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.20.2.tgz#9b1642aa47bb9f43a86f9630011780dab7f86506" + integrity sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg== + dependencies: + "@babel/compat-data" "^7.20.1" + "@babel/helper-compilation-targets" "^7.20.0" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-validator-option" "^7.18.6" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.18.9" + "@babel/plugin-proposal-async-generator-functions" "^7.20.1" + "@babel/plugin-proposal-class-properties" "^7.18.6" + "@babel/plugin-proposal-class-static-block" "^7.18.6" + "@babel/plugin-proposal-dynamic-import" "^7.18.6" + "@babel/plugin-proposal-export-namespace-from" "^7.18.9" + "@babel/plugin-proposal-json-strings" "^7.18.6" + "@babel/plugin-proposal-logical-assignment-operators" "^7.18.9" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.6" + "@babel/plugin-proposal-numeric-separator" "^7.18.6" + "@babel/plugin-proposal-object-rest-spread" "^7.20.2" + "@babel/plugin-proposal-optional-catch-binding" "^7.18.6" + "@babel/plugin-proposal-optional-chaining" "^7.18.9" + "@babel/plugin-proposal-private-methods" "^7.18.6" + "@babel/plugin-proposal-private-property-in-object" "^7.18.6" + "@babel/plugin-proposal-unicode-property-regex" "^7.18.6" + "@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-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-transform-arrow-functions" "^7.18.6" + "@babel/plugin-transform-async-to-generator" "^7.18.6" + "@babel/plugin-transform-block-scoped-functions" "^7.18.6" + "@babel/plugin-transform-block-scoping" "^7.20.2" + "@babel/plugin-transform-classes" "^7.20.2" + "@babel/plugin-transform-computed-properties" "^7.18.9" + "@babel/plugin-transform-destructuring" "^7.20.2" + "@babel/plugin-transform-dotall-regex" "^7.18.6" + "@babel/plugin-transform-duplicate-keys" "^7.18.9" + "@babel/plugin-transform-exponentiation-operator" "^7.18.6" + "@babel/plugin-transform-for-of" "^7.18.8" + "@babel/plugin-transform-function-name" "^7.18.9" + "@babel/plugin-transform-literals" "^7.18.9" + "@babel/plugin-transform-member-expression-literals" "^7.18.6" + "@babel/plugin-transform-modules-amd" "^7.19.6" + "@babel/plugin-transform-modules-commonjs" "^7.19.6" + "@babel/plugin-transform-modules-systemjs" "^7.19.6" + "@babel/plugin-transform-modules-umd" "^7.18.6" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.19.1" + "@babel/plugin-transform-new-target" "^7.18.6" + "@babel/plugin-transform-object-super" "^7.18.6" + "@babel/plugin-transform-parameters" "^7.20.1" + "@babel/plugin-transform-property-literals" "^7.18.6" + "@babel/plugin-transform-regenerator" "^7.18.6" + "@babel/plugin-transform-reserved-words" "^7.18.6" + "@babel/plugin-transform-shorthand-properties" "^7.18.6" + "@babel/plugin-transform-spread" "^7.19.0" + "@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.18.10" + "@babel/plugin-transform-unicode-regex" "^7.18.6" + "@babel/preset-modules" "^0.1.5" + "@babel/types" "^7.20.2" + babel-plugin-polyfill-corejs2 "^0.3.3" + babel-plugin-polyfill-corejs3 "^0.6.0" + babel-plugin-polyfill-regenerator "^0.4.1" + core-js-compat "^3.25.1" + 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/preset-react@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.18.6.tgz#979f76d6277048dc19094c217b507f3ad517dd2d" + integrity sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-validator-option" "^7.18.6" + "@babel/plugin-transform-react-display-name" "^7.18.6" + "@babel/plugin-transform-react-jsx" "^7.18.6" + "@babel/plugin-transform-react-jsx-development" "^7.18.6" + "@babel/plugin-transform-react-pure-annotations" "^7.18.6" + +"@babel/runtime@^7.8.4": + version "7.20.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.6.tgz#facf4879bfed9b5326326273a64220f099b0fce3" + integrity sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA== + dependencies: + regenerator-runtime "^0.13.11" + +"@babel/template@^7.18.10": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71" + integrity sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/parser" "^7.18.10" + "@babel/types" "^7.18.10" + +"@babel/traverse@^7.19.1", "@babel/traverse@^7.20.1", "@babel/traverse@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.5.tgz#78eb244bea8270fdda1ef9af22a5d5e5b7e57133" + integrity sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.20.5" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.20.5" + "@babel/types" "^7.20.5" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.5", "@babel/types@^7.4.4": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.5.tgz#e206ae370b5393d94dfd1d04cd687cace53efa84" + integrity sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg== + dependencies: + "@babel/helper-string-parser" "^7.19.4" + "@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.1.0": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" + integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== + dependencies: + "@jridgewell/set-array" "^1.0.0" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" + integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== + 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.0", "@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.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb" + integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": + 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/trace-mapping@^0.3.14", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.17" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" + integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== + dependencies: + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" + +"@paypal/paypal-js@^6.0.0": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@paypal/paypal-js/-/paypal-js-6.0.1.tgz#5d68d5863a5176383fee9424bc944231668fcffd" + integrity sha512-bvYetmkg2GEC6onsUJQx1E9hdAJWff2bS3IPeiZ9Sh9U7h26/fIgMKm240cq/908sbSoDjHys75XXd8at9OpQA== + dependencies: + promise-polyfill "^8.3.0" + +"@types/eslint-scope@^3.7.3": + version "3.7.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.4.10" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.4.10.tgz#19731b9685c19ed1552da7052b6f668ed7eb64bb" + integrity sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2" + integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ== + +"@types/estree@^0.0.51": + version "0.0.51" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40" + integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== + +"@types/json-schema@*", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8": + version "7.0.11" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" + integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== + +"@types/node@*": + version "18.11.11" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.11.tgz#1d455ac0211549a8409d3cdb371cd55cc971e8dc" + integrity sha512-KJ021B1nlQUBLopzZmPBVuGU9un7WJd/W4ya7Ih02B4Uwky5Nja0yGYav2EfYIk0RR2Q9oVhf60S2XR1BCWJ2g== + +"@webassemblyjs/ast@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" + integrity sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw== + dependencies: + "@webassemblyjs/helper-numbers" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + +"@webassemblyjs/floating-point-hex-parser@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz#f6c61a705f0fd7a6aecaa4e8198f23d9dc179e4f" + integrity sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ== + +"@webassemblyjs/helper-api-error@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz#1a63192d8788e5c012800ba6a7a46c705288fd16" + integrity sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg== + +"@webassemblyjs/helper-buffer@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz#832a900eb444884cde9a7cad467f81500f5e5ab5" + integrity sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA== + +"@webassemblyjs/helper-numbers@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz#64d81da219fbbba1e3bd1bfc74f6e8c4e10a62ae" + integrity sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.1" + "@webassemblyjs/helper-api-error" "1.11.1" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz#f328241e41e7b199d0b20c18e88429c4433295e1" + integrity sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q== + +"@webassemblyjs/helper-wasm-section@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz#21ee065a7b635f319e738f0dd73bfbda281c097a" + integrity sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + +"@webassemblyjs/ieee754@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz#963929e9bbd05709e7e12243a099180812992614" + integrity sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.1.tgz#ce814b45574e93d76bae1fb2644ab9cdd9527aa5" + integrity sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.1.tgz#d1f8b764369e7c6e6bae350e854dec9a59f0a3ff" + integrity sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ== + +"@webassemblyjs/wasm-edit@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz#ad206ebf4bf95a058ce9880a8c092c5dec8193d6" + integrity sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/helper-wasm-section" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + "@webassemblyjs/wasm-opt" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + "@webassemblyjs/wast-printer" "1.11.1" + +"@webassemblyjs/wasm-gen@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz#86c5ea304849759b7d88c47a32f4f039ae3c8f76" + integrity sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/ieee754" "1.11.1" + "@webassemblyjs/leb128" "1.11.1" + "@webassemblyjs/utf8" "1.11.1" + +"@webassemblyjs/wasm-opt@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz#657b4c2202f4cf3b345f8a4c6461c8c2418985f2" + integrity sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + +"@webassemblyjs/wasm-parser@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz#86ca734534f417e9bd3c67c7a1c75d8be41fb199" + integrity sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-api-error" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/ieee754" "1.11.1" + "@webassemblyjs/leb128" "1.11.1" + "@webassemblyjs/utf8" "1.11.1" + +"@webassemblyjs/wast-printer@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz#d0c73beda8eec5426f10ae8ef55cee5e7084c2f0" + integrity sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@xtuc/long" "4.2.2" + +"@webpack-cli/configtest@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.2.0.tgz#7b20ce1c12533912c3b217ea68262365fa29a6f5" + integrity sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg== + +"@webpack-cli/info@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.5.0.tgz#6c78c13c5874852d6e2dd17f08a41f3fe4c261b1" + integrity sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ== + dependencies: + envinfo "^7.7.3" + +"@webpack-cli/serve@^1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.7.0.tgz#e1993689ac42d2b16e9194376cfb6753f6254db1" + integrity sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q== + +"@woocommerce/dependency-extraction-webpack-plugin@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@woocommerce/dependency-extraction-webpack-plugin/-/dependency-extraction-webpack-plugin-2.2.0.tgz#230d674a67585bc32e31bc28485bec99b41dbd1f" + integrity sha512-0wDY3EIUwWrPm0KrWvt1cf2SZDSX7CzBXvv4TyCqWOPuVPvC/ajyY8kD1HTFI80q6/RHoxWf3BYCmhuBzPbe9A== + dependencies: + "@wordpress/dependency-extraction-webpack-plugin" "^3.3.0" + +"@wordpress/dependency-extraction-webpack-plugin@^3.3.0": + version "3.7.0" + resolved "https://registry.yarnpkg.com/@wordpress/dependency-extraction-webpack-plugin/-/dependency-extraction-webpack-plugin-3.7.0.tgz#e52ef31f66b8c4add3d773a87e11007375127b04" + integrity sha512-SHyp88D1ICSaRVMfs/kKEicjKXWf1y2wecUeZIiMtkfAi8Bnk3JsnUo11LH7drJIXfjmDoer2B2rrBMZmRm8VA== + dependencies: + json2php "^0.0.4" + webpack-sources "^3.2.2" + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + +acorn-import-assertions@^1.7.6: + version "1.8.0" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz#ba2b5939ce62c238db6d93d81c9b111b29b855e9" + integrity sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw== + +acorn@^8.5.0, acorn@^8.7.1: + version "8.8.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73" + integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA== + +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.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz#5d1bd3836d0a19e1b84bbf2d9640ccb6f951c122" + integrity sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q== + dependencies: + "@babel/compat-data" "^7.17.7" + "@babel/helper-define-polyfill-provider" "^0.3.3" + semver "^6.1.1" + +babel-plugin-polyfill-corejs3@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz#56ad88237137eade485a71b52f72dbed57c6230a" + integrity sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.3.3" + core-js-compat "^3.25.1" + +babel-plugin-polyfill-regenerator@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz#390f91c38d90473592ed43351e801a9d3e0fd747" + integrity sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.3.3" + +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.4: + version "4.21.4" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987" + integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw== + dependencies: + caniuse-lite "^1.0.30001400" + electron-to-chromium "^1.4.251" + node-releases "^2.0.6" + update-browserslist-db "^1.0.9" + +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.30001400: + version "1.0.30001436" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001436.tgz#22d7cbdbbbb60cdc4ca1030ccd6dea9f5de4848b" + integrity sha512-ZmWkKsnC2ifEPoWUvSAIGyOYwT+keAaaWPHiQ9DfMqS1t6tfuyFYoWR78TeZtznkEQ64+vGXH9cZrElwR2Mrxg== + +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.19" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" + integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== + +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.25.1: + version "3.26.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.26.1.tgz#0e710b09ebf689d719545ac36e49041850f943df" + integrity sha512-622/KzTudvXCDLRw70iHW4KKs1aGpcRcowGWyYJr2DEBfRrd6hNJybxSWJFuZYD4ma86xhrwDDHxmDaIq4EA8A== + dependencies: + browserslist "^4.21.4" + +core-js@^3.25.0: + version "3.26.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.26.1.tgz#7a9816dabd9ee846c1c0fe0e8fcad68f3709134e" + integrity sha512-21491RRQVzUn0GGM9Z1Jrpr6PNPxPi+Za8OM9q4tksTSnlbXXGKK1nXNg/QvwFYettXvSX6zWKCtHHfjN4puyA== + +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.251: + version "1.4.284" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz#61046d1e4cab3a25238f6bf7413795270f125592" + integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA== + +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.10.0: + version "5.12.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz#300e1c90228f5b570c4d35babf263f6da7155634" + integrity sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ== + 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@^0.9.0: + version "0.9.3" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19" + integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ== + +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.10" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" + integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== + +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.1.0" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.1.0.tgz#f795787f0db780183307b9eb2091fcac1f6fafef" + integrity sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ== + +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.9.0: + version "2.11.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" + integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== + 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== + +json2php@^0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/json2php/-/json2php-0.0.4.tgz#6bd85a1dda6a5dd7e91022bb24403cc1b7c2ee34" + integrity sha512-hFzejhs28f70sGnutcsRS459MnAsjRVI85RgPAL1KQIZEpjiDitc27CZv4IgOtaR86vrqOVlu9vJNew2XyTH4g== + +json5@^2.1.2, json5@^2.2.1: + 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.5" + resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.5.tgz#d166574d90076395d9963aa7a928fabb8d76afbc" + integrity sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ== + +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== + +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.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503" + integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg== + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.0.4, picomatch@^2.2.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pkg-dir@^4.1.0, pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +promise-polyfill@^8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/promise-polyfill/-/promise-polyfill-8.3.0.tgz#9284810268138d103807b11f4e23d5e945a4db63" + integrity sha512-H5oELycFml5yto/atYqmjyigJoAo3+OXwolYiH7OfQuYlAqhxNvTfiNMbV9hsC6Yp83yE5r2KTVmtrG6R9i6Pg== + +punycode@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +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.2.1: + version "5.2.2" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.2.2.tgz#3e4e5d12103b64748711c3aad69934d7718e75fc" + integrity sha512-T0+1Zp2wjF/juXMrMxHxidqGYn8U4R+zleSJhX9tQ1PUsS8a9UtYfbsF9LdiVgNX3kiX8RNaKM42nfSgvFJjmw== + dependencies: + regenerate "^1.4.2" + regenerate-unicode-properties "^10.1.0" + regjsgen "^0.7.1" + regjsparser "^0.9.1" + unicode-match-property-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript "^2.1.0" + +regjsgen@^0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.7.1.tgz#ee5ef30e18d3f09b7c369b76e7c2373ed25546f6" + integrity sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA== + +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.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" + integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== + dependencies: + is-core-module "^2.9.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.56.1" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.56.1.tgz#94d3910cd468fd075fa87f5bb17437a0b617d8a7" + integrity sha512-VpEyKpyBPCxE7qGDtOcdJ6fFbcpOM+Emu7uZLxVrkX8KVU/Dp5UF7WLvzqRuUhB6mqqQt1xffLoG+AndxTZrCQ== + 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.0, schema-utils@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281" + integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw== + 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.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" + integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== + 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.1.3: + version "5.3.6" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz#5590aec31aa3c6f771ce1b1acca60639eab3195c" + integrity sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ== + dependencies: + "@jridgewell/trace-mapping" "^0.3.14" + jest-worker "^27.4.5" + schema-utils "^3.1.1" + serialize-javascript "^6.0.0" + terser "^5.14.1" + +terser@^5.14.1: + version "5.16.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.16.1.tgz#5af3bc3d0f24241c7fb2024199d5c461a1075880" + integrity sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw== + dependencies: + "@jridgewell/source-map" "^0.3.2" + acorn "^8.5.0" + 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.9: + version "1.0.10" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" + integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== + 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.8.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.8.0.tgz#2b39dbf22af87776ad744c390223731d30a68f61" + integrity sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q== + dependencies: + clone-deep "^4.0.1" + wildcard "^2.0.0" + +webpack-sources@^3.2.2, webpack-sources@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" + integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== + +webpack@^5.76: + version "5.76.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.76.0.tgz#f9fb9fb8c4a7dbdcd0d56a98e56b8a942ee2692c" + integrity sha512-l5sOdYBDunyf72HW8dF23rFtWq/7Zgvt/9ftMof71E/yUb1YLOBmTgA2K4vQthB3kotMrSj609txVE0dnr2fjA== + dependencies: + "@types/eslint-scope" "^3.7.3" + "@types/estree" "^0.0.51" + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/wasm-edit" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + acorn "^8.7.1" + acorn-import-assertions "^1.7.6" + browserslist "^4.14.5" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.10.0" + es-module-lexer "^0.9.0" + 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.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.1.3" + 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.0" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" + integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== diff --git a/modules/ppcp-blocks/resources/js/checkout-block.js b/modules/ppcp-blocks/resources/js/checkout-block.js index 21c4372b3..3256f075b 100644 --- a/modules/ppcp-blocks/resources/js/checkout-block.js +++ b/modules/ppcp-blocks/resources/js/checkout-block.js @@ -121,8 +121,13 @@ const PayPalComponent = ({ }; const createSubscription = async (data, actions) => { + let planId = config.scriptData.subscription_plan_id; + if (config.scriptData.variable_paypal_subscription_variation_from_cart !== '') { + planId = config.scriptData.variable_paypal_subscription_variation_from_cart; + } + return actions.subscription.create({ - 'plan_id': config.scriptData.subscription_plan_id + 'plan_id': planId }); }; diff --git a/modules/ppcp-button/resources/js/button.js b/modules/ppcp-button/resources/js/button.js index 0f5d573ce..d0e1a8543 100644 --- a/modules/ppcp-button/resources/js/button.js +++ b/modules/ppcp-button/resources/js/button.js @@ -137,7 +137,12 @@ const bootstrap = () => { } const isFreeTrial = PayPalCommerceGateway.is_free_trial_cart; - if (isFreeTrial && data.fundingSource !== 'card' && ! PayPalCommerceGateway.subscription_plan_id) { + if ( + isFreeTrial + && data.fundingSource !== 'card' + && ! PayPalCommerceGateway.subscription_plan_id + && ! PayPalCommerceGateway.vault_v3_enabled + ) { freeTrialHandler.handle(); return actions.reject(); } diff --git a/modules/ppcp-button/resources/js/modules/ActionHandler/CartActionHandler.js b/modules/ppcp-button/resources/js/modules/ActionHandler/CartActionHandler.js index 4b7b20e9e..0400c8013 100644 --- a/modules/ppcp-button/resources/js/modules/ActionHandler/CartActionHandler.js +++ b/modules/ppcp-button/resources/js/modules/ActionHandler/CartActionHandler.js @@ -9,11 +9,11 @@ class CartActionHandler { this.errorHandler = errorHandler; } - subscriptionsConfiguration() { + subscriptionsConfiguration(subscription_plan_id) { return { createSubscription: (data, actions) => { return actions.subscription.create({ - 'plan_id': this.config.subscription_plan_id + 'plan_id': subscription_plan_id }); }, onApprove: (data, actions) => { diff --git a/modules/ppcp-button/resources/js/modules/ActionHandler/CheckoutActionHandler.js b/modules/ppcp-button/resources/js/modules/ActionHandler/CheckoutActionHandler.js index 93ecf7c8f..923c0b772 100644 --- a/modules/ppcp-button/resources/js/modules/ActionHandler/CheckoutActionHandler.js +++ b/modules/ppcp-button/resources/js/modules/ActionHandler/CheckoutActionHandler.js @@ -12,7 +12,7 @@ class CheckoutActionHandler { this.spinner = spinner; } - subscriptionsConfiguration() { + subscriptionsConfiguration(subscription_plan_id) { return { createSubscription: async (data, actions) => { try { @@ -22,7 +22,7 @@ class CheckoutActionHandler { } return actions.subscription.create({ - 'plan_id': this.config.subscription_plan_id + 'plan_id': subscription_plan_id }); }, onApprove: (data, actions) => { @@ -144,6 +144,54 @@ class CheckoutActionHandler { } } } + + addPaymentMethodConfiguration() { + return { + createVaultSetupToken: async () => { + const response = await fetch(this.config.ajax.create_setup_token.endpoint, { + method: "POST", + credentials: 'same-origin', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + nonce: this.config.ajax.create_setup_token.nonce, + }) + }); + + const result = await response.json() + if (result.data.id) { + return result.data.id + } + + console.error(result) + }, + onApprove: async ({vaultSetupToken}) => { + const response = await fetch(this.config.ajax.create_payment_token_for_guest.endpoint, { + method: "POST", + credentials: 'same-origin', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + nonce: this.config.ajax.create_payment_token_for_guest.nonce, + vault_setup_token: vaultSetupToken, + }) + }) + + const result = await response.json(); + if (result.success === true) { + document.querySelector('#place_order').click() + return; + } + + console.error(result) + }, + onError: (error) => { + console.error(error) + } + } + } } export default CheckoutActionHandler; diff --git a/modules/ppcp-button/resources/js/modules/ContextBootstrap/CartBootstap.js b/modules/ppcp-button/resources/js/modules/ContextBootstrap/CartBootstap.js index c40e03ce1..bf8ad10ef 100644 --- a/modules/ppcp-button/resources/js/modules/ContextBootstrap/CartBootstap.js +++ b/modules/ppcp-button/resources/js/modules/ContextBootstrap/CartBootstap.js @@ -90,7 +90,12 @@ class CartBootstrap { PayPalCommerceGateway.data_client_id.has_subscriptions && PayPalCommerceGateway.data_client_id.paypal_subscriptions_enabled ) { - this.renderer.render(actionHandler.subscriptionsConfiguration()); + let subscription_plan_id = PayPalCommerceGateway.subscription_plan_id + if(PayPalCommerceGateway.variable_paypal_subscription_variation_from_cart !== '') { + subscription_plan_id = PayPalCommerceGateway.variable_paypal_subscription_variation_from_cart + } + + this.renderer.render(actionHandler.subscriptionsConfiguration(subscription_plan_id)); if(!PayPalCommerceGateway.subscription_product_allowed) { this.gateway.button.is_disabled = true; diff --git a/modules/ppcp-button/resources/js/modules/ContextBootstrap/CheckoutBootstap.js b/modules/ppcp-button/resources/js/modules/ContextBootstrap/CheckoutBootstap.js index e7fae33b0..a14fa2f4b 100644 --- a/modules/ppcp-button/resources/js/modules/ContextBootstrap/CheckoutBootstap.js +++ b/modules/ppcp-button/resources/js/modules/ContextBootstrap/CheckoutBootstap.js @@ -106,7 +106,11 @@ class CheckoutBootstap { PayPalCommerceGateway.data_client_id.has_subscriptions && PayPalCommerceGateway.data_client_id.paypal_subscriptions_enabled ) { - this.renderer.render(actionHandler.subscriptionsConfiguration(), {}, actionHandler.configuration()); + let subscription_plan_id = PayPalCommerceGateway.subscription_plan_id + if(PayPalCommerceGateway.variable_paypal_subscription_variation_from_cart !== '') { + subscription_plan_id = PayPalCommerceGateway.variable_paypal_subscription_variation_from_cart + } + this.renderer.render(actionHandler.subscriptionsConfiguration(subscription_plan_id), {}, actionHandler.configuration()); if(!PayPalCommerceGateway.subscription_product_allowed) { this.gateway.button.is_disabled = true; @@ -116,6 +120,14 @@ class CheckoutBootstap { return; } + if( + PayPalCommerceGateway.is_free_trial_cart + && PayPalCommerceGateway.vault_v3_enabled + ) { + this.renderer.render(actionHandler.addPaymentMethodConfiguration(), {}, actionHandler.configuration()); + return; + } + this.renderer.render(actionHandler.configuration(), {}, actionHandler.configuration()); } diff --git a/modules/ppcp-button/resources/js/modules/Helper/ScriptLoading.js b/modules/ppcp-button/resources/js/modules/Helper/ScriptLoading.js index cbe867ae6..c2b7a6dda 100644 --- a/modules/ppcp-button/resources/js/modules/Helper/ScriptLoading.js +++ b/modules/ppcp-button/resources/js/modules/Helper/ScriptLoading.js @@ -60,6 +60,13 @@ export const loadPaypalScript = (config, onLoaded, onError = null) => { scriptOptions = merge(scriptOptions, config.script_attributes); } + // Axo SDK options + const sdkClientToken = config?.axo?.sdk_client_token; + if(sdkClientToken) { + scriptOptions['data-sdk-client-token'] = sdkClientToken; + scriptOptions['data-client-metadata-id'] = 'ppcp-cm-id'; + } + // Load PayPal script for special case with data-client-token if (config.data_client_id?.set_attribute) { dataClientIdAttributeHandler(scriptOptions, config.data_client_id, callback, errorCallback); diff --git a/modules/ppcp-button/services.php b/modules/ppcp-button/services.php index a812dc53e..6f781911b 100644 --- a/modules/ppcp-button/services.php +++ b/modules/ppcp-button/services.php @@ -145,6 +145,8 @@ return array( $container->get( 'button.early-wc-checkout-validation-enabled' ), $container->get( 'button.pay-now-contexts' ), $container->get( 'wcgateway.funding-sources-without-redirect' ), + $container->get( 'vaulting.vault-v3-enabled' ), + $container->get( 'api.endpoint.payment-tokens' ), $container->get( 'woocommerce.logger.woocommerce' ) ); }, diff --git a/modules/ppcp-button/src/Assets/SmartButton.php b/modules/ppcp-button/src/Assets/SmartButton.php index 83092d9f5..3eb12a900 100644 --- a/modules/ppcp-button/src/Assets/SmartButton.php +++ b/modules/ppcp-button/src/Assets/SmartButton.php @@ -14,6 +14,7 @@ use Psr\Log\LoggerInterface; use WC_Order; use WC_Product; use WC_Product_Variation; +use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentTokensEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Entity\Money; use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentToken; use WooCommerce\PayPalCommerce\ApiClient\Factory\PayerFactory; @@ -34,6 +35,9 @@ use WooCommerce\PayPalCommerce\Button\Helper\MessagesApply; use WooCommerce\PayPalCommerce\Onboarding\Environment; use WooCommerce\PayPalCommerce\PayLaterBlock\PayLaterBlockModule; use WooCommerce\PayPalCommerce\PayLaterWCBlocks\PayLaterWCBlocksModule; +use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreatePaymentToken; +use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreateSetupToken; +use WooCommerce\PayPalCommerce\SavePaymentMethods\Endpoint\CreatePaymentTokenForGuest; use WooCommerce\PayPalCommerce\Session\SessionHandler; use WooCommerce\PayPalCommerce\WcSubscriptions\FreeTrialHandlerTrait; use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper; @@ -185,13 +189,6 @@ class SmartButton implements SmartButtonInterface { */ private $funding_sources_without_redirect; - /** - * The logger. - * - * @var LoggerInterface - */ - private $logger; - /** * Session handler. * @@ -199,6 +196,27 @@ class SmartButton implements SmartButtonInterface { */ private $session_handler; + /** + * Whether Vault v3 module is enabled. + * + * @var bool + */ + private $vault_v3_enabled; + + /** + * Payment tokens endpoint. + * + * @var PaymentTokensEndpoint + */ + private $payment_tokens_endpoint; + + /** + * The logger. + * + * @var LoggerInterface + */ + private $logger; + /** * SmartButton constructor. * @@ -221,6 +239,8 @@ class SmartButton implements SmartButtonInterface { * @param bool $early_validation_enabled Whether to execute WC validation of the checkout form. * @param array $pay_now_contexts The contexts that should have the Pay Now button. * @param string[] $funding_sources_without_redirect The sources that do not cause issues about redirecting (on mobile, ...) and sometimes not returning back. + * @param bool $vault_v3_enabled Whether Vault v3 module is enabled. + * @param PaymentTokensEndpoint $payment_tokens_endpoint Payment tokens endpoint. * @param LoggerInterface $logger The logger. */ public function __construct( @@ -243,6 +263,8 @@ class SmartButton implements SmartButtonInterface { bool $early_validation_enabled, array $pay_now_contexts, array $funding_sources_without_redirect, + bool $vault_v3_enabled, + PaymentTokensEndpoint $payment_tokens_endpoint, LoggerInterface $logger ) { @@ -265,7 +287,9 @@ class SmartButton implements SmartButtonInterface { $this->early_validation_enabled = $early_validation_enabled; $this->pay_now_contexts = $pay_now_contexts; $this->funding_sources_without_redirect = $funding_sources_without_redirect; + $this->vault_v3_enabled = $vault_v3_enabled; $this->logger = $logger; + $this->payment_tokens_endpoint = $payment_tokens_endpoint; } /** @@ -1062,45 +1086,59 @@ document.querySelector("#payment").before(document.querySelector(".ppcp-messages 'redirect' => wc_get_checkout_url(), 'context' => $this->context(), 'ajax' => array( - 'simulate_cart' => array( + 'simulate_cart' => array( 'endpoint' => \WC_AJAX::get_endpoint( SimulateCartEndpoint::ENDPOINT ), 'nonce' => wp_create_nonce( SimulateCartEndpoint::nonce() ), ), - 'change_cart' => array( + 'change_cart' => array( 'endpoint' => \WC_AJAX::get_endpoint( ChangeCartEndpoint::ENDPOINT ), 'nonce' => wp_create_nonce( ChangeCartEndpoint::nonce() ), ), - 'create_order' => array( + 'create_order' => array( 'endpoint' => \WC_AJAX::get_endpoint( CreateOrderEndpoint::ENDPOINT ), 'nonce' => wp_create_nonce( CreateOrderEndpoint::nonce() ), ), - 'approve_order' => array( + 'approve_order' => array( 'endpoint' => \WC_AJAX::get_endpoint( ApproveOrderEndpoint::ENDPOINT ), 'nonce' => wp_create_nonce( ApproveOrderEndpoint::nonce() ), ), - 'approve_subscription' => array( + 'approve_subscription' => array( 'endpoint' => \WC_AJAX::get_endpoint( ApproveSubscriptionEndpoint::ENDPOINT ), 'nonce' => wp_create_nonce( ApproveSubscriptionEndpoint::nonce() ), ), - 'vault_paypal' => array( + 'vault_paypal' => array( 'endpoint' => \WC_AJAX::get_endpoint( StartPayPalVaultingEndpoint::ENDPOINT ), 'nonce' => wp_create_nonce( StartPayPalVaultingEndpoint::nonce() ), ), - 'save_checkout_form' => array( + 'save_checkout_form' => array( 'endpoint' => \WC_AJAX::get_endpoint( SaveCheckoutFormEndpoint::ENDPOINT ), 'nonce' => wp_create_nonce( SaveCheckoutFormEndpoint::nonce() ), ), - 'validate_checkout' => array( + 'validate_checkout' => array( 'endpoint' => \WC_AJAX::get_endpoint( ValidateCheckoutEndpoint::ENDPOINT ), 'nonce' => wp_create_nonce( ValidateCheckoutEndpoint::nonce() ), ), - 'cart_script_params' => array( + 'cart_script_params' => array( 'endpoint' => \WC_AJAX::get_endpoint( CartScriptParamsEndpoint::ENDPOINT ), ), + 'create_setup_token' => array( + 'endpoint' => \WC_AJAX::get_endpoint( CreateSetupToken::ENDPOINT ), + 'nonce' => wp_create_nonce( CreateSetupToken::nonce() ), + ), + 'create_payment_token' => array( + 'endpoint' => \WC_AJAX::get_endpoint( CreatePaymentToken::ENDPOINT ), + 'nonce' => wp_create_nonce( CreatePaymentToken::nonce() ), + ), + 'create_payment_token_for_guest' => array( + 'endpoint' => \WC_AJAX::get_endpoint( CreatePaymentTokenForGuest::ENDPOINT ), + 'nonce' => wp_create_nonce( CreatePaymentTokenForGuest::nonce() ), + ), ), 'cart_contains_subscription' => $this->subscription_helper->cart_contains_subscription(), 'subscription_plan_id' => $this->subscription_helper->paypal_subscription_id(), + 'vault_v3_enabled' => $this->vault_v3_enabled, 'variable_paypal_subscription_variations' => $this->subscription_helper->variable_paypal_subscription_variations(), + 'variable_paypal_subscription_variation_from_cart' => $this->subscription_helper->paypal_subscription_variation_from_cart(), 'subscription_product_allowed' => $this->subscription_helper->checkout_subscription_product_allowed(), 'locations_with_subscription_product' => $this->subscription_helper->locations_with_subscription_product(), 'enforce_vault' => $this->has_subscriptions(), @@ -1931,8 +1969,18 @@ document.querySelector("#payment").before(document.querySelector(".ppcp-messages */ private function get_vaulted_paypal_email(): string { try { - $tokens = $this->get_payment_tokens(); + $customer_id = get_user_meta( get_current_user_id(), '_ppcp_target_customer_id', true ); + if ( $customer_id ) { + $customer_tokens = $this->payment_tokens_endpoint->payment_tokens_for_customer( $customer_id ); + foreach ( $customer_tokens as $token ) { + $email_address = $token['payment_source']->properties()->email_address ?? ''; + if ( $email_address ) { + return $email_address; + } + } + } + $tokens = $this->get_payment_tokens(); foreach ( $tokens as $token ) { if ( isset( $token->source()->paypal ) ) { return $token->source()->paypal->payer->email_address; @@ -1941,6 +1989,7 @@ document.querySelector("#payment").before(document.querySelector(".ppcp-messages } catch ( Exception $exception ) { $this->logger->error( 'Failed to get PayPal vaulted email. ' . $exception->getMessage() ); } + return ''; } diff --git a/modules/ppcp-googlepay/assets/images/googlepay.png b/modules/ppcp-googlepay/assets/images/googlepay.png new file mode 100644 index 000000000..b264fd0ee Binary files /dev/null and b/modules/ppcp-googlepay/assets/images/googlepay.png differ diff --git a/modules/ppcp-googlepay/extensions.php b/modules/ppcp-googlepay/extensions.php index fe915aba4..31b65e6f1 100644 --- a/modules/ppcp-googlepay/extensions.php +++ b/modules/ppcp-googlepay/extensions.php @@ -39,6 +39,8 @@ return array( $display_manager = $container->get( 'wcgateway.display-manager' ); assert( $display_manager instanceof DisplayManager ); + $module_url = $container->get( 'googlepay.url' ); + // Connection tab fields. $fields = $insert_after( $fields, @@ -62,10 +64,15 @@ return array( $connection_link = ''; return $insert_after( $fields, - 'allow_card_button_gateway', + 'digital_wallet_heading', array( 'googlepay_button_enabled' => array( 'title' => __( 'Google Pay Button', 'woocommerce-paypal-payments' ), + 'title_html' => sprintf( + '%s', + $module_url, + __( 'Google Pay', 'woocommerce-paypal-payments' ) + ), 'type' => 'checkbox', 'class' => array( 'ppcp-grayed-out-text' ), 'input_class' => array( 'ppcp-disabled-checkbox' ), @@ -80,7 +87,7 @@ return array( . '

', 'default' => 'yes', 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', + 'gateway' => 'dcc', 'requirements' => array(), 'custom_attributes' => array( 'data-ppcp-display' => wp_json_encode( @@ -93,6 +100,7 @@ return array( ) ), ), + 'classes' => array( 'ppcp-valign-label-middle', 'ppcp-align-label-center' ), ), ) ); @@ -101,10 +109,15 @@ return array( // Standard Payments tab fields. return $insert_after( $fields, - 'allow_card_button_gateway', + 'digital_wallet_heading', array( 'googlepay_button_enabled' => array( 'title' => __( 'Google Pay Button', 'woocommerce-paypal-payments' ), + 'title_html' => sprintf( + '%s', + $module_url, + __( 'Google Pay', 'woocommerce-paypal-payments' ) + ), 'type' => 'checkbox', 'label' => __( 'Enable Google Pay button', 'woocommerce-paypal-payments' ) . '

' @@ -117,7 +130,7 @@ return array( . '

', 'default' => 'yes', 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', + 'gateway' => 'dcc', 'requirements' => array(), 'custom_attributes' => array( 'data-ppcp-display' => wp_json_encode( @@ -129,10 +142,12 @@ return array( ->action_visible( 'googlepay_button_color' ) ->action_visible( 'googlepay_button_language' ) ->action_visible( 'googlepay_button_shipping_enabled' ) + ->action_class( 'googlepay_button_enabled', 'active' ) ->to_array(), ) ), ), + 'classes' => array( 'ppcp-valign-label-middle', 'ppcp-align-label-center' ), ), 'googlepay_button_type' => array( 'title' => __( 'Button Label', 'woocommerce-paypal-payments' ), @@ -148,7 +163,7 @@ return array( 'default' => 'pay', 'options' => PropertiesDictionary::button_types(), 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', + 'gateway' => 'dcc', 'requirements' => array(), ), 'googlepay_button_color' => array( @@ -166,7 +181,7 @@ return array( 'default' => 'black', 'options' => PropertiesDictionary::button_colors(), 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', + 'gateway' => 'dcc', 'requirements' => array(), ), 'googlepay_button_language' => array( @@ -183,7 +198,7 @@ return array( 'default' => 'en', 'options' => PropertiesDictionary::button_languages(), 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', + 'gateway' => 'dcc', 'requirements' => array(), ), 'googlepay_button_shipping_enabled' => array( @@ -198,7 +213,7 @@ return array( 'label' => __( 'Enable Google Pay shipping callback', 'woocommerce-paypal-payments' ), 'default' => 'no', 'screens' => array( State::STATE_ONBOARDED ), - 'gateway' => 'paypal', + 'gateway' => 'dcc', 'requirements' => array(), ), ) diff --git a/modules/ppcp-googlepay/resources/js/GooglepayButton.js b/modules/ppcp-googlepay/resources/js/GooglepayButton.js index 3331a4d49..00b8f7f7d 100644 --- a/modules/ppcp-googlepay/resources/js/GooglepayButton.js +++ b/modules/ppcp-googlepay/resources/js/GooglepayButton.js @@ -28,7 +28,7 @@ class GooglepayButton { this.log = function() { if ( this.buttonConfig.is_debug ) { - console.log('[GooglePayButton]', ...arguments); + //console.log('[GooglePayButton]', ...arguments); } } } diff --git a/modules/ppcp-onboarding/src/Render/OnboardingOptionsRenderer.php b/modules/ppcp-onboarding/src/Render/OnboardingOptionsRenderer.php index a5d7628ec..df99653f8 100644 --- a/modules/ppcp-onboarding/src/Render/OnboardingOptionsRenderer.php +++ b/modules/ppcp-onboarding/src/Render/OnboardingOptionsRenderer.php @@ -136,7 +136,10 @@ class OnboardingOptionsRenderer { __( 'For Standard payments, Casual sellers may connect their Personal PayPal account in eligible countries to sell on WooCommerce. For Advanced payments, a Business PayPal account is required.', 'woocommerce-paypal-payments' ) ), ); - $items[] = ' + + $basic_table_rows = apply_filters( 'ppcp_onboarding_basic_table_rows', $basic_table_rows ); + + $items[] = '
  • tag. - __( 'Securely store your customers’ credit cards for a seamless checkout experience and subscription features. Payment methods are saved in the secure %1$sPayPal Vault%2$s.', 'woocommerce-paypal-payments' ), - '', - '' - ), - 'description' => __( 'Allow registered buyers to save Credit Card payments.', 'woocommerce-paypal-payments' ), - 'default' => false, - 'screens' => array( - State::STATE_ONBOARDED, - ), - 'requirements' => array(), - 'gateway' => 'dcc', - 'input_class' => $container->get( 'wcgateway.helper.vaulting-scope' ) ? array() : array( 'ppcp-disabled-checkbox' ), - ), '3d_secure_heading' => array( 'heading' => __( '3D Secure', 'woocommerce-paypal-payments' ), 'type' => 'ppcp-heading', @@ -881,6 +866,52 @@ return array( ), 'gateway' => 'dcc', ), + 'saved_payments_heading' => array( + 'heading' => __( 'Saved Payments', 'woocommerce-paypal-payments' ), + 'type' => 'ppcp-heading', + 'description' => wp_kses_post( + sprintf( + // translators: %1$s and %2$s is a link tag. + __( + 'PayPal can securely store your customers’ payment methods for + %1$sfuture payments and subscriptions%2$s, simplifying the checkout + process and enabling recurring transactions on your website.', + 'woocommerce-paypal-payments' + ), + '', + '' + ) + ), + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array( + 'dcc', + ), + 'gateway' => 'dcc', + ), + 'vault_enabled_dcc' => array( + 'title' => __( 'Vaulting', 'woocommerce-paypal-payments' ), + 'type' => 'checkbox', + 'desc_tip' => true, + 'label' => sprintf( + // translators: %1$s and %2$s are the opening and closing of HTML tag. + __( 'Securely store your customers’ credit cards for a seamless checkout experience and subscription features. Payment methods are saved in the secure %1$sPayPal Vault%2$s.', 'woocommerce-paypal-payments' ), + '', + '' + ), + 'description' => __( 'Allow registered buyers to save Credit Card payments.', 'woocommerce-paypal-payments' ), + 'default' => false, + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array(), + 'gateway' => 'dcc', + 'input_class' => $container->get( 'wcgateway.helper.vaulting-scope' ) ? array() : array( 'ppcp-disabled-checkbox' ), + ), 'paypal_saved_payments' => array( 'heading' => __( 'Saved payments', 'woocommerce-paypal-payments' ), 'description' => sprintf( @@ -919,6 +950,32 @@ return array( 'gateway' => 'paypal', 'input_class' => $container->get( 'wcgateway.helper.vaulting-scope' ) ? array() : array( 'ppcp-disabled-checkbox' ), ), + 'digital_wallet_heading' => array( + 'heading' => __( 'Digital Wallet Services', 'woocommerce-paypal-payments' ), + 'type' => 'ppcp-heading', + 'description' => wp_kses_post( + sprintf( + // translators: %1$s and %2$s is a link tag. + __( + 'PayPal supports digital wallet services like Apple Pay or Google Pay + to give your buyers more options to pay without a PayPal account.', + 'woocommerce-paypal-payments' + ), + '', + '' + ) + ), + 'screens' => array( + State::STATE_ONBOARDED, + ), + 'requirements' => array( + 'dcc', + ), + 'gateway' => 'dcc', + ), ); if ( ! $subscription_helper->plugin_is_active() ) { @@ -1506,6 +1563,7 @@ return array( PayUponInvoiceGateway::ID, CardButtonGateway::ID, OXXOGateway::ID, + AxoGateway::ID, ); }, 'wcgateway.gateway-repository' => static function ( ContainerInterface $container ): GatewayRepository { diff --git a/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php b/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php index 4032869d1..6e21e70df 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/CreditCardGateway.php @@ -33,6 +33,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Processor\PaymentsStatusHandlingTrait; use WooCommerce\PayPalCommerce\WcGateway\Processor\RefundProcessor; use WooCommerce\PayPalCommerce\WcGateway\Processor\TransactionIdHandlingTrait; use WooCommerce\PayPalCommerce\WcGateway\Settings\SettingsRenderer; +use WooCommerce\PayPalCommerce\WcSubscriptions\FreeTrialHandlerTrait; use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper; /** @@ -40,7 +41,7 @@ use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper; */ class CreditCardGateway extends \WC_Payment_Gateway_CC { - use ProcessPaymentTrait, GatewaySettingsRendererTrait, TransactionIdHandlingTrait, PaymentsStatusHandlingTrait; + use ProcessPaymentTrait, GatewaySettingsRendererTrait, TransactionIdHandlingTrait, PaymentsStatusHandlingTrait, FreeTrialHandlerTrait; const ID = 'ppcp-credit-card-gateway'; @@ -454,6 +455,17 @@ class CreditCardGateway extends \WC_Payment_Gateway_CC { // phpcs:ignore WordPress.Security.NonceVerification.Missing $card_payment_token_id = wc_clean( wp_unslash( $_POST['wc-ppcp-credit-card-gateway-payment-token'] ?? '' ) ); + + if ( $this->is_free_trial_order( $wc_order ) && $card_payment_token_id ) { + $customer_tokens = $this->wc_payment_tokens->customer_tokens( get_current_user_id() ); + foreach ( $customer_tokens as $token ) { + if ( $token['payment_source']->name() === 'card' ) { + $wc_order->payment_complete(); + return $this->handle_payment_success( $wc_order ); + } + } + } + if ( $card_payment_token_id ) { $customer_tokens = $this->wc_payment_tokens->customer_tokens( get_current_user_id() ); diff --git a/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php b/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php index 19ebe9d08..6c6568b8d 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/PayPalGateway.php @@ -14,12 +14,14 @@ use Psr\Log\LoggerInterface; use WC_Order; use WC_Payment_Tokens; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint; +use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentTokensEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus; use WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentToken; use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException; use WooCommerce\PayPalCommerce\Onboarding\Environment; use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Session\SessionHandler; +use WooCommerce\PayPalCommerce\Vaulting\WooCommercePaymentTokens; use WooCommerce\PayPalCommerce\WcSubscriptions\FreeTrialHandlerTrait; use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper; use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenRepository; @@ -179,26 +181,50 @@ class PayPalGateway extends \WC_Payment_Gateway { */ private $paypal_checkout_url_factory; + /** + * Payment tokens endpoint. + * + * @var PaymentTokensEndpoint + */ + private $payment_tokens_endpoint; + + /** + * Whether Vault v3 module is enabled. + * + * @var bool + */ + private $vault_v3_enabled; + + /** + * WooCommerce payment tokens. + * + * @var WooCommercePaymentTokens + */ + private $wc_payment_tokens; + /** * PayPalGateway constructor. * - * @param SettingsRenderer $settings_renderer The Settings Renderer. - * @param FundingSourceRenderer $funding_source_renderer The funding source renderer. - * @param OrderProcessor $order_processor The Order Processor. - * @param ContainerInterface $config The settings. - * @param SessionHandler $session_handler The Session Handler. - * @param RefundProcessor $refund_processor The Refund Processor. - * @param State $state The state. - * @param TransactionUrlProvider $transaction_url_provider Service providing transaction view URL based on order. - * @param SubscriptionHelper $subscription_helper The subscription helper. - * @param string $page_id ID of the current PPCP gateway settings page, or empty if it is not such page. - * @param Environment $environment The environment. - * @param PaymentTokenRepository $payment_token_repository The payment token repository. - * @param LoggerInterface $logger The logger. - * @param string $api_shop_country The api shop country. - * @param OrderEndpoint $order_endpoint The order endpoint. - * @param callable(string):string $paypal_checkout_url_factory The function return the PayPal checkout URL for the given order ID. - * @param string $place_order_button_text The text for the standard "Place order" button. + * @param SettingsRenderer $settings_renderer The Settings Renderer. + * @param FundingSourceRenderer $funding_source_renderer The funding source renderer. + * @param OrderProcessor $order_processor The Order Processor. + * @param ContainerInterface $config The settings. + * @param SessionHandler $session_handler The Session Handler. + * @param RefundProcessor $refund_processor The Refund Processor. + * @param State $state The state. + * @param TransactionUrlProvider $transaction_url_provider Service providing transaction view URL based on order. + * @param SubscriptionHelper $subscription_helper The subscription helper. + * @param string $page_id ID of the current PPCP gateway settings page, or empty if it is not such page. + * @param Environment $environment The environment. + * @param PaymentTokenRepository $payment_token_repository The payment token repository. + * @param LoggerInterface $logger The logger. + * @param string $api_shop_country The api shop country. + * @param OrderEndpoint $order_endpoint The order endpoint. + * @param callable(string):string $paypal_checkout_url_factory The function return the PayPal checkout URL for the given order ID. + * @param string $place_order_button_text The text for the standard "Place order" button. + * @param PaymentTokensEndpoint $payment_tokens_endpoint Payment tokens endpoint. + * @param bool $vault_v3_enabled Whether Vault v3 module is enabled. + * @param WooCommercePaymentTokens $wc_payment_tokens WooCommerce payment tokens. */ public function __construct( SettingsRenderer $settings_renderer, @@ -217,7 +243,10 @@ class PayPalGateway extends \WC_Payment_Gateway { string $api_shop_country, OrderEndpoint $order_endpoint, callable $paypal_checkout_url_factory, - string $place_order_button_text + string $place_order_button_text, + PaymentTokensEndpoint $payment_tokens_endpoint, + bool $vault_v3_enabled, + WooCommercePaymentTokens $wc_payment_tokens ) { $this->id = self::ID; $this->settings_renderer = $settings_renderer; @@ -237,6 +266,10 @@ class PayPalGateway extends \WC_Payment_Gateway { $this->api_shop_country = $api_shop_country; $this->paypal_checkout_url_factory = $paypal_checkout_url_factory; $this->order_button_text = $place_order_button_text; + $this->order_endpoint = $order_endpoint; + $this->payment_tokens_endpoint = $payment_tokens_endpoint; + $this->vault_v3_enabled = $vault_v3_enabled; + $this->wc_payment_tokens = $wc_payment_tokens; if ( $this->onboarded ) { $this->supports = array( 'refunds', 'tokenization' ); @@ -265,6 +298,8 @@ class PayPalGateway extends \WC_Payment_Gateway { 'subscription_payment_method_change_admin', 'multiple_subscriptions' ); + } elseif ( $this->config->has( 'subscriptions_mode' ) && $this->config->get( 'subscriptions_mode' ) === 'subscriptions_api' ) { + $this->supports[] = 'gateway_scheduled_payments'; } elseif ( $this->config->has( 'vault_enabled_dcc' ) && $this->config->get( 'vault_enabled_dcc' ) ) { $this->supports[] = 'tokenization'; } @@ -299,8 +334,6 @@ class PayPalGateway extends \WC_Payment_Gateway { 'process_admin_options', ) ); - - $this->order_endpoint = $order_endpoint; } /** @@ -500,7 +533,49 @@ class PayPalGateway extends \WC_Payment_Gateway { $wc_order->save(); } - if ( 'card' !== $funding_source && $this->is_free_trial_order( $wc_order ) && ! $this->subscription_helper->paypal_subscription_id() ) { + if ( + 'card' !== $funding_source + && $this->is_free_trial_order( $wc_order ) + && ! $this->subscription_helper->paypal_subscription_id() + ) { + $ppcp_guest_payment_for_free_trial = WC()->session->get( 'ppcp_guest_payment_for_free_trial' ) ?? null; + if ( $this->vault_v3_enabled && $ppcp_guest_payment_for_free_trial ) { + $customer_id = $ppcp_guest_payment_for_free_trial->customer->id ?? ''; + if ( $customer_id ) { + update_user_meta( $wc_order->get_customer_id(), '_ppcp_target_customer_id', $customer_id ); + } + + if ( isset( $ppcp_guest_payment_for_free_trial->payment_source->paypal ) ) { + $email = ''; + if ( isset( $ppcp_guest_payment_for_free_trial->payment_source->paypal->email_address ) ) { + $email = $ppcp_guest_payment_for_free_trial->payment_source->paypal->email_address; + } + + $this->wc_payment_tokens->create_payment_token_paypal( + $wc_order->get_customer_id(), + $ppcp_guest_payment_for_free_trial->id, + $email + ); + } + + WC()->session->set( 'ppcp_guest_payment_for_free_trial', null ); + + $wc_order->payment_complete(); + return $this->handle_payment_success( $wc_order ); + } + + $customer_id = get_user_meta( $wc_order->get_customer_id(), '_ppcp_target_customer_id', true ); + if ( $customer_id ) { + $customer_tokens = $this->payment_tokens_endpoint->payment_tokens_for_customer( $customer_id ); + foreach ( $customer_tokens as $token ) { + $payment_source_name = $token['payment_source']->name() ?? ''; + if ( $payment_source_name === 'paypal' || $payment_source_name === 'venmo' ) { + $wc_order->payment_complete(); + return $this->handle_payment_success( $wc_order ); + } + } + } + $user_id = (int) $wc_order->get_customer_id(); $tokens = $this->payment_token_repository->all_for_user_id( $user_id ); if ( ! array_filter( @@ -513,7 +588,6 @@ class PayPalGateway extends \WC_Payment_Gateway { } $wc_order->payment_complete(); - return $this->handle_payment_success( $wc_order ); } diff --git a/modules/ppcp-wc-gateway/src/Helper/DisplayRule.php b/modules/ppcp-wc-gateway/src/Helper/DisplayRule.php index 807ab4b36..8a9b351d2 100644 --- a/modules/ppcp-wc-gateway/src/Helper/DisplayRule.php +++ b/modules/ppcp-wc-gateway/src/Helper/DisplayRule.php @@ -16,8 +16,9 @@ use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; */ class DisplayRule { - const CONDITION_TYPE_ELEMENT = 'element'; - const CONDITION_TYPE_BOOL = 'bool'; + const CONDITION_TYPE_ELEMENT = 'element'; + const CONDITION_TYPE_BOOL = 'bool'; + const CONDITION_TYPE_JS_VARIABLE = 'js_variable'; const CONDITION_OPERATION_EQUALS = 'equals'; const CONDITION_OPERATION_NOT_EQUALS = 'not_equals'; @@ -26,10 +27,12 @@ class DisplayRule { const CONDITION_OPERATION_EMPTY = 'empty'; const CONDITION_OPERATION_NOT_EMPTY = 'not_empty'; - const ACTION_TYPE_ELEMENT = 'element'; + const ACTION_TYPE_VISIBILITY = 'visibility'; + const ACTION_TYPE_ATTRIBUTE = 'attribute'; const ACTION_VISIBLE = 'visible'; const ACTION_ENABLE = 'enable'; + const ACTION_CLASS = 'class'; /** * The element selector. @@ -132,6 +135,24 @@ class DisplayRule { return $this; } + /** + * Adds a condition related to js variable check. + * + * @param string $variable_name The javascript variable name. + * @param mixed $value The value to enable / disable the condition. + * @return self + */ + public function condition_js_variable( string $variable_name, $value ): self { + $this->add_condition( + array( + 'type' => self::CONDITION_TYPE_JS_VARIABLE, + 'variable' => $variable_name, + 'value' => $value, + ) + ); + return $this; + } + /** * Adds a condition to show/hide the element. * @@ -140,7 +161,7 @@ class DisplayRule { public function action_visible( string $selector ): self { $this->add_action( array( - 'type' => self::ACTION_TYPE_ELEMENT, + 'type' => self::ACTION_TYPE_VISIBILITY, 'selector' => $selector, 'action' => self::ACTION_VISIBLE, ) @@ -148,6 +169,24 @@ class DisplayRule { return $this; } + /** + * Adds a condition to add/remove a html class. + * + * @param string $selector The condition selector. + * @param string $class The class. + */ + public function action_class( string $selector, string $class ): self { + $this->add_action( + array( + 'type' => self::ACTION_TYPE_ATTRIBUTE, + 'selector' => $selector, + 'html_class' => $class, + 'action' => self::ACTION_CLASS, + ) + ); + return $this; + } + /** * Adds a condition to enable/disable the element. * @@ -156,7 +195,7 @@ class DisplayRule { public function action_enable( string $selector ): self { $this->add_action( array( - 'type' => self::ACTION_TYPE_ELEMENT, + 'type' => self::ACTION_TYPE_VISIBILITY, 'selector' => $selector, 'action' => self::ACTION_ENABLE, ) diff --git a/modules/ppcp-wc-gateway/src/Processor/CreditCardOrderInfoHandlingTrait.php b/modules/ppcp-wc-gateway/src/Processor/CreditCardOrderInfoHandlingTrait.php index 4cd9e07ed..51a3a741c 100644 --- a/modules/ppcp-wc-gateway/src/Processor/CreditCardOrderInfoHandlingTrait.php +++ b/modules/ppcp-wc-gateway/src/Processor/CreditCardOrderInfoHandlingTrait.php @@ -140,7 +140,13 @@ trait CreditCardOrderInfoHandlingTrait { ); $wc_order->add_order_note( $cvv_response_order_note ); - $meta_details = array_merge( $fraud_responses, array( 'card_brand' => $card_brand ) ); + $meta_details = array_merge( + $fraud_responses, + array( + 'card_brand' => $card_brand, + 'card_last_digits' => $card_last_digits, + ) + ); $wc_order->update_meta_data( PayPalGateway::FRAUD_RESULT_META_KEY, $meta_details ); $wc_order->save_meta_data(); diff --git a/modules/ppcp-wc-gateway/src/Processor/OrderProcessor.php b/modules/ppcp-wc-gateway/src/Processor/OrderProcessor.php index 26defd836..d2b145b0b 100644 --- a/modules/ppcp-wc-gateway/src/Processor/OrderProcessor.php +++ b/modules/ppcp-wc-gateway/src/Processor/OrderProcessor.php @@ -270,6 +270,40 @@ class OrderProcessor { do_action( 'woocommerce_paypal_payments_after_order_processor', $wc_order, $order ); } + /** + * Processes a given WooCommerce order and captured/authorizes the connected PayPal orders. + * + * @param WC_Order $wc_order The WooCommerce order. + * @param Order $order The PayPal order. + * + * @throws Exception If processing fails. + */ + public function process_captured_and_authorized( WC_Order $wc_order, Order $order ): void { + $this->add_paypal_meta( $wc_order, $order, $this->environment ); + + if ( $order->intent() === 'AUTHORIZE' ) { + $wc_order->update_meta_data( AuthorizedPaymentsProcessor::CAPTURED_META_KEY, 'false' ); + + if ( $this->subscription_helper->has_subscription( $wc_order->get_id() ) ) { + $wc_order->update_meta_data( '_ppcp_captured_vault_webhook', 'false' ); + } + } + + $transaction_id = $this->get_paypal_order_transaction_id( $order ); + + if ( $transaction_id ) { + $this->update_transaction_id( $transaction_id, $wc_order ); + } + + $this->handle_new_order_status( $order, $wc_order ); + + if ( $this->capture_authorized_downloads( $order ) ) { + $this->authorized_payments_processor->capture_authorized_payment( $wc_order ); + } + + do_action( 'woocommerce_paypal_payments_after_order_processor', $wc_order, $order ); + } + /** * Creates a PayPal order for the given WC order. * diff --git a/modules/ppcp-wc-gateway/src/Settings/Fields/connection-tab-fields.php b/modules/ppcp-wc-gateway/src/Settings/Fields/connection-tab-fields.php index 25a367693..73a17081b 100644 --- a/modules/ppcp-wc-gateway/src/Settings/Fields/connection-tab-fields.php +++ b/modules/ppcp-wc-gateway/src/Settings/Fields/connection-tab-fields.php @@ -436,7 +436,7 @@ return function ( ContainerInterface $container, array $fields ): array { 'desc_tip' => true, 'label' => $container->get( 'wcgateway.settings.fraudnet-label' ), 'description' => __( 'FraudNet is a JavaScript library developed by PayPal and embedded into a merchant’s web page to collect browser-based data to help reduce fraud.', 'woocommerce-paypal-payments' ), - 'default' => false, + 'default' => true, 'screens' => array( State::STATE_ONBOARDED, ), @@ -522,8 +522,8 @@ return function ( ContainerInterface $container, array $fields ): array { 'woocommerce-paypal-payments' ), 'options' => array( - PurchaseUnitSanitizer::MODE_DITCH => __( 'Do not send line items to PayPal', 'woocommerce-paypal-payments' ), PurchaseUnitSanitizer::MODE_EXTRA_LINE => __( 'Add another line item', 'woocommerce-paypal-payments' ), + PurchaseUnitSanitizer::MODE_DITCH => __( 'Do not send line items to PayPal', 'woocommerce-paypal-payments' ), ), 'screens' => array( State::STATE_START, @@ -538,6 +538,7 @@ return function ( ContainerInterface $container, array $fields ): array { ->rule() ->condition_element( 'subtotal_mismatch_behavior', PurchaseUnitSanitizer::MODE_EXTRA_LINE ) ->action_visible( 'subtotal_mismatch_line_name' ) + ->action_class( 'subtotal_mismatch_behavior', 'active' ) ->to_array(), ) ), @@ -548,6 +549,7 @@ return function ( ContainerInterface $container, array $fields ): array { 'type' => 'text', 'desc_tip' => true, 'description' => __( 'The name of the extra line that will be sent to PayPal to correct the subtotal mismatch.', 'woocommerce-paypal-payments' ), + 'classes' => array( 'ppcp-field-indent' ), 'maxlength' => 22, 'default' => '', 'screens' => array( diff --git a/modules/ppcp-wc-gateway/src/Settings/Fields/paypal-smart-button-fields.php b/modules/ppcp-wc-gateway/src/Settings/Fields/paypal-smart-button-fields.php index 93c0ac8e7..83363c08a 100644 --- a/modules/ppcp-wc-gateway/src/Settings/Fields/paypal-smart-button-fields.php +++ b/modules/ppcp-wc-gateway/src/Settings/Fields/paypal-smart-button-fields.php @@ -91,7 +91,7 @@ return function ( ContainerInterface $container, array $fields ): array { 'title' => __( 'Customize Smart Buttons Per Location', 'woocommerce-paypal-payments' ), 'type' => 'checkbox', 'label' => __( 'Customize smart button style per location', 'woocommerce-paypal-payments' ), - 'default' => true, + 'default' => false, 'screens' => array( State::STATE_START, State::STATE_ONBOARDED ), 'requirements' => array(), 'gateway' => 'paypal', diff --git a/modules/ppcp-wc-gateway/src/Settings/Settings.php b/modules/ppcp-wc-gateway/src/Settings/Settings.php index 9ba1529c2..ebd6f1081 100644 --- a/modules/ppcp-wc-gateway/src/Settings/Settings.php +++ b/modules/ppcp-wc-gateway/src/Settings/Settings.php @@ -142,7 +142,7 @@ class Settings implements ContainerInterface { 'woocommerce-paypal-payments' ), 'smart_button_locations' => $this->default_button_locations, - 'smart_button_enable_styling_per_location' => true, + 'smart_button_enable_styling_per_location' => false, 'pay_later_messaging_enabled' => true, 'pay_later_button_enabled' => true, 'pay_later_button_locations' => $this->default_pay_later_button_locations, diff --git a/modules/ppcp-wc-gateway/src/Settings/SettingsRenderer.php b/modules/ppcp-wc-gateway/src/Settings/SettingsRenderer.php index aa33dd28c..12dc70b4b 100644 --- a/modules/ppcp-wc-gateway/src/Settings/SettingsRenderer.php +++ b/modules/ppcp-wc-gateway/src/Settings/SettingsRenderer.php @@ -262,7 +262,7 @@ class SettingsRenderer { $html = sprintf( '

    %s

    ', esc_attr( implode( ' ', $config['class'] ) ), - esc_html( $config['heading'] ) + isset( $config['heading_html'] ) ? $config['heading_html'] : esc_html( $config['heading'] ) ); return $html; @@ -388,7 +388,12 @@ $data_rows_html + > + + cart ?? null; + if ( ! $cart || $cart->is_empty() ) { + return ''; + } + + $items = $cart->get_cart_contents(); + foreach ( $items as $item ) { + $variation_id = $item['variation_id'] ?? 0; + if ( $variation_id ) { + $variation_product = wc_get_product( $variation_id ) ?? ''; + if ( $variation_product && $variation_product->meta_exists( 'ppcp_subscription_plan' ) ) { + return $variation_product->get_meta( 'ppcp_subscription_plan' )['id']; + } + } + } + + return ''; + } } diff --git a/package.json b/package.json index 821beaf28..05be431d3 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "install:modules:ppcp-order-tracking": "cd modules/ppcp-order-tracking && yarn install", "install:modules:ppcp-paypal-subscriptions": "cd modules/ppcp-paypal-subscriptions && yarn install", "install:modules:ppcp-save-payment-methods": "cd modules/ppcp-save-payment-methods && yarn install", + "install:modules:ppcp-axo": "cd modules/ppcp-axo && yarn install", "install:modules:ppcp-onboarding": "cd modules/ppcp-onboarding && yarn install", "install:modules:ppcp-compat": "cd modules/ppcp-compat && yarn install", "install:modules:ppcp-uninstall": "cd modules/ppcp-uninstall && yarn install", @@ -33,6 +34,7 @@ "build:modules:ppcp-webhooks": "cd modules/ppcp-webhooks && yarn run build", "build:modules:ppcp-order-tracking": "cd modules/ppcp-order-tracking && yarn run build", "build:modules:ppcp-save-payment-methods": "cd modules/ppcp-save-payment-methods && yarn run build", + "build:modules:ppcp-axo": "cd modules/ppcp-axo && yarn run build", "build:modules:ppcp-paypal-subscriptions": "cd modules/ppcp-paypal-subscriptions && yarn run build", "build:modules:ppcp-onboarding": "cd modules/ppcp-onboarding && yarn run build", "build:modules:ppcp-compat": "cd modules/ppcp-compat && yarn run build", @@ -50,6 +52,7 @@ "watch:modules:ppcp-order-tracking": "cd modules/ppcp-order-tracking && yarn run watch", "watch:modules:ppcp-paypal-subscriptions": "cd modules/ppcp-paypal-subscriptions && yarn run watch", "watch:modules:ppcp-save-payment-methods": "cd modules/ppcp-save-payment-methods && yarn run watch", + "watch:modules:ppcp-axo": "cd modules/ppcp-axo && yarn run watch", "watch:modules:ppcp-onboarding": "cd modules/ppcp-onboarding && yarn run watch", "watch:modules:ppcp-compat": "cd modules/ppcp-compat && yarn run watch", "watch:modules:ppcp-uninstall": "cd modules/ppcp-uninstall && yarn run watch", diff --git a/tests/PHPUnit/ApiClient/Exception/PayPalApiExceptionTest.php b/tests/PHPUnit/ApiClient/Exception/PayPalApiExceptionTest.php new file mode 100644 index 000000000..37b676d81 --- /dev/null +++ b/tests/PHPUnit/ApiClient/Exception/PayPalApiExceptionTest.php @@ -0,0 +1,20 @@ +assertEquals( + 'PayPal rejected the payment. Please reach out to the PayPal support for more information.', + $testee->get_customer_friendly_message($response) + ); + } +} diff --git a/tests/PHPUnit/WcGateway/Gateway/WcGatewayTest.php b/tests/PHPUnit/WcGateway/Gateway/WcGatewayTest.php index 5239af7dc..041141492 100644 --- a/tests/PHPUnit/WcGateway/Gateway/WcGatewayTest.php +++ b/tests/PHPUnit/WcGateway/Gateway/WcGatewayTest.php @@ -6,11 +6,13 @@ namespace WooCommerce\PayPalCommerce\WcGateway\Gateway; use Exception; use Psr\Log\LoggerInterface; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint; +use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PaymentTokensEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Entity\Order; use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus; use WooCommerce\PayPalCommerce\Onboarding\Environment; use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Session\SessionHandler; +use WooCommerce\PayPalCommerce\Vaulting\WooCommercePaymentTokens; use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper; use WooCommerce\PayPalCommerce\TestCase; use WooCommerce\PayPalCommerce\Vaulting\PaymentTokenRepository; @@ -44,6 +46,9 @@ class WcGatewayTest extends TestCase private $logger; private $apiShopCountry; private $orderEndpoint; + private $paymentTokensEndpoint; + private $vaultV3Enabled; + private $wcPaymentTokens; public function setUp(): void { parent::setUp(); @@ -88,6 +93,10 @@ class WcGatewayTest extends TestCase $this->logger->shouldReceive('info'); $this->logger->shouldReceive('error'); + + $this->paymentTokensEndpoint = Mockery::mock(PaymentTokensEndpoint::class); + $this->vaultV3Enabled = true; + $this->wcPaymentTokens = Mockery::mock(WooCommercePaymentTokens::class); } private function createGateway() @@ -111,7 +120,10 @@ class WcGatewayTest extends TestCase function ($id) { return 'checkoutnow=' . $id; }, - 'Pay via PayPal' + 'Pay via PayPal', + $this->paymentTokensEndpoint, + $this->vaultV3Enabled, + $this->wcPaymentTokens ); } diff --git a/tests/PHPUnit/WcGateway/Repository/ApplicationContextRepositoryTest.php b/tests/PHPUnit/WcGateway/Repository/ApplicationContextRepositoryTest.php index 1bd5b59f3..48af8f3a4 100644 --- a/tests/PHPUnit/WcGateway/Repository/ApplicationContextRepositoryTest.php +++ b/tests/PHPUnit/WcGateway/Repository/ApplicationContextRepositoryTest.php @@ -36,7 +36,7 @@ class ApplicationContextRepositoryTest extends TestCase ->andReturn($value); } - expect('network_home_url') + expect('home_url') ->andReturn('https://example.com/'); expect('wc_get_checkout_url') ->andReturn('https://example.com/checkout/'); diff --git a/tests/Playwright/tests/save-payment-methods.spec.js b/tests/Playwright/tests/save-payment-methods.spec.js index 4648eebca..f6fb46315 100644 --- a/tests/Playwright/tests/save-payment-methods.spec.js +++ b/tests/Playwright/tests/save-payment-methods.spec.js @@ -37,6 +37,53 @@ test('Save during purchase', async ({page}) => { await expectOrderReceivedPage(page); }); +test('PayPal add payment method', async ({page}) => { + await loginAsCustomer(page); + await page.goto('/my-account/add-payment-method'); + + const popup = await openPaypalPopup(page); + await loginIntoPaypal(popup); + popup.locator('#consentButton').click(); + + await page.waitForURL('/my-account/payment-methods'); +}); + +test('ACDC add payment method', async ({page}) => { + await loginAsCustomer(page); + await page.goto('/my-account/add-payment-method'); + + await page.click("text=Debit & Credit Cards"); + + const creditCardNumber = await page.frameLocator('[title="paypal_card_number_field"]').locator('.card-field-number'); + await creditCardNumber.fill('4005519200000004'); + + const expirationDate = await page.frameLocator('[title="paypal_card_expiry_field"]').locator('.card-field-expiry'); + await expirationDate.fill('01/25'); + + const cvv = await page.frameLocator('[title="paypal_card_cvv_field"]').locator('.card-field-cvv'); + await cvv.fill('123'); + + await page.waitForURL('/my-account/payment-methods'); +}); + +test('PayPal logged-in user free trial subscription without payment token', async ({page}) => { + await loginAsCustomer(page); + + await page.goto('/shop'); + await page.click("text=Sign up now"); + await page.goto('/classic-checkout'); + + const popup = await openPaypalPopup(page); + await loginIntoPaypal(popup); + popup.locator('#consentButton').click(); + + await page.click("text=Proceed to PayPal"); + + const title = await page.locator('.entry-title'); + await expect(title).toHaveText('Order received'); +}) + +