◆ FIELD NOTES

WooCommerce Checkout Customization: 30+ Code Snippets

WooCommerce’s default checkout is a fine starting point and a terrible ending point. Every store I’ve built — and I’ve built over thirty — has needed at least five customizations to the checkout flow before launch. Sometimes it’s removing fields that don’t apply to the business. Sometimes it’s adding fields the business actually needs. Sometimes it’s redesigning the entire layout because the default two-column shows order summary on the right and your customer is on mobile.

This is the snippet collection I copy from on every WooCommerce build. Each one is production-ready code I’ve shipped to live stores. Drop them into your child theme’s functions.php, a custom plugin, or the Code Snippets plugin. Group them however you want — they’re modular.

Fields: remove, add, reorder, relabel

1. Remove the company name field

add_filter( 'woocommerce_checkout_fields', function( $fields ) {
    unset( $fields['billing']['billing_company'] );
    return $fields;
} );

2. Remove the second address line

add_filter( 'woocommerce_checkout_fields', function( $fields ) {
    unset( $fields['billing']['billing_address_2'] );
    return $fields;
} );

3. Remove the order notes textarea

add_filter( 'woocommerce_enable_order_notes_field', '__return_false' );

4. Make phone optional instead of required

add_filter( 'woocommerce_checkout_fields', function( $fields ) {
    $fields['billing']['billing_phone']['required'] = false;
    return $fields;
} );

5. Add a custom field (delivery instructions)

add_filter( 'woocommerce_checkout_fields', function( $fields ) {
    $fields['order']['delivery_instructions'] = [
        'type'        => 'textarea',
        'label'       => __( 'Delivery instructions', 'mytheme' ),
        'placeholder' => 'Gate code, drop-off preferences, etc.',
        'required'    => false,
        'class'       => [ 'form-row-wide' ],
        'priority'    => 100,
    ];
    return $fields;
} );

add_action( 'woocommerce_checkout_update_order_meta', function( $order_id ) {
    if ( ! empty( $_POST['delivery_instructions'] ) ) {
        update_post_meta( $order_id, '_delivery_instructions',
            sanitize_textarea_field( $_POST['delivery_instructions'] ) );
    }
} );

6. Reorder fields with priority

add_filter( 'woocommerce_checkout_fields', function( $fields ) {
    $fields['billing']['billing_first_name']['priority'] = 10;
    $fields['billing']['billing_last_name']['priority']  = 20;
    $fields['billing']['billing_email']['priority']      = 30;
    $fields['billing']['billing_phone']['priority']      = 40;
    return $fields;
} );

7. Relabel “Place order” to a clearer CTA

add_filter( 'woocommerce_order_button_text', function() {
    return 'Pay Securely Now';
} );

8. Change “Your order” heading

add_filter( 'gettext', function( $translated, $original, $domain ) {
    if ( 'Your order' === $original && 'woocommerce' === $domain ) {
        return 'Order Summary';
    }
    return $translated;
}, 20, 3 );

9. Make a required field optional based on country

add_filter( 'woocommerce_checkout_fields', function( $fields ) {
    if ( isset( $_POST['billing_country'] ) && $_POST['billing_country'] === 'PK' ) {
        $fields['billing']['billing_state']['required'] = false;
    }
    return $fields;
} );

10. Pre-fill fields for logged-in users

add_filter( 'woocommerce_checkout_get_value', function( $value, $input ) {
    if ( ! is_user_logged_in() ) return $value;
    $user_id = get_current_user_id();
    $custom  = get_user_meta( $user_id, $input, true );
    return $custom ?: $value;
}, 10, 2 );

Validation rules

11. Force phone to be 10+ digits

add_action( 'woocommerce_checkout_process', function() {
    $phone = preg_replace( '/[^0-9]/', '', $_POST['billing_phone'] ?? '' );
    if ( strlen( $phone ) < 10 ) {
        wc_add_notice( 'Please enter a valid phone number (10+ digits).', 'error' );
    }
} );

12. Block disposable email domains

add_action( 'woocommerce_checkout_process', function() {
    $email   = sanitize_email( $_POST['billing_email'] ?? '' );
    $blocked = [ 'mailinator.com', 'tempmail.com', '10minutemail.com', 'guerrillamail.com' ];
    foreach ( $blocked as $bad ) {
        if ( stripos( $email, $bad ) !== false ) {
            wc_add_notice( 'Please use a real email address.', 'error' );
        }
    }
} );

13. Minimum order total

add_action( 'woocommerce_checkout_process', function() {
    $min = 25;
    if ( WC()->cart->total < $min ) {
        wc_add_notice(
            sprintf( 'Minimum order is $%s.', $min ), 'error'
        );
    }
} );

14. Maximum quantity per product

add_filter( 'woocommerce_quantity_input_args', function( $args, $product ) {
    $args['max_value'] = 5;
    return $args;
}, 10, 2 );

Payment + shipping logic

15. Hide a payment gateway based on cart total

add_filter( 'woocommerce_available_payment_gateways', function( $gateways ) {
    if ( is_admin() ) return $gateways;
    if ( WC()->cart->total > 500 && isset( $gateways['cod'] ) ) {
        unset( $gateways['cod'] );
    }
    return $gateways;
} );

16. Free shipping over a threshold (without the coupon)

add_filter( 'woocommerce_package_rates', function( $rates, $package ) {
    if ( WC()->cart->subtotal >= 100 ) {
        foreach ( $rates as $rate_id => $rate ) {
            if ( $rate->method_id !== 'free_shipping' ) {
                unset( $rates[ $rate_id ] );
            }
        }
    }
    return $rates;
}, 10, 2 );

17. Hide shipping methods for specific countries

add_filter( 'woocommerce_package_rates', function( $rates, $package ) {
    if ( ( $package['destination']['country'] ?? '' ) === 'PK' ) {
        unset( $rates['flat_rate:1'] );
    }
    return $rates;
}, 10, 2 );

18. Force a default payment gateway

add_filter( 'woocommerce_available_payment_gateways', function( $gateways ) {
    if ( WC()->session ) {
        WC()->session->set( 'chosen_payment_method', 'stripe' );
    }
    return $gateways;
} );

UX improvements

19. Auto-update checkout when fields change

add_filter( 'woocommerce_update_order_review_fragments', function( $fragments ) {
    ob_start();
    woocommerce_order_review();
    $fragments['.woocommerce-checkout-review-order-table'] = ob_get_clean();
    return $fragments;
} );

20. Add a progress indicator above the form

add_action( 'woocommerce_before_checkout_form', function() {
    echo '<div class="checkout-steps">'
       . '<span class="active">1. Details</span> → '
       . '<span>2. Payment</span> → '
       . '<span>3. Confirmation</span>'
       . '</div>';
}, 5 );

21. Show trust badges below the place order button

add_action( 'woocommerce_review_order_after_submit', function() {
    echo '<div class="checkout-trust">'
       . '🔒 256-bit SSL encryption · 💳 Stripe verified · ↩️ 30-day refund'
       . '</div>';
} );

22. Remove the “Login” notice for guest checkout

add_filter( 'woocommerce_checkout_login_message', '__return_empty_string' );

23. Add a coupon link below the order total

add_action( 'woocommerce_review_order_before_payment', function() {
    if ( wc_coupons_enabled() ) {
        echo '<a href="#" class="showcoupon">Have a coupon code?</a>';
    }
} );

24. Persist cart for 30 days

add_filter( 'wc_session_expiring', fn() => 60 * 60 * 24 * 30 - 60 );
add_filter( 'wc_session_expiration', fn() => 60 * 60 * 24 * 30 );

Admin-side: capturing the custom data

25. Display custom field in admin order screen

add_action( 'woocommerce_admin_order_data_after_billing_address', function( $order ) {
    $instr = get_post_meta( $order->get_id(), '_delivery_instructions', true );
    if ( $instr ) {
        echo '<p><strong>Delivery instructions:</strong> ' . esc_html( $instr ) . '</p>';
    }
} );

26. Include custom field in admin email

add_filter( 'woocommerce_email_order_meta_fields', function( $fields, $sent_to_admin, $order ) {
    $instr = get_post_meta( $order->get_id(), '_delivery_instructions', true );
    if ( $instr ) {
        $fields['delivery_instructions'] = [
            'label' => 'Delivery instructions',
            'value' => $instr,
        ];
    }
    return $fields;
}, 10, 3 );

27. Include custom field in CSV export

add_filter( 'woocommerce_order_export_csv_data', function( $row, $order ) {
    $row['delivery_instructions'] = get_post_meta( $order->get_id(), '_delivery_instructions', true );
    return $row;
}, 10, 2 );

Conversion lifts

28. Strip the State/County field for Pakistan to reduce form length

add_filter( 'woocommerce_get_country_locale', function( $locale ) {
    $locale['PK']['state']['required'] = false;
    $locale['PK']['state']['hidden']   = true;
    return $locale;
} );

29. Save abandoned-cart emails

add_action( 'woocommerce_checkout_update_order_review', function( $post_data ) {
    parse_str( $post_data, $data );
    if ( ! empty( $data['billing_email'] ) ) {
        WC()->session->set( 'pending_email', sanitize_email( $data['billing_email'] ) );
        // Forward to your email tool here (Klaviyo, Mailchimp, n8n webhook).
    }
} );

30. Auto-apply a coupon from a URL parameter

add_action( 'wp_loaded', function() {
    if ( ! empty( $_GET['coupon'] ) && ! is_admin() ) {
        WC()->cart->apply_coupon( sanitize_text_field( $_GET['coupon'] ) );
    }
} );

31. Bonus: redirect to a custom thank-you page per product

add_action( 'woocommerce_thankyou', function( $order_id ) {
    $order = wc_get_order( $order_id );
    foreach ( $order->get_items() as $item ) {
        if ( $item->get_product_id() === 123 ) {
            wp_safe_redirect( '/thanks-premium/' );
            exit;
        }
    }
}, 5 );

How I use these in real client builds

On every WooCommerce build I do at CofCode, the first day after install is spent removing default fields that don’t apply (company, second address line, often order notes), adding the one or two fields that do apply (delivery instructions, GST number, gift message), and renaming the place-order button to something more conversion-friendly. That’s snippets 1-8 right there.

For B2B clients we layer in 9-14 (validation, minimum order value, max quantity, country-specific logic). For high-volume D2C stores we focus on 19-24 (UX polish, trust badges, persistent carts). The result is a checkout that converts better than the default by 10-25% on most stores, with zero plugin bloat.

If you want the same pattern shipped to your store, the case studies page has live examples. Or just reach out with your current checkout URL and I’ll send back three specific lifts I’d recommend.

When to stop with snippets and rebuild the checkout

If you’re more than 15 snippets deep and the checkout still feels janky, it’s time for a custom checkout. WooCommerce’s block-based checkout (introduced in WC 8.3) is much more flexible than the legacy shortcode-based one — but it also requires React knowledge to extend properly. For most small-to-mid stores the shortcode + snippets approach is fine. For stores doing $100k/month+, a custom React-based checkout is usually worth the investment.

Either way, start with the right field set, validate aggressively, and remove anything the customer doesn’t strictly need to complete the purchase. The default checkout is a developer’s compromise; your checkout should be a buyer’s path of least resistance.

◆ END OF ARTICLE
◆ LIKED THIS?

Let's build your next WordPress project.


Download CV