WooCommerce Checkout Field Validation Generator
Add server-side checkout validation to WooCommerce: pattern, length and range rules per field, with error messages that name the field.
<?php
/**
* Checkout validation.
*
* Server side, because anything in the browser can be skipped. This runs
* after WooCommerce's own validation, so required and email checks are done.
*/
/**
* Checks the submitted fields.
*
* @param array $data Posted checkout data.
* @param WP_Error $errors Errors to add to.
*/
function my_store_validate_checkout( $data, $errors ) {
if ( ! empty( $data['billing_phone'] ) ) {
$digits = preg_replace( '/[^0-9]/', '', $data['billing_phone'] );
if ( strlen( $digits ) < 7 || strlen( $digits ) > 15 ) {
$errors->add( 'validation', esc_html__( 'Enter a phone number we can reach you on, digits only.', 'my-store' ) );
}
}
}
add_action( 'woocommerce_after_checkout_validation', 'my_store_validate_checkout', 10, 2 );
Output is valid and updates as you type.
Fix the highlighted fields to update the output.
Add server-side checkout validation to WooCommerce: per-field rules, a minimum order value, and messages that tell the customer what to do rather than that something is wrong.
How to use
- Write rules against the field key, not the label.
billing_phone, not “Phone”. - Let WooCommerce do the basics. This runs after its own validation, so required and email format are already checked.
- Write messages a customer can act on. “Enter a phone number we can reach you on” beats “Invalid phone number”.
- Check the minimum order on the cart as well as at checkout, which is what the generated code does. Telling someone at the payment step is how a basket is abandoned.
- Test with the browser’s validation disabled. Anything enforced only in the browser is not enforced.
Example
function acme_validate_checkout( $data, $errors ) {
if ( ! empty( $data['billing_phone'] ) ) {
$digits = preg_replace( '/[^0-9]/', '', $data['billing_phone'] );
if ( strlen( $digits ) < 7 || strlen( $digits ) > 15 ) {
$errors->add( 'validation', esc_html__( 'Enter a phone number we can reach you on.', 'acme' ) );
}
}
}
add_action( 'woocommerce_after_checkout_validation', 'acme_validate_checkout', 10, 2 );
Stripping non-digits before counting is deliberate: +44 20 7946 0000 and 02079460000 are the same number, and a length check on the raw string rejects the first.
Pitfalls
- Browser validation is a convenience, not a control. Everything real happens on the server, which is what this hook is for.
woocommerce_after_checkout_validationpasses$errorsas aWP_Error. Adding a notice withwc_add_notice()inside it double-reports the problem in some themes.- Phone number formats vary enormously by country. A strict pattern written for one market rejects real customers in another.
- A user-supplied regex can be catastrophically slow on a crafted input. Keep patterns simple and anchored.
- PO box matching catches the common spellings and misses the rest. Treat it as a filter, not a guarantee.
- The minimum order check should look at the subtotal, not the total, or a coupon or a shipping charge changes whether the rule fires.
- The Checkout block does not use this hook. Blocks validate through their own API and the Store API.
- Validation that fires only at checkout lets someone fill a basket they can never buy. Check the cart too.
Compatibility
woocommerce_after_checkout_validation has been available since WooCommerce 3.0 with the $errors argument, and woocommerce_check_cart_items since 2.x. Both apply to the classic shortcode checkout in WooCommerce 8 and 9. The Checkout block validates through the Store API, so these rules do not run there. The generated code targets PHP 7.0 and up, and the tool runs entirely in your browser.
Frequently asked questions
Why is my validation not firing?
Should I validate phone numbers strictly?
Where does the error appear?
Can I validate a custom field?
$data, exactly as it appears in the posted form.How do I stop a specific product being ordered below a quantity?
woocommerce_check_cart_items and inspect the cart contents. The minimum order option here shows the shape.