api = $api; $this->automation = $automation; $this->logger = $logger; add_action( 'admin_menu', array( $this, 'menu' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'assets' ) ); add_action( 'wp_ajax_authkey_action', array( $this, 'ajax' ) ); add_filter( 'plugin_action_links_' . plugin_basename( AUTHKEY_FILE ), array( $this, 'settings_link' ) ); } /** Return one scalar POST value with WordPress's request slashes removed. */ private function post_string( string $key, string $default = '' ) : string { if ( ! isset( $_POST[ $key ] ) || ! is_scalar( $_POST[ $key ] ) ) { return $default; } return wp_unslash( (string) $_POST[ $key ] ); } /** Return one scalar GET value with WordPress's request slashes removed. */ private function get_string( string $key, string $default = '' ) : string { if ( ! isset( $_GET[ $key ] ) || ! is_scalar( $_GET[ $key ] ) ) { return $default; } return wp_unslash( (string) $_GET[ $key ] ); } /** Read a bounded, de-duplicated list of selected WooCommerce order IDs. */ private function posted_order_ids() : array { if ( ! isset( $_POST['order_ids'] ) || ! is_array( $_POST['order_ids'] ) ) { return array(); } $order_ids = array_filter( wp_unslash( $_POST['order_ids'] ), 'is_scalar' ); $order_ids = array_map( 'absint', $order_ids ); $order_ids = array_filter( $order_ids ); return array_slice( array_values( array_unique( $order_ids ) ), 0, 50 ); } public function settings_link( array $links ) : array { array_unshift( $links, '' . esc_html__( 'Settings', 'authkey' ) . '' ); return $links; } public function menu() : void { $cap = 'manage_options'; add_menu_page( __( 'Authkey', 'authkey' ), __( 'Authkey', 'authkey' ), $cap, 'authkey', array( $this, 'page' ), 'dashicons-whatsapp', 56 ); foreach ( array( 'authkey' => __( 'Dashboard', 'authkey' ), 'authkey-sync-data' => __( 'Sync Data', 'authkey' ), 'authkey-automations' => __( 'WooCommerce Automation', 'authkey' ), 'authkey-templates' => __( 'Templates', 'authkey' ), 'authkey-settings' => __( 'Settings', 'authkey' ), 'authkey-logs' => __( 'Logs', 'authkey' ), 'authkey-help' => __( 'Help', 'authkey' ) ) as $slug => $title ) { add_submenu_page( 'authkey', $title, $title, $cap, $slug, array( $this, 'page' ) ); } } public function assets( string $hook ) : void { if ( false === strpos( $hook, 'authkey' ) ) { return; } wp_enqueue_style( 'authkey-admin', AUTHKEY_URL . 'assets/css/admin.css', array(), AUTHKEY_VERSION ); wp_enqueue_script( 'authkey-admin', AUTHKEY_URL . 'assets/js/admin.js', array( 'jquery' ), AUTHKEY_VERSION, true ); wp_localize_script( 'authkey-admin', 'authkeyAdmin', array( 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'authkey_admin' ), 'strings' => array( 'confirm' => __( 'Are you sure?', 'authkey' ), 'connected' => __( 'Connected.', 'authkey' ), 'template_variable' => __( 'Template variable', 'authkey' ), 'map_template_variables' => __( 'Map template variables', 'authkey' ), 'map_template_values' => __( 'Choose the WooCommerce value to send for each variable.', 'authkey' ), 'no_template_variables' => __( 'This template has no variables to map.', 'authkey' ), 'mapping_choices' => array( 'customer_name' => __( 'Customer name', 'authkey' ), 'billing_first_name' => __( 'Customer first name', 'authkey' ), 'billing_last_name' => __( 'Customer last name', 'authkey' ), 'order_id' => __( 'Order ID', 'authkey' ), 'order_number' => __( 'Order number', 'authkey' ), 'product_names' => __( 'Product name(s)', 'authkey' ), 'order_total' => __( 'Order amount', 'authkey' ), 'currency' => __( 'Order currency', 'authkey' ), 'order_status' => __( 'Order status', 'authkey' ), 'billing_phone' => __( 'Billing phone', 'authkey' ), 'billing_email' => __( 'Billing email', 'authkey' ), 'store_name' => __( 'Store name', 'authkey' ), 'store_url' => __( 'Store URL', 'authkey' ) ) ) ) ); } public function ajax() : void { check_ajax_referer( 'authkey_admin', 'nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'message' => __( 'Permission denied.', 'authkey' ) ), 403 ); } $action = sanitize_key( $this->post_string( 'task' ) ); if ( 'login' === $action ) { $result = $this->api->login( sanitize_text_field( $this->post_string( 'username' ) ), $this->post_string( 'password' ) ); $this->json( $result ); } if ( 'sync_templates' === $action ) { $this->json( $this->api->templates( true ) ); } if ( 'disconnect' === $action ) { Settings::disconnect(); wp_send_json_success(); } if ( 'save_automation' === $action ) { $this->save_automation(); } if ( 'delete_automation' === $action ) { $automation_id = absint( $this->post_string( 'id' ) ); if ( ! $automation_id ) { wp_send_json_error( array( 'message' => __( 'A valid automation is required.', 'authkey' ) ), 400 ); } global $wpdb; $wpdb->delete( Database::table( 'automations' ), array( 'id' => $automation_id ), array( '%d' ) ); wp_send_json_success(); } if ( 'toggle_automation' === $action ) { $automation_id = absint( $this->post_string( 'id' ) ); if ( ! $automation_id ) { wp_send_json_error( array( 'message' => __( 'A valid automation is required.', 'authkey' ) ), 400 ); } global $wpdb; $wpdb->update( Database::table( 'automations' ), array( 'enabled' => '1' === $this->post_string( 'enabled' ) ? 1 : 0 ), array( 'id' => $automation_id ), array( '%d' ), array( '%d' ) ); wp_send_json_success(); } wp_send_json_error( array( 'message' => __( 'Unknown action.', 'authkey' ) ), 400 ); } private function json( array|\WP_Error $result ) : void { is_wp_error( $result ) ? wp_send_json_error( array( 'message' => $result->get_error_message() ), 400 ) : wp_send_json_success( $result ); } private function save_automation() : void { $raw_mappings = $this->post_string( 'mappings', '{}' ); $mappings = json_decode( $raw_mappings, true ); if ( JSON_ERROR_NONE !== json_last_error() || ! is_array( $mappings ) ) { wp_send_json_error( array( 'message' => __( 'Template mappings must contain valid JSON.', 'authkey' ) ), 400 ); } $sanitized_mappings = Automation::sanitize_mappings( $mappings ); if ( count( $sanitized_mappings ) !== count( $mappings ) ) { wp_send_json_error( array( 'message' => __( 'One or more template mappings are invalid.', 'authkey' ) ), 400 ); } $trigger = sanitize_key( $this->post_string( 'trigger_name' ) ); if ( ! array_key_exists( $trigger, $this->triggers() ) ) { wp_send_json_error( array( 'message' => __( 'Choose a valid WooCommerce trigger.', 'authkey' ) ), 400 ); } $data = array( 'name' => sanitize_text_field( $this->post_string( 'name' ) ), 'enabled' => '1' === $this->post_string( 'enabled' ) ? 1 : 0, 'trigger_name' => $trigger, 'template' => sanitize_text_field( $this->post_string( 'template' ) ), 'template_id' => absint( $this->post_string( 'template_id' ) ), 'recipient_type' => 'billing', 'recipient_value' => '', 'mappings' => wp_json_encode( $sanitized_mappings ), 'conditions' => wp_json_encode( array() ), 'delay_minutes' => 0, 'updated_at' => current_time( 'mysql', true ), ); if ( empty( $data['name'] ) || empty( $data['template'] ) || empty( $data['template_id'] ) ) { wp_send_json_error( array( 'message' => __( 'Automation name and a WhatsApp template are required.', 'authkey' ) ), 400 ); } $id = absint( $this->post_string( 'id' ) ); global $wpdb; $saved = $id ? $wpdb->update( Database::table( 'automations' ), $data, array( 'id' => $id ) ) : false; if ( ! $id ) { $data['created_at'] = current_time( 'mysql', true ); $saved = $wpdb->insert( Database::table( 'automations' ), $data ); } if ( false === $saved ) { wp_send_json_error( array( 'message' => __( 'Could not save this automation. Please refresh this page to apply the database update and try again.', 'authkey' ) ), 500 ); } wp_send_json_success( array( 'id' => $id ?: $wpdb->insert_id ) ); } public function page() : void { if ( ! current_user_can( 'manage_options' ) ) { return; } $page = sanitize_key( $this->get_string( 'page', 'authkey' ) ); echo '

' . esc_html__( 'Authkey', 'authkey' ) . '

'; if ( 'authkey-settings' === $page ) { $this->settings(); } elseif ( 'authkey-sync-data' === $page ) { $this->sync_data(); } elseif ( 'authkey-templates' === $page ) { $this->templates(); } elseif ( 'authkey-automations' === $page ) { $this->automations(); } elseif ( 'authkey-logs' === $page ) { $this->logs(); } elseif ( 'authkey-help' === $page ) { $this->help(); } else { $this->dashboard(); } echo '
'; } private function dashboard() : void { global $wpdb; $c = Settings::credentials(); $count = (int) $wpdb->get_var( 'SELECT COUNT(*) FROM ' . Database::table( 'automations' ) . ' WHERE enabled = 1' ); echo '

' . esc_html__( 'Dashboard', 'authkey' ) . '

' . esc_html__( 'Monitor your Authkey connection, balance, and WooCommerce automations.', 'authkey' ) . '

' . esc_html__( 'Connection', 'authkey' ) . '

' . esc_html( Settings::connected() ? __( 'Connected', 'authkey' ) : __( 'Not connected', 'authkey' ) ) . '

' . esc_html( $c['user_name'] ) . '

' . esc_html__( 'Balance', 'authkey' ) . '

' . esc_html( $c['balance'] . ' ' . $c['currency'] ) . '

' . esc_html__( 'Active automations', 'authkey' ) . '

' . esc_html( (string) $count ) . '

' . esc_html__( 'Recent logs', 'authkey' ) . '

' . esc_html__( 'Latest delivery activity from your automations.', 'authkey' ) . '

'; $this->log_table( $this->logger->recent( 10 ) ); echo '
'; } private function settings() : void { echo '

' . esc_html__( 'Settings', 'authkey' ) . '

' . esc_html__( 'Configure the plugin and connect your Authkey account.', 'authkey' ) . '

'; if ( isset( $_POST['authkey_save_settings'] ) ) { check_admin_referer( 'authkey_settings' ); $settings = Settings::all(); foreach ( array( 'enabled','logs','retry','debug','delete_data' ) as $key ) { $settings[ $key ] = isset( $_POST[ $key ] ) ? 'yes' : 'no'; } update_option( 'authkey_settings', $settings ); echo '

' . esc_html__( 'Settings saved.', 'authkey' ) . '

'; } $s = Settings::all(); echo '

' . esc_html__( 'Plugin settings', 'authkey' ) . '

'; wp_nonce_field( 'authkey_settings' ); foreach ( array( 'enabled' => __( 'Enable plugin', 'authkey' ), 'logs' => __( 'Enable logs', 'authkey' ), 'retry' => __( 'Retry failed requests once', 'authkey' ), 'debug' => __( 'Debug mode', 'authkey' ), 'delete_data' => __( 'Delete data on uninstall', 'authkey' ) ) as $key => $label ) { echo ''; } submit_button( __( 'Save settings', 'authkey' ), 'primary', 'authkey_save_settings' ); echo '

' . esc_html__( 'Connect Authkey', 'authkey' ) . '

' . esc_html__( 'Sign in to connect your Authkey account.', 'authkey' ) . '

'; } private function templates() : void { $templates = $this->api->templates(); echo '

' . esc_html__( 'Templates', 'authkey' ) . '

' . esc_html__( 'Review the WhatsApp templates available in your Authkey account.', 'authkey' ) . '

'; if ( is_wp_error( $templates ) ) { echo '

' . esc_html( $templates->get_error_message() ) . '

'; return; } $items = $this->template_items( $templates ); echo '
'; foreach ( $items as $template ) { $body = (string) ( $template['temp_body'] ?? $template['full_content'] ?? $template['body'] ?? $template['message'] ?? '' ); preg_match_all( '/{{\s*(\d+)\s*}}/', $body, $matches ); $status = isset( $template['temp_status'] ) ? ( (int) $template['temp_status'] === 1 ? __( 'Active', 'authkey' ) : __( 'Inactive', 'authkey' ) ) : ( $template['status'] ?? '' ); echo ''; } echo '
' . esc_html__( 'Template Name', 'authkey' ) . '' . esc_html__( 'Category', 'authkey' ) . '' . esc_html__( 'Language', 'authkey' ) . '' . esc_html__( 'Status', 'authkey' ) . '' . esc_html__( 'Variables', 'authkey' ) . '' . esc_html__( 'Preview', 'authkey' ) . '
' . esc_html( $template['temp_name'] ?? $template['name'] ?? $template['template_name'] ?? '' ) . '' . esc_html( $template['temp_category'] ?? $template['category'] ?? '' ) . '' . esc_html( $template['temp_language'] ?? $template['language'] ?? '' ) . '' . esc_html( $status ) . '' . esc_html( implode( ', ', $matches[0] ?? array() ) ) . '' . esc_html( wp_trim_words( $body, 15 ) ) . '
'; } private function sync_data() : void { echo '

' . esc_html__( 'Sync Data', 'authkey' ) . '

' . esc_html__( 'Add WooCommerce billing contacts to an Authkey contact list for campaigns.', 'authkey' ) . '

'; if ( ! function_exists( 'wc_get_orders' ) ) { echo '

' . esc_html__( 'WooCommerce must be active to sync order contacts.', 'authkey' ) . '

'; return; } $list_name = ''; $country_code = '91'; if ( isset( $_POST['authkey_sync_order_data'] ) ) { check_admin_referer( 'authkey_sync_order_data' ); $list_name = sanitize_text_field( $this->post_string( 'list_name' ) ); $country_code = preg_replace( '/[^0-9]/', '', $this->post_string( 'country_code', '91' ) ); $order_ids = $this->posted_order_ids(); if ( '' === $list_name || empty( $order_ids ) ) { echo '

' . esc_html__( 'Enter a contact list name and select at least one order.', 'authkey' ) . '

'; } else { $synced = 0; $failed = 0; foreach ( $order_ids as $order_id ) { $order = wc_get_order( $order_id ); if ( ! $order ) { $failed++; continue; } $result = $this->api->add_list_contact( $list_name, $country_code, (string) $order->get_billing_phone(), (string) $order->get_formatted_billing_full_name() ); is_wp_error( $result ) ? $failed++ : $synced++; } echo '

' . esc_html( sprintf( __( '%1$d contact(s) synced to "%2$s". %3$d failed.', 'authkey' ), $synced, $list_name, $failed ) ) . '

'; } } $page = max( 1, absint( $this->get_string( 'paged' ) ) ); $results = wc_get_orders( array( 'limit' => 50, 'paged' => $page, 'orderby' => 'date', 'order' => 'DESC', 'return' => 'objects', 'paginate' => true ) ); echo '
'; wp_nonce_field( 'authkey_sync_order_data' ); echo '
'; foreach ( $results->orders as $order ) { echo ''; } echo '
' . esc_html__( 'Order', 'authkey' ) . '' . esc_html__( 'Customer', 'authkey' ) . '' . esc_html__( 'Billing phone', 'authkey' ) . '' . esc_html__( 'Date', 'authkey' ) . '
#' . esc_html( $order->get_order_number() ) . '' . esc_html( $order->get_formatted_billing_full_name() ) . '' . esc_html( $order->get_billing_phone() ) . '' . esc_html( $order->get_date_created() ? wc_format_datetime( $order->get_date_created() ) : '' ) . '
'; if ( $results->max_num_pages > 1 ) { echo '
' . wp_kses_post( paginate_links( array( 'base' => add_query_arg( 'paged', '%#%' ), 'format' => '', 'current' => $page, 'total' => $results->max_num_pages ) ) ) . '
'; } } private function template_items( array $response ) : array { $items = $response['templates'] ?? $response['data'] ?? $response['result'] ?? $response; if ( isset( $items['templates'] ) && is_array( $items['templates'] ) ) { $items = $items['templates']; } return is_array( $items ) ? array_values( array_filter( $items, 'is_array' ) ) : array(); } private function automations() : void { global $wpdb; $rules = $wpdb->get_results( 'SELECT * FROM ' . Database::table( 'automations' ) . ' ORDER BY id DESC' ); $response = $this->api->templates(); $templates = is_wp_error( $response ) ? array() : $this->template_items( $response ); echo '

' . esc_html__( 'WooCommerce automations', 'authkey' ) . '

' . esc_html__( 'Send the right WhatsApp template when an order or customer event occurs.', 'authkey' ) . '

'; echo ''; if ( is_wp_error( $response ) ) { echo '

' . esc_html__( 'Connect Authkey and sync templates before creating an automation.', 'authkey' ) . '

'; } echo ''; foreach ( $rules as $rule ) { $stored_mappings = json_decode( $rule->mappings ?: '{}', true ); $rule_data = array( 'id' => (int) $rule->id, 'name' => $rule->name, 'trigger' => $rule->trigger_name, 'templateId' => (int) $rule->template_id, 'mappings' => Automation::sanitize_mappings( is_array( $stored_mappings ) ? $stored_mappings : array() ) ); echo ''; } echo '
' . esc_html__( 'Name', 'authkey' ) . '' . esc_html__( 'Trigger', 'authkey' ) . '' . esc_html__( 'Template', 'authkey' ) . '' . esc_html__( 'Status', 'authkey' ) . '' . esc_html__( 'Actions', 'authkey' ) . '
' . esc_html( $rule->name ) . '' . esc_html( $this->triggers()[ $rule->trigger_name ] ?? $rule->trigger_name ) . '' . esc_html( $rule->template ) . '' . esc_html( $rule->enabled ? __( 'Enabled', 'authkey' ) : __( 'Disabled', 'authkey' ) ) . '
'; } private function triggers() : array { $triggers = array( 'order_created' => __( 'Order created', 'authkey' ), 'checkout_completed' => __( 'Checkout completed', 'authkey' ), 'payment_success' => __( 'Payment success', 'authkey' ), 'payment_failed' => __( 'Payment failed', 'authkey' ), 'customer_registered' => __( 'Customer registered', 'authkey' ), 'customer_login' => __( 'Customer login', 'authkey' ), 'coupon_used' => __( 'Coupon used', 'authkey' ) ); foreach ( array( 'pending','failed','on-hold','processing','completed','cancelled','refunded','draft','trash' ) as $status ) { $triggers[ 'status_' . $status ] = sprintf( __( 'Order %s', 'authkey' ), $status ); } return apply_filters( 'authkey_automation_triggers', $triggers ); } private function logs() : void { echo '

' . esc_html__( 'Logs', 'authkey' ) . '

' . esc_html__( 'Review recent Authkey delivery activity.', 'authkey' ) . '

'; $this->log_table( $this->logger->recent( 100 ) ); echo '
'; } private function log_table( array $logs ) : void { echo ''; foreach ( $logs as $log ) { echo ''; } echo '
' . esc_html__( 'Date', 'authkey' ) . '' . esc_html__( 'Order', 'authkey' ) . '' . esc_html__( 'Template', 'authkey' ) . '' . esc_html__( 'Trigger', 'authkey' ) . '' . esc_html__( 'Status', 'authkey' ) . '
' . esc_html( $log->created_at ) . '' . esc_html( $log->order_id ) . '' . esc_html( $log->template ) . '' . esc_html( $log->trigger_name ) . '' . esc_html( $log->status ) . '
'; } private function help() : void { echo '

' . esc_html__( 'Help', 'authkey' ) . '

' . esc_html__( 'Find documentation and support for the Authkey WooCommerce plugin.', 'authkey' ) . '

' . esc_html__( 'Documentation and website', 'authkey' ) . '

' . esc_html__( 'Support', 'authkey' ) . '

' . esc_html( sprintf( __( 'Version %s', 'authkey' ), AUTHKEY_VERSION ) ) . '

'; } }