- Add console logging for authentication initialization, login attempts, and user retrieval to improve debugging and traceability. - Introduce a new getCurrentUser function in useAuth for better user data management. - Update useTicketPurchase to include detailed logging for user wallets and balance checks, enhancing visibility into wallet states. - Refactor LNBits API request and response logging for clearer error handling and debugging.
257 lines
No EOL
7.2 KiB
TypeScript
257 lines
No EOL
7.2 KiB
TypeScript
import { ref, computed, onUnmounted } from 'vue'
|
|
import { purchaseTicket, checkPaymentStatus, payInvoiceWithWallet } from '@/lib/api/events'
|
|
import { useAuth } from './useAuth'
|
|
import { toast } from 'vue-sonner'
|
|
|
|
export function useTicketPurchase() {
|
|
const { isAuthenticated, currentUser } = useAuth()
|
|
|
|
// State
|
|
const isLoading = ref(false)
|
|
const error = ref<string | null>(null)
|
|
const paymentHash = ref<string | null>(null)
|
|
const paymentRequest = ref<string | null>(null)
|
|
const qrCode = ref<string | null>(null)
|
|
const isPaymentPending = ref(false)
|
|
const isPayingWithWallet = ref(false)
|
|
|
|
// Ticket QR code state
|
|
const ticketQRCode = ref<string | null>(null)
|
|
const purchasedTicketId = ref<string | null>(null)
|
|
const showTicketQR = ref(false)
|
|
|
|
// Computed properties
|
|
const canPurchase = computed(() => isAuthenticated.value && currentUser.value)
|
|
const userDisplay = computed(() => {
|
|
if (!currentUser.value) return null
|
|
return {
|
|
name: currentUser.value.username || currentUser.value.id,
|
|
shortId: currentUser.value.id.slice(0, 8)
|
|
}
|
|
})
|
|
|
|
const userWallets = computed(() => {
|
|
const wallets = currentUser.value?.wallets || []
|
|
console.log('User wallets computed:', {
|
|
currentUser: currentUser.value,
|
|
wallets: wallets,
|
|
walletCount: wallets.length,
|
|
hasWallets: wallets.length > 0
|
|
})
|
|
return wallets
|
|
})
|
|
const hasWalletWithBalance = computed(() => {
|
|
const hasBalance = userWallets.value.some((wallet: any) => wallet.balance_msat > 0)
|
|
console.log('Wallet balance check:', {
|
|
wallets: userWallets.value,
|
|
hasBalance: hasBalance,
|
|
walletBalances: userWallets.value.map((w: any) => ({ id: w.id, balance: w.balance_msat }))
|
|
})
|
|
return hasBalance
|
|
})
|
|
|
|
// Generate QR code for Lightning payment
|
|
async function generateQRCode(bolt11: string) {
|
|
try {
|
|
const qrcode = await import('qrcode')
|
|
const dataUrl = await qrcode.toDataURL(bolt11, {
|
|
width: 256,
|
|
margin: 2,
|
|
color: {
|
|
dark: '#000000',
|
|
light: '#FFFFFF'
|
|
}
|
|
})
|
|
qrCode.value = dataUrl
|
|
} catch (err) {
|
|
console.error('Error generating QR code:', err)
|
|
error.value = 'Failed to generate QR code'
|
|
}
|
|
}
|
|
|
|
// Generate QR code for ticket
|
|
async function generateTicketQRCode(ticketId: string) {
|
|
try {
|
|
const qrcode = await import('qrcode')
|
|
const ticketUrl = `ticket://${ticketId}`
|
|
const dataUrl = await qrcode.toDataURL(ticketUrl, {
|
|
width: 128,
|
|
margin: 2,
|
|
color: {
|
|
dark: '#000000',
|
|
light: '#FFFFFF'
|
|
}
|
|
})
|
|
ticketQRCode.value = dataUrl
|
|
return dataUrl
|
|
} catch (error) {
|
|
console.error('Error generating ticket QR code:', error)
|
|
return null
|
|
}
|
|
}
|
|
|
|
// Pay with wallet
|
|
async function payWithWallet(paymentRequest: string) {
|
|
const walletWithBalance = userWallets.value.find((wallet: any) => wallet.balance_msat > 0)
|
|
|
|
if (!walletWithBalance) {
|
|
throw new Error('No wallet with sufficient balance found')
|
|
}
|
|
|
|
try {
|
|
await payInvoiceWithWallet(paymentRequest, walletWithBalance.id, walletWithBalance.adminkey)
|
|
return true
|
|
} catch (error) {
|
|
console.error('Wallet payment failed:', error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
// Purchase ticket for event
|
|
async function purchaseTicketForEvent(eventId: string) {
|
|
if (!canPurchase.value) {
|
|
throw new Error('User must be authenticated to purchase tickets')
|
|
}
|
|
|
|
isLoading.value = true
|
|
error.value = null
|
|
paymentHash.value = null
|
|
paymentRequest.value = null
|
|
qrCode.value = null
|
|
ticketQRCode.value = null
|
|
purchasedTicketId.value = null
|
|
showTicketQR.value = false
|
|
|
|
try {
|
|
// Get the invoice
|
|
const invoice = await purchaseTicket(eventId)
|
|
paymentHash.value = invoice.payment_hash
|
|
paymentRequest.value = invoice.payment_request
|
|
|
|
// Generate QR code for payment
|
|
await generateQRCode(invoice.payment_request)
|
|
|
|
// Try to pay with wallet if available
|
|
if (hasWalletWithBalance.value) {
|
|
isPayingWithWallet.value = true
|
|
try {
|
|
await payWithWallet(invoice.payment_request)
|
|
// If wallet payment succeeds, proceed to check payment status
|
|
await startPaymentStatusCheck(eventId, invoice.payment_hash)
|
|
} catch (walletError) {
|
|
// If wallet payment fails, fall back to manual payment
|
|
console.log('Wallet payment failed, falling back to manual payment:', walletError)
|
|
isPayingWithWallet.value = false
|
|
await startPaymentStatusCheck(eventId, invoice.payment_hash)
|
|
}
|
|
} else {
|
|
// No wallet balance, proceed with manual payment
|
|
await startPaymentStatusCheck(eventId, invoice.payment_hash)
|
|
}
|
|
} catch (err) {
|
|
error.value = err instanceof Error ? err.message : 'Failed to purchase ticket'
|
|
console.error('Error purchasing ticket:', err)
|
|
} finally {
|
|
isLoading.value = false
|
|
}
|
|
}
|
|
|
|
// Start payment status check
|
|
async function startPaymentStatusCheck(eventId: string, hash: string) {
|
|
isPaymentPending.value = true
|
|
let checkInterval: NodeJS.Timeout | null = null
|
|
|
|
const checkPayment = async () => {
|
|
try {
|
|
const result = await checkPaymentStatus(eventId, hash)
|
|
|
|
if (result.paid) {
|
|
isPaymentPending.value = false
|
|
if (checkInterval) {
|
|
clearInterval(checkInterval)
|
|
}
|
|
|
|
// Generate ticket QR code
|
|
if (result.ticket_id) {
|
|
purchasedTicketId.value = result.ticket_id
|
|
await generateTicketQRCode(result.ticket_id)
|
|
showTicketQR.value = true
|
|
}
|
|
|
|
toast.success('Ticket purchased successfully!')
|
|
}
|
|
} catch (err) {
|
|
console.error('Error checking payment status:', err)
|
|
}
|
|
}
|
|
|
|
// Check immediately
|
|
await checkPayment()
|
|
|
|
// Then check every 2 seconds
|
|
checkInterval = setInterval(checkPayment, 2000)
|
|
}
|
|
|
|
// Stop payment status check
|
|
function stopPaymentStatusCheck() {
|
|
isPaymentPending.value = false
|
|
}
|
|
|
|
// Reset payment state
|
|
function resetPaymentState() {
|
|
isLoading.value = false
|
|
error.value = null
|
|
paymentHash.value = null
|
|
paymentRequest.value = null
|
|
qrCode.value = null
|
|
isPaymentPending.value = false
|
|
isPayingWithWallet.value = false
|
|
ticketQRCode.value = null
|
|
purchasedTicketId.value = null
|
|
showTicketQR.value = false
|
|
}
|
|
|
|
// Open Lightning wallet
|
|
function handleOpenLightningWallet() {
|
|
if (paymentRequest.value) {
|
|
window.open(`lightning:${paymentRequest.value}`, '_blank')
|
|
}
|
|
}
|
|
|
|
// Cleanup function
|
|
function cleanup() {
|
|
stopPaymentStatusCheck()
|
|
}
|
|
|
|
// Lifecycle
|
|
onUnmounted(() => {
|
|
cleanup()
|
|
})
|
|
|
|
return {
|
|
// State
|
|
isLoading,
|
|
error,
|
|
paymentHash,
|
|
paymentRequest,
|
|
qrCode,
|
|
isPaymentPending,
|
|
isPayingWithWallet,
|
|
ticketQRCode,
|
|
purchasedTicketId,
|
|
showTicketQR,
|
|
|
|
// Computed
|
|
canPurchase,
|
|
userDisplay,
|
|
userWallets,
|
|
hasWalletWithBalance,
|
|
|
|
// Actions
|
|
purchaseTicketForEvent,
|
|
handleOpenLightningWallet,
|
|
resetPaymentState,
|
|
cleanup,
|
|
generateTicketQRCode
|
|
}
|
|
}
|