mirror of
https://github.com/aljazceru/breez-woocommerce.git
synced 2025-12-19 15:04:24 +01:00
working
This commit is contained in:
@@ -13,17 +13,31 @@
|
||||
|
||||
.breez-payment-status {
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 20px;
|
||||
margin: 10px 0;
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.breez-payment-pending,
|
||||
.breez-payment-confirming,
|
||||
.breez-payment-unknown {
|
||||
background-color: #f8f9fa;
|
||||
border: 1px solid #e9ecef;
|
||||
padding: 15px;
|
||||
margin: 10px 0;
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.breez-payment-completed {
|
||||
background: #d4edda;
|
||||
background-color: #d4edda;
|
||||
border: 1px solid #c3e6cb;
|
||||
color: #155724;
|
||||
}
|
||||
|
||||
.breez-payment-failed {
|
||||
background: #f8d7da;
|
||||
background-color: #f8d7da;
|
||||
border: 1px solid #f5c6cb;
|
||||
color: #721c24;
|
||||
}
|
||||
|
||||
@@ -89,3 +103,61 @@
|
||||
border-radius: 5px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.breez-payment-details {
|
||||
margin-top: 15px;
|
||||
padding: 10px;
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.breez-error-details {
|
||||
font-family: monospace;
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
padding: 10px;
|
||||
margin-top: 10px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* Loading Spinner */
|
||||
.breez-spinner {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin: 10px auto;
|
||||
border: 3px solid #f3f3f3;
|
||||
border-top: 3px solid #3498db;
|
||||
border-radius: 50%;
|
||||
animation: breez-spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes breez-spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* Status Icons */
|
||||
.breez-payment-status::before {
|
||||
font-family: dashicons;
|
||||
font-size: 24px;
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.breez-payment-completed::before {
|
||||
content: "\f147"; /* Dashicon: yes */
|
||||
color: #155724;
|
||||
}
|
||||
|
||||
.breez-payment-failed::before {
|
||||
content: "\f335"; /* Dashicon: no */
|
||||
color: #721c24;
|
||||
}
|
||||
|
||||
.breez-payment-pending::before,
|
||||
.breez-payment-confirming::before,
|
||||
.breez-payment-unknown::before {
|
||||
content: "\f463"; /* Dashicon: update */
|
||||
color: #856404;
|
||||
animation: breez-spin 2s linear infinite;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
return window.wp.element.createElement(
|
||||
'div',
|
||||
null,
|
||||
window.breezSettings?.description || 'Pay with Bitcoin via Lightning Network or on-chain transaction.'
|
||||
window.breezSettings?.description || 'Pay with Lightning.'
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -565,7 +565,7 @@ function breez_register_blocks_scripts() {
|
||||
[
|
||||
'breez' => [
|
||||
'title' => !empty($settings['title']) ? $settings['title'] : 'Breez Nodeless Payments',
|
||||
'description' => !empty($settings['description']) ? $settings['description'] : 'Pay with Bitcoin via Lightning Network or on-chain transaction.',
|
||||
'description' => !empty($settings['description']) ? $settings['description'] : 'Pay with Lightning',
|
||||
'supports' => ['products'],
|
||||
'showSavedCards' => false,
|
||||
'canMakePayment' => true,
|
||||
|
||||
@@ -20,14 +20,14 @@ return array(
|
||||
'title' => __('Title', 'breez-woocommerce'),
|
||||
'type' => 'text',
|
||||
'description' => __('This controls the title which the user sees during checkout.', 'breez-woocommerce'),
|
||||
'default' => __('Breez Nodeless Payments', 'breez-woocommerce'),
|
||||
'default' => __('Lightning Payments', 'breez-woocommerce'),
|
||||
'desc_tip' => true,
|
||||
),
|
||||
'description' => array(
|
||||
'title' => __('Description', 'breez-woocommerce'),
|
||||
'type' => 'textarea',
|
||||
'description' => __('This controls the description which the user sees during checkout.', 'breez-woocommerce'),
|
||||
'default' => __('Pay with Bitcoin via Lightning Network or on-chain transaction.', 'breez-woocommerce'),
|
||||
'default' => __('Pay with Lightning', 'breez-woocommerce'),
|
||||
'desc_tip' => true,
|
||||
),
|
||||
'instructions' => array(
|
||||
|
||||
@@ -14,7 +14,7 @@ const BreezLabel = () => {
|
||||
};
|
||||
|
||||
const BreezComponent = () => {
|
||||
const description = window.wcSettings?.breez?.description || 'Pay with Bitcoin via Lightning Network or on-chain transaction.';
|
||||
const description = window.wcSettings?.breez?.description || 'Pay with Lightning';
|
||||
return createElement('div', { className: 'wc-block-components-payment-method-description' },
|
||||
decodeEntities(description)
|
||||
);
|
||||
|
||||
@@ -108,7 +108,7 @@ class Breez_API_Client {
|
||||
/**
|
||||
* Check payment status using API endpoint
|
||||
*
|
||||
* @param string $invoice_id Invoice ID or payment destination
|
||||
* @param string $invoice_id Invoice ID or payment identifier
|
||||
* @return array Payment status response
|
||||
*/
|
||||
public function check_payment_status($invoice_id) {
|
||||
@@ -126,26 +126,101 @@ class Breez_API_Client {
|
||||
));
|
||||
|
||||
// If the payment is not found, return pending instead of throwing an error
|
||||
if (isset($response['error']) && $response['error'] === 'Payment not found') {
|
||||
if ($response['status'] === 'UNKNOWN') {
|
||||
return array(
|
||||
'status' => 'pending',
|
||||
'destination' => $invoice_id
|
||||
'destination' => $invoice_id,
|
||||
'sdk_status' => 'UNKNOWN'
|
||||
);
|
||||
}
|
||||
|
||||
// Return the response as is
|
||||
return $response;
|
||||
// Map SDK payment states to WooCommerce states
|
||||
$status = $this->map_payment_status($response['status']);
|
||||
|
||||
// Build response with all available details
|
||||
$result = array(
|
||||
'status' => $status,
|
||||
'sdk_status' => $response['status'], // Include original SDK status
|
||||
'destination' => $invoice_id,
|
||||
'amount_sat' => $response['amount_sat'] ?? null,
|
||||
'fees_sat' => $response['fees_sat'] ?? null,
|
||||
'timestamp' => $response['timestamp'] ?? null,
|
||||
'error' => $response['error'] ?? null
|
||||
);
|
||||
|
||||
// Include payment details if available
|
||||
if (isset($response['payment_details'])) {
|
||||
$result['payment_details'] = $response['payment_details'];
|
||||
}
|
||||
|
||||
// Add human-readable status description
|
||||
$result['status_description'] = $this->get_status_description($response['status']);
|
||||
|
||||
return $result;
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->logger->log('Payment status check error: ' . $e->getMessage(), 'error');
|
||||
// Return pending status instead of throwing an error
|
||||
return array(
|
||||
'status' => 'pending',
|
||||
'destination' => $invoice_id
|
||||
'sdk_status' => 'UNKNOWN',
|
||||
'destination' => $invoice_id,
|
||||
'error' => $e->getMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Map SDK payment states to WooCommerce payment states
|
||||
*
|
||||
* @param string $sdk_status The status from the SDK
|
||||
* @return string WooCommerce payment status
|
||||
*/
|
||||
private function map_payment_status($sdk_status) {
|
||||
switch ($sdk_status) {
|
||||
case 'SUCCEEDED':
|
||||
case 'WAITING_CONFIRMATION': // Consider payment complete when claim tx is broadcast
|
||||
return 'completed';
|
||||
|
||||
case 'PENDING': // Lockup transaction broadcast
|
||||
return 'pending';
|
||||
|
||||
case 'WAITING_FEE_ACCEPTANCE': // Needs fee approval
|
||||
return 'pending';
|
||||
|
||||
case 'FAILED': // Swap failed (expired or lockup tx failed)
|
||||
return 'failed';
|
||||
|
||||
case 'UNKNOWN': // Payment not found or error
|
||||
default:
|
||||
return 'pending';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get human-readable status description
|
||||
*
|
||||
* @param string $sdk_status The SDK status
|
||||
* @return string Human-readable description
|
||||
*/
|
||||
private function get_status_description($sdk_status) {
|
||||
switch ($sdk_status) {
|
||||
case 'SUCCEEDED':
|
||||
return __('Payment confirmed and completed.', 'breez-woocommerce');
|
||||
case 'WAITING_CONFIRMATION':
|
||||
return __('Payment received and being confirmed.', 'breez-woocommerce');
|
||||
case 'PENDING':
|
||||
return __('Payment initiated, waiting for completion.', 'breez-woocommerce');
|
||||
case 'WAITING_FEE_ACCEPTANCE':
|
||||
return __('Waiting for fee approval.', 'breez-woocommerce');
|
||||
case 'FAILED':
|
||||
return __('Payment failed or expired.', 'breez-woocommerce');
|
||||
case 'UNKNOWN':
|
||||
default:
|
||||
return __('Payment status unknown.', 'breez-woocommerce');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a webhook URL
|
||||
*
|
||||
|
||||
@@ -137,7 +137,7 @@ class Breez_Blocks_Support extends Automattic\WooCommerce\Blocks\Payments\Integr
|
||||
public function get_payment_method_data() {
|
||||
$data = [
|
||||
'title' => !empty($this->settings['title']) ? $this->settings['title'] : 'Breez Nodeless Payments',
|
||||
'description' => !empty($this->settings['description']) ? $this->settings['description'] : 'Pay with Bitcoin via Lightning Network or on-chain transaction.',
|
||||
'description' => !empty($this->settings['description']) ? $this->settings['description'] : 'Pay with Lightning',
|
||||
'supports' => ['products'],
|
||||
'showSavedCards' => false,
|
||||
'canMakePayment' => true,
|
||||
|
||||
@@ -212,6 +212,18 @@ class Breez_Payment_Handler {
|
||||
|
||||
/**
|
||||
* Check payment status
|
||||
*
|
||||
* Payment states from SDK are mapped to WooCommerce states as follows:
|
||||
* - SUCCEEDED -> completed (claim tx confirmed)
|
||||
* - WAITING_CONFIRMATION -> completed (claim tx broadcast but not confirmed)
|
||||
* - PENDING -> pending (lockup tx broadcast)
|
||||
* - WAITING_FEE_ACCEPTANCE -> pending (needs fee approval)
|
||||
* - FAILED -> failed (expired or lockup tx failed)
|
||||
* - UNKNOWN -> pending (not found or error)
|
||||
*
|
||||
* Note: WAITING_CONFIRMATION is considered completed because the claim transaction
|
||||
* has been broadcast or a direct Liquid transaction has been seen, making the
|
||||
* payment effectively irreversible at this point.
|
||||
*
|
||||
* @param string $invoice_id Invoice ID
|
||||
* @return string Payment status (pending, completed, failed)
|
||||
@@ -228,20 +240,30 @@ class Breez_Payment_Handler {
|
||||
// First check the local database
|
||||
$payment = $this->db_manager->get_payment_by_invoice($invoice_id);
|
||||
|
||||
if ($payment && $payment['status'] !== 'pending') {
|
||||
$this->logger->log('Using cached payment status', 'debug', array(
|
||||
if ($payment && $payment['status'] === 'completed') {
|
||||
$this->logger->log('Using cached completed payment status', 'debug', array(
|
||||
'invoice_id' => $invoice_id,
|
||||
'status' => $payment['status']
|
||||
));
|
||||
return $payment['status']; // Return cached status if not pending
|
||||
return $payment['status']; // Only return cached status if completed
|
||||
}
|
||||
|
||||
// Check with API
|
||||
$response = $this->client->check_payment_status($invoice_id);
|
||||
$status = $response['status'];
|
||||
|
||||
// If pending or not found, check with API
|
||||
$status = $this->client->check_payment_status($invoice_id);
|
||||
|
||||
// Log detailed payment state information
|
||||
$this->logger->log('Payment status details', 'debug', array(
|
||||
'invoice_id' => $invoice_id,
|
||||
'status' => $status,
|
||||
'sdk_status' => $response['payment_details']['status'] ?? 'unknown',
|
||||
'amount_sat' => $response['amount_sat'],
|
||||
'timestamp' => $response['timestamp'],
|
||||
'error' => $response['error']
|
||||
));
|
||||
|
||||
// Update database if status has changed and we have payment data
|
||||
if ($payment && $status !== 'pending' && $status !== $payment['status']) {
|
||||
if ($payment && $status !== $payment['status']) {
|
||||
$this->db_manager->update_payment_status($payment['order_id'], $status);
|
||||
|
||||
$this->logger->log('Payment status updated', 'info', array(
|
||||
@@ -250,6 +272,13 @@ class Breez_Payment_Handler {
|
||||
'new_status' => $status,
|
||||
'order_id' => $payment['order_id']
|
||||
));
|
||||
|
||||
// Process status changes
|
||||
if ($status === 'completed') {
|
||||
$this->process_successful_payment($invoice_id);
|
||||
} else if ($status === 'failed') {
|
||||
$this->process_failed_payment($invoice_id);
|
||||
}
|
||||
}
|
||||
|
||||
$duration = round(microtime(true) - $start_time, 3);
|
||||
|
||||
@@ -181,25 +181,46 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
// Clear any existing error message
|
||||
statusIndicator.classList.remove('breez-payment-error');
|
||||
|
||||
// If status is pending, don't show any status message
|
||||
if (paymentData.status === 'pending') {
|
||||
// Update status message based on SDK status
|
||||
if (paymentData.sdk_status === 'PENDING' || paymentData.sdk_status === 'WAITING_FEE_ACCEPTANCE') {
|
||||
statusIndicator.innerHTML = `
|
||||
<div class="breez-payment-pending">
|
||||
<p>${paymentData.status_description || '<?php _e('Payment is being processed...', 'breez-woocommerce'); ?>'}</p>
|
||||
<div class="breez-spinner"></div>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
if (paymentData.status === 'SUCCEEDED' || (data.status && data.status === 'SUCCEEDED')) {
|
||||
if (paymentData.sdk_status === 'WAITING_CONFIRMATION') {
|
||||
statusIndicator.innerHTML = `
|
||||
<div class="breez-payment-confirming">
|
||||
<p>${paymentData.status_description || '<?php _e('Payment received! Waiting for confirmation...', 'breez-woocommerce'); ?>'}</p>
|
||||
<div class="breez-spinner"></div>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
if (paymentData.sdk_status === 'SUCCEEDED') {
|
||||
// Stop checking status immediately
|
||||
clearInterval(statusCheckInterval);
|
||||
|
||||
// Update UI to show payment completed
|
||||
// Format amounts with commas for better readability
|
||||
const amountSats = paymentData.amount_sat ? Number(paymentData.amount_sat).toLocaleString() : '0';
|
||||
const feesSats = paymentData.fees_sat ? Number(paymentData.fees_sat).toLocaleString() : '0';
|
||||
|
||||
// Update UI to show payment completed with amount details
|
||||
paymentBox.innerHTML = `
|
||||
<h3><?php _e('Bitcoin/Lightning Payment', 'breez-woocommerce'); ?></h3>
|
||||
<div class="breez-payment-status breez-payment-completed">
|
||||
<p><?php _e('Payment received! Thank you for your payment.', 'breez-woocommerce'); ?></p>
|
||||
<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>
|
||||
<p class="breez-payment-details">
|
||||
<?php _e('Amount paid:', 'breez-woocommerce'); ?> ${Number(paymentData.amount_sat).toLocaleString()} sats<br>
|
||||
<?php _e('Network fee:', 'breez-woocommerce'); ?> ${Number(paymentData.fees_sat).toLocaleString()} sats
|
||||
</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>
|
||||
`;
|
||||
|
||||
@@ -211,29 +232,32 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
clearInterval(statusCheckInterval);
|
||||
statusCheckInterval = null;
|
||||
}
|
||||
} else if (paymentData.status === 'FAILED' || (data.status && data.status === 'FAILED')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (paymentData.sdk_status === 'FAILED') {
|
||||
// Stop checking status immediately
|
||||
clearInterval(statusCheckInterval);
|
||||
|
||||
// Update UI to show payment failed
|
||||
// Show error message
|
||||
paymentBox.innerHTML = `
|
||||
<h3><?php _e('Bitcoin/Lightning Payment', 'breez-woocommerce'); ?></h3>
|
||||
<div class="breez-payment-status breez-payment-failed">
|
||||
<p><?php _e('Payment failed.', 'breez-woocommerce'); ?></p>
|
||||
<p><?php _e('Please try again or contact us for assistance.', 'breez-woocommerce'); ?></p>
|
||||
${paymentData.error ? `<p class="breez-error-details">${paymentData.error}</p>` : ''}
|
||||
<p>${paymentData.status_description || '<?php _e('Payment failed or expired.', 'breez-woocommerce'); ?>'}</p>
|
||||
<p>${paymentData.error || ''}</p>
|
||||
<p><?php _e('Please try again or contact support if the problem persists.', 'breez-woocommerce'); ?></p>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Notify WordPress about the failed payment
|
||||
notifyServer(paymentData);
|
||||
|
||||
// Double check interval is cleared
|
||||
if (statusCheckInterval) {
|
||||
clearInterval(statusCheckInterval);
|
||||
statusCheckInterval = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// For unknown status, show generic message
|
||||
statusIndicator.innerHTML = `
|
||||
<div class="breez-payment-unknown">
|
||||
<p>${paymentData.status_description || '<?php _e('Checking payment status...', 'breez-woocommerce'); ?>'}</p>
|
||||
<div class="breez-spinner"></div>
|
||||
</div>
|
||||
`;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error checking payment status:', error);
|
||||
|
||||
@@ -37,7 +37,7 @@ class Breez_Unit_Test_Case extends WP_UnitTestCase {
|
||||
update_option('woocommerce_breez_settings', [
|
||||
'enabled' => 'yes',
|
||||
'title' => 'Breez Nodeless Payments',
|
||||
'description' => 'Pay with Bitcoin via Lightning Network or on-chain transaction.',
|
||||
'description' => 'Pay with Lightning',
|
||||
'api_url' => 'https://api.test.breez.com',
|
||||
'api_key' => 'test_api_key',
|
||||
'webhook_secret' => 'test_webhook_secret',
|
||||
|
||||
Reference in New Issue
Block a user