Docs Bouncer

Developer Guide

Developer Guide#

Bouncer is extensible through hooks, filters, a public API, and the ability to register custom CAPTCHA providers.

Action hooks#

cfwc_loaded#

Fires after the plugin is fully initialized. Safe to extend.

add_action( 'cfwc_loaded', function() {
    // Plugin is ready
});

cfwc_register_providers#

Register custom CAPTCHA providers. The manager’s register() method takes a single argument: the provider instance.

add_action( 'cfwc_register_providers', function( $manager ) {
    $manager->register( new My_Custom_Provider() );
});

Timing: as of version 1.0.4 this action fires on init (priority 0), so you can hook it from a plugin or from your theme’s functions.php. In 1.0.3 and earlier it fired while plugins were still loading, before theme code was read, so it could only be hooked from a plugin. A small plugin remains the recommended home for provider code either way, so it survives theme switches.

See Custom providers below.

cfwc_before_render / cfwc_after_render#

Fires before and after the CAPTCHA widget renders on a form.

add_action( 'cfwc_before_render', function( $form_type, $args ) {
    // Add custom markup before the CAPTCHA widget
}, 10, 2 );

add_action( 'cfwc_after_render', function( $form_type, $args ) {
    // Add custom markup after the CAPTCHA widget
}, 10, 2 );

cfwc_before_verify#

Fires before CAPTCHA verification begins.

add_action( 'cfwc_before_verify', function( $form_type ) {
    // Log or modify state before verification
});

cfwc_verified#

Fires on successful CAPTCHA verification.

add_action( 'cfwc_verified', function( $form_type, $result ) {
    // Log successful verification, update stats, etc.
}, 10, 2 );

cfwc_failed#

Fires on failed CAPTCHA verification.

add_action( 'cfwc_failed', function( $form_type, $error ) {
    // Custom logging, notifications, etc.
    // $error is a WP_Error object
}, 10, 2 );

cfwc_after_verification#

Fires after any CAPTCHA verification completes (success or failure). Used by the global rate limiter. Available since 1.0.4.

add_action( 'cfwc_after_verification', function( $passed, $form_type ) {
    // $passed is boolean, $form_type is the form identifier
}, 10, 2 );

Filter hooks#

cfwc_skip_verification#

Skip CAPTCHA for specific conditions.

add_filter( 'cfwc_skip_verification', function( $skip, $form_type, $context ) {
    // Skip CAPTCHA on checkout for orders over $500 (trust high-value customers)
    if ( $form_type === 'wc_checkout_block' && WC()->cart->get_total( 'edit' ) > 500 ) {
        return true;
    }
    return $skip;
}, 10, 3 );

cfwc_form_enabled#

Override whether a form type is protected.

add_filter( 'cfwc_form_enabled', function( $enabled, $form_type ) {
    // Disable CAPTCHA on login during business hours
    if ( $form_type === 'wc_login' ) {
        $hour = (int) current_time( 'G' );
        if ( $hour >= 9 && $hour < 17 ) {
            return false;
        }
    }
    return $enabled;
}, 10, 2 );

cfwc_supported_forms#

Add custom form types to the supported forms list.

add_filter( 'cfwc_supported_forms', function( $forms ) {
    $forms['my_custom_form'] = [
        'label'    => 'My Custom Form',
        'category' => 'custom',
    ];
    return $forms;
});

cfwc_error_message#

Customize error messages shown to users.

add_filter( 'cfwc_error_message', function( $message, $code, $provider ) {
    if ( $code === 'verification_failed' ) {
        return 'Security check failed. Please try again.';
    }
    return $message;
}, 10, 3 );

cfwc_widget_container_class#

Customize the CSS class on the CAPTCHA widget container.

add_filter( 'cfwc_widget_container_class', function( $class, $form_type ) {
    return $class . ' my-custom-class';
}, 10, 2 );

cfwc_verification_request_body#

Modify the API request body sent to the CAPTCHA provider.

add_filter( 'cfwc_verification_request_body', function( $body, $provider_id ) {
    // Add custom parameters
    $body['custom_param'] = 'value';
    return $body;
}, 10, 2 );

cfwc_verification_timeout#

Adjust the API request timeout (default 30 seconds).

add_filter( 'cfwc_verification_timeout', function( $timeout, $provider_id ) {
    return 10; // 10 seconds
}, 10, 2 );

cfwc_should_load_assets#

Force CAPTCHA assets to load on specific pages (assets normally only load on pages with protected forms).

add_filter( 'cfwc_should_load_assets', function( $should_load ) {
    if ( is_page( 'custom-checkout' ) ) {
        return true;
    }
    return $should_load;
});

cfwc_get_sections#

Add or modify the sections shown on the settings page (WooCommerce > Settings > Bouncer).

add_filter( 'cfwc_get_sections', function( $sections ) {
    $sections['my_section'] = 'My Section';
    return $sections;
});

cfwc_get_settings_{section}#

Modify the settings fields of a specific section. The section slug is appended to the filter name: forms, security, fraud, notifications, advanced, license, or an empty string for General (cfwc_get_settings_). Fields use the WooCommerce Settings API format.

add_filter( 'cfwc_get_settings_advanced', function( $settings ) {
    $settings[] = [
        'title'   => 'My custom option',
        'id'      => 'my_custom_option',
        'type'    => 'checkbox',
        'default' => 'no',
    ];
    return $settings;
});

cfwc_recaptcha_v3_threshold#

Override the reCAPTCHA v3 score threshold programmatically.

add_filter( 'cfwc_recaptcha_v3_threshold', function( $threshold ) {
    return 0.7; // Stricter than default
});

cfwc_honeypot_min_time#

Override the honeypot minimum submission time.

add_filter( 'cfwc_honeypot_min_time', function( $min_time ) {
    return 5; // 5 seconds instead of default 3
});

cfwc_fraud_rules#

Register custom fraud scoring rules. Each rule must implement the CFWC\Fraud\Rule_Interface.

add_filter( 'cfwc_fraud_rules', function( $rules ) {
    $rules[] = new My_Custom_Fraud_Rule();
    return $rules;
});

cfwc_fraud_score_result#

Modify the fraud score result before it’s saved to the order.

add_filter( 'cfwc_fraud_score_result', function( $result, $order ) {
    // $result contains 'score', 'level', 'details'
    return $result;
}, 10, 2 );

cfwc_geoip_country_code#

Override the GeoIP-resolved country code.

add_filter( 'cfwc_geoip_country_code', function( $country_code, $ip ) {
    return $country_code;
}, 10, 2 );

cfwc_should_block_request#

Block form submissions before CAPTCHA verification runs. The global rate limiter hooks this to reject bursts of requests site-wide. Available since 1.0.4.

add_filter( 'cfwc_should_block_request', function( $should_block ) {
    // Return true to reject the submission
    return $should_block;
});

Public API#

Access the plugin instance and its components:

$plugin = CFWC\Plugin::instance();

Render CAPTCHA on a custom form#

// In your form template
$plugin->render( 'my_custom_form', [
    'container_class' => 'my-captcha-wrapper',
] );

Verify CAPTCHA on form submission#

// In your form handler
$result = $plugin->verify( 'my_custom_form' );

if ( is_wp_error( $result ) ) {
    // Verification failed
    $error_message = $result->get_error_message();
} else {
    // Verification passed, process form
}

Render CAPTCHA via shortcode#

Use [cfwc_captcha] in any page or template. For programmatic rendering:

echo do_shortcode( '[cfwc_captcha theme="dark" size="compact"]' );

Verify the shortcode CAPTCHA via AJAX:

// POST to admin-ajax.php with action=cfwc_verify_shortcode
// Include cfwc_shortcode_nonce from the rendered form

Access settings#

$settings = $plugin->settings();

$provider    = $settings->get( 'provider' );
$site_key    = $settings->get( 'site_key' );
$is_debug    = $settings->get( 'enable_debug_logging' );

Access provider manager#

$providers = $plugin->providers();

// Get active provider
$active = $plugin->provider();

// Get all registered providers
$all = $providers->get_all();

// Check if a provider is registered
$exists = $providers->exists( 'turnstile' );

Custom providers#

The simplest path is extending CFWC\Providers\Abstract_Provider, which is what the built-in providers do. It covers most of Provider_Interface for you; the only abstract method is render_widget(). Set the $id, $name, and $token_field properties and override get_description(), verify(), and test_connection().

Put the code in a small plugin so it survives theme switches. Keep the class in its own file: it declares a namespace, and a namespace declaration must be the first statement in its file.

<?php
/**
 * Plugin Name: My Custom Provider for Bouncer
 */

defined( 'ABSPATH' ) || exit;

add_action( 'cfwc_register_providers', function( $manager ) {
    require_once __DIR__ . '/class-my-custom-provider.php';
    $manager->register( new \CFWC\Providers\My_Custom_Provider() );
});

And in class-my-custom-provider.php:

<?php
namespace CFWC\Providers;

defined( 'ABSPATH' ) || exit;

class My_Custom_Provider extends Abstract_Provider {

    protected $id            = 'my_provider';
    protected $name          = 'My Custom Provider';
    protected $token_field   = 'my_provider_token';
    protected $requires_keys = false;

    public function get_description() {
        return 'Description of my provider, shown on the settings page.';
    }

    protected function render_widget( $form_type, $args ) {
        // Output the CAPTCHA widget HTML.
        echo '<div id="my-provider-' . esc_attr( $form_type ) . '"></div>';
    }

    public function verify( $token = '' ) {
        // Verify the response. Return true on success,
        // or a WP_Error (via $this->create_error()) on failure.
        return true;
    }

    public function test_connection( $site_key, $secret_key ) {
        // Called from the settings page key test. Return true
        // or a WP_Error.
        return true;
    }
}

If you build on Provider_Interface directly instead of the abstract class, you must provide all of its methods: get_id(), get_name(), get_description(), requires_api_keys(), get_api_key_url(), render( $form_type, $args ), verify( $token ), get_token_field_name(), and test_connection( $site_key, $secret_key ).

The custom provider will appear in the provider dropdown on the settings page.

Logging#

When debug logging is enabled, the plugin logs to WooCommerce’s logging system:

  • Verification attempts (success and failure)
  • Rate limit events (lockouts, releases)
  • Provider errors (API timeouts, invalid responses)
  • IP blocklist matches

View logs at WooCommerce > Status > Logs and filter by source captcha-for-woocommerce.

// Log programmatically (only writes when debug logging is enabled)
\CFWC\Logger::log( 'Custom log message', [ 'context' => 'value' ], 'info' );

Form type identifiers#

When working with hooks, use these form type string identifiers:

FormID
WordPress Loginwp_login
WordPress Registrationwp_register
WordPress Lost Passwordwp_lost_password
WordPress Commentswp_comment
WooCommerce Loginwc_login
WooCommerce Registrationwc_register
WooCommerce Lost Passwordwc_lost_password
Classic Checkoutwc_checkout_classic
Block Checkoutwc_checkout_block
Pay for Orderwc_pay_order
Product Vendorswcpv_registration
Subscriptionswc_subscriptions
Membershipswc_memberships
WordPress Reset Passwordwp_reset_password
WooCommerce Reset Passwordwc_reset_password
Product Reviewswc_review
Order Trackingwc_order_tracking
WooCommerce Bookingswc_bookings
Elementor Pro Formselementor_form
Shortcodeshortcode