count_sessions_by_ip( $context['ip_address'] ?? '', 3600 ); if ( $ip_sessions > 10 ) { $score += 40; } elseif ( $ip_sessions > 5 ) { $score += 20; } // Signal 2: Unusually high cart total. $avg = $this->get_average_cart_total(); if ( $avg > 0 ) { $total = (float) ( $context['total_amount'] ?? 0 ); if ( $total > $avg * 10 ) { $score += 25; } elseif ( $total > $avg * 5 ) { $score += 10; } } // Signal 3: Too many sessions from same API key in 1 hour. $key_sessions = $this->count_sessions_by_key( $context['agent_identity'] ?? '', 3600 ); if ( $key_sessions > 20 ) { $score += 25; } elseif ( $key_sessions > 10 ) { $score += 10; } // Signal 4: Attempting to buy all available stock. $cart_items = $context['cart_items'] ?? array(); foreach ( $cart_items as $item ) { $pid = (int) ( $item['variation_id'] ?? 0 ) ?: (int) $item['product_id']; $product = wc_get_product( $pid ); if ( $product && $product->managing_stock() ) { $stock = $product->get_stock_quantity(); if ( null !== $stock && $stock > 0 && $stock <= (int) $item['quantity'] ) { $score += 15; } } } return min( 100, $score ); } /** * Check if a fraud score exceeds the blocking threshold. * * @param int $score Fraud score. * * @return bool True if request should be blocked. */ public function is_blocked( int $score ): bool { $threshold = (int) get_option( 'aicommerce_fraud_threshold', self::DEFAULT_THRESHOLD ); return $score >= $threshold; } /** * Count sessions from a given IP within a time window. * * @param string $ip IP address. * @param int $seconds Time window in seconds. * * @return int Session count. */ private function count_sessions_by_ip( string $ip, int $seconds ): int { if ( empty( $ip ) ) { return 0; } global $wpdb; // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date $since = date( 'Y-m-d H:i:s', time() - $seconds ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching return (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}aicommerce_agent_sessions WHERE ip_address = %s AND created_at >= %s", $ip, $since ) ); } /** * Count sessions from a given API key within a time window. * * @param string $key_id Agent identity hash. * @param int $seconds Time window in seconds. * * @return int Session count. */ private function count_sessions_by_key( string $key_id, int $seconds ): int { if ( empty( $key_id ) || 'unknown' === $key_id ) { return 0; } global $wpdb; // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date $since = date( 'Y-m-d H:i:s', time() - $seconds ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching return (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}aicommerce_agent_sessions WHERE agent_identity = %s AND created_at >= %s", $key_id, $since ) ); } /** * Get average cart total from recent sessions. * * @return float Average total or 0. */ private function get_average_cart_total(): float { global $wpdb; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared $avg = $wpdb->get_var( "SELECT AVG(total_amount) FROM {$wpdb->prefix}aicommerce_agent_sessions WHERE total_amount > 0" ); return $avg ? (float) $avg : 0.0; } }