diff --git a/dapper.php b/dapper.php index 0b429e2..0a4281d 100644 --- a/dapper.php +++ b/dapper.php @@ -1093,6 +1093,8 @@ function dapper_woo_conditional_load() { if ($lowercase_name || $suspicious_company || (float)$order->get_total_tax() === 0) { $order->update_status('cancelled', 'Blocked: Invalid or missing human verification for PayPal Express (bot pattern detected).'); + $order->update_meta_data( '_dapper_blocked_bot', '1' ); + $order->save(); dapper_debug_log("Blocked fake PayPal order #$order_id - No valid token | Name: $first $last | Company: $company"); } } @@ -1102,260 +1104,185 @@ function dapper_woo_conditional_load() { add_action('woocommerce_order_status_cancelled', 'dapper_trash_obvious_bot_paypal_orders', 20, 1); - function dapper_trash_obvious_bot_paypal_orders($order_id) { - if (!class_exists('WooCommerce')) return; + function dapper_trash_obvious_bot_paypal_orders( $order_id ) { + if ( ! class_exists( 'WooCommerce' ) ) return; - $order = wc_get_order($order_id); - if (!$order) return; + $order = wc_get_order( $order_id ); + if ( ! $order ) return; - // Only act on PayPal orders $method = $order->get_payment_method(); - if (!in_array($method, ['paypal', 'ppec_paypal', 'ppcp-gateway', 'paypal_express', 'braintree_paypal'], true)) { + if ( ! in_array( $method, [ 'paypal', 'ppec_paypal', 'ppcp-gateway', 'paypal_express', 'braintree_paypal' ], true ) ) { return; } - // Check if this cancellation came from our bot block function - $notes = $order->get_customer_order_notes(); - $latest_note = !empty($notes) ? end($notes)->comment_content : ''; - - $bot_patterns = [ - 'Blocked: Invalid or missing human verification', - 'bot pattern detected', - ]; - - $is_bot = false; - foreach ($bot_patterns as $pattern) { - if (stripos($latest_note, $pattern) !== false) { - $is_bot = true; - break; - } + // Primary check: our custom meta flag + if ( $order->get_meta( '_dapper_blocked_bot' ) === '1' ) { + wp_trash_post( $order_id ); + $order->add_order_note( 'Trashed by Dapper — obvious PayPal bot order (blocked by human check)' ); + dapper_debug_log( "Trashed obvious bot PayPal order #$order_id (meta flag present)" ); + return; } - // Extra safety: very young cancelled orders with 0 tax and suspicious name/company - if (!$is_bot) { - $age_minutes = (time() - strtotime($order->get_date_created())) / 60; - $first = strtolower(trim($order->get_billing_first_name() ?: '')); - $last = strtolower(trim($order->get_billing_last_name() ?: '')); - $company = strtolower(trim($order->get_billing_company() ?: '')); + // Fallback safety net (still catch obvious patterns even if meta missed somehow) + $age_minutes = ( time() - strtotime( $order->get_date_created() ) ) / 60; + $first = strtolower( trim( $order->get_billing_first_name() ?: '' ) ); + $last = strtolower( trim( $order->get_billing_last_name() ?: '' ) ); + $company = strtolower( trim( $order->get_billing_company() ?: '' ) ); - if ($age_minutes < 15 && - (float)$order->get_total_tax() === 0 && - ($first === $last || preg_match('/^[a-z]{5,12}$/', $company))) { - $is_bot = true; - } - } + if ( $age_minutes < 15 && + (float) $order->get_total_tax() === 0 && + ( $first === $last || preg_match( '/^[a-z]{5,12}$/', $company ) ) ) { - if ($is_bot) { - wp_trash_post($order_id); - $order->add_order_note('Moved to trash by Dapper — obvious PayPal bot order'); - dapper_debug_log("Trashed obvious bot PayPal order #$order_id"); - } + wp_trash_post( $order_id ); + $order->add_order_note( 'Trashed by Dapper — obvious PayPal bot pattern (fallback check)' ); + dapper_debug_log( "Trashed obvious bot PayPal order #$order_id (fallback pattern match)" ); } - - - +} } add_action( 'plugins_loaded', 'dapper_woo_conditional_load' ); // Late hook – safe for class_exists() // ──────────────────────────────────────────────── // Contact Form 7 protection (always loaded if CF7 exists) // ──────────────────────────────────────────────── +if ( class_exists( 'WPCF7' ) ) { -if (class_exists('WPCF7')) { - - // Option to enable/disable CF7 protection separately - // You can add this to settings later if you want - - add_action('wpcf7_before_send_mail', 'dapper_cf7_human_check', 9, 3); - - function dapper_cf7_human_check($contact_form, &$abort, $submission) { - // Skip if disabled via future option - // if (get_option('dapper_enable_cf7_human_check', 'on') !== 'on') return; - + // 1. Old honeypot + token + speed check (keep it — layers are good) + add_action( 'wpcf7_before_send_mail', 'dapper_cf7_human_check', 9, 3 ); + function dapper_cf7_human_check( $contact_form, &$abort, $submission ) { $form_id = $contact_form->id(); + $posted = $_POST; - // Very fast submission (< 5 seconds) → almost always bot - $posted_time = isset($_POST['dapper_cf7_time']) ? (int)$_POST['dapper_cf7_time'] : 0; - if ($posted_time && (time() - $posted_time < 5)) { + // Speed check + $posted_time = isset( $posted['dapper_cf7_time'] ) ? (int) $posted['dapper_cf7_time'] : 0; + if ( $posted_time && ( time() - $posted_time < 5 ) ) { $abort = true; - $submission->add_error('dapper_speed', 'Submission too fast — please try again.'); - dapper_debug_log("CF7 #$form_id blocked — too fast"); + $submission->add_error( 'dapper_speed', 'Submission too fast — please try again.' ); + dapper_debug_log( "CF7 #$form_id blocked — too fast" ); return; } - // Honeypot field (random name) - foreach ($_POST as $key => $val) { - if (strpos($key, 'dapper_cf7_hp_') === 0 && strlen(trim($val)) > 0) { + // Honeypot + foreach ( $posted as $key => $val ) { + if ( strpos( $key, 'dapper_cf7_hp_' ) === 0 && strlen( trim( $val ) ) > 0 ) { $abort = true; - $submission->add_error('dapper_honeypot', 'Spam detected.'); - dapper_debug_log("CF7 #$form_id blocked — honeypot filled"); + $submission->add_error( 'dapper_honeypot', 'Spam detected.' ); + dapper_debug_log( "CF7 #$form_id blocked — honeypot filled" ); return; } } - // Optional: require JS-set token (stronger) - $token = trim($_POST['dapper_cf7_token'] ?? ''); - if (empty($token) || strpos($token, 'cf7human_') !== 0) { + // Old JS token (keep for extra layer) + $token = trim( $posted['dapper_cf7_token'] ?? '' ); + if ( empty( $token ) || strpos( $token, 'cf7human_' ) !== 0 ) { $abort = true; - $submission->add_error('dapper_js', 'Please enable JavaScript and try again.'); - dapper_debug_log("CF7 #$form_id blocked — missing/invalid JS token"); + $submission->add_error( 'dapper_js', 'Please enable JavaScript and try again.' ); + dapper_debug_log( "CF7 #$form_id blocked — missing/invalid JS token" ); } } - // Add fields + JS to every CF7 form - add_filter('wpcf7_form_elements', 'dapper_cf7_inject_anti_spam_fields'); - - function dapper_cf7_inject_anti_spam_fields($form) { - $honeypot_name = 'dapper_cf7_hp_' . wp_generate_password(7, false); - + add_filter( 'wpcf7_form_elements', 'dapper_cf7_inject_anti_spam_fields' ); + function dapper_cf7_inject_anti_spam_fields( $form ) { + $honeypot_name = 'dapper_cf7_hp_' . wp_generate_password( 7, false ); $hidden_fields = '

- +

'; - - // Insert just before - $form = str_replace('', $hidden_fields . '', $form); - - return $form; + return str_replace( '', $hidden_fields . '', $form ); } - // Tiny JS — add to footer when CF7 shortcode exists on page - add_action('wp_footer', 'dapper_cf7_anti_spam_js', 90); - + add_action( 'wp_footer', 'dapper_cf7_anti_spam_js', 90 ); function dapper_cf7_anti_spam_js() { - if (!function_exists('wpcf7') || !did_action('wp_footer')) return; + if ( ! did_action( 'wpcf7_enqueue_scripts' ) ) return; ?> - - - -

Quick check to help stop spam. Thanks!

- '; - - // 1. Try to insert before the submit input/button (most common cases) - // Look for or - $form = preg_replace( - '/(<(?:input|button)[^>]*type=["\']submit["\'][^>]*>)/i', - $checkbox_html . '$1', - $form, - 1 // limit to first match - ); - - // 2. If that didn't work (rare), try before the wrapping

of submit - if ( strpos( $form, $checkbox_html ) === false ) { - $form = preg_replace( - '/(]*>[\s\S]*?(?:]*type=["\']submit["\'][^>]*>|<\/button>)[\s\S]*?<\/p>)/i', - $checkbox_html . '$1', - $form, - 1 - ); - } - - // 3. Ultimate fallback: just before - if ( strpos( $form, $checkbox_html ) === false ) { - $form = str_replace( '', $checkbox_html . '', $form ); - } - - return $form; -} - - // 2. Very small JS — runs on every page that has CF7 (cheap) - add_action( 'wp_footer', 'dapper_cf7_human_checkbox_js', 95 ); - - function dapper_cf7_human_checkbox_js() { - // Only output if at least one CF7 form exists on page - if ( ! did_action( 'wpcf7_enqueue_scripts' ) ) { - return; + // 2. NEW: Visible checkbox — use reliable append method + add_filter( 'wpcf7_form_elements', 'dapper_cf7_append_human_checkbox', 30 ); // Higher priority = later + function dapper_cf7_append_human_checkbox( $form ) { + if ( get_option( 'dapper_enable_cf7_human_checkbox', 'on' ) !== 'on' ) { + return $form; } + $unique = uniqid(); + $checkbox_html = ' +

+ + + +

Quick check to stop spam — thank you!

+
'; + + // Append right before the closing — most reliable + return str_replace( '', $checkbox_html . '', $form ); + } + + // Tiny JS to set token + disable submit until checked + add_action( 'wp_footer', 'dapper_cf7_human_checkbox_js', 100 ); + function dapper_cf7_human_checkbox_js() { + if ( ! did_action( 'wpcf7_enqueue_scripts' ) ) return; ?> add_error( 'dapper_human', __( 'Please confirm you are human.', 'dapper' ) ); - dapper_debug_log( "CF7 #{$contact_form->id()} blocked — human checkbox not checked" ); + $submission->add_error( 'dapper_human', 'Please confirm you are human.' ); + dapper_debug_log( "CF7 #{$contact_form->id()} blocked — checkbox not checked" ); return; } - // B. Token must exist and look valid $token = trim( $posted['dapper_cf7_human_token'] ?? '' ); - if ( empty( $token ) || strpos( $token, 'cf7_human_' ) !== 0 || strlen( $token ) < 18 ) { + if ( empty( $token ) || strpos( $token, 'cf7_human_' ) !== 0 || strlen( $token ) < 20 ) { $abort = true; - $submission->add_error( 'dapper_human', __( 'Verification failed. Please try again.', 'dapper' ) ); + $submission->add_error( 'dapper_human', 'Verification failed — please try again.' ); dapper_debug_log( "CF7 #{$contact_form->id()} blocked — invalid/missing human token" ); return; } - // C. Not submitted in < 4 seconds (very strong signal of bot) $time = (int) ( $posted['dapper_cf7_human_time'] ?? 0 ); if ( $time && ( time() - $time < 4 ) ) { $abort = true; - $submission->add_error( 'dapper_human', __( 'Submission too fast — please try again.', 'dapper' ) ); + $submission->add_error( 'dapper_human', 'Submission too fast — please try again.' ); dapper_debug_log( "CF7 #{$contact_form->id()} blocked — human check too fast" ); } } @@ -1797,7 +1724,7 @@ function dapper_register_settings() { register_setting('dapper-backup-group', 'dapper_backup_plugins'); register_setting('dapper-backup-group', 'dapper_backup_include_media'); register_setting('dapper-settings-group', 'dapper_enable_paypal_human_check'); - register_setting( 'dapper-settings-group', 'dapper_enable_cf7_human_checkbox' ); + register_setting('dapper-settings-group', 'dapper_enable_cf7_human_checkbox'); // Add other settings as needed }