improved webhooks and status tracking in database

This commit is contained in:
2025-05-22 18:46:34 +02:00
parent 04eeac5075
commit d5c6d7bdeb
6 changed files with 270 additions and 140 deletions

View File

@@ -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');

View File

@@ -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/'
);
}
}

View File

@@ -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();
}
}

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -216,11 +216,6 @@ document.addEventListener('DOMContentLoaded', function() {
<div class="breez-payment-status breez-payment-completed">
<p>${paymentData.status_description || '<?php _e('Payment confirmed! Thank you for your payment.', 'breez-woocommerce'); ?>'}</p>
<p><?php _e('Your order is now being processed.', 'breez-woocommerce'); ?></p>
<div class="breez-payment-details">
<p><strong><?php _e('Payment Details:', 'breez-woocommerce'); ?></strong></p>
<p><?php _e('Amount paid:', 'breez-woocommerce'); ?> ${amountSats} sats</p>
<p><?php _e('Network fee:', 'breez-woocommerce'); ?> ${feesSats} sats</p>
</div>
</div>
`;