Recently, a customer approached me with a request to create a dynamic scenario on a WordPress site where clicking a button would lead the user directly to the Checkout page, which already contains a number of products in the shopping cart as decided by the site owner.
It turns out that WooCommerce allows you to do this very easily for a single product using the following code, where XX in the first line is the ID of the product you want to add to the cart:
$product_id = XX;
$url = esc_url_raw( add_query_arg( 'add-to-cart', $product_id, wc_get_checkout_url() ) );All you need to do is print the variable $url inside an href somewhere, and you have a link to the payment page that automatically adds product XX to the cart.
However, when I looked for a way to add multiple products to the cart in parallel through a link, I found that it’s a bit more complicated…
Adding Multiple Products in Parallel to the Cart
Browsing the web led me to an article by a developer who created a function that works great and allows adding multiple products in parallel to the shopping cart, as well as choosing the quantity for each product. Take a look at the following code:
function woocommerce_add_multiple_products_to_cart( $url = false ) {
// Make sure WC is installed, and add-to-cart query arg exists, and contains at least one comma.
if ( ! class_exists( 'WC_Form_Handler' ) || empty( $_REQUEST['add-to-cart'] ) || false === strpos( $_REQUEST['add-to-cart'], ',' ) ) {
return;
}
// Remove WooCommerce's hook, as it's useless (doesn't handle multiple products).
remove_action( 'wp_loaded', array( 'WC_Form_Handler', 'add_to_cart_action' ), 20 );
$product_ids = explode( ',', $_REQUEST['add-to-cart'] );
$count = count( $product_ids );
$number = 0;
foreach ( $product_ids as $id_and_quantity ) {
// Check for quantities defined in curie notation (<product_id>:<product_quantity>)
// https://dsgnwrks.pro/snippets/woocommerce-allow-adding-multiple-products-to-the-cart-via-the-add-to-cart-query-string/#comment-12236
$id_and_quantity = explode( ':', $id_and_quantity );
$product_id = $id_and_quantity[0];
$_REQUEST['quantity'] = ! empty( $id_and_quantity[1] ) ? absint( $id_and_quantity[1] ) : 1;
if ( ++$number === $count ) {
// Ok, final item, let's send it back to woocommerce's add_to_cart_action method for handling.
$_REQUEST['add-to-cart'] = $product_id;
return WC_Form_Handler::add_to_cart_action( $url );
}
$product_id = apply_filters( 'woocommerce_add_to_cart_product_id', absint( $product_id ) );
$was_added_to_cart = false;
$adding_to_cart = wc_get_product( $product_id );
if ( ! $adding_to_cart ) {
continue;
}
$add_to_cart_handler = apply_filters( 'woocommerce_add_to_cart_handler', $adding_to_cart->get_type(), $adding_to_cart );
// Variable product handling
if ( 'variable' === $add_to_cart_handler ) {
woo_hack_invoke_private_method( 'WC_Form_Handler', 'add_to_cart_handler_variable', $product_id );
// Grouped Products
} elseif ( 'grouped' === $add_to_cart_handler ) {
woo_hack_invoke_private_method( 'WC_Form_Handler', 'add_to_cart_handler_grouped', $product_id );
// Custom Handler
} elseif ( has_action( 'woocommerce_add_to_cart_handler_' . $add_to_cart_handler ) ){
do_action( 'woocommerce_add_to_cart_handler_' . $add_to_cart_handler, $url );
// Simple Products
} else {
woo_hack_invoke_private_method( 'WC_Form_Handler', 'add_to_cart_handler_simple', $product_id );
}
}
}
// Fire before the WC_Form_Handler::add_to_cart_action callback.
add_action( 'wp_loaded', 'woocommerce_add_multiple_products_to_cart', 15 );
/**
Invoke class private method
* @since 0.1.0
*
* @param string $class_name
* @param string $methodName
*
* @return mixed
*/
function woo_hack_invoke_private_method( $class_name, $methodName ) {
if ( version_compare( phpversion(), '5.3', '<' ) ) {
throw new Exception( 'PHP version does not support ReflectionClass::setAccessible()', __LINE__ );
}
$args = func_get_args();
unset( $args[0], $args[1] );
$reflection = new ReflectionClass( $class_name );
$method = $reflection->getMethod( $methodName );
$method->setAccessible( true );
$args = array_merge( array( $class_name ), $args );
return call_user_func_array( array( $method, 'invoke' ), $args );
}The code above was originally written when PHP 5.3 was current. WooCommerce now requires PHP 7.4 or higher (8.3 recommended), so the PHP version check inside woo_hack_invoke_private_method is no longer relevant. Also note that this snippet uses PHP Reflection to access private WooCommerce methods, which means it may break after a WooCommerce update if those internal methods change. Test thoroughly after every WooCommerce upgrade.
Add this code to the functions.php file in your child theme, and afterward, you can add products to the cart using the following links:
// Multiple products, multiple quantities per product.
$product_ids = '53:2,68:4';
$add_to_cart_url = esc_url_raw( add_query_arg( 'add-to-cart', $product_ids, wc_get_checkout_url() ) );
// Multiple products (default quantity of 1)
$product_ids = '53,68';
$add_to_cart_url = esc_url_raw( add_query_arg( 'add-to-cart', $product_ids, wc_get_checkout_url() ) );
// Normal add-to-cart URL
$product_ids = '53';
$add_to_cart_url = esc_url_raw( add_query_arg( 'add-to-cart', $product_ids, wc_get_checkout_url() ) );In line 2, for example, we specified that the link will add two units of product ID 53 and four units of product ID 68 to the shopping cart. The format is product_id:quantity, separated by commas.
Note that in the above code, we use the wc_get_checkout_url function, which directly leads to the payment page. If, on the other hand, you want to direct the user to the shopping cart page instead of the payment page, you should replace this function with wc_get_cart_url.
For simple cases where you only need to add a single product, you can skip the custom function entirely and build the URL directly:
https://yoursite.com/checkout/?add-to-cart=PRODUCT_ID. WooCommerce handles single-product add-to-cart URLs natively without any additional code.
Native Shareable Checkout URLs (WooCommerce 10.0+)
Starting with WooCommerce 10.0 (released July 2025), there is a built-in way to create add-to-cart links that support multiple products, quantities, and even automatic coupon codes. WooCommerce calls this feature Shareable Checkout URLs.
The URL format uses the /checkout-link/ endpoint:
https://yourstore.com/checkout-link/?products=53:2,68:4&coupon=SPRING10In this example, the link adds two units of product 53, four units of product 68, and automatically applies the SPRING10 coupon. The customer is redirected straight to the checkout page with a pre-populated cart.
If your store runs WooCommerce 10.0 or later, you should use the native
/checkout-link/endpoint instead of the custom PHP function shown earlier in this post. It is more reliable, requires no custom code, and supports coupons out of the box.
The custom code approach described above remains useful for stores running older WooCommerce versions or for cases where you need more control over the add-to-cart behavior (such as custom redirects or programmatic cart manipulation).
FAQs
Common questions about adding products to the WooCommerce cart via URL:
wc_get_checkout_url() with wc_get_cart_url() in the URL-building code. This sends the user to the cart page instead of the checkout page after the product is added./checkout-link/ endpoint supports multiple products using the format ?products=ID:QTY,ID:QTY. For older WooCommerce versions, only a single product ID is processed by default, and you need custom code like the function shown in this post.post=PRODUCT_ID.Summary
The idea of creating a call-to-action that takes the potential customer directly to the payment page along with a specific number of products can certainly increase sales for those products that require a boost.
By using custom fields or with the help of the Advanced Custom Fields plugin, you can allow your customers to easily choose the products they want to promote.

