id = 'bankily_bpay'; $this->icon = ''; $this->has_fields = true; $this->method_title = __('B-PAY Bankily', 'bankily-bpay'); $this->method_description = __('Paiement mobile via B-PAY Bankily avec gestion complète des transactions', 'bankily-bpay'); // Charger les paramètres $this->init_form_fields(); $this->init_settings(); // Définir les propriétés utilisateur $this->title = $this->get_option('title'); $this->description = $this->get_option('description'); $this->enabled = $this->get_option('enabled'); $this->testmode = 'yes' === $this->get_option('testmode'); $this->username = $this->get_option('username'); $this->password = $this->get_option('password'); $this->client_id = $this->get_option('client_id'); // URLs de l'API $this->api_url = $this->testmode ? 'https://ebankily-tst.appspot.com' : 'https://ebankily.appspot.com'; // Actions WordPress add_action('woocommerce_update_options_payment_gateways_' . $this->id, array($this, 'process_admin_options')); add_action('wp_enqueue_scripts', array($this, 'payment_scripts')); add_action('woocommerce_api_' . strtolower(get_class($this)), array($this, 'webhook')); } /** * Initialiser les champs du formulaire de configuration */ public function init_form_fields() { $this->form_fields = array( 'enabled' => array( 'title' => __('Activer/Désactiver', 'bankily-bpay'), 'type' => 'checkbox', 'label' => __('Activer B-PAY Bankily', 'bankily-bpay'), 'default' => 'yes' ), 'title' => array( 'title' => __('Titre', 'bankily-bpay'), 'type' => 'text', 'description' => __('Ceci contrôle le titre que l\'utilisateur voit lors du checkout.', 'bankily-bpay'), 'default' => __('Paiement Mobile B-PAY', 'bankily-bpay'), 'desc_tip' => true, ), 'description' => array( 'title' => __('Description', 'bankily-bpay'), 'type' => 'textarea', 'description' => __('Description de la méthode de paiement que le client verra sur votre site.', 'bankily-bpay'), 'default' => __('Payez avec votre mobile via B-PAY Bankily. Vous recevrez un SMS de confirmation.', 'bankily-bpay'), ), 'testmode' => array( 'title' => __('Mode Test', 'bankily-bpay'), 'type' => 'checkbox', 'label' => __('Activer le mode test', 'bankily-bpay'), 'default' => 'yes', 'description' => __('Placez la passerelle de paiement en mode test en utilisant les serveurs de test.', 'bankily-bpay'), ), 'username' => array( 'title' => __('Nom d\'utilisateur', 'bankily-bpay'), 'type' => 'text', 'description' => __('Votre nom d\'utilisateur B-PAY Bankily.', 'bankily-bpay'), 'default' => '', 'desc_tip' => true, ), 'password' => array( 'title' => __('Mot de passe', 'bankily-bpay'), 'type' => 'password', 'description' => __('Votre mot de passe B-PAY Bankily.', 'bankily-bpay'), 'default' => '', 'desc_tip' => true, ), 'client_id' => array( 'title' => __('Client ID', 'bankily-bpay'), 'type' => 'text', 'description' => __('Votre Client ID B-PAY Bankily.', 'bankily-bpay'), 'default' => 'e-bankily', 'desc_tip' => true, ), ); } /** * Interface d'administration personnalisée */ public function admin_options() { ?>
get_method_description()); ?>
testmode): ?>-
'; echo '' . __('Mode Test:', 'bankily-bpay') . ' '; echo __('Utilisez le numéro 22123456 et le code PIN 1234 pour tester.', 'bankily-bpay'); echo '
'; } ?> 6) { wc_add_notice(__('Le code PIN doit contenir entre 4 et 6 chiffres.', 'bankily-bpay'), 'error'); return false; } return true; } /** * Traiter le paiement */ public function process_payment($order_id) { $order = wc_get_order($order_id); // Log du début du processus $this->log('Début du processus de paiement pour la commande #' . $order_id); // Obtenir le token d'accès $access_token = $this->get_access_token(); if (!$access_token) { $this->log('Erreur d\'authentification - impossible d\'obtenir le token'); wc_add_notice(__('Erreur d\'authentification. Veuillez réessayer.', 'bankily-bpay'), 'error'); return array( 'result' => 'fail', 'redirect' => '', ); } // Données de paiement $payment_data = array( 'clientPhone' => sanitize_text_field($_POST['bankily_phone']), 'passcode' => sanitize_text_field($_POST['bankily_passcode']), 'operationId' => $order->get_id() . '_' . time() . '_' . wp_rand(1000, 9999), 'amount' => (string) $order->get_total(), 'language' => 'FR' ); $this->log('Données de paiement préparées', $payment_data); // Effectuer le paiement $response = $this->make_payment($access_token, $payment_data); // Toujours enregistrer la transaction dans notre base de données $this->save_transaction($order_id, $payment_data, $response); if ($response && isset($response['errorCode']) && $response['errorCode'] == '0') { // Vérifier si nous avons un ID de transaction (paiement immédiatement confirmé) if (isset($response['transactionId']) && !empty($response['transactionId'])) { // Paiement réussi immédiatement $order->payment_complete($response['transactionId']); $order->add_order_note(sprintf( __('Paiement B-PAY réussi immédiatement. ID Transaction: %s, ID Opération: %s', 'bankily-bpay'), $response['transactionId'], $payment_data['operationId'] )); $this->log('Paiement réussi immédiatement', $response); // Vider le panier WC()->cart->empty_cart(); return array( 'result' => 'success', 'redirect' => $this->get_return_url($order), ); } else { // Paiement initié mais en attente de confirmation (statut TA) $order->update_status('on-hold', __('Paiement B-PAY en cours de vérification. Le client doit confirmer sur son mobile.', 'bankily-bpay')); $order->add_order_note(sprintf( __('Paiement B-PAY initié et en attente de confirmation. ID Opération: %s. Le système vérifiera automatiquement le statut.', 'bankily-bpay'), $payment_data['operationId'] )); $this->log('Paiement en attente de confirmation', $response); // Vider le panier WC()->cart->empty_cart(); // Afficher un message informatif au client wc_add_notice(sprintf( __('Votre paiement de %s MRU est en cours de traitement. Confirmez la transaction sur votre mobile B-PAY. Vous recevrez une notification par SMS.', 'bankily-bpay'), number_format($order->get_total(), 0, ',', ' ') ), 'notice'); return array( 'result' => 'success', 'redirect' => $this->get_return_url($order), ); } } else { // Paiement échoué $error_code = isset($response['errorCode']) ? $response['errorCode'] : 'unknown'; $error_message = isset($response['errorMessage']) ? $response['errorMessage'] : __('Erreur de paiement inconnue', 'bankily-bpay'); $this->log('Paiement échoué', array('error_code' => $error_code, 'error_message' => $error_message)); // Messages d'erreur personnalisés selon le code switch ($error_code) { case '2': $user_message = __('Erreur d\'authentification. Veuillez vérifier vos identifiants et réessayer.', 'bankily-bpay'); break; case '4': $user_message = __('Erreur technique temporaire. Veuillez réessayer dans quelques instants.', 'bankily-bpay'); break; default: $user_message = $error_message; } wc_add_notice($user_message, 'error'); $order->add_order_note(sprintf( __('Paiement B-PAY échoué - Code: %s, Message: %s, ID Opération: %s', 'bankily-bpay'), $error_code, $error_message, $payment_data['operationId'] )); return array( 'result' => 'fail', 'redirect' => '', ); } } /** * Enregistrer la transaction dans la base de données */ private function save_transaction($order_id, $payment_data, $response) { global $wpdb; $table_name = $wpdb->prefix . 'bankily_transactions'; // Déterminer le statut initial $status = 'TA'; // Par défaut en attente if ($response && isset($response['errorCode'])) { if ($response['errorCode'] == '0') { // Si nous avons un ID de transaction, c'est confirmé immédiatement if (isset($response['transactionId']) && !empty($response['transactionId'])) { $status = 'TS'; } // Sinon reste en 'TA' (en attente) } else { $status = 'TF'; // Échec confirmé } } $data = array( 'order_id' => $order_id, 'operation_id' => $payment_data['operationId'], 'transaction_id' => isset($response['transactionId']) ? $response['transactionId'] : '', 'client_phone' => $payment_data['clientPhone'], 'amount' => floatval($payment_data['amount']), 'status' => $status, 'error_code' => isset($response['errorCode']) ? $response['errorCode'] : '', 'error_message' => isset($response['errorMessage']) ? $response['errorMessage'] : '', 'created_at' => current_time('mysql'), 'last_checked' => current_time('mysql'), 'check_count' => 0 ); $result = $wpdb->insert($table_name, $data); if ($result === false) { $this->log('Erreur lors de l\'enregistrement de la transaction en base de données', $wpdb->last_error); } else { $this->log('Transaction enregistrée avec succès en base de données', $data); } return $result; } /** * Obtenir un token d'accès pour l'API */ private function get_access_token() { // Vérifier si nous avons un token valide en cache $cached_token = get_transient('bankily_bpay_access_token'); if ($cached_token) { $this->log('Token d\'accès récupéré depuis le cache'); return $cached_token; } $url = $this->api_url . '/authentification'; $body = array( 'grant_type' => 'password', 'username' => $this->username, 'password' => $this->password, 'client_id' => $this->client_id ); $args = array( 'body' => $body, 'headers' => array( 'Content-Type' => 'application/x-www-form-urlencoded' ), 'method' => 'POST', 'timeout' => 45, ); $this->log('Tentative d\'authentification API', array('url' => $url, 'username' => $this->username)); $response = wp_remote_post($url, $args); if (is_wp_error($response)) { $this->log('Erreur de connexion lors de l\'authentification', $response->get_error_message()); return false; } $http_code = wp_remote_retrieve_response_code($response); $body = wp_remote_retrieve_body($response); $data = json_decode($body, true); $this->log('Réponse d\'authentification', array('http_code' => $http_code, 'response' => $data)); if ($http_code === 200 && isset($data['access_token'])) { // Mettre en cache le token (durée d'expiration - 60 secondes de marge de sécurité) $expires_in = isset($data['expires_in']) ? intval($data['expires_in']) - 60 : 3540; set_transient('bankily_bpay_access_token', $data['access_token'], $expires_in); // Stocker aussi le refresh token si présent if (isset($data['refresh_token'])) { $refresh_expires = isset($data['refresh_expires_in']) ? intval($data['refresh_expires_in']) - 60 : 86340; set_transient('bankily_bpay_refresh_token', $data['refresh_token'], $refresh_expires); } $this->log('Token d\'accès obtenu et mis en cache avec succès'); return $data['access_token']; } $this->log('Erreur lors de l\'obtention du token d\'accès', $body); return false; } /** * Effectuer un paiement via l'API */ private function make_payment($access_token, $payment_data) { $url = $this->api_url . '/payment'; $args = array( 'body' => json_encode($payment_data), 'headers' => array( 'Content-Type' => 'application/json', 'Authorization' => 'Bearer ' . $access_token ), 'method' => 'POST', 'timeout' => 60, // Timeout plus long pour les paiements ); $this->log('Tentative de paiement API', array('url' => $url, 'data' => $payment_data)); $response = wp_remote_post($url, $args); if (is_wp_error($response)) { $this->log('Erreur de connexion lors du paiement', $response->get_error_message()); return false; } $http_code = wp_remote_retrieve_response_code($response); $body = wp_remote_retrieve_body($response); $data = json_decode($body, true); $this->log('Réponse de paiement', array('http_code' => $http_code, 'response' => $data)); return $data; } /** * Vérifier le statut d'une transaction */ public function check_transaction($operation_id) { $access_token = $this->get_access_token(); if (!$access_token) { $this->log('Impossible de vérifier la transaction - pas de token d\'accès', $operation_id); return false; } $url = $this->api_url . '/checkTransaction'; $body = array( 'operationID' => $operation_id ); $args = array( 'body' => json_encode($body), 'headers' => array( 'Content-Type' => 'application/json', 'Authorization' => 'Bearer ' . $access_token ), 'method' => 'POST', 'timeout' => 45, ); $this->log('Vérification du statut de la transaction', array('url' => $url, 'operation_id' => $operation_id)); $response = wp_remote_post($url, $args); if (is_wp_error($response)) { $this->log('Erreur de connexion lors de la vérification', $response->get_error_message()); return false; } $http_code = wp_remote_retrieve_response_code($response); $body = wp_remote_retrieve_body($response); $data = json_decode($body, true); $this->log('Réponse de vérification', array('http_code' => $http_code, 'response' => $data)); return $data; } /** * Logger les messages (actif en mode test et debug) */ private function log($message, $data = null) { if ($this->testmode || (defined('WP_DEBUG') && WP_DEBUG)) { $log_entry = '[B-PAY] ' . $message; if ($data) { $log_entry .= ' | Data: ' . json_encode($data, JSON_UNESCAPED_UNICODE); } if (class_exists('WC_Logger')) { $logger = new WC_Logger(); $logger->add('bankily-bpay', $log_entry); } // Aussi dans les logs WordPress si le debug est activé if (defined('WP_DEBUG_LOG') && WP_DEBUG_LOG) { error_log($log_entry); } } } /** * Gestion des webhooks (pour usage futur) */ public function webhook() { $raw_body = file_get_contents('php://input'); $this->log('Webhook reçu', $raw_body); http_response_code(200); echo "OK"; exit; } } ?>