Sync 273 (#38983)
Co-authored-by: barryhughes <3594411+barryhughes@users.noreply.github.com>
This commit is contained in:
parent
714e50bf4c
commit
1cd947a320
|
@ -171,6 +171,18 @@ class WC_Shortcode_Checkout {
|
|||
}
|
||||
}
|
||||
|
||||
// If we cannot match the order with the current user, ask that they verify their email address.
|
||||
if ( self::guest_should_verify_email( $order, 'order-pay' ) ) {
|
||||
wc_get_template(
|
||||
'checkout/form-verify-email.php',
|
||||
array(
|
||||
'failed_submission' => ! empty( $_POST['email'] ), // phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
'verify_url' => $order->get_checkout_payment_url(),
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
WC()->customer->set_props(
|
||||
array(
|
||||
'billing_country' => $order->get_billing_country() ? $order->get_billing_country() : null,
|
||||
|
@ -258,6 +270,7 @@ class WC_Shortcode_Checkout {
|
|||
|
||||
if ( $order_id > 0 ) {
|
||||
$order = wc_get_order( $order_id );
|
||||
|
||||
if ( ! $order || ! hash_equals( $order->get_order_key(), $order_key ) ) {
|
||||
$order = false;
|
||||
}
|
||||
|
@ -276,6 +289,36 @@ class WC_Shortcode_Checkout {
|
|||
// Empty current cart.
|
||||
wc_empty_cart();
|
||||
|
||||
// If the specified order ID was invalid, we still render the default order received page (which will simply
|
||||
// state that the order was received, but will not output any other details: this makes it harder to probe for
|
||||
// valid order IDs than if we state that the order ID was not recognized).
|
||||
if ( ! $order ) {
|
||||
wc_get_template( 'checkout/thankyou.php', array( 'order' => false ) );
|
||||
return;
|
||||
}
|
||||
|
||||
$order_customer_id = $order->get_customer_id();
|
||||
|
||||
// For non-guest orders, require the user to be logged in before showing this page.
|
||||
if ( $order_customer_id && get_current_user_id() !== $order_customer_id ) {
|
||||
wc_print_notice( esc_html__( 'Please log in to your account to view this order.', 'woocommerce' ), 'notice' );
|
||||
woocommerce_login_form( array( 'redirect' => $order->get_checkout_order_received_url() ) );
|
||||
return;
|
||||
}
|
||||
|
||||
// For guest orders, request they verify their email address (unless we can identify them via the active user session).
|
||||
if ( self::guest_should_verify_email( $order, 'order-received' ) ) {
|
||||
wc_get_template(
|
||||
'checkout/form-verify-email.php',
|
||||
array(
|
||||
'failed_submission' => ! empty( $_POST['email'] ), // phpcs:ignore WordPress.Security.NonceVerification.Missing
|
||||
'verify_url' => $order->get_checkout_order_received_url(),
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, display the thank you (order received) page.
|
||||
wc_get_template( 'checkout/thankyou.php', array( 'order' => $order ) );
|
||||
}
|
||||
|
||||
|
@ -317,4 +360,65 @@ class WC_Shortcode_Checkout {
|
|||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to determine if the user's email address should be verified before rendering either the 'order received' or
|
||||
* 'order pay' pages. This should only be applied to guest orders.
|
||||
*
|
||||
* @param WC_Order $order The order for which a need for email verification is being determined.
|
||||
* @param string $context The context in which email verification is being tested.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private static function guest_should_verify_email( WC_Order $order, string $context ): bool {
|
||||
$order_email = $order->get_billing_email();
|
||||
$order_customer_id = $order->get_customer_id();
|
||||
|
||||
// If we do not have a billing email for the order (could happen in the order is created manually, or if the
|
||||
// requirement for this has been removed from the checkout flow), email verification does not make sense.
|
||||
if ( empty( $order_email ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// No verification step is needed if the user is logged in and is already associated with the order.
|
||||
if ( $order_customer_id && get_current_user_id() === $order_customer_id ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
||||
if ( ! empty( $_POST ) && ! wp_verify_nonce( $_POST['check_submission'] ?? '', 'wc_verify_email' ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$session = wc()->session;
|
||||
$session_email = '';
|
||||
|
||||
if ( is_a( $session, WC_Session::class ) ) {
|
||||
$customer = $session->get( 'customer' );
|
||||
$session_email = is_array( $customer ) && isset( $customer['email'] ) ? $customer['email'] : '';
|
||||
}
|
||||
|
||||
$session_email_match = $session_email === $order->get_billing_email();
|
||||
$supplied_email_match = isset( $_POST['email'] ) && sanitize_email( wp_unslash( $_POST['email'] ) ?? '' ) === $order->get_billing_email();
|
||||
$can_view_orders = current_user_can( 'read_private_shop_orders' );
|
||||
|
||||
// If we cannot match the order with the current user, the user should verify their email address.
|
||||
$email_verification_required = ! $session_email_match && ! $supplied_email_match && ! $can_view_orders;
|
||||
|
||||
/**
|
||||
* Provides an opportunity to override the (potential) requirement for shoppers to verify their email address
|
||||
* before we show information such as the order summary, or order payment page.
|
||||
*
|
||||
* Note that this hook is not always triggered, therefore it is (for example) unsuitable as a way of forcing
|
||||
* email verification across all order confirmation/order payment scenarios. Instead, the filter primarily
|
||||
* exists as a way to *remove* the email verification step.
|
||||
*
|
||||
* @since 7.9.0
|
||||
*
|
||||
* @param bool $email_verification_required If email verification is required.
|
||||
* @param WC_Order $order The relevant order.
|
||||
* @param string $context The context under which we are performing this check.
|
||||
*/
|
||||
return (bool) apply_filters( 'woocommerce_order_email_verification_required', $email_verification_required, $order, $context );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
/**
|
||||
* Email verification page.
|
||||
*
|
||||
* This displays instead of the thankyou page any time that the customer cannot be identified.
|
||||
*
|
||||
* This template can be overridden by copying it to yourtheme/woocommerce/checkout/thankyou-verify-email.php.
|
||||
*
|
||||
* HOWEVER, on occasion WooCommerce will need to update template files and you (the theme developer) will need to copy
|
||||
* the new files to your theme to maintain compatibility. We try to do this as little as possible, but it does happen.
|
||||
* When this occurs the version of the template file will be bumped and the readme will list any important changes.
|
||||
*
|
||||
* @see https://docs.woocommerce.com/document/template-structure/
|
||||
* @package WooCommerce\Templates
|
||||
* @version 7.9.0
|
||||
*
|
||||
* @var bool $failed_submission Indicates if the last attempt to verify failed.
|
||||
* @var string $verify_url The URL for the email verification form.
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
?>
|
||||
<form name="checkout" method="post" class="woocommerce-form woocommerce-verify-email" action="<?php echo esc_url( $verify_url ); ?>" enctype="multipart/form-data">
|
||||
|
||||
<?php
|
||||
wp_nonce_field( 'wc_verify_email', 'check_submission' );
|
||||
|
||||
if ( $failed_submission ) {
|
||||
wc_print_notice( esc_html__( 'We were unable to verify the email address you provided. Please try again.', 'woocommerce' ), 'error' );
|
||||
}
|
||||
?>
|
||||
<p>
|
||||
<?php
|
||||
printf(
|
||||
/* translators: 1: opening login link 2: closing login link */
|
||||
esc_html__( 'To view this page, you must either %1$slogin%2$s or verify the email address associated with the order.', 'woocommerce' ),
|
||||
'<a href="' . esc_url( wc_get_page_permalink( 'myaccount' ) ) . '">',
|
||||
'</a>'
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
|
||||
<p class="form-row">
|
||||
<label for="email"><?php esc_html_e( 'Email address', 'woocommerce' ); ?> <span class="required">*</span></label>
|
||||
<input type="email" class="input-text" name="email" id="email" autocomplete="email" />
|
||||
</p>
|
||||
|
||||
<p class="form-row">
|
||||
<button type="submit" class="woocommerce-button button <?php echo esc_attr( wc_wp_theme_get_element_class_name( 'button' ) ); ?>" name="verify" value="1">
|
||||
<?php esc_html_e( 'Verify', 'woocommerce' ); ?>
|
||||
</button>
|
||||
</p>
|
||||
</form>
|
|
@ -245,6 +245,32 @@ test.describe( 'Checkout page', () => {
|
|||
.textContent();
|
||||
guestOrderId = await orderReceivedText.split( /(\s+)/ )[ 6 ].toString();
|
||||
|
||||
// If we simulate a new browser context by dropping all cookies, and reload the page, the shopper should be
|
||||
// prompted to complete an email validation step before they can proceed.
|
||||
await page.context().clearCookies();
|
||||
await page.reload();
|
||||
await expect( page.locator( 'form.woocommerce-verify-email p:nth-child(3)' ) ).toContainText(
|
||||
/verify the email address associated with the order/
|
||||
);
|
||||
|
||||
// Supplying an email address other than the actual order billing email address will take them back to the same
|
||||
// page with an error message.
|
||||
await page.fill( '#email', 'incorrect@email.address' );
|
||||
await page.locator( 'form.woocommerce-verify-email button' ).click();
|
||||
await expect( page.locator( 'form.woocommerce-verify-email p:nth-child(4)' ) ).toContainText(
|
||||
/verify the email address associated with the order/
|
||||
);
|
||||
await expect( page.locator( 'ul.woocommerce-error li' ) ).toContainText(
|
||||
/We were unable to verify the email address you provided/
|
||||
);
|
||||
|
||||
// However if they supply the *correct* billing email address, they should see the order received page again.
|
||||
await page.fill( '#email', guestEmail );
|
||||
await page.locator( 'form.woocommerce-verify-email button' ).click();
|
||||
await expect( page.locator( 'h1.entry-title' ) ).toContainText(
|
||||
'Order received'
|
||||
);
|
||||
|
||||
await page.goto( 'wp-login.php' );
|
||||
await page.locator( 'input[name="log"]' ).fill( admin.username );
|
||||
await page.locator( 'input[name="pwd"]' ).fill( admin.password );
|
||||
|
@ -273,9 +299,9 @@ test.describe( 'Checkout page', () => {
|
|||
} );
|
||||
|
||||
test( 'allows existing customer to place order', async ( { page } ) => {
|
||||
await page.goto( 'wp-admin/' );
|
||||
await page.locator( 'input[name="log"]' ).fill( customer.username );
|
||||
await page.locator( 'input[name="pwd"]' ).fill( customer.password );
|
||||
await page.goto( 'my-account/' );
|
||||
await page.locator( 'input[name="username"]' ).fill( customer.username );
|
||||
await page.locator( 'input[name="password"]' ).fill( customer.password );
|
||||
await page.locator( 'text=Log In' ).click();
|
||||
await page.waitForLoadState( 'networkidle' );
|
||||
for ( let i = 1; i < 3; i++ ) {
|
||||
|
@ -320,9 +346,17 @@ test.describe( 'Checkout page', () => {
|
|||
.split( /(\s+)/ )[ 6 ]
|
||||
.toString();
|
||||
|
||||
await page.goto( 'wp-login.php?loggedout=true' );
|
||||
await page.waitForLoadState( 'networkidle' );
|
||||
// Effect a log out/simulate a new browsing session by dropping all cookies.
|
||||
await page.context().clearCookies();
|
||||
await page.reload();
|
||||
|
||||
// Now we are logged out, return to the confirmation page: we should be asked to log back in.
|
||||
await expect( page.locator( '.woocommerce-info' ) ).toContainText(
|
||||
/Please log in to your account to view this order/
|
||||
);
|
||||
|
||||
// Switch to admin user.
|
||||
await page.goto( 'wp-login.php?loggedout=true' );
|
||||
await page.locator( 'input[name="log"]' ).fill( admin.username );
|
||||
await page.locator( 'input[name="pwd"]' ).fill( admin.password );
|
||||
await page.locator( 'text=Log In' ).click();
|
||||
|
|
Loading…
Reference in New Issue