Merge pull request #2520 from woocommerce/fastlane

Fastlane
This commit is contained in:
Emili Castells 2024-08-19 12:12:41 +02:00 committed by GitHub
commit 7806195c7f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 304 additions and 76 deletions

1
.megaignore Normal file
View file

@ -0,0 +1 @@
-s:*

View file

@ -83,7 +83,7 @@ return array(
->rule()
->condition_element( 'axo_enabled', '1' )
->action_visible( 'axo_gateway_title' )
->action_visible( 'axo_checkout_config_notice' )
->action_visible( 'axo_main_notice' )
->action_visible( 'axo_privacy' )
->action_visible( 'axo_name_on_card' )
->action_visible( 'axo_style_heading' )
@ -114,9 +114,17 @@ return array(
),
'classes' => array( 'ppcp-valign-label-middle', 'ppcp-align-label-center' ),
),
'axo_checkout_config_notice' => array(
'axo_main_notice' => array(
'heading' => '',
'html' => $container->get( 'axo.checkout-config-notice' ),
'html' => implode(
'',
array(
$container->get( 'axo.settings-conflict-notice' ),
$container->get( 'axo.shipping-config-notice' ),
$container->get( 'axo.checkout-config-notice' ),
$container->get( 'axo.incompatible-plugins-notice' ),
)
),
'type' => 'ppcp-html',
'classes' => array( 'ppcp-field-indent' ),
'class' => array(),

View file

@ -709,6 +709,8 @@ class AxoManager {
}`
);
this.emailInput.value = this.stripSpaces( this.emailInput.value );
this.$( this.el.paymentContainer.selector + '-detail' ).html( '' );
this.$( this.el.paymentContainer.selector + '-form' ).html( '' );
@ -1134,6 +1136,10 @@ class AxoManager {
return emailPattern.test( value );
}
stripSpaces( str ) {
return str.replace( /\s+/g, '' );
}
validateEmail( billingEmail ) {
const billingEmailSelector = document.querySelector( billingEmail );
const value = document.querySelector( billingEmail + ' input' ).value;

View file

@ -1,7 +1,19 @@
export function log( message, level = 'info' ) {
const wpDebug = window.wc_ppcp_axo?.wp_debug;
const endpoint = window.wc_ppcp_axo?.ajax?.frontend_logger?.endpoint;
if ( ! endpoint ) {
const loggingEnabled = window.wc_ppcp_axo?.logging_enabled;
if ( wpDebug ) {
switch ( level ) {
case 'error':
console.error( `[AXO] ${ message }` );
break;
default:
console.log( `[AXO] ${ message }` );
}
}
if ( ! endpoint || ! loggingEnabled ) {
return;
}
@ -15,15 +27,5 @@ export function log( message, level = 'info' ) {
level,
},
} ),
} ).then( () => {
if ( wpDebug ) {
switch ( level ) {
case 'error':
console.error( `[AXO] ${ message }` );
break;
default:
console.log( `[AXO] ${ message }` );
}
}
} );
}

View file

@ -12,10 +12,10 @@ namespace WooCommerce\PayPalCommerce\Axo;
use WooCommerce\PayPalCommerce\Axo\Assets\AxoManager;
use WooCommerce\PayPalCommerce\Axo\Gateway\AxoGateway;
use WooCommerce\PayPalCommerce\Axo\Helper\ApmApplies;
use WooCommerce\PayPalCommerce\Axo\Helper\SettingsNoticeGenerator;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
use WooCommerce\PayPalCommerce\WcGateway\Helper\CartCheckoutDetector;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
return array(
@ -25,7 +25,7 @@ return array(
$apm_applies = $container->get( 'axo.helpers.apm-applies' );
assert( $apm_applies instanceof ApmApplies );
return $apm_applies->for_country_currency() && $apm_applies->for_settings();
return $apm_applies->for_country_currency();
},
'axo.helpers.apm-applies' => static function ( ContainerInterface $container ) : ApmApplies {
@ -36,6 +36,10 @@ return array(
);
},
'axo.helpers.settings-notice-generator' => static function ( ContainerInterface $container ) : SettingsNoticeGenerator {
return new SettingsNoticeGenerator();
},
// If AXO is configured and onboarded.
'axo.available' => static function ( ContainerInterface $container ): bool {
return true;
@ -159,48 +163,35 @@ return array(
);
},
'axo.settings-conflict-notice' => static function ( ContainerInterface $container ) : string {
$settings_notice_generator = $container->get( 'axo.helpers.settings-notice-generator' );
assert( $settings_notice_generator instanceof SettingsNoticeGenerator );
$settings = $container->get( 'wcgateway.settings' );
assert( $settings instanceof Settings );
return $settings_notice_generator->generate_settings_conflict_notice( $settings );
},
'axo.checkout-config-notice' => static function ( ContainerInterface $container ) : string {
$checkout_page_link = esc_url( get_edit_post_link( wc_get_page_id( 'checkout' ) ) ?? '' );
$block_checkout_docs_link = __(
'https://woocommerce.com/document/cart-checkout-blocks-status/#reverting-to-the-cart-and-checkout-shortcodes',
'woocommerce-paypal-payments'
);
$settings_notice_generator = $container->get( 'axo.helpers.settings-notice-generator' );
assert( $settings_notice_generator instanceof SettingsNoticeGenerator );
if ( CartCheckoutDetector::has_elementor_checkout() ) {
$notice_content = sprintf(
/* translators: %1$s: URL to the Checkout edit page. %2$s: URL to the block checkout docs. */
__(
'<span class="highlight">Warning:</span> The <a href="%1$s">Checkout page</a> of your store currently uses the <code>Elementor Checkout widget</code>. To enable Fastlane and accelerate payments, the page must include either the <code>Classic Checkout</code> or the <code>[woocommerce_checkout]</code> shortcode. See <a href="%2$s">this page</a> for instructions on how to switch to the classic layout.',
'woocommerce-paypal-payments'
),
esc_url( $checkout_page_link ),
esc_url( $block_checkout_docs_link )
);
} elseif ( CartCheckoutDetector::has_block_checkout() ) {
$notice_content = sprintf(
/* translators: %1$s: URL to the Checkout edit page. %2$s: URL to the block checkout docs. */
__(
'<span class="highlight">Warning:</span> The <a href="%1$s">Checkout page</a> of your store currently uses the WooCommerce <code>Checkout</code> block. To enable Fastlane and accelerate payments, the page must include either the <code>Classic Checkout</code> or the <code>[woocommerce_checkout]</code> shortcode. See <a href="%2$s">this page</a> for instructions on how to switch to the classic layout.',
'woocommerce-paypal-payments'
),
esc_url( $checkout_page_link ),
esc_url( $block_checkout_docs_link )
);
} elseif ( ! CartCheckoutDetector::has_classic_checkout() ) {
$notice_content = sprintf(
/* translators: %1$s: URL to the Checkout edit page. %2$s: URL to the block checkout docs. */
__(
'<span class="highlight">Warning:</span> The <a href="%1$s">Checkout page</a> of your store does not seem to be properly configured or uses an incompatible <code>third-party Checkout</code> solution. To enable Fastlane and accelerate payments, the page must include either the <code>Classic Checkout</code> or the <code>[woocommerce_checkout]</code> shortcode. See <a href="%2$s">this page</a> for instructions on how to switch to the classic layout.',
'woocommerce-paypal-payments'
),
esc_url( $checkout_page_link ),
esc_url( $block_checkout_docs_link )
);
} else {
return '';
}
return $settings_notice_generator->generate_checkout_notice();
},
return '<div class="ppcp-notice ppcp-notice-error"><p>' . $notice_content . '</p></div>';
'axo.shipping-config-notice' => static function ( ContainerInterface $container ) : string {
$settings_notice_generator = $container->get( 'axo.helpers.settings-notice-generator' );
assert( $settings_notice_generator instanceof SettingsNoticeGenerator );
return $settings_notice_generator->generate_shipping_notice();
},
'axo.incompatible-plugins-notice' => static function ( ContainerInterface $container ) : string {
$settings_notice_generator = $container->get( 'axo.helpers.settings-notice-generator' );
assert( $settings_notice_generator instanceof SettingsNoticeGenerator );
return $settings_notice_generator->generate_incompatible_plugins_notice();
},
'axo.smart-button-location-notice' => static function ( ContainerInterface $container ) : string {
@ -230,6 +221,7 @@ return array(
return '<div class="ppcp-notice ppcp-notice-warning"><p>' . $notice_content . '</p></div>';
},
'axo.endpoint.frontend-logger' => static function ( ContainerInterface $container ): FrontendLoggerEndpoint {
return new FrontendLoggerEndpoint(
$container->get( 'button.request-data' ),

View file

@ -216,6 +216,7 @@ class AxoManager {
'nonce' => wp_create_nonce( FrontendLoggerEndpoint::nonce() ),
),
),
'logging_enabled' => $this->settings->has( 'logging_enabled' ) ? $this->settings->get( 'logging_enabled' ) : '',
'wp_debug' => defined( 'WP_DEBUG' ) && WP_DEBUG,
'billing_email_button_text' => __( 'Continue', 'woocommerce-paypal-payments' ),
);

View file

@ -66,7 +66,7 @@ class AxoModule implements ModuleInterface {
// Add the gateway in admin area.
if ( is_admin() ) {
$methods[] = $gateway;
// $methods[] = $gateway; - Temporarily remove Fastlane from the payment gateway list in admin area.
return $methods;
}
@ -77,9 +77,10 @@ class AxoModule implements ModuleInterface {
$settings = $c->get( 'wcgateway.settings' );
assert( $settings instanceof Settings );
$is_paypal_enabled = $settings->has( 'enabled' ) && $settings->get( 'enabled' ) ?? false;
$is_dcc_enabled = $settings->has( 'dcc_enabled' ) && $settings->get( 'dcc_enabled' ) ?? false;
if ( ! $is_dcc_enabled ) {
if ( ! $is_paypal_enabled || ! $is_dcc_enabled ) {
return $methods;
}
@ -87,6 +88,10 @@ class AxoModule implements ModuleInterface {
return $methods;
}
if ( ! $this->is_compatible_shipping_config() ) {
return $methods;
}
$methods[] = $gateway;
return $methods;
},
@ -144,13 +149,20 @@ class AxoModule implements ModuleInterface {
function () use ( $c ) {
$module = $this;
$settings = $c->get( 'wcgateway.settings' );
assert( $settings instanceof Settings );
$is_paypal_enabled = $settings->has( 'enabled' ) && $settings->get( 'enabled' ) ?? false;
$subscription_helper = $c->get( 'wc-subscriptions.helper' );
assert( $subscription_helper instanceof SubscriptionHelper );
// Check if the module is applicable, correct country, currency, ... etc.
if ( ! $c->get( 'axo.eligible' )
if ( ! $is_paypal_enabled
|| ! $c->get( 'axo.eligible' )
|| 'continuation' === $c->get( 'button.context' )
|| $subscription_helper->cart_contains_subscription() ) {
|| $subscription_helper->cart_contains_subscription()
|| ! $this->is_compatible_shipping_config() ) {
return;
}
@ -194,9 +206,17 @@ class AxoModule implements ModuleInterface {
add_action(
'wp_head',
function () {
function () use ( $c ) {
// phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript
echo '<script async src="https://www.paypalobjects.com/insights/v1/paypal-insights.sandbox.min.js"></script>';
// Add meta tag to allow feature-detection of the site's AXO payment state.
$settings = $c->get( 'wcgateway.settings' );
assert( $settings instanceof Settings );
$this->add_feature_detection_tag(
$settings->has( 'axo_enabled' ) && $settings->get( 'axo_enabled' )
);
}
);
@ -333,6 +353,7 @@ class AxoModule implements ModuleInterface {
return ! is_user_logged_in()
&& CartCheckoutDetector::has_classic_checkout()
&& $this->is_compatible_shipping_config()
&& $is_axo_enabled
&& $is_dcc_enabled
&& ! $this->is_excluded_endpoint();
@ -388,4 +409,32 @@ class AxoModule implements ModuleInterface {
// Exclude the Order Pay endpoint.
return is_wc_endpoint_url( 'order-pay' );
}
/**
* Condition to evaluate if the shipping configuration is compatible.
*
* @return bool
*/
private function is_compatible_shipping_config(): bool {
return ! wc_shipping_enabled() || ( wc_shipping_enabled() && ! wc_ship_to_billing_address_only() );
}
/**
* Outputs a meta tag to allow feature detection on certain pages.
*
* @param bool $axo_enabled Whether the gateway is enabled.
* @return void
*/
private function add_feature_detection_tag( bool $axo_enabled ) {
$show_tag = is_checkout() || is_cart() || is_shop();
if ( ! $show_tag ) {
return;
}
printf(
'<meta name="ppcp.axo" content="%s" />',
$axo_enabled ? 'enabled' : 'disabled'
);
}
}

View file

@ -168,7 +168,7 @@ class AxoGateway extends WC_Payment_Gateway {
? $this->ppcp_settings->get( 'axo_gateway_title' )
: $this->get_option( 'title', $this->method_title );
$this->description = __( 'Enter your email address to continue.', 'woocommerce-paypal-payments' );
$this->description = __( 'Enter your email address above to continue.', 'woocommerce-paypal-payments' );
$this->init_form_fields();
$this->init_settings();

View file

@ -64,19 +64,4 @@ class ApmApplies {
}
return in_array( $this->currency, $this->allowed_country_currency_matrix[ $this->country ], true );
}
/**
* Returns whether the settings are compatible with AXO.
*
* @return bool
*/
public function for_settings(): bool {
if ( get_option( 'woocommerce_ship_to_destination' ) === 'billing_only' ) { // Force shipping to the customer billing address.
return false;
}
return true;
}
}

View file

@ -0,0 +1,179 @@
<?php
/**
* Settings notice generator.
* Generates the settings notices.
*
* @package WooCommerce\PayPalCommerce\Axo\Helper
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Axo\Helper;
use WooCommerce\PayPalCommerce\WcGateway\Helper\CartCheckoutDetector;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
use WooCommerce\PayPalCommerce\WcGateway\Exception\NotFoundException;
/**
* Class SettingsNoticeGenerator
*/
class SettingsNoticeGenerator {
/**
* Generates the full HTML of the notification.
*
* @param string $message HTML of the inner message contents.
* @param bool $is_error Whether the provided message is an error. Affects the notice color.
*
* @return string The full HTML code of the notification, or an empty string.
*/
private function render_notice( string $message, bool $is_error = false ) : string {
if ( ! $message ) {
return '';
}
return sprintf(
'<div class="ppcp-notice %1$s"><p>%2$s</p></div>',
$is_error ? 'ppcp-notice-error' : '',
$message
);
}
/**
* Generates the checkout notice.
*
* @return string
*/
public function generate_checkout_notice(): string {
$checkout_page_link = esc_url( get_edit_post_link( wc_get_page_id( 'checkout' ) ) ?? '' );
$block_checkout_docs_link = __(
'https://woocommerce.com/document/cart-checkout-blocks-status/#reverting-to-the-cart-and-checkout-shortcodes',
'woocommerce-paypal-payments'
);
$notice_content = '';
if ( CartCheckoutDetector::has_elementor_checkout() ) {
$notice_content = sprintf(
/* translators: %1$s: URL to the Checkout edit page. %2$s: URL to the block checkout docs. */
__(
'<span class="highlight">Warning:</span> The <a href="%1$s">Checkout page</a> of your store currently uses the <code>Elementor Checkout widget</code>. To enable Fastlane and accelerate payments, the page must include either the <code>Classic Checkout</code> or the <code>[woocommerce_checkout]</code> shortcode. See <a href="%2$s">this page</a> for instructions on how to switch to the classic layout.',
'woocommerce-paypal-payments'
),
esc_url( $checkout_page_link ),
esc_url( $block_checkout_docs_link )
);
} elseif ( CartCheckoutDetector::has_block_checkout() ) {
$notice_content = sprintf(
/* translators: %1$s: URL to the Checkout edit page. %2$s: URL to the block checkout docs. */
__(
'<span class="highlight">Warning:</span> The <a href="%1$s">Checkout page</a> of your store currently uses the WooCommerce <code>Checkout</code> block. To enable Fastlane and accelerate payments, the page must include either the <code>Classic Checkout</code> or the <code>[woocommerce_checkout]</code> shortcode. See <a href="%2$s">this page</a> for instructions on how to switch to the classic layout.',
'woocommerce-paypal-payments'
),
esc_url( $checkout_page_link ),
esc_url( $block_checkout_docs_link )
);
} elseif ( ! CartCheckoutDetector::has_classic_checkout() ) {
$notice_content = sprintf(
/* translators: %1$s: URL to the Checkout edit page. %2$s: URL to the block checkout docs. */
__(
'<span class="highlight">Warning:</span> The <a href="%1$s">Checkout page</a> of your store does not seem to be properly configured or uses an incompatible <code>third-party Checkout</code> solution. To enable Fastlane and accelerate payments, the page must include either the <code>Classic Checkout</code> or the <code>[woocommerce_checkout]</code> shortcode. See <a href="%2$s">this page</a> for instructions on how to switch to the classic layout.',
'woocommerce-paypal-payments'
),
esc_url( $checkout_page_link ),
esc_url( $block_checkout_docs_link )
);
}
return $notice_content ? '<div class="ppcp-notice ppcp-notice-error"><p>' . $notice_content . '</p></div>' : '';
}
/**
* Generates the shipping notice.
*
* @return string
*/
public function generate_shipping_notice(): string {
$shipping_settings_link = admin_url( 'admin.php?page=wc-settings&tab=shipping&section=options' );
$notice_content = '';
if ( wc_shipping_enabled() && wc_ship_to_billing_address_only() ) {
$notice_content = sprintf(
/* translators: %1$s: URL to the Shipping destination settings page. */
__(
'<span class="highlight">Warning:</span> The <a href="%1$s">Shipping destination</a> of your store is currently configured to <code>Force shipping to the customer billing address</code>. To enable Fastlane and accelerate payments, the shipping destination must be configured either to <code>Default to customer shipping address</code> or <code>Default to customer billing address</code> so buyers can set separate billing and shipping details.',
'woocommerce-paypal-payments'
),
esc_url( $shipping_settings_link )
);
}
return $notice_content ? '<div class="ppcp-notice ppcp-notice-error"><p>' . $notice_content . '</p></div>' : '';
}
/**
* Generates the incompatible plugins notice.
*
* @return string
*/
public function generate_incompatible_plugins_notice(): string {
$incompatible_plugins = array(
'Elementor' => did_action( 'elementor/loaded' ),
'CheckoutWC' => defined( 'CFW_NAME' ),
);
$active_plugins_list = array_filter( $incompatible_plugins );
if ( empty( $active_plugins_list ) ) {
return '';
}
$incompatible_plugin_items = array_map(
function ( $plugin ) {
return "<li>{$plugin}</li>";
},
array_keys( $active_plugins_list )
);
$plugins_settings_link = esc_url( admin_url( 'plugins.php' ) );
$notice_content = sprintf(
/* translators: %1$s: URL to the plugins settings page. %2$s: List of incompatible plugins. */
__(
'<span class="highlight">Note:</span> The accelerated guest buyer experience provided by Fastlane may not be fully compatible with some of the following <a href="%1$s">active plugins</a>: <ul class="ppcp-notice-list">%2$s</ul>',
'woocommerce-paypal-payments'
),
$plugins_settings_link,
implode( '', $incompatible_plugin_items )
);
return '<div class="ppcp-notice"><p>' . $notice_content . '</p></div>';
}
/**
* Generates a warning notice with instructions on conflicting plugin-internal settings.
*
* @param Settings $settings The plugin settings container, which is checked for conflicting
* values.
* @return string
*/
public function generate_settings_conflict_notice( Settings $settings ) : string {
$notice_content = '';
$is_dcc_enabled = false;
try {
$is_dcc_enabled = $settings->has( 'dcc_enabled' ) && $settings->get( 'dcc_enabled' );
// phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
} catch ( NotFoundException $ignored ) {
// Never happens.
}
if ( ! $is_dcc_enabled ) {
$notice_content = __(
'<span class="highlight">Warning:</span> To enable Fastlane and accelerate payments, the <strong>Advanced Card Processing</strong> payment method must also be enabled.',
'woocommerce-paypal-payments'
);
}
return $this->render_notice( $notice_content, true );
}
}

View file

@ -133,6 +133,11 @@ $background-ident-color: #fbfbfb;
}
}
.ppcp-notice-list {
list-style-type: disc;
padding-left: 20px;
}
th, td {
border-top: 1px solid $border-color;
}