$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_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)
);
?>
__('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);
}
}
?>