diff --git a/breez-woocommerce.php b/breez-woocommerce.php index 9151642..9a0f0d4 100755 --- a/breez-woocommerce.php +++ b/breez-woocommerce.php @@ -307,6 +307,18 @@ function breez_wc_check_payment_status_endpoint($request) { $order_id = $request->get_param('order_id'); $logger->log("Checking payment status for order #$order_id", 'debug'); + // Initialize DB manager + $db_manager = new Breez_DB_Manager(); + $payment = $db_manager->get_payment_by_order($order_id); + + if (!$payment) { + $logger->log("No payment found for order #$order_id", 'error'); + return new WP_REST_Response(array( + 'success' => false, + 'message' => 'Payment not found' + ), 404); + } + $order = wc_get_order($order_id); if (!$order) { $logger->log("Order #$order_id not found", 'error'); @@ -325,69 +337,11 @@ function breez_wc_check_payment_status_endpoint($request) { ), 400); } - // If this is a POST request, update the order based on the payment data - if ($request->get_method() === 'POST') { - $payment_data = $request->get_json_params(); - $logger->log("Received payment data for order #$order_id: " . print_r($payment_data, true), 'debug'); - - if ($payment_data['status'] === 'SUCCEEDED') { - if ($order->get_status() === 'pending') { - // Add payment details to order meta - $order->update_meta_data('_breez_payment_amount_sat', $payment_data['amount_sat']); - $order->update_meta_data('_breez_payment_fees_sat', $payment_data['fees_sat']); - $order->update_meta_data('_breez_payment_time', $payment_data['payment_time']); - $order->update_meta_data('_breez_payment_hash', $payment_data['payment_hash']); - - // Complete the order - $order->payment_complete($payment_data['payment_hash']); - $order->add_order_note(sprintf( - __('Payment confirmed. Amount: %d sats, Fees: %d sats, Hash: %s', 'breez-woocommerce'), - $payment_data['amount_sat'], - $payment_data['fees_sat'], - $payment_data['payment_hash'] - )); - $order->save(); - - $logger->log("Order #$order_id marked as complete", 'info'); - } - - return new WP_REST_Response(array( - 'success' => true, - 'status' => 'completed' - ), 200); - } elseif ($payment_data['status'] === 'FAILED') { - if ($order->get_status() === 'pending') { - $order->update_status('failed', $payment_data['error'] ?? __('Payment failed', 'breez-woocommerce')); - $order->save(); - - $logger->log("Order #$order_id marked as failed: " . ($payment_data['error'] ?? 'No error message'), 'info'); - } - - return new WP_REST_Response(array( - 'success' => true, - 'status' => 'failed' - ), 200); - } - } - - // For GET requests, check payment status via API - $invoice_id = $order->get_meta('_breez_invoice_id'); - if (!$invoice_id) { - $logger->log("No payment invoice found for order #$order_id", 'error'); - return new WP_REST_Response(array( - 'success' => false, - 'message' => 'No payment invoice found' - ), 404); - } - - // Initialize API client - directly fetch gateway settings + // Initialize API client $gateway_settings = get_option('woocommerce_breez_settings'); $api_url = isset($gateway_settings['api_url']) ? $gateway_settings['api_url'] : ''; $api_key = isset($gateway_settings['api_key']) ? $gateway_settings['api_key'] : ''; - $logger->log("API settings: URL=" . (!empty($api_url) ? 'Set' : 'Missing') . - ", Key=" . (!empty($api_key) ? 'Set' : 'Missing'), 'debug'); - if (empty($api_url) || empty($api_key)) { $logger->log("API credentials not configured", 'error'); return new WP_REST_Response(array( @@ -397,13 +351,40 @@ function breez_wc_check_payment_status_endpoint($request) { } $client = new Breez_API_Client($api_url, $api_key); - $payment_status = $client->check_payment_status($invoice_id); - $logger->log("Payment status for order #$order_id: " . print_r($payment_status, true), 'debug'); + + // Check payment status from API + $api_status = $client->check_payment_status($payment['invoice_id']); + $logger->log("API payment status for invoice {$payment['invoice_id']}: " . print_r($api_status, true), 'debug'); + + // Update payment status in database if it has changed + if ($api_status['status'] !== $payment['status']) { + $db_manager->update_payment_status($order_id, $api_status['status']); + + // Update order status if needed + if ($api_status['status'] === 'SUCCEEDED' && $order->get_status() === 'pending') { + $order->payment_complete($payment['invoice_id']); + $order->add_order_note(sprintf( + __('Payment confirmed. Amount: %d sats, Hash: %s', 'breez-woocommerce'), + $payment['metadata']['amount_sat'], + $payment['invoice_id'] + )); + $order->save(); + $logger->log("Order #$order_id marked as complete", 'info'); + } else if ($api_status['status'] === 'FAILED' && $order->get_status() === 'pending') { + $order->update_status('failed', __('Payment failed or expired.', 'breez-woocommerce')); + $logger->log("Order #$order_id marked as failed", 'info'); + } + } return new WP_REST_Response(array( 'success' => true, - 'status' => $payment_status['status'], - 'data' => $payment_status + 'status' => $api_status['status'], + 'data' => array_merge($api_status, array( + 'order_status' => $order->get_status(), + 'payment_method' => $payment['metadata']['payment_method'], + 'amount_sat' => $payment['metadata']['amount_sat'], + 'expires_at' => $payment['metadata']['expires_at'] + )) ), 200); } catch (Exception $e) { @@ -431,41 +412,31 @@ function breez_wc_check_pending_payments() { * Plugin activation hook */ function breez_wc_activate() { - if (!breez_wc_is_woocommerce_active()) { - deactivate_plugins(plugin_basename(__FILE__)); - wp_die(__('Breez Nodeless Payments requires WooCommerce to be installed and active.', 'breez-woocommerce')); - } - // Include required files before using them require_once BREEZ_WC_PLUGIN_DIR . 'includes/class-breez-logger.php'; require_once BREEZ_WC_PLUGIN_DIR . 'includes/class-breez-db-manager.php'; - // Create required directories if they don't exist - $directories = array( - BREEZ_WC_PLUGIN_DIR . 'includes/admin', - BREEZ_WC_PLUGIN_DIR . 'includes/block', - BREEZ_WC_PLUGIN_DIR . 'assets/js', - BREEZ_WC_PLUGIN_DIR . 'assets/css', - ); + // Initialize logger + $logger = new Breez_Logger(true); + $logger->log('Plugin activation started', 'info'); - foreach ($directories as $directory) { - if (!file_exists($directory)) { - wp_mkdir_p($directory); - } - } - - // Create database tables + // Initialize DB manager $db_manager = new Breez_DB_Manager(); - $db_manager->install_tables(); - // Schedule payment status check - if (!wp_next_scheduled('breez_wc_check_pending_payments')) { - wp_schedule_event(time(), 'five_minutes', 'breez_wc_check_pending_payments'); + // Force drop and recreate the payments table with new schema + $logger->log('Forcing table recreation to update schema', 'info'); + $db_manager->recreate_table(); + + // Create required directories + $upload_dir = wp_upload_dir(); + $breez_dir = $upload_dir['basedir'] . '/breez-wc'; + if (!file_exists($breez_dir)) { + wp_mkdir_p($breez_dir); } - // Flush rewrite rules for webhook endpoint - flush_rewrite_rules(); + $logger->log('Plugin activation completed', 'info'); } +register_activation_hook(__FILE__, 'breez_wc_activate'); /** * Plugin deactivation hook @@ -701,6 +672,7 @@ function breez_wc_filter_blocks_checkout_order_data($order, $request) { $order->save(); if (class_exists('Breez_Logger')) { + $logger = new Breez_Logger(true); $logger->log('Stored Breez payment data in order meta', 'debug'); } } @@ -708,3 +680,33 @@ function breez_wc_filter_blocks_checkout_order_data($order, $request) { return $order; } + +/** + * Display payment instructions on the payment page + */ +function breez_wc_display_payment_instructions() { + if (!isset($_GET['show_payment']) || !isset($_GET['order_id'])) { + return; + } + + $order_id = absint($_GET['order_id']); + $order = wc_get_order($order_id); + + if (!$order || $order->get_payment_method() !== 'breez') { + return; + } + + // Verify order key + if (!isset($_GET['key']) || $_GET['key'] !== $order->get_order_key()) { + return; + } + + // Get gateway instance + $gateways = WC()->payment_gateways->payment_gateways(); + $gateway = isset($gateways['breez']) ? $gateways['breez'] : null; + + if ($gateway) { + $gateway->payment_page($order_id); + } +} +add_action('woocommerce_receipt_breez', 'breez_wc_display_payment_instructions'); diff --git a/class-wc-gateway-breez.php b/class-wc-gateway-breez.php index 8ba47f2..c9e4b5e 100644 --- a/class-wc-gateway-breez.php +++ b/class-wc-gateway-breez.php @@ -271,14 +271,31 @@ class WC_Gateway_Breez extends WC_Payment_Gateway { * @return array */ public function process_payment($order_id) { - $this->logger->log("Processing payment for order #$order_id", 'debug'); - try { + $this->logger->log("Processing payment for order #$order_id", 'debug'); + $order = wc_get_order($order_id); if (!$order) { throw new Exception(__('Order not found', 'breez-woocommerce')); } - + + // Check for existing payment in database + $existing_payment = $this->db_manager->get_payment_by_order($order_id); + $current_time = time(); + + if ($existing_payment && $existing_payment['status'] === 'pending') { + $payment_created = strtotime($existing_payment['created_at']); + $expiry_time = $payment_created + ($this->expiry_minutes * 60); + + if ($current_time < $expiry_time) { + $this->logger->log("Using existing valid payment for order #$order_id", 'debug'); + return array( + 'result' => 'success', + 'redirect' => $this->get_return_url($order) + ); + } + } + // Get the selected payment method $payment_method = $this->get_payment_method_from_request(); @@ -323,9 +340,9 @@ class WC_Gateway_Breez extends WC_Payment_Gateway { throw new Exception(__('Invalid amount conversion', 'breez-woocommerce')); } - // Create payment + // Create payment request $payment_data = array( - 'amount' => $amount_sat, // Send amount in satoshis + 'amount' => $amount_sat, 'method' => $payment_method === 'onchain' ? 'BITCOIN_ADDRESS' : 'LIGHTNING', 'description' => sprintf(__('Payment for order #%s', 'breez-woocommerce'), $order->get_order_number()) ); @@ -342,16 +359,26 @@ class WC_Gateway_Breez extends WC_Payment_Gateway { throw new Exception(__('Failed to create payment', 'breez-woocommerce')); } - // Save payment data to order meta - $order->update_meta_data('_breez_payment_id', $response['destination']); - $order->update_meta_data('_breez_invoice_id', $response['destination']); - $order->update_meta_data('_breez_payment_method', $payment_method); - $order->update_meta_data('_breez_payment_amount', $order_total); - $order->update_meta_data('_breez_payment_amount_sat', $amount_sat); - $order->update_meta_data('_breez_payment_currency', $currency); - $order->update_meta_data('_breez_payment_status', 'pending'); - $order->update_meta_data('_breez_payment_created', time()); - $order->update_meta_data('_breez_payment_expires', time() + ($this->expiry_minutes * 60)); + // Save payment in database + $metadata = array( + 'payment_method' => $payment_method, + 'exchange_rate' => $exchange_rate_response['rate'], + 'amount_sat' => $amount_sat, + 'expires_at' => time() + ($this->expiry_minutes * 60) + ); + + $saved = $this->db_manager->save_payment( + $order_id, + $response['destination'], + $order_total, + $currency, + 'pending', + $metadata + ); + + if (!$saved) { + throw new Exception(__('Failed to save payment in database', 'breez-woocommerce')); + } // Set order status to pending $order->update_status('pending', __('Awaiting Bitcoin/Lightning payment', 'breez-woocommerce')); @@ -367,7 +394,7 @@ class WC_Gateway_Breez extends WC_Payment_Gateway { // Save the order $order->save(); - $this->logger->log("Order meta data updated for #$order_id", 'debug'); + $this->logger->log("Payment saved for order #$order_id", 'debug'); // Reduce stock levels wc_reduce_stock_levels($order_id); @@ -375,13 +402,21 @@ class WC_Gateway_Breez extends WC_Payment_Gateway { // Empty cart WC()->cart->empty_cart(); - // Return success with redirect to order-received page - $return_url = $this->get_return_url($order); - $this->logger->log("Redirecting to: $return_url", 'debug'); + // Return success with redirect to payment instructions page + $redirect_url = add_query_arg( + array( + 'order_id' => $order->get_id(), + 'key' => $order->get_order_key(), + 'show_payment' => true + ), + $order->get_checkout_payment_url(true) + ); + + $this->logger->log("Redirecting to payment page: $redirect_url", 'debug'); return array( 'result' => 'success', - 'redirect' => $return_url + 'redirect' => $redirect_url ); } catch (Exception $e) { @@ -705,4 +740,39 @@ class WC_Gateway_Breez extends WC_Payment_Gateway { 'message' => $message ? $message : __('An error occurred during the payment process.', 'breez-woocommerce'), ); } + + /** + * Display payment instructions on the payment page + */ + public function payment_page($order_id) { + $this->logger->log("Displaying payment page for order #$order_id", 'debug'); + + $order = wc_get_order($order_id); + if (!$order) { + $this->logger->log("Order not found: #$order_id", 'error'); + return; + } + + // Get payment details from database + $payment = $this->db_manager->get_payment_by_order($order_id); + if (!$payment) { + $this->logger->log("Payment not found for order #$order_id", 'error'); + return; + } + + // Load the payment instructions template + wc_get_template( + 'payment-instructions.php', + array( + 'order' => $order, + 'invoice_id' => $payment['invoice_id'], + 'payment_method' => $payment['metadata']['payment_method'], + 'expiry' => $payment['metadata']['expires_at'], + 'current_time' => time(), + 'payment_status' => $payment['status'] + ), + '', + BREEZ_WC_PLUGIN_DIR . 'templates/' + ); + } } diff --git a/includes/class-breez-db-manager.php b/includes/class-breez-db-manager.php index 0f9cdc5..9484145 100644 --- a/includes/class-breez-db-manager.php +++ b/includes/class-breez-db-manager.php @@ -54,7 +54,7 @@ public function install_tables() { $sql = "CREATE TABLE IF NOT EXISTS {$table_name} ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, order_id BIGINT UNSIGNED NOT NULL, - invoice_id VARCHAR(255) NOT NULL, + invoice_id TEXT NOT NULL, amount DECIMAL(16,8) NOT NULL, currency VARCHAR(10) NOT NULL, status VARCHAR(20) NOT NULL, @@ -62,8 +62,8 @@ public function install_tables() { updated_at DATETIME NOT NULL, metadata TEXT, PRIMARY KEY (id), - UNIQUE KEY order_id (order_id), - KEY invoice_id (invoice_id) + KEY order_id (order_id), + KEY invoice_id (invoice_id(255)) ) $charset_collate;"; $this->logger->log("SQL query: {$sql}", 'debug'); @@ -102,30 +102,55 @@ public function install_tables() { $now = current_time('mysql'); try { + // Log input parameters + $this->logger->log("Attempting to save payment with parameters:", 'debug'); + $this->logger->log("order_id: $order_id", 'debug'); + $this->logger->log("invoice_id: $invoice_id", 'debug'); + $this->logger->log("amount: $amount", 'debug'); + $this->logger->log("currency: $currency", 'debug'); + $this->logger->log("status: $status", 'debug'); + $this->logger->log("metadata: " . print_r($metadata, true), 'debug'); + + // Verify table exists + $table_exists = $wpdb->get_var("SHOW TABLES LIKE '{$this->table_name}'") === $this->table_name; + if (!$table_exists) { + $this->logger->log("Table {$this->table_name} does not exist!", 'error'); + return false; + } + + // Prepare data for insert + $data = array( + 'order_id' => $order_id, + 'invoice_id' => $invoice_id, + 'amount' => $amount, + 'currency' => $currency, + 'status' => $status, + 'created_at' => $now, + 'updated_at' => $now, + 'metadata' => $metadata ? json_encode($metadata) : null + ); + + // Log the SQL query that will be executed + $this->logger->log("Inserting with data: " . print_r($data, true), 'debug'); + $result = $wpdb->insert( $this->table_name, - array( - 'order_id' => $order_id, - 'invoice_id' => $invoice_id, - 'amount' => $amount, - 'currency' => $currency, - 'status' => $status, - 'created_at' => $now, - 'updated_at' => $now, - 'metadata' => $metadata ? json_encode($metadata) : null - ), + $data, array('%d', '%s', '%f', '%s', '%s', '%s', '%s', '%s') ); - if ($result) { - $this->logger->log("Payment saved: order_id=$order_id, invoice_id=$invoice_id, status=$status"); - return true; - } else { - $this->logger->log("Error saving payment: " . $wpdb->last_error); + if ($result === false) { + $this->logger->log("Database error: " . $wpdb->last_error, 'error'); + $this->logger->log("Last query: " . $wpdb->last_query, 'debug'); return false; } + + $this->logger->log("Payment saved successfully: order_id=$order_id, invoice_id=$invoice_id, status=$status"); + return true; + } catch (Exception $e) { - $this->logger->log("Exception saving payment: " . $e->getMessage()); + $this->logger->log("Exception saving payment: " . $e->getMessage(), 'error'); + $this->logger->log("Stack trace: " . $e->getTraceAsString(), 'debug'); return false; } } @@ -258,4 +283,21 @@ public function install_tables() { return array(); } } + + /** + * Drop and recreate the payments table + * This is needed when changing the table schema + */ + public function recreate_table() { + global $wpdb; + + $this->logger->log("Dropping and recreating payments table", 'info'); + + // Drop the existing table + $table_name = $wpdb->prefix . 'wc_breez_payments'; + $wpdb->query("DROP TABLE IF EXISTS {$table_name}"); + + // Create the table with new schema + $this->install_tables(); + } } diff --git a/includes/class-breez-payment-handler.php b/includes/class-breez-payment-handler.php index 7b0a99a..0b532a7 100644 --- a/includes/class-breez-payment-handler.php +++ b/includes/class-breez-payment-handler.php @@ -360,8 +360,16 @@ class Breez_Payment_Handler { // Update order status if still pending if ($order->has_status('pending')) { + // Clear the payment data to allow new payment creation + $order->delete_meta_data('_breez_payment_id'); + $order->delete_meta_data('_breez_invoice_id'); + $order->delete_meta_data('_breez_payment_expires'); + $order->update_status('failed', __('Payment failed or expired.', 'breez-woocommerce')); $this->logger->log("Payment failed for order #$order_id (invoice: $invoice_id)"); + + // Save the order + $order->save(); } return true; diff --git a/includes/class-breez-webhook-handler.php b/includes/class-breez-webhook-handler.php index 648211c..4f8ef90 100644 --- a/includes/class-breez-webhook-handler.php +++ b/includes/class-breez-webhook-handler.php @@ -165,20 +165,33 @@ class Breez_Webhook_Handler { ), 404); } - $payment_handler = new Breez_Payment_Handler(null, $db_manager); + // Update payment status in database + $db_manager->update_payment_status($order_id, $status); - // Update payment status based on the webhook data + // Update order status based on payment status if ($status === 'SUCCEEDED') { - $payment_handler->process_successful_payment($invoice_id); - } elseif ($status === 'FAILED') { - $payment_handler->process_failed_payment($invoice_id); - } else { - self::$logger->log("Unknown payment status: $status"); + if ($order->get_status() === 'pending') { + // Complete the order + $order->payment_complete($invoice_id); + $order->add_order_note(sprintf( + __('Payment confirmed. Amount: %d sats, Hash: %s', 'breez-woocommerce'), + $payment['metadata']['amount_sat'], + $invoice_id + )); + $order->save(); + + self::$logger->log("Order #$order_id marked as complete", 'info'); + } + } else if ($status === 'FAILED') { + if ($order->get_status() === 'pending') { + $order->update_status('failed', __('Payment failed or expired.', 'breez-woocommerce')); + self::$logger->log("Order #$order_id marked as failed", 'info'); + } } return new WP_REST_Response(array( 'success' => true, - 'message' => 'Webhook processed successfully' + 'status' => $status ), 200); } } diff --git a/templates/payment-instructions.php b/templates/payment-instructions.php index 8859f8e..d31011e 100644 --- a/templates/payment-instructions.php +++ b/templates/payment-instructions.php @@ -216,11 +216,6 @@ document.addEventListener('DOMContentLoaded', function() {

${paymentData.status_description || ''}

-
-

-

${amountSats} sats

-

${feesSats} sats

-
`;