Agent Stamp: Agents, Readiness, Orders, Rules. * * The Agents tab shows every agent visit the store keeps (90 days): the last 7, 30 or 90 days. * * @package AgentStamp */ if ( ! defined( 'ABSPATH' ) ) { exit; } class AgentStamp_Admin { public static function init() { add_action( 'admin_post_agent_stamp_rules', array( __CLASS__, 'save_rules' ) ); add_action( 'admin_post_agent_stamp_fix', array( __CLASS__, 'apply_fix' ) ); } /** * @return string */ private static function current_tab() { // Read-only view choice, checked against a fixed list. $tab = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended if ( '' === $tab && isset( $_GET['period'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended $tab = 'orders'; } return in_array( $tab, array( 'agents', 'readiness', 'orders', 'rules' ), true ) ? $tab : 'agents'; } /** * @param string $tab * @param array $args * @return string */ public static function url( $tab, array $args = array() ) { return add_query_arg( array_merge( array( 'page' => 'agent-stamp', 'tab' => $tab ), $args ), admin_url( 'admin.php' ) ); } /** * @return string The current tab. */ public static function render_tabs() { $current = self::current_tab(); $tabs = array( 'agents' => __( 'Agents', 'agent-stamp' ), 'readiness' => __( 'Readiness', 'agent-stamp' ), 'orders' => __( 'Orders', 'agent-stamp' ), 'rules' => __( 'Rules', 'agent-stamp' ), ); echo ''; // Result of a one-click fix or a save. if ( isset( $_GET['agent_stamp_done'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended echo '
' . esc_html__( 'Saved.', 'agent-stamp' ) . '
'; foreach ( self::funnel_periods() as $index => $period ) { $label = sprintf( /* translators: %d: days */ __( 'Last %d days', 'agent-stamp' ), $period ); if ( $index > 0 ) { echo ' · '; } if ( $period === $days ) { echo '' . esc_html( $label ) . ''; } else { echo '' . esc_html( $label ) . ''; } } echo '
'; if ( 0 === $funnel['sessions'] ) { echo '' . esc_html__( 'That is normal at first. Agent visits show here as they happen, from the first page an agent opens to the order, with the reason when your store stops one. A person\'s visit is not recorded.', 'agent-stamp' ) . '
'; echo '' . esc_html__( 'Fix what would stop an agent before the first one arrives.', 'agent-stamp' ) . ' ' . esc_html__( 'Open Readiness', 'agent-stamp' ) . '
'; self::render_blind_spots(); return; } $visited = sprintf( /* translators: %d: visits */ _n( 'AI agents visited %d time', 'AI agents visited %d times', $funnel['sessions'], 'agent-stamp' ), $funnel['sessions'] ); $paid = sprintf( /* translators: 1: orders, 2: revenue */ _n( 'and paid for %1$d order (%2$s).', 'and paid for %1$d orders (%2$s).', $funnel['orders'], 'agent-stamp' ), $funnel['orders'], self::money( $funnel['revenue'] ) ); echo '' . esc_html( sprintf( /* translators: 1: visits, 2: cart value */ _n( '%1$d visit hit an error from your store. Carts stopped that way: %2$s.', '%1$d visits hit an error from your store. Carts stopped that way: %2$s.', $funnel['failed'], 'agent-stamp' ), $funnel['failed'], self::money( $funnel['lost_value'] ) ) ) . '
'; } if ( $funnel['scripts'] > 0 ) { echo '' . esc_html( sprintf( /* translators: 1: script visits, 2: failed ones */ _n( 'Scripts also reached the cart %1$d time, %2$d with an error. They are not shoppers, so they are not counted above. Card testers look like this: see By agent.', 'Scripts also reached the cart %1$d times, %2$d with an error. They are not shoppers, so they are not counted above. Card testers look like this: see By agent.', $funnel['scripts'], 'agent-stamp' ), $funnel['scripts'], $funnel['script_failures'] ) ) . '
'; } $steps = array( __( 'Visited', 'agent-stamp' ) => $funnel['sessions'], __( 'Opened a product', 'agent-stamp' ) => $funnel['product'], __( 'Built a cart', 'agent-stamp' ) => $funnel['cart'], __( 'Reached checkout', 'agent-stamp' ) => $funnel['checkout'], __( 'Visits that paid', 'agent-stamp' ) => $funnel['ordered'], ); echo '| ' . esc_html( $label ) . ' | ' . esc_html( (string) $count ) . ' |
' . esc_html__( 'No failures recorded.', 'agent-stamp' ) . '
'; } else { echo '| ' . esc_html__( 'Reason', 'agent-stamp' ) . ' | ' . esc_html__( 'Visits', 'agent-stamp' ) . ' | ' . esc_html__( 'Cart value', 'agent-stamp' ) . ' | ' . esc_html__( 'What to do', 'agent-stamp' ) . ' |
|---|---|---|---|
| ' . esc_html( $explain['title'] ) . '';
if ( '' !== $reason['message'] ) {
echo ' ' . esc_html( $reason['message'] ) . ''; } echo ' | ' . esc_html( (string) $reason['sessions'] ) . ' | ' . esc_html( 'no_error' === $reason['reason'] ? '—' : self::money( $reason['value'] ) ) . ' | ' . esc_html( $explain['fix'] ) . ' |
| ' . esc_html__( 'Agent', 'agent-stamp' ) . ' | ' . esc_html__( 'Visits', 'agent-stamp' ) . ' | ' . esc_html__( 'Cart', 'agent-stamp' ) . ' | ' . esc_html__( 'Checkout', 'agent-stamp' ) . ' | ' . esc_html__( 'Paid', 'agent-stamp' ) . ' | ' . esc_html__( 'Revenue', 'agent-stamp' ) . ' | ' . esc_html__( 'Store errors', 'agent-stamp' ) . ' |
|---|---|---|---|---|---|---|
| ' . esc_html( $agent['agent'] ) . ' | ' . esc_html( (string) $agent['sessions'] ) . ' | ' . esc_html( (string) $agent['cart'] ) . ' | ' . esc_html( (string) $agent['checkout'] ) . ' | ' . esc_html( (string) $agent['ordered'] ) . ' | ' . esc_html( self::money( $agent['revenue'] ) ) . ' | ' . esc_html( (string) $agent['failed'] ) . ' |
| ' . esc_html__( 'When', 'agent-stamp' ) . ' | ' . esc_html__( 'Agent', 'agent-stamp' ) . ' | ' . esc_html__( 'Step', 'agent-stamp' ) . ' | ' . esc_html__( 'Reason', 'agent-stamp' ) . ' | ' . esc_html__( 'What the store said', 'agent-stamp' ) . ' |
|---|---|---|---|---|
| ' . esc_html( wp_date( 'M j H:i', (int) $row['created_at'] ) ) . ' | ' . esc_html( $row['agent'] ) . ' | ' . esc_html( $row['stage'] ) . ' | ' . esc_html( $explain['title'] ) . ' | ' . esc_html( $row['message'] ) . ' |
' . esc_html( sprintf( /* translators: 1: edge network, 2: where to look */ __( '%1$s is in front of this store. Agents it refuses never reach WordPress, so they are not counted here. In %1$s, %2$s', 'agent-stamp' ), $edge, AgentStamp_Readiness::edge_settings( $edge ) ) ) . '
' . esc_html( sprintf( /* translators: %s: page cache plugin, or "A page cache" */ __( '%s is on. Pages it serves from cache never run WordPress, so agent visits to them are not counted here. Cart and checkout are normally not cached, so those steps still count.', 'agent-stamp' ), $cache ) ) . '
' . esc_html__( 'What this cannot see: requests a firewall or CDN refused before WordPress ran (check your Cloudflare or host AI-bot setting), pages a full-page cache served, and agent browsers that look like an ordinary person until checkout. Cart value is the cart at the moment the store stopped the agent.', 'agent-stamp' ) . '
'; } public static function render_readiness() { $kept = min( 30, AgentStamp_Events::keep_days() ); $funnel = AgentStamp_Sensor::summary( $kept ); $readiness = AgentStamp_Readiness::check( AgentStamp_Readiness::facts( $funnel['blocked'], $kept ) ); echo '' . esc_html__( 'Can an AI agent find your products, read them, and check out? Each line says what to change.', 'agent-stamp' ) . '
'; $icons = array( 'pass' => '✔', 'warn' => '!', 'fail' => '✖', ); $colors = array( 'pass' => '#008a20', 'warn' => '#996800', 'fail' => '#d63638', ); echo '| ' . esc_html( $icons[ $check['status'] ] ) . ' | '; echo '' . esc_html( $check['title'] ) . ' | ' . esc_html( $check['detail'] );
if ( '' !== $check['fix'] ) {
echo ' ' . esc_html( $check['fix'] ) . ''; } echo ' | '; if ( '' !== $check['action'] ) { echo ''; } echo ' |
' . esc_html__( 'Share the outside view of any store at getagentstamp.com/check.', 'agent-stamp' ) . '
'; } public static function apply_fix() { if ( ! current_user_can( 'manage_woocommerce' ) ) { wp_die( esc_html__( 'You cannot change this setting.', 'agent-stamp' ), 403 ); } check_admin_referer( 'agent_stamp_fix' ); $fix = isset( $_POST['fix'] ) ? sanitize_key( wp_unslash( $_POST['fix'] ) ) : ''; if ( 'enable_guest_checkout' === $fix ) { update_option( 'woocommerce_enable_guest_checkout', 'yes' ); } elseif ( 'enable_fast_lane' === $fix ) { $saved = get_option( AgentStamp_Policy::OPTION, array() ); $saved = is_array( $saved ) ? $saved : array(); $saved['fast_lane'] = true; update_option( AgentStamp_Policy::OPTION, AgentStamp_Policy::sanitize( $saved ) ); } wp_safe_redirect( self::url( 'readiness', array( 'agent_stamp_done' => 1 ) ) ); exit; } public static function render_rules() { $saved = get_option( AgentStamp_Policy::OPTION, array() ); $rules = AgentStamp_Policy::sanitize( is_array( $saved ) ? $saved : array() ); $hold = array( 'allow' => __( 'Allow', 'agent-stamp' ), 'hold' => __( 'Allow, then put the paid order on hold for review', 'agent-stamp' ), ); $refuse = $hold + array( 'block' => __( 'Refuse, and ask the agent to identify itself', 'agent-stamp' ), ); echo '' . esc_html__( 'Each row is one kind of checkout. Nothing is blocked or held unless you choose it here. An agent with a verified signature is always accepted.', 'agent-stamp' ) . '
'; echo ''; } /** * A row with no control. The text is already translated. * * @param string $who * @param string $text */ private static function policy_row( $who, $text ) { echo '' . esc_html( $help ) . '