docs/block-development/tutorials/integrating-protection-checkout-block.md
If you're a developer of a Captcha or fraud protection plugin, make sure your solution is hooking into the Store API and integrating with the Checkout block. This tutorial will guide you through the process of adding protection mechanisms to the WooCommerce Checkout block.
The WooCommerce Checkout block uses the Store API for processing orders, which provides a secure, unauthenticated API for customer-facing functionality. To integrate protection mechanisms like CAPTCHA or fraud detection, you'll need to:
The first step is to render your CAPTCHA or protection element in the checkout block. You can use the render_block filter to inject your HTML before or after specific checkout blocks.
The render_block filter allows you to modify the output of any WordPress block. For the checkout block, you'll want to target the woocommerce/checkout-actions-block which contains the "Place order" button.
add_filter(
'render_block_woocommerce/checkout-actions-block',
function( $block_content ) {
ob_start();
?>
<div class="my-captcha-element" data-sitekey="<?php echo esc_attr( get_option( 'plugin_captcha_sitekey' ) ); ?>">
</div>
<?php
echo $block_content;
$block_content = ob_get_contents();
ob_end_clean();
return $block_content;
},
999,
1
);
Key points about this code:
woocommerce/checkout-actions-block which is the block containing the place order button999 to ensure our content is added after other modificationsdata-sitekey attribute stores your CAPTCHA configuration, but this may be different for your pluginOnce your protection element is rendered, you need to integrate it with the checkout data store to capture the validation token and pass it to the server.
The checkout block uses a data store to manage state. You can use the setExtensionData method to pass your protection token to the server.
/* Woo Checkout Block */
document.addEventListener( 'DOMContentLoaded', function () {
if ( wp && wp.data ) {
var unsubscribe = wp.data.subscribe( function () {
const turnstileItem = document.querySelector(
'.my-captcha-element'
);
if ( turnstile && turnstileItem ) {
turnstile.render( turnstileItem, {
sitekey: turnstileItem.dataset.sitekey,
callback: function ( data ) {
wp.data
.dispatch( 'wc/store/checkout' )
.setExtensionData( 'plugin-namespace-turnstile', {
token: data,
} );
},
} );
unsubscribe();
}
}, 'wc/store/cart' );
}
} );
Key points about this JavaScript:
turnstile.render() method initializes your CAPTCHA (replace with your specific implementation)setExtensionData() stores the token in the checkout data storemy-plugin-turnstile)For more information about the checkout data store and available methods, see the Checkout Data Store documentation.
The most critical step is validating the protection token on the server side. This should happen early in the authentication process to prevent any unauthorized checkout attempts.
rest_authentication_errors filterThe rest_authentication_errors filter is the ideal place to validate your protection token because it runs before any checkout processing begins.
add_filter( 'rest_authentication_errors', 'plugin_check_turnstile_token' );
function plugin_check_turnstile_token( $result ) {
// Skip if this is not a POST request.
if ( isset( $_SERVER['REQUEST_METHOD'] ) && $_SERVER['REQUEST_METHOD'] !== 'POST' ) {
// Always return the result or an error, never a boolean. This ensures other checks aren't thrown away like rate limiting or authentication.
return $result;
}
// Skip if this is not the checkout endpoint.
if ( ! preg_match( '#/wc/store(?:/v\d+)?/checkout#', $GLOBALS['wp']->query_vars['rest_route'] ) ) {
return $result;
}
// get request body
$request_body = json_decode( \WP_REST_Server::get_raw_data(), true );
if ( isset( $request_body['payment_method'] ) ) {
$chosen_payment_method = sanitize_text_field( $request_body['payment_method'] );
// Provide ability to short circuit the check to allow express payments or hosted checkouts to bypass the check.
$selected_payment_methods = apply_filters( 'plugin_payment_methods_to_skip', array('woocommerce_payments' ) );
if( is_array( $selected_payment_methods ) ) {
if ( in_array( $chosen_payment_method, $selected_payment_methods, true ) ) {
return $result;
}
}
}
$extensions = $request_body['extensions'];
if ( empty( $extensions ) || ! isset( $extensions['plugin-namespace-turnstile'] ) ) {
return new WP_Error( 'challenge_failed', 'Captcha challenge failed' );
}
$token = sanitize_text_field( $extensions['plugin-namespace-turnstile']['token'] );
/**
* Note: The function `my_token_check_function` would be
* implemented in your plugin to handle token validation.
**/
$check = my_token_check_function( $token );
$success = $check['success'];
if( $success !== true ) {
return new WP_Error( 'challenge_failed', 'Captcha challenge failed' );
}
return $result;
}
Key points about server-side validation:
$request_body['extensions']['your-namespace']$result parameter to avoid interfering with other authentication checksWP_Error object if validation failsWhen testing your protection integration: