[Checkout] Support for shareable checkout URLs which pre-populate the cart/coupons - #58140
Conversation
|
@mikejolley May I ask what page type this will be? I couldn't find anything in the PR. Will I'm asking as I want to implement this with my Pixel Manager for WooCommerce and trigger an |
… cart/coupons (#58140) * Shared session utils * Add token handling middleware to client * Use SessionUtils * Checkout Link handler * Linting error * Linting * Revert "Linting error" This reverts commit 1d08d92. * Revert "Add token handling middleware to client" This reverts commit 592df3a. * Update error notices to make more friendly * Update wc_rand_hash function to make use of random_bytes We'll use this for customer ID generation * Session handling * Refactor session handler to restore session from query string * remove debugging * No need to set cookie manually after refactor * Remove example * update wc_rand_hash tests * Remove set_customer_id as its unused * Rename to CartTokenUtils and handle payload * Tidy up classes * Add CartTokenUtilsTests * Basic test to ensure products are added to cart when using checkout link * Lint * e2e test and empty cart handling * Handle qty * Changelog * Update wc_rand_hash and test to handle 0 length provided * Check if wp_fast_hash exists before usage * Put back wc_empty_cart to prevent test breakage * Load data when init session * Update test text * Update plugins/woocommerce/changelog/integra-28-add-ability-to-generate-a-cart-from-link-or-load-up-via Co-authored-by: Seghir Nadir <nadir.seghir@gmail.com> * Validate before clear cart. Add helper for getting products from query string * Preserve the query string * Apply coupon after checking if products were added * Improve error handling * When restoring token, exclude customer address * Add test for invalid link * Tidy up logic * Clone carts * Handle older cookies with double pipes * Cover logged in and guest for checkout links --------- Co-authored-by: Seghir Nadir <nadir.seghir@gmail.com>
|
@alewolf There is no page type; its a redirect early during |
Does that include front-end JavaScript/jQuery |
|
@alewolf Server side |
|
How does this differ from the existing add-to-cart URL parameter? |
How would you support customizable products, like product addons, bundles, name your price, etc For example if product 18 is name your price how would I support passing an additional parameter for price? |
From my understanding, one element that this has is the ability to add coupons, which the add-to-cart feature lacks. This can also add multiple different products, rather than just 1. |
|
Please, allow multiple coupons to be added. |
@helgatheviking About your question – which I am interested in as well –, a viable approach would be to add to the cart page a "Share this cart" custom button that generates the cart token for the current cart. This method also simplifies the creation of the link for complex cart contents, so you don't have to compile a URL manually. The process would be this:
I created a proof of concept with the code snippet below and it works on the Classic Cart. add_action( 'woocommerce_proceed_to_checkout', 'dev_test_woocommerce_proceed_to_checkout', 21 );
function dev_test_woocommerce_proceed_to_checkout() {
if ( ! class_exists( 'Automattic\WooCommerce\StoreApi\Utilities\CartTokenUtils' ) ) {
return;
}
$session_token = Automattic\WooCommerce\StoreApi\Utilities\CartTokenUtils::get_cart_token( (string) wc()->session->get_customer_id() );
$checkout_link_url = add_query_arg( 'session', $session_token, wc_get_checkout_url() );
?>
<a href="<?php echo esc_url( $checkout_link_url ); ?>" class="button alt<?php echo esc_attr( wc_wp_theme_get_element_class_name( 'button' ) ? ' ' . wc_wp_theme_get_element_class_name( 'button' ) : '' ); ?>">
<?php esc_html_e( 'Share this cart', 'woocommerce' ); ?>
</a>
<?php
} |
|
@LuigiPulcini that's clever, I like it! Is the session token limited to the user who made the link? Or you can send it to anybody? I also took a pass at adding support for additional params to the checkout link in this PR. I was able to get it working with my name your price plugin (so you can add a product with a custom price to the cart via url), but another of my plugins is a bit more complex and would require more integration. |
@helgatheviking As long as the user creating the cart is a guest (i.e. you are not logged in), the cart token can be shared to anyone. You can easily verify that by creating a link and use it in a different browser or computer. The check I mentioned in my previous comment bails out if the user who created the cart is a registered user of the website. @pacotole The technique I described here also allows the use of multiple coupons because the cart is captured as a whole via its cart token and that includes any coupons added to it, no matter how many. |
|
@helgatheviking About your other PR, I think that manually adding fully-configured products to a cart and generating a cart token for it is much less laborious than any attempt at compiling the URL, even when that is done programmatically (which would require additional code anyway). Leveraging WooCommerce's built-in capability of storing guest carts in the database makes even more sense now that we have the |
|
I'm inclined to agree with you, esp if there's a way to support generating a guest token even while logged in- since I'm guessing store owners would want to generate links for customers. but I think we could do both. If the checkout url feature exists then it ought to be extensible and support plugins. I'm pretty much always on that soapbox. |
|
@LuigiPulcini I am not a developer. May I request you to explore some option that this could be seen / created ONLY by Admin and not been seen by all customers in the front-end? Do you think this can be done? Edit: I am a store owner and we have been craving this since years. |
|
@helgatheviking I agree that the perfect method should simplify how site owners can create links for customers. The reason behind the use of guest users is that the @KPYahoo A simple way to hide the button from regular visitors is adding a query argument (e.g. |
|
@LuigiPulcini what happens in your snippet if you do not pass a customer ID when generating the token? And instead you generate the random hash yourself? Which is how the customer ID is generated for non -logged in users. Which would also allow for restricting display to admins. I have an idea but I'm away from my keyboard. 😅 |
|
@helgatheviking As long as you pass a customer ID that:
then, the new The problem, though, is how to make it usable from the currently logged-in admin user which doesn't store its session data as a guest user in the
This should allow us to effortlessly create checkout links while being logged in as admins and limit the presence of the button with a check to |
|
@helgatheviking It works! The following code snippet adds a button that is visible to admins or shop managers only and allows them to generate checkout links from the cart page. The link can be shared with any customers and is valid for 48 hours. add_action( 'woocommerce_proceed_to_checkout', 'dev_test_woocommerce_proceed_to_checkout', 21 );
function dev_test_woocommerce_proceed_to_checkout() {
if ( ! class_exists( 'Automattic\WooCommerce\StoreApi\Utilities\CartTokenUtils' ) || ! current_user_can( 'manage_woocommerce' ) ) {
return;
}
global $wpdb;
$customer_id = wc()->session->get_customer_id();
$session_data = wc()->session->get_session( $customer_id, array() );
$session_data = array_diff_key( $session_data, array( 'customer' => true ) );
$guest_user_id = wc_rand_hash( 't_', 30 );
$expiration = time() + intval( apply_filters( 'wc_session_expiration', 2 * DAY_IN_SECONDS ) );
$wpdb->query(
$wpdb->prepare(
'INSERT INTO %i (`session_key`, `session_value`, `session_expiry`) VALUES (%s, %s, %d)
ON DUPLICATE KEY UPDATE `session_value` = VALUES(`session_value`), `session_expiry` = VALUES(`session_expiry`)',
$wpdb->prefix . 'woocommerce_sessions',
$guest_user_id,
maybe_serialize( $session_data ),
$expiration
)
);
wp_cache_set( WC_Cache_Helper::get_cache_prefix( WC_SESSION_CACHE_GROUP ) . $guest_user_id, $session_data, WC_SESSION_CACHE_GROUP, $expiration - time() );
$session_token = Automattic\WooCommerce\StoreApi\Utilities\CartTokenUtils::get_cart_token( (string) $guest_user_id );
$checkout_link_url = add_query_arg( 'session', $session_token, wc_get_checkout_url() );
?>
<a href="<?php echo esc_url( $checkout_link_url ); ?>" class="button alt<?php echo esc_attr( wc_wp_theme_get_element_class_name( 'button' ) ? ' ' . wc_wp_theme_get_element_class_name( 'button' ) : '' ); ?>">
<?php esc_html_e( 'Share this cart', 'woocommerce' ); ?>
</a>
<?php
}Please note that a new guest session data row is created every time the cart page gets reloaded. Nevertheless, all those sessions will eventually disappear from the table when their expiration time comes up. |
|
Awesome! I totally forgot you would need to save the session token to the DB |
Created a plugin that for now can do simple and variable product. See if it works for you, and leave a review :) |
Submission Review Guidelines:
Changes proposed in this Pull Request
This PR introduces the ability to generate a cart from a link which redirects to checkout with a unique session ID, or load a cart via a token in the query string.
An example link:
or without encoding:
The main changes include:
/checkout-linkendpoint that allows users to pre-populate their cart by visiting a URL with product IDs and product Qty, and optional coupon code.class-wc-coupon.phpandclass-wc-discounts.phpfor consistency and clarity.Benefits
Testing instructions
Valid Cart Link
/checkout-link?products=<valid_id_1>,<valid_id_2>&coupon=<valid_coupon>Valid Cart Link w/ product quantities
/checkout-link?products=<valid_id_1>:2,<valid_id_2>&coupon=<valid_coupon>Invalid Coupon
/checkout-link?products=<valid_id_1>,<valid_id_2>&coupon=INVALID_COUPONCoupon "INVALID_COUPON" cannot be applied because it does not exist.Invalid Product (mixed)
/checkout-link?products=<valid_id_1>,999999Product with ID "999999" was not found and cannot be added to the cart.Only Invalid Product
/checkout-link?products=999999The provided checkout link was out of date or invalid. No products were added to the cart.Cart Token (logged out)
session=is part of the URL.E2E Tests
plugins/woocommerce/tests/e2e-pw/tests/checkout/checkout-link.spec.jsto verify all scenarios.Checklist
Changelog entry
Changelog Entry Details
Significance
Type
Message
Changelog Entry Comment
Comment