Speedy Checkouts with WooCommerce Subscriptions

I collect my hosting payments using WooCommerce Subscriptions. To make the checkout really straightforward I slimmed down the process to a single payment link. Here is how to create really speedy checkouts with WooCommerce Subscriptions.

Start by creating a new WordPress user and WooCommerce subscription for each customer

When creating WordPress users, I uncheck “Send the new user an email about their account.” While the user account is necessary, it is not necessary for the customer to know those details before their first payment. Next, prep a new WooCommerce subscription with their unique details and email the order details. This sends a unique payment link.

Use a custom payment link for speedy checkout

The regular payment link will take the customer to a login page. We can skip the login by automatically signing in based on their link. To do this we need to first takeover the regular payment link with something we can control.

The following function will take a payment url with /checkout/order-pay/ and replace with /checkout-express/. This allows me to change the regular payment links to my own custom template.

// Custom payment link for speedy checkout
function anchor_get_checkout_payment_url( $payment_url ) {

    $home_url = esc_url( home_url( '/' ) );

    $new_payment_url = str_replace( $home_url . 'checkout/order-pay/', $home_url . 'checkout-express/', $payment_url );

    return $new_payment_url;

In order to complete the customization there are 2 theme files needed. The page-checkout-express.php and woocommerce/emails/customer-invoice.php. This is what my page-checkout-express.php looks like.


$order_id = get_query_var( 'callback' );
$key      = $_GET['key'];

// Loads user and key from order
$customer_id = get_field( '_customer_user', $order_id );
$order_key   = get_field( '_order_key', $order_id );

// Loads order
$order = new WC_Order( $order_id );

// Fetch payment url
$payment_url = $order->get_checkout_payment_url();

// Fetch user
$user = get_user_by( 'id', $customer_id );

if ( $user and $order_key == $key ) {

    // Login as new user
    wp_set_current_user( $user->ID, $user->user_login );
    wp_set_auth_cookie( $user->ID );

    // Redirect to payment url
    wp_redirect( $payment_url );

} else {

    // Redirect to homepage
    wp_redirect( get_home_url() );


Lastly we need to customize the email to use our unique URLs. This can be done by copying over the stock WooCommerce customer invoice email template and wrapping $order->get_checkout_payment_url() with the custom function anchor_get_checkout_payment_url(). This is what my woocommerce/emails/customer-invoice.php looks like.

 * Customer invoice email
 * This template can be overridden by copying it to yourtheme/woocommerce/emails/customer-invoice.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/
 * @author      WooThemes
 * @package     WooCommerce/Templates/Emails
 * @version     3.3.0

if ( ! defined( 'ABSPATH' ) ) {

 * Executes the e-mail header.
 * @hooked WC_Emails::email_header() Output the email header
do_action( 'woocommerce_email_header', $email_heading, $email ); ?>

<?php if ( $order->has_status( 'pending' ) ) : ?>
            /* translators: %1s item is the name of the site, %2s is a html link */
            __( 'An order has been created for you on %1$s. %2$s', 'woocommerce' ),
                'a' => array(
                    'href' => array(),
        esc_html( get_bloginfo( 'name', 'display' ) ),
        '<a href="' . esc_url( anchor_get_checkout_payment_url( $order->get_checkout_payment_url() ) ) . '">' . esc_html__( 'Pay for this order', 'woocommerce' ) . '</a>'
<?php endif; ?>


 * Hook for the woocommerce_email_order_details.
 * @hooked WC_Emails::order_details() Shows the order details table.
 * @hooked WC_Structured_Data::generate_order_data() Generates structured data.
 * @hooked WC_Structured_Data::output_structured_data() Outputs structured data.
 * @since 2.5.0
do_action( 'woocommerce_email_order_details', $order, $sent_to_admin, $plain_text, $email );

 * Hook for the woocommerce_email_order_meta.
 * @hooked WC_Emails::order_meta() Shows order meta data.
do_action( 'woocommerce_email_order_meta', $order, $sent_to_admin, $plain_text, $email );

 * Hook for woocommerce_email_customer_details.
 * @hooked WC_Emails::customer_details() Shows customer details
 * @hooked WC_Emails::email_address() Shows email address
do_action( 'woocommerce_email_customer_details', $order, $sent_to_admin, $plain_text, $email );

 * Executes the email footer.
 * @hooked WC_Emails::email_footer() Output the email footer
do_action( 'woocommerce_email_footer', $email );

With that the customer doesn’t have to think. They simply click a link and enter payment info. Pretty amazing!