$order->get_id(), 'status' => $order->get_status(), 'date' => $order->get_date_created(), 'total' => $order->get_total() ); } /** * Obtenir les transactions avec les détails des commandes (compatible HPOS) */ function bankily_get_transactions_with_orders($where_clause = '', $limit_clause = '') { global $wpdb; $table_name = $wpdb->prefix . 'bankily_transactions'; // Récupérer d'abord les transactions $sql = "SELECT * FROM $table_name"; if ($where_clause) { $sql .= " WHERE $where_clause"; } $sql .= " ORDER BY created_at DESC"; if ($limit_clause) { $sql .= " $limit_clause"; } $transactions = $wpdb->get_results($sql); // Enrichir avec les données des commandes via WooCommerce foreach ($transactions as $transaction) { $order_data = bankily_get_order_data($transaction->order_id); if ($order_data) { $transaction->order_status = $order_data['status']; $transaction->order_date = $order_data['date']; $transaction->order_total = $order_data['total']; } else { $transaction->order_status = 'not_found'; $transaction->order_date = null; $transaction->order_total = 0; } } return $transactions; } /** * Créer le menu d'administration */ function bankily_bpay_admin_menu() { add_menu_page( __('B-PAY Transactions', 'bankily-bpay'), __('B-PAY Transactions', 'bankily-bpay'), 'manage_woocommerce', 'bankily-transactions', 'bankily_bpay_admin_page', 'dashicons-money-alt', 56 ); add_submenu_page( 'bankily-transactions', __('Tableau de bord', 'bankily-bpay'), __('Tableau de bord', 'bankily-bpay'), 'manage_woocommerce', 'bankily-transactions', 'bankily_bpay_admin_page' ); add_submenu_page( 'bankily-transactions', __('Transactions en attente', 'bankily-bpay'), __('En attente', 'bankily-bpay'), 'manage_woocommerce', 'bankily-pending', 'bankily_bpay_pending_page' ); add_submenu_page( 'bankily-transactions', __('Paramètres avancés', 'bankily-bpay'), __('Paramètres', 'bankily-bpay'), 'manage_woocommerce', 'bankily-settings', 'bankily_bpay_settings_page' ); } /** * Charger les scripts d'administration */ function bankily_bpay_admin_scripts($hook) { if (strpos($hook, 'bankily') === false) { return; } wp_enqueue_script('jquery'); wp_enqueue_script('bankily-admin', BANKILY_BPAY_PLUGIN_URL . 'assets/bankily-admin.js', array('jquery'), BANKILY_BPAY_VERSION, true); wp_enqueue_style('bankily-admin', BANKILY_BPAY_PLUGIN_URL . 'assets/bankily-admin.css', array(), BANKILY_BPAY_VERSION); wp_localize_script('bankily-admin', 'bankily_admin_ajax', array( 'ajax_url' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('bankily_admin_nonce'), 'strings' => array( 'checking' => __('Vérification en cours...', 'bankily-bpay'), 'success' => __('Succès', 'bankily-bpay'), 'failed' => __('Échec', 'bankily-bpay'), 'pending' => __('En attente', 'bankily-bpay'), 'error' => __('Erreur', 'bankily-bpay'), 'confirm_bulk' => __('Êtes-vous sûr de vouloir vérifier toutes les transactions sélectionnées ?', 'bankily-bpay') ) )); } /** * Page principale du tableau de bord */ function bankily_bpay_admin_page() { global $wpdb; $table_name = $wpdb->prefix . 'bankily_transactions'; // Statistiques générales $stats = $wpdb->get_row(" SELECT COUNT(*) as total, SUM(CASE WHEN status = 'TS' THEN 1 ELSE 0 END) as success, SUM(CASE WHEN status = 'TF' THEN 1 ELSE 0 END) as failed, SUM(CASE WHEN status = 'TA' THEN 1 ELSE 0 END) as pending, SUM(CASE WHEN status = 'TS' THEN amount ELSE 0 END) as total_amount FROM $table_name WHERE created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY) "); // Transactions récentes (compatible HPOS) $recent_transactions = bankily_get_transactions_with_orders('', 'LIMIT 20'); ?>

total); ?>

success); ?>

pending); ?>

failed); ?>

total_amount, 0, ',', ' '); ?> MRU

prefix . 'bankily_transactions'; // Pagination $per_page = 50; $current_page = isset($_GET['paged']) ? max(1, intval($_GET['paged'])) : 1; $offset = ($current_page - 1) * $per_page; // Filtres $where_clause = "WHERE status = 'TA'"; $search = isset($_GET['search']) ? sanitize_text_field($_GET['search']) : ''; if ($search) { $where_clause .= $wpdb->prepare(" AND (operation_id LIKE %s OR client_phone LIKE %s OR order_id LIKE %s)", '%' . $search . '%', '%' . $search . '%', '%' . $search . '%'); } // Compter le total $total_items = $wpdb->get_var("SELECT COUNT(*) FROM $table_name $where_clause"); $total_pages = ceil($total_items / $per_page); // Récupérer les transactions (compatible HPOS) $pending_transactions = bankily_get_transactions_with_orders( "status = 'TA'" . ($search ? $wpdb->prepare(" AND (operation_id LIKE %s OR client_phone LIKE %s OR order_id LIKE %s)", '%' . $search . '%', '%' . $search . '%', '%' . $search . '%') : ''), $wpdb->prepare("LIMIT %d OFFSET %d", $per_page, $offset) ); ?>

1): ?>
add_query_arg('paged', '%#%'), 'format' => '', 'prev_text' => __('« Précédent', 'bankily-bpay'), 'next_text' => __('Suivant »', 'bankily-bpay'), 'current' => $current_page, 'total' => $total_pages, )); ?>
operation_id); ?> transaction_id): ?>
TXN: transaction_id); ?>
#order_id; ?>
order_status); ?>
client_phone); ?> amount, 0, ',', ' '); ?> MRU status); ?> error_message): ?>
error_message, 0, 50)) . '...'; ?>
created_at)); ?> last_checked != $transaction->created_at) { echo date_i18n('d/m/Y H:i', strtotime($transaction->last_checked)); } else { echo '-'; } ?> check_count; ?> status == 'TA' && $transaction->check_count > 3): ?>
__('Succès', 'bankily-bpay'), 'TF' => __('Échec', 'bankily-bpay'), 'TA' => __('En attente', 'bankily-bpay'), ); return isset($labels[$status]) ? $labels[$status] : $status; } /** * Page des paramètres avancés */ function bankily_bpay_settings_page() { // Traitement de la sauvegarde if (isset($_POST['submit'])) { update_option('bankily_auto_check_interval', intval($_POST['auto_check_interval'])); update_option('bankily_max_check_attempts', intval($_POST['max_check_attempts'])); update_option('bankily_auto_mark_failed', isset($_POST['auto_mark_failed'])); echo '

' . __('Paramètres sauvegardés.', 'bankily-bpay') . '

'; } $auto_check_interval = get_option('bankily_auto_check_interval', 15); $max_check_attempts = get_option('bankily_max_check_attempts', 5); $auto_mark_failed = get_option('bankily_auto_mark_failed', false); ?>

prefix . 'bankily_transactions'; // Obtenir les détails de la transaction $transaction = $wpdb->get_row($wpdb->prepare( "SELECT * FROM $table_name WHERE id = %d", $transaction_id )); if (!$transaction) { wp_send_json_error(__('Transaction non trouvée.', 'bankily-bpay')); } // Créer une instance de la gateway pour utiliser check_transaction $gateway = new WC_Bankily_BPay_Gateway(); $result = $gateway->check_transaction($operation_id); if ($result && $result['errorCode'] == '0') { // Mettre à jour la transaction $wpdb->update( $table_name, array( 'status' => $result['status'], 'transaction_id' => $result['transactionId'], 'last_checked' => current_time('mysql'), 'check_count' => $transaction->check_count + 1, 'error_code' => $result['errorCode'], 'error_message' => isset($result['errorMessage']) ? $result['errorMessage'] : '' ), array('id' => $transaction_id) ); // Si transaction réussie, mettre à jour la commande WooCommerce if ($result['status'] == 'TS') { $order = wc_get_order($transaction->order_id); if ($order) { $order->payment_complete($result['transactionId']); $order->add_order_note(__('Paiement B-PAY confirmé via vérification manuelle. ID Transaction: ' . $result['transactionId'], 'bankily-bpay')); } } wp_send_json_success(array( 'status' => $result['status'], 'status_label' => bankily_get_status_label($result['status']), 'transaction_id' => $result['transactionId'], 'message' => __('Transaction vérifiée avec succès.', 'bankily-bpay') )); } else { // Mettre à jour le compteur même en cas d'erreur $wpdb->update( $table_name, array( 'last_checked' => current_time('mysql'), 'check_count' => $transaction->check_count + 1, 'error_code' => isset($result['errorCode']) ? $result['errorCode'] : '1', 'error_message' => isset($result['errorMessage']) ? $result['errorMessage'] : __('Erreur de vérification', 'bankily-bpay') ), array('id' => $transaction_id) ); wp_send_json_error(isset($result['errorMessage']) ? $result['errorMessage'] : __('Erreur lors de la vérification.', 'bankily-bpay')); } } /** * AJAX: Vérification en lot */ function bankily_ajax_bulk_check() { check_ajax_referer('bankily_admin_nonce', 'nonce'); if (!current_user_can('manage_woocommerce')) { wp_die(__('Permissions insuffisantes.', 'bankily-bpay')); } $transaction_ids = array_map('intval', $_POST['transaction_ids']); global $wpdb; $table_name = $wpdb->prefix . 'bankily_transactions'; $gateway = new WC_Bankily_BPay_Gateway(); $results = array(); foreach ($transaction_ids as $transaction_id) { $transaction = $wpdb->get_row($wpdb->prepare( "SELECT * FROM $table_name WHERE id = %d", $transaction_id )); if ($transaction) { $result = $gateway->check_transaction($transaction->operation_id); if ($result && $result['errorCode'] == '0') { $wpdb->update( $table_name, array( 'status' => $result['status'], 'transaction_id' => $result['transactionId'], 'last_checked' => current_time('mysql'), 'check_count' => $transaction->check_count + 1, 'error_code' => $result['errorCode'], 'error_message' => isset($result['errorMessage']) ? $result['errorMessage'] : '' ), array('id' => $transaction_id) ); if ($result['status'] == 'TS') { $order = wc_get_order($transaction->order_id); if ($order) { $order->payment_complete($result['transactionId']); $order->add_order_note(__('Paiement B-PAY confirmé via vérification groupée.', 'bankily-bpay')); } } $results[] = array( 'id' => $transaction_id, 'status' => 'success', 'new_status' => $result['status'] ); } else { $results[] = array( 'id' => $transaction_id, 'status' => 'error', 'message' => isset($result['errorMessage']) ? $result['errorMessage'] : __('Erreur de vérification', 'bankily-bpay') ); } } } wp_send_json_success($results); } /** * AJAX: Marquer comme échouées en lot */ function bankily_ajax_bulk_mark_failed() { check_ajax_referer('bankily_admin_nonce', 'nonce'); if (!current_user_can('manage_woocommerce')) { wp_die(__('Permissions insuffisantes.', 'bankily-bpay')); } $transaction_ids = array_map('intval', $_POST['transaction_ids']); global $wpdb; $table_name = $wpdb->prefix . 'bankily_transactions'; $updated = 0; foreach ($transaction_ids as $transaction_id) { $result = $wpdb->update( $table_name, array( 'status' => 'TF', 'last_checked' => current_time('mysql'), 'error_message' => __('Marqué comme échoué manuellement', 'bankily-bpay') ), array('id' => $transaction_id) ); if ($result) { $updated++; // Mettre à jour la commande WooCommerce $transaction = $wpdb->get_row($wpdb->prepare( "SELECT order_id FROM $table_name WHERE id = %d", $transaction_id )); if ($transaction) { $order = wc_get_order($transaction->order_id); if ($order && $order->get_status() == 'on-hold') { $order->update_status('failed', __('Paiement B-PAY marqué comme échoué manuellement.', 'bankily-bpay')); } } } } wp_send_json_success(array( 'updated' => $updated, 'message' => sprintf(__('%d transactions marquées comme échouées.', 'bankily-bpay'), $updated) )); } /** * AJAX: Exporter les transactions (compatible HPOS) */ function bankily_ajax_export_transactions() { check_ajax_referer('bankily_admin_nonce', 'nonce'); if (!current_user_can('manage_woocommerce')) { wp_die(__('Permissions insuffisantes.', 'bankily-bpay')); } // Récupérer les données avec compatibilité HPOS $transactions = bankily_get_transactions_with_orders(); // Headers pour le téléchargement CSV header('Content-Type: text/csv; charset=utf-8'); header('Content-Disposition: attachment; filename="bankily-transactions-' . date('Y-m-d') . '.csv"'); $output = fopen('php://output', 'w'); fprintf($output, chr(0xEF).chr(0xBB).chr(0xBF)); // BOM UTF-8 // En-têtes du CSV fputcsv($output, array( 'ID Transaction', 'ID Commande', 'ID Opération', 'Téléphone Client', 'Montant (MRU)', 'Statut Transaction', 'Statut Commande', 'Code Erreur', 'Message Erreur', 'Créé le', 'Dernière Vérification', 'Nombre de Vérifications' )); // Données foreach ($transactions as $transaction) { fputcsv($output, array( $transaction->transaction_id, $transaction->order_id, $transaction->operation_id, $transaction->client_phone, number_format($transaction->amount, 2), bankily_get_status_label($transaction->status), ucfirst($transaction->order_status ?: 'N/A'), $transaction->error_code, $transaction->error_message, $transaction->created_at, $transaction->last_checked, $transaction->check_count )); } fclose($output); exit; } /** * AJAX: Nettoyer les anciennes transactions */ function bankily_ajax_clean_old_transactions() { check_ajax_referer('bankily_admin_nonce', 'nonce'); if (!current_user_can('manage_woocommerce')) { wp_die(__('Permissions insuffisantes.', 'bankily-bpay')); } global $wpdb; $table_name = $wpdb->prefix . 'bankily_transactions'; $deleted = $wpdb->query(" DELETE FROM $table_name WHERE created_at < DATE_SUB(NOW(), INTERVAL 6 MONTH) "); wp_send_json_success(array( 'deleted' => $deleted, 'message' => sprintf(__('%d anciennes transactions supprimées.', 'bankily-bpay'), $deleted) )); } /** * AJAX: Ping de connexion */ function bankily_ajax_ping() { check_ajax_referer('bankily_admin_nonce', 'nonce'); wp_send_json_success(array('status' => 'online', 'timestamp' => time())); } // ============================================================================ // FONCTIONNALITÉS SUPPLÉMENTAIRES // ============================================================================ /** * Widget tableau de bord WordPress */ function bankily_add_dashboard_widget() { wp_add_dashboard_widget( 'bankily_bpay_dashboard_widget', __('B-PAY Bankily - Résumé', 'bankily-bpay'), 'bankily_dashboard_widget_content' ); } function bankily_dashboard_widget_content() { global $wpdb; $table_name = $wpdb->prefix . 'bankily_transactions'; $stats_24h = $wpdb->get_row(" SELECT COUNT(*) as total, SUM(CASE WHEN status = 'TS' THEN 1 ELSE 0 END) as success, SUM(CASE WHEN status = 'TA' THEN 1 ELSE 0 END) as pending, SUM(CASE WHEN status = 'TS' THEN amount ELSE 0 END) as total_amount FROM $table_name WHERE created_at >= DATE_SUB(NOW(), INTERVAL 24 HOUR) "); ?>

success); ?> | pending); ?>

total_amount, 0, ',', ' '); ?> MRU

prefix . 'bankily_transactions'; $urgent_count = $wpdb->get_var(" SELECT COUNT(*) FROM $table_name WHERE status = 'TA' AND created_at < DATE_SUB(NOW(), INTERVAL 2 HOUR) "); if ($urgent_count > 0) { $wp_admin_bar->add_node(array( 'id' => 'bankily-urgent-notifications', 'title' => '' . '' . sprintf(__('B-PAY: %d urgent', 'bankily-bpay'), $urgent_count) . '', 'href' => admin_url('admin.php?page=bankily-pending'), )); } } /** * Colonnes personnalisées dans la liste des commandes */ function bankily_add_order_column($columns) { $columns['bankily_status'] = __('B-PAY', 'bankily-bpay'); return $columns; } function bankily_show_order_column_content($column, $order_id) { if ($column !== 'bankily_status') { return; } $order = wc_get_order($order_id); if (!$order || $order->get_payment_method() !== 'bankily_bpay') { echo '-'; return; } bankily_display_order_status($order_id); } /** * Afficher le statut pour HPOS */ function bankily_show_order_column_content_hpos($column, $order) { if ($column !== 'bankily_status') { return; } if (!$order || $order->get_payment_method() !== 'bankily_bpay') { echo '-'; return; } bankily_display_order_status($order->get_id()); } /** * Fonction commune pour afficher le statut B-PAY */ function bankily_display_order_status($order_id) { global $wpdb; $table_name = $wpdb->prefix . 'bankily_transactions'; $transaction = $wpdb->get_row($wpdb->prepare( "SELECT * FROM $table_name WHERE order_id = %d ORDER BY created_at DESC LIMIT 1", $order_id )); if ($transaction) { $status_class = 'bankily-status-' . strtolower($transaction->status); echo '' . bankily_get_status_label($transaction->status) . ''; if ($transaction->status == 'TA') { $age_hours = (time() - strtotime($transaction->created_at)) / 3600; if ($age_hours > 2) { echo '
⚠ ' . sprintf(__('%.1fh', 'bankily-bpay'), $age_hours) . ''; } } } else { echo '-'; } } /** * CSS pour les colonnes des commandes */ function bankily_admin_order_list_css() { global $pagenow, $post_type; if ($pagenow === 'edit.php' && $post_type === 'shop_order') { ?> prefix . 'bankily_transactions'; $max_attempts = get_option('bankily_max_check_attempts', 5); $auto_mark_failed = get_option('bankily_auto_mark_failed', false); // Récupérer les transactions en attente $pending_transactions = $wpdb->get_results($wpdb->prepare(" SELECT * FROM $table_name WHERE status = 'TA' AND check_count < %d AND last_checked < DATE_SUB(NOW(), INTERVAL 5 MINUTE) ORDER BY created_at ASC LIMIT 20 ", $max_attempts)); if (empty($pending_transactions)) { return; } $gateway = new WC_Bankily_BPay_Gateway(); foreach ($pending_transactions as $transaction) { $result = $gateway->check_transaction($transaction->operation_id); if ($result && $result['errorCode'] == '0') { $wpdb->update( $table_name, array( 'status' => $result['status'], 'transaction_id' => $result['transactionId'], 'last_checked' => current_time('mysql'), 'check_count' => $transaction->check_count + 1, 'error_code' => $result['errorCode'], 'error_message' => isset($result['errorMessage']) ? $result['errorMessage'] : '' ), array('id' => $transaction->id) ); if ($result['status'] == 'TS') { $order = wc_get_order($transaction->order_id); if ($order) { $order->payment_complete($result['transactionId']); $order->add_order_note(__('Paiement B-PAY confirmé automatiquement.', 'bankily-bpay')); } } } else { $new_check_count = $transaction->check_count + 1; $new_status = $transaction->status; // Marquer comme échoué si on a atteint le maximum et que l'option est activée if ($new_check_count >= $max_attempts && $auto_mark_failed) { $new_status = 'TF'; } $wpdb->update( $table_name, array( 'status' => $new_status, 'last_checked' => current_time('mysql'), 'check_count' => $new_check_count, 'error_code' => isset($result['errorCode']) ? $result['errorCode'] : '1', 'error_message' => isset($result['errorMessage']) ? $result['errorMessage'] : __('Erreur de vérification automatique', 'bankily-bpay') ), array('id' => $transaction->id) ); } // Délai pour éviter de surcharger l'API sleep(1); } } /** * Générer un rapport quotidien */ function bankily_generate_daily_report() { global $wpdb; $table_name = $wpdb->prefix . 'bankily_transactions'; $yesterday_start = date('Y-m-d 00:00:00', strtotime('-1 day')); $yesterday_end = date('Y-m-d 23:59:59', strtotime('-1 day')); $report = $wpdb->get_row($wpdb->prepare(" SELECT COUNT(*) as total, SUM(CASE WHEN status = 'TS' THEN 1 ELSE 0 END) as success, SUM(CASE WHEN status = 'TF' THEN 1 ELSE 0 END) as failed, SUM(CASE WHEN status = 'TA' THEN 1 ELSE 0 END) as pending, SUM(CASE WHEN status = 'TS' THEN amount ELSE 0 END) as revenue FROM $table_name WHERE created_at BETWEEN %s AND %s ", $yesterday_start, $yesterday_end)); if ($report && $report->total > 0) { $success_rate = round(($report->success / $report->total) * 100, 1); $message = sprintf( "Rapport quotidien B-PAY (%s)\n\n" . "📊 Transactions: %d\n" . "✅ Réussies: %d (%s%%)\n" . "❌ Échouées: %d\n" . "⏳ En attente: %d\n" . "💰 Chiffre d'affaires: %s MRU", date('d/m/Y', strtotime('-1 day')), $report->total, $report->success, $success_rate, $report->failed, $report->pending, number_format($report->revenue, 0, ',', ' ') ); wp_mail(get_option('admin_email'), 'Rapport quotidien B-PAY', $message); } } ?>