init_hooks(); } /** * Initialize hooks. */ private function init_hooks() { add_action( 'admin_menu', array( $this, 'add_admin_menu' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) ); add_action( 'wp_ajax_afridirect_save_method', array( $this, 'ajax_save_method' ) ); add_action( 'wp_ajax_afridirect_delete_method', array( $this, 'ajax_delete_method' ) ); add_action( 'wp_ajax_afridirect_get_method', array( $this, 'ajax_get_method' ) ); } /** * Add admin menu. */ public function add_admin_menu() { $menu_manager = AfriDirect_Payment_Admin_Menu::instance(); $parent_slug = $menu_manager->get_menu_slug(); add_submenu_page( $parent_slug, __( 'Payment Methods', 'afridirect-payment' ), __( 'Payment Methods', 'afridirect-payment' ), $menu_manager->get_capability(), 'afridirect-payment-methods', array( $this, 'render_page' ) ); } /** * Enqueue scripts. */ public function enqueue_scripts( $hook ) { // Some admin setups/plugins can alter the $hook suffix. Fall back to the page query param. // This only decides whether to enqueue assets for the current screen; it does // not process or act on the value, so no nonce is required here. $is_methods_page = ( 'afridirect-payment_page_afridirect-payment-methods' === $hook ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended || ( isset( $_GET['page'] ) && 'afridirect-payment-methods' === $_GET['page'] ); if ( ! $is_methods_page ) { return; } wp_enqueue_media(); wp_enqueue_script( 'jquery-ui-sortable' ); wp_enqueue_style( 'afridirect-admin-methods', AFRIDIRECT_PAYMENT_PLUGIN_URL . 'admin/css/methods.css', array(), afridirect_payment_asset_ver( 'admin/css/methods.css' ) ); wp_enqueue_script( 'afridirect-admin-methods', AFRIDIRECT_PAYMENT_PLUGIN_URL . 'admin/js/methods.js', array( 'jquery', 'jquery-ui-sortable' ), afridirect_payment_asset_ver( 'admin/js/methods.js' ), true ); wp_localize_script( 'afridirect-admin-methods', 'afridirectMethods', array( 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'afridirect-methods-nonce' ), 'fields' => $this->get_field_templates(), ) ); } /** * Field definitions for the add/edit form, keyed by method type. * * @return array Type => field key => array( 'label', 'type' ). */ private function get_field_templates() { $templates = array(); foreach ( AfriDirect_Payment_Methods::get_definitions() as $type => $definition ) { $templates[ $type ] = $definition['fields']; } return $templates; } /** * Render admin page. */ public function render_page() { if ( ! current_user_can( AfriDirect_Payment_Admin_Menu::instance()->get_capability() ) ) { wp_die( esc_html__( 'You do not have sufficient permissions.', 'afridirect-payment' ) ); } $methods = AfriDirect_Payment_Methods::get_all(); $types = AfriDirect_Payment_Methods::get_types(); include AFRIDIRECT_PAYMENT_PLUGIN_DIR . 'admin/views/methods.php'; } /** * AJAX save method. */ public function ajax_save_method() { // Check nonce. if ( ! check_ajax_referer( 'afridirect-methods-nonce', 'nonce', false ) ) { wp_send_json_error( array( 'message' => __( 'Security check failed. Please refresh the page and try again.', 'afridirect-payment' ) ) ); } // Check capability. if ( ! current_user_can( AfriDirect_Payment_Admin_Menu::instance()->get_capability() ) ) { wp_send_json_error( array( 'message' => __( 'You do not have permission to perform this action.', 'afridirect-payment' ) ) ); } // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Structured array; each leaf is sanitized below and again in AfriDirect_Payment_Methods::sanitize_method(). $raw = ( isset( $_POST['method'] ) && is_array( $_POST['method'] ) ) ? wp_unslash( $_POST['method'] ) : array(); $method_data = array( 'id' => isset( $raw['id'] ) ? sanitize_key( $raw['id'] ) : '', 'label' => isset( $raw['label'] ) ? sanitize_text_field( $raw['label'] ) : '', 'type' => isset( $raw['type'] ) ? sanitize_key( $raw['type'] ) : '', 'instructions' => isset( $raw['instructions'] ) ? wp_kses_post( $raw['instructions'] ) : '', 'qr_attachment_id' => isset( $raw['qr_attachment_id'] ) ? absint( $raw['qr_attachment_id'] ) : 0, 'fields' => ( isset( $raw['fields'] ) && is_array( $raw['fields'] ) ) ? array_map( 'sanitize_text_field', $raw['fields'] ) : array(), 'copy_fields' => ( isset( $raw['copy_fields'] ) && is_array( $raw['copy_fields'] ) ) ? array_map( 'sanitize_key', $raw['copy_fields'] ) : array(), ); // Validate required fields. if ( empty( $method_data['label'] ) ) { wp_send_json_error( array( 'message' => __( 'Method label is required.', 'afridirect-payment' ) ) ); } if ( empty( $method_data['type'] ) ) { wp_send_json_error( array( 'message' => __( 'Method type is required.', 'afridirect-payment' ) ) ); } // Save method (ID will be auto-generated if missing). // AfriDirect_Payment_Methods::sanitize_method() applies type-aware sanitisation // (sanitize_email() for e-mail fields, wp_kses_post() for instructions, etc.). $result = AfriDirect_Payment_Methods::save( $method_data ); if ( $result ) { wp_send_json_success( array( 'message' => __( 'Method saved successfully.', 'afridirect-payment' ) ) ); } else { wp_send_json_error( array( 'message' => __( 'Failed to save method. Please ensure all required fields are filled.', 'afridirect-payment' ) ) ); } } /** * AJAX delete method. */ public function ajax_delete_method() { check_ajax_referer( 'afridirect-methods-nonce', 'nonce' ); if ( ! current_user_can( AfriDirect_Payment_Admin_Menu::instance()->get_capability() ) ) { wp_send_json_error( array( 'message' => __( 'Unauthorized.', 'afridirect-payment' ) ) ); } $method_id = isset( $_POST['method_id'] ) ? sanitize_key( $_POST['method_id'] ) : ''; if ( empty( $method_id ) ) { wp_send_json_error( array( 'message' => __( 'Invalid method ID.', 'afridirect-payment' ) ) ); } $result = AfriDirect_Payment_Methods::delete( $method_id ); if ( $result ) { wp_send_json_success( array( 'message' => __( 'Method deleted successfully.', 'afridirect-payment' ) ) ); } else { wp_send_json_error( array( 'message' => __( 'Failed to delete method.', 'afridirect-payment' ) ) ); } } /** * AJAX get method. */ public function ajax_get_method() { check_ajax_referer( 'afridirect-methods-nonce', 'nonce' ); if ( ! current_user_can( AfriDirect_Payment_Admin_Menu::instance()->get_capability() ) ) { wp_send_json_error( array( 'message' => __( 'Unauthorized.', 'afridirect-payment' ) ) ); } $method_id = isset( $_POST['method_id'] ) ? sanitize_key( $_POST['method_id'] ) : ''; if ( empty( $method_id ) ) { wp_send_json_error( array( 'message' => __( 'Invalid method ID.', 'afridirect-payment' ) ) ); } $method = AfriDirect_Payment_Methods::get( $method_id ); if ( ! $method ) { wp_send_json_error( array( 'message' => __( 'Method not found.', 'afridirect-payment' ) ) ); } // Add QR URL if attachment exists. if ( ! empty( $method['qr_attachment_id'] ) ) { $method['qr_url'] = wp_get_attachment_image_url( $method['qr_attachment_id'], 'full' ); } wp_send_json_success( array( 'method' => $method ) ); } }