Adds balance payment feature
Implements a feature that allows users to pay their outstanding balance via Lightning. The changes include: - Adds the UI elements for invoice generation and display, including QR code. - Integrates backend endpoints to generate and record payments. - Adds polling mechanism to track payments and update balance. - Creates new database models to support manual payment requests.
This commit is contained in:
parent
eb9a3c1600
commit
ef3e2d9e0d
4 changed files with 232 additions and 49 deletions
|
|
@ -32,6 +32,8 @@ window.app = Vue.createApp({
|
|||
payDialog: {
|
||||
show: false,
|
||||
amount: null,
|
||||
paymentRequest: null,
|
||||
paymentHash: null,
|
||||
loading: false
|
||||
},
|
||||
settingsDialog: {
|
||||
|
|
@ -311,39 +313,108 @@ window.app = Vue.createApp({
|
|||
async submitPayment() {
|
||||
this.payDialog.loading = true
|
||||
try {
|
||||
// First, generate an invoice for the payment
|
||||
const invoiceResponse = await LNbits.api.request(
|
||||
// Generate an invoice on the Castle wallet
|
||||
const response = await LNbits.api.request(
|
||||
'POST',
|
||||
'/api/v1/payments',
|
||||
'/castle/api/v1/generate-payment-invoice',
|
||||
this.g.user.wallets[0].inkey,
|
||||
{
|
||||
out: false,
|
||||
amount: this.payDialog.amount,
|
||||
memo: `Payment to Castle - ${this.payDialog.amount} sats`,
|
||||
unit: 'sat'
|
||||
amount: this.payDialog.amount
|
||||
}
|
||||
)
|
||||
|
||||
// Show the invoice to the user
|
||||
// Show the payment request in the dialog
|
||||
this.payDialog.paymentRequest = response.data.payment_request
|
||||
this.payDialog.paymentHash = response.data.payment_hash
|
||||
|
||||
this.$q.notify({
|
||||
type: 'positive',
|
||||
message: 'Invoice generated! Pay it to settle your balance.',
|
||||
timeout: 5000
|
||||
message: 'Invoice generated! Scan QR code or copy to pay.',
|
||||
timeout: 3000
|
||||
})
|
||||
|
||||
// TODO: After payment, call /castle/api/v1/pay-balance to record it
|
||||
// This would typically be done via a webhook or payment verification
|
||||
|
||||
this.payDialog.show = false
|
||||
this.payDialog.amount = null
|
||||
// Poll for payment completion
|
||||
this.pollForPayment(response.data.payment_hash)
|
||||
} catch (error) {
|
||||
LNbits.utils.notifyApiError(error)
|
||||
} finally {
|
||||
this.payDialog.loading = false
|
||||
}
|
||||
},
|
||||
async pollForPayment(paymentHash) {
|
||||
// Poll every 2 seconds for payment status
|
||||
const checkPayment = async () => {
|
||||
try {
|
||||
const response = await LNbits.api.request(
|
||||
'GET',
|
||||
`/api/v1/payments/${paymentHash}`,
|
||||
this.g.user.wallets[0].inkey
|
||||
)
|
||||
|
||||
if (response.data && response.data.paid) {
|
||||
// Record payment in accounting
|
||||
try {
|
||||
await LNbits.api.request(
|
||||
'POST',
|
||||
'/castle/api/v1/record-payment',
|
||||
this.g.user.wallets[0].inkey,
|
||||
{
|
||||
payment_hash: paymentHash
|
||||
}
|
||||
)
|
||||
} catch (error) {
|
||||
console.error('Error recording payment:', error)
|
||||
}
|
||||
|
||||
this.$q.notify({
|
||||
type: 'positive',
|
||||
message: 'Payment received! Your balance has been updated.',
|
||||
timeout: 3000
|
||||
})
|
||||
this.payDialog.show = false
|
||||
this.payDialog.paymentRequest = null
|
||||
this.payDialog.amount = null
|
||||
await this.loadBalance()
|
||||
await this.loadTransactions()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
} catch (error) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Check every 2 seconds for up to 5 minutes
|
||||
let attempts = 0
|
||||
const maxAttempts = 150 // 5 minutes
|
||||
const intervalId = setInterval(async () => {
|
||||
attempts++
|
||||
const paid = await checkPayment()
|
||||
if (paid || attempts >= maxAttempts) {
|
||||
clearInterval(intervalId)
|
||||
}
|
||||
}, 2000)
|
||||
},
|
||||
showManualPaymentOption() {
|
||||
// TODO: Show manual payment request dialog
|
||||
this.$q.notify({
|
||||
type: 'info',
|
||||
message: 'Manual payment feature coming soon!',
|
||||
timeout: 3000
|
||||
})
|
||||
},
|
||||
copyToClipboard(text) {
|
||||
navigator.clipboard.writeText(text)
|
||||
this.$q.notify({
|
||||
type: 'positive',
|
||||
message: 'Copied to clipboard!',
|
||||
timeout: 1000
|
||||
})
|
||||
},
|
||||
showPayBalanceDialog() {
|
||||
this.payDialog.amount = Math.abs(this.balance.balance)
|
||||
this.payDialog.paymentRequest = null
|
||||
this.payDialog.paymentHash = null
|
||||
this.payDialog.show = true
|
||||
},
|
||||
async showReceivableDialog() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue