1102 lines
42 KiB
PHP
1102 lines
42 KiB
PHP
<?php
|
|
/**
|
|
* Interface d'administration pour la gestion des transactions B-PAY
|
|
* Compatible avec HPOS (High Performance Order Storage)
|
|
*
|
|
* @package Bankily_BPay
|
|
* @version 1.0.1
|
|
*/
|
|
|
|
// Empêcher l'accès direct
|
|
if (!defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
// Hooks d'initialisation de l'administration
|
|
add_action('admin_menu', 'bankily_bpay_admin_menu');
|
|
add_action('admin_enqueue_scripts', 'bankily_bpay_admin_scripts');
|
|
|
|
// Hooks AJAX
|
|
add_action('wp_ajax_bankily_check_transaction', 'bankily_ajax_check_transaction');
|
|
add_action('wp_ajax_bankily_bulk_check', 'bankily_ajax_bulk_check');
|
|
add_action('wp_ajax_bankily_bulk_mark_failed', 'bankily_ajax_bulk_mark_failed');
|
|
add_action('wp_ajax_bankily_export_transactions', 'bankily_ajax_export_transactions');
|
|
add_action('wp_ajax_bankily_clean_old_transactions', 'bankily_ajax_clean_old_transactions');
|
|
add_action('wp_ajax_bankily_ping', 'bankily_ajax_ping');
|
|
|
|
// Hooks pour les fonctionnalités supplémentaires
|
|
add_action('wp_dashboard_setup', 'bankily_add_dashboard_widget');
|
|
add_action('admin_bar_menu', 'bankily_admin_bar_notification', 100);
|
|
// Hooks pour les colonnes WooCommerce (compatibilité HPOS)
|
|
if (class_exists('\Automattic\WooCommerce\Utilities\OrderUtil') && \Automattic\WooCommerce\Utilities\OrderUtil::custom_orders_table_usage_is_enabled()) {
|
|
// HPOS activé - utiliser les nouveaux hooks
|
|
add_filter('manage_woocommerce_page_wc-orders_columns', 'bankily_add_order_column');
|
|
add_action('manage_woocommerce_page_wc-orders_custom_column', 'bankily_show_order_column_content_hpos', 10, 2);
|
|
} else {
|
|
// HPOS désactivé - utiliser les anciens hooks
|
|
add_filter('manage_edit-shop_order_columns', 'bankily_add_order_column');
|
|
add_action('manage_shop_order_posts_custom_column', 'bankily_show_order_column_content', 10, 2);
|
|
}
|
|
add_action('admin_head', 'bankily_admin_order_list_css');
|
|
|
|
// Hook pour la vérification automatique
|
|
add_action('bankily_auto_check_transactions', 'bankily_auto_check_pending_transactions');
|
|
add_action('bankily_daily_report', 'bankily_generate_daily_report');
|
|
|
|
/**
|
|
* Fonctions utilitaires pour la compatibilité HPOS
|
|
*/
|
|
|
|
/**
|
|
* Obtenir les détails d'une commande de manière compatible HPOS
|
|
*/
|
|
function bankily_get_order_data($order_id) {
|
|
$order = wc_get_order($order_id);
|
|
if (!$order) {
|
|
return null;
|
|
}
|
|
|
|
return array(
|
|
'id' => $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');
|
|
|
|
?>
|
|
<div class="wrap">
|
|
<h1><?php _e('Tableau de bord B-PAY', 'bankily-bpay'); ?></h1>
|
|
|
|
<!-- Statistiques -->
|
|
<div class="bankily-stats-grid">
|
|
<div class="bankily-stat-card total">
|
|
<h3><?php echo number_format($stats->total); ?></h3>
|
|
<p><?php _e('Total transactions (30j)', 'bankily-bpay'); ?></p>
|
|
</div>
|
|
<div class="bankily-stat-card success">
|
|
<h3><?php echo number_format($stats->success); ?></h3>
|
|
<p><?php _e('Succès', 'bankily-bpay'); ?></p>
|
|
</div>
|
|
<div class="bankily-stat-card pending">
|
|
<h3><?php echo number_format($stats->pending); ?></h3>
|
|
<p><?php _e('En attente', 'bankily-bpay'); ?></p>
|
|
</div>
|
|
<div class="bankily-stat-card failed">
|
|
<h3><?php echo number_format($stats->failed); ?></h3>
|
|
<p><?php _e('Échecs', 'bankily-bpay'); ?></p>
|
|
</div>
|
|
<div class="bankily-stat-card amount">
|
|
<h3><?php echo number_format($stats->total_amount, 0, ',', ' '); ?> MRU</h3>
|
|
<p><?php _e('Montant total (succès)', 'bankily-bpay'); ?></p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Actions rapides -->
|
|
<div class="bankily-quick-actions">
|
|
<h2><?php _e('Actions rapides', 'bankily-bpay'); ?></h2>
|
|
<div class="bankily-action-buttons">
|
|
<button class="button button-primary" onclick="bankilyCheckAllPending()">
|
|
<?php _e('Vérifier toutes les transactions en attente', 'bankily-bpay'); ?>
|
|
</button>
|
|
<a href="<?php echo admin_url('admin.php?page=bankily-pending'); ?>" class="button">
|
|
<?php _e('Voir les transactions en attente', 'bankily-bpay'); ?>
|
|
</a>
|
|
<button class="button" onclick="bankilyExportTransactions()">
|
|
<?php _e('Exporter les données', 'bankily-bpay'); ?>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Transactions récentes -->
|
|
<div class="bankily-recent-transactions">
|
|
<h2><?php _e('Transactions récentes', 'bankily-bpay'); ?></h2>
|
|
<?php bankily_render_transactions_table($recent_transactions, false); ?>
|
|
</div>
|
|
</div>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Page des transactions en attente
|
|
*/
|
|
function bankily_bpay_pending_page() {
|
|
global $wpdb;
|
|
$table_name = $wpdb->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)
|
|
);
|
|
|
|
?>
|
|
<div class="wrap">
|
|
<h1><?php _e('Transactions en attente', 'bankily-bpay'); ?></h1>
|
|
|
|
<!-- Barre de recherche et filtres -->
|
|
<div class="bankily-filters">
|
|
<form method="get" action="">
|
|
<input type="hidden" name="page" value="bankily-pending">
|
|
<div class="bankily-search-box">
|
|
<input type="text" name="search" value="<?php echo esc_attr($search); ?>"
|
|
placeholder="<?php _e('Rechercher par ID opération, téléphone ou commande...', 'bankily-bpay'); ?>">
|
|
<button type="submit" class="button"><?php _e('Rechercher', 'bankily-bpay'); ?></button>
|
|
<?php if ($search): ?>
|
|
<a href="<?php echo admin_url('admin.php?page=bankily-pending'); ?>" class="button">
|
|
<?php _e('Effacer', 'bankily-bpay'); ?>
|
|
</a>
|
|
<?php endif; ?>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- Actions en lot -->
|
|
<div class="bankily-bulk-actions">
|
|
<div class="alignleft actions">
|
|
<select name="bulk-action" id="bulk-action-selector-top">
|
|
<option value=""><?php _e('Actions groupées', 'bankily-bpay'); ?></option>
|
|
<option value="check"><?php _e('Vérifier les transactions', 'bankily-bpay'); ?></option>
|
|
<option value="mark-failed"><?php _e('Marquer comme échouées', 'bankily-bpay'); ?></option>
|
|
</select>
|
|
<button type="button" class="button action" onclick="bankilyBulkAction()">
|
|
<?php _e('Appliquer', 'bankily-bpay'); ?>
|
|
</button>
|
|
</div>
|
|
<div class="alignright">
|
|
<span class="displaying-num">
|
|
<?php printf(__('%d éléments', 'bankily-bpay'), $total_items); ?>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Tableau des transactions -->
|
|
<?php bankily_render_transactions_table($pending_transactions, true); ?>
|
|
|
|
<!-- Pagination -->
|
|
<?php if ($total_pages > 1): ?>
|
|
<div class="bankily-pagination">
|
|
<?php
|
|
echo paginate_links(array(
|
|
'base' => add_query_arg('paged', '%#%'),
|
|
'format' => '',
|
|
'prev_text' => __('« Précédent', 'bankily-bpay'),
|
|
'next_text' => __('Suivant »', 'bankily-bpay'),
|
|
'current' => $current_page,
|
|
'total' => $total_pages,
|
|
));
|
|
?>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Afficher le tableau des transactions
|
|
*/
|
|
function bankily_render_transactions_table($transactions, $show_bulk = false) {
|
|
?>
|
|
<form method="post" id="bankily-transactions-form">
|
|
<table class="wp-list-table widefat fixed striped">
|
|
<thead>
|
|
<tr>
|
|
<?php if ($show_bulk): ?>
|
|
<td class="manage-column column-cb check-column">
|
|
<input type="checkbox" id="cb-select-all">
|
|
</td>
|
|
<?php endif; ?>
|
|
<th><?php _e('ID Opération', 'bankily-bpay'); ?></th>
|
|
<th><?php _e('Commande', 'bankily-bpay'); ?></th>
|
|
<th><?php _e('Téléphone', 'bankily-bpay'); ?></th>
|
|
<th><?php _e('Montant', 'bankily-bpay'); ?></th>
|
|
<th><?php _e('Statut', 'bankily-bpay'); ?></th>
|
|
<th><?php _e('Créé le', 'bankily-bpay'); ?></th>
|
|
<th><?php _e('Dernière vérif.', 'bankily-bpay'); ?></th>
|
|
<th><?php _e('Tentatives', 'bankily-bpay'); ?></th>
|
|
<th><?php _e('Actions', 'bankily-bpay'); ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php if (empty($transactions)): ?>
|
|
<tr>
|
|
<td colspan="<?php echo $show_bulk ? '9' : '8'; ?>" class="no-items">
|
|
<?php _e('Aucune transaction trouvée.', 'bankily-bpay'); ?>
|
|
</td>
|
|
</tr>
|
|
<?php else: ?>
|
|
<?php foreach ($transactions as $transaction): ?>
|
|
<tr id="transaction-<?php echo $transaction->id; ?>" class="transaction-row">
|
|
<?php if ($show_bulk): ?>
|
|
<th class="check-column">
|
|
<input type="checkbox" name="transaction_ids[]" value="<?php echo $transaction->id; ?>">
|
|
</th>
|
|
<?php endif; ?>
|
|
<td class="operation-id">
|
|
<strong><?php echo esc_html($transaction->operation_id); ?></strong>
|
|
<?php if ($transaction->transaction_id): ?>
|
|
<br><small>TXN: <?php echo esc_html($transaction->transaction_id); ?></small>
|
|
<?php endif; ?>
|
|
</td>
|
|
<td>
|
|
<a href="<?php echo admin_url('post.php?post=' . $transaction->order_id . '&action=edit'); ?>">
|
|
#<?php echo $transaction->order_id; ?>
|
|
</a>
|
|
<br><small class="order-status-<?php echo $transaction->order_status; ?>">
|
|
<?php echo ucfirst($transaction->order_status); ?>
|
|
</small>
|
|
</td>
|
|
<td><?php echo esc_html($transaction->client_phone); ?></td>
|
|
<td><?php echo number_format($transaction->amount, 0, ',', ' '); ?> MRU</td>
|
|
<td>
|
|
<span class="bankily-status bankily-status-<?php echo strtolower($transaction->status); ?>">
|
|
<?php echo bankily_get_status_label($transaction->status); ?>
|
|
</span>
|
|
<?php if ($transaction->error_message): ?>
|
|
<br><small class="error-message" title="<?php echo esc_attr($transaction->error_message); ?>">
|
|
<?php echo esc_html(substr($transaction->error_message, 0, 50)) . '...'; ?>
|
|
</small>
|
|
<?php endif; ?>
|
|
</td>
|
|
<td>
|
|
<?php echo date_i18n('d/m/Y H:i', strtotime($transaction->created_at)); ?>
|
|
</td>
|
|
<td>
|
|
<?php
|
|
if ($transaction->last_checked != $transaction->created_at) {
|
|
echo date_i18n('d/m/Y H:i', strtotime($transaction->last_checked));
|
|
} else {
|
|
echo '-';
|
|
}
|
|
?>
|
|
</td>
|
|
<td><?php echo $transaction->check_count; ?></td>
|
|
<td class="actions">
|
|
<button type="button" class="button button-small"
|
|
onclick="bankilyCheckTransaction('<?php echo $transaction->operation_id; ?>', <?php echo $transaction->id; ?>)">
|
|
<?php _e('Vérifier', 'bankily-bpay'); ?>
|
|
</button>
|
|
<?php if ($transaction->status == 'TA' && $transaction->check_count > 3): ?>
|
|
<br>
|
|
<button type="button" class="button button-small button-link-delete"
|
|
onclick="bankilyMarkAsFailed(<?php echo $transaction->id; ?>)">
|
|
<?php _e('Marquer échec', 'bankily-bpay'); ?>
|
|
</button>
|
|
<?php endif; ?>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
<?php endif; ?>
|
|
</tbody>
|
|
</table>
|
|
</form>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Obtenir le libellé du statut
|
|
*/
|
|
function bankily_get_status_label($status) {
|
|
$labels = array(
|
|
'TS' => __('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 '<div class="notice notice-success"><p>' . __('Paramètres sauvegardés.', 'bankily-bpay') . '</p></div>';
|
|
}
|
|
|
|
$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);
|
|
|
|
?>
|
|
<div class="wrap">
|
|
<h1><?php _e('Paramètres avancés B-PAY', 'bankily-bpay'); ?></h1>
|
|
|
|
<form method="post" action="">
|
|
<table class="form-table">
|
|
<tr>
|
|
<th scope="row"><?php _e('Intervalle de vérification automatique', 'bankily-bpay'); ?></th>
|
|
<td>
|
|
<input type="number" name="auto_check_interval" value="<?php echo $auto_check_interval; ?>" min="5" max="60">
|
|
<?php _e('minutes', 'bankily-bpay'); ?>
|
|
<p class="description">
|
|
<?php _e('Fréquence de vérification automatique des transactions en attente (via cron).', 'bankily-bpay'); ?>
|
|
</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php _e('Nombre maximum de tentatives', 'bankily-bpay'); ?></th>
|
|
<td>
|
|
<input type="number" name="max_check_attempts" value="<?php echo $max_check_attempts; ?>" min="3" max="20">
|
|
<p class="description">
|
|
<?php _e('Nombre de tentatives de vérification avant d\'abandonner.', 'bankily-bpay'); ?>
|
|
</p>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th scope="row"><?php _e('Marquage automatique des échecs', 'bankily-bpay'); ?></th>
|
|
<td>
|
|
<label>
|
|
<input type="checkbox" name="auto_mark_failed" <?php checked($auto_mark_failed); ?>>
|
|
<?php _e('Marquer automatiquement comme échouées après le nombre maximum de tentatives', 'bankily-bpay'); ?>
|
|
</label>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
|
|
<?php submit_button(); ?>
|
|
</form>
|
|
|
|
<h2><?php _e('Maintenance', 'bankily-bpay'); ?></h2>
|
|
<table class="form-table">
|
|
<tr>
|
|
<th scope="row"><?php _e('Nettoyage des données', 'bankily-bpay'); ?></th>
|
|
<td>
|
|
<button type="button" class="button" onclick="bankilyCleanOldTransactions()">
|
|
<?php _e('Supprimer les transactions de plus de 6 mois', 'bankily-bpay'); ?>
|
|
</button>
|
|
<p class="description">
|
|
<?php _e('Supprime les enregistrements de transactions anciennes pour optimiser les performances.', 'bankily-bpay'); ?>
|
|
</p>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<?php
|
|
}
|
|
|
|
// ============================================================================
|
|
// FONCTIONS AJAX
|
|
// ============================================================================
|
|
|
|
/**
|
|
* AJAX: Vérifier une transaction individuelle
|
|
*/
|
|
function bankily_ajax_check_transaction() {
|
|
check_ajax_referer('bankily_admin_nonce', 'nonce');
|
|
|
|
if (!current_user_can('manage_woocommerce')) {
|
|
wp_die(__('Permissions insuffisantes.', 'bankily-bpay'));
|
|
}
|
|
|
|
$operation_id = sanitize_text_field($_POST['operation_id']);
|
|
$transaction_id = intval($_POST['transaction_id']);
|
|
|
|
global $wpdb;
|
|
$table_name = $wpdb->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)
|
|
");
|
|
|
|
?>
|
|
<div style="text-align: center;">
|
|
<p><strong><?php echo number_format($stats_24h->success); ?></strong> <?php _e('succès', 'bankily-bpay'); ?> |
|
|
<strong><?php echo number_format($stats_24h->pending); ?></strong> <?php _e('en attente', 'bankily-bpay'); ?></p>
|
|
<p><strong><?php echo number_format($stats_24h->total_amount, 0, ',', ' '); ?> MRU</strong><br>
|
|
<small><?php _e('Chiffre d\'affaires (24h)', 'bankily-bpay'); ?></small></p>
|
|
<p><a href="<?php echo admin_url('admin.php?page=bankily-transactions'); ?>" class="button button-primary">
|
|
<?php _e('Voir le tableau de bord', 'bankily-bpay'); ?></a></p>
|
|
</div>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Notifications dans l'admin bar
|
|
*/
|
|
function bankily_admin_bar_notification($wp_admin_bar) {
|
|
if (!current_user_can('manage_woocommerce')) {
|
|
return;
|
|
}
|
|
|
|
global $wpdb;
|
|
$table_name = $wpdb->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' => '<span class="ab-icon dashicons dashicons-warning" style="color: #ffc107;"></span>' .
|
|
'<span class="ab-label">' . sprintf(__('B-PAY: %d urgent', 'bankily-bpay'), $urgent_count) . '</span>',
|
|
'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 '<span class="bankily-status ' . $status_class . '">' . bankily_get_status_label($transaction->status) . '</span>';
|
|
|
|
if ($transaction->status == 'TA') {
|
|
$age_hours = (time() - strtotime($transaction->created_at)) / 3600;
|
|
if ($age_hours > 2) {
|
|
echo '<br><small style="color: #dc3545;">⚠ ' . sprintf(__('%.1fh', 'bankily-bpay'), $age_hours) . '</small>';
|
|
}
|
|
}
|
|
} else {
|
|
echo '<span style="color: #666;">-</span>';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* CSS pour les colonnes des commandes
|
|
*/
|
|
function bankily_admin_order_list_css() {
|
|
global $pagenow, $post_type;
|
|
|
|
if ($pagenow === 'edit.php' && $post_type === 'shop_order') {
|
|
?>
|
|
<style>
|
|
.bankily-status {
|
|
display: inline-block;
|
|
padding: 2px 6px;
|
|
border-radius: 3px;
|
|
font-size: 11px;
|
|
font-weight: bold;
|
|
text-transform: uppercase;
|
|
}
|
|
.bankily-status-ts { background: #d4edda; color: #155724; }
|
|
.bankily-status-tf { background: #f8d7da; color: #721c24; }
|
|
.bankily-status-ta { background: #fff3cd; color: #856404; }
|
|
.column-bankily_status { width: 80px; }
|
|
</style>
|
|
<?php
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// FONCTIONS DE VÉRIFICATION AUTOMATIQUE
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Vérification automatique des transactions en attente
|
|
*/
|
|
function bankily_auto_check_pending_transactions() {
|
|
global $wpdb;
|
|
$table_name = $wpdb->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);
|
|
}
|
|
}
|
|
|
|
?>
|