- Update PurchaseTicketDialog.vue to integrate authentication checks and enhance ticket purchasing flow with wallet support. - Implement useTicketPurchase composable for managing ticket purchase logic, including payment handling and QR code generation. - Improve user experience by displaying user information and wallet status during the ticket purchase process. - Refactor API interactions in events.ts to streamline ticket purchasing and payment status checks.
220 lines
No EOL
6.1 KiB
TypeScript
220 lines
No EOL
6.1 KiB
TypeScript
import { ref, computed } from 'vue'
|
|
import { purchaseTicket, checkPaymentStatus, payInvoiceWithWallet } from '@/lib/api/events'
|
|
import { useAuth } from './useAuth'
|
|
import { toast } from 'vue-sonner'
|
|
|
|
interface Wallet {
|
|
id: string
|
|
user: string
|
|
name: string
|
|
adminkey: string
|
|
inkey: string
|
|
deleted: boolean
|
|
created_at: string
|
|
updated_at: string
|
|
currency?: string
|
|
balance_msat: number
|
|
extra?: {
|
|
icon: string
|
|
color: string
|
|
pinned: boolean
|
|
}
|
|
}
|
|
|
|
export function useTicketPurchase() {
|
|
const { isAuthenticated, currentUser } = useAuth()
|
|
const isLoading = ref(false)
|
|
const error = ref<string | null>(null)
|
|
const paymentHash = ref('')
|
|
const paymentRequest = ref('')
|
|
const qrCode = ref('')
|
|
const isPaymentPending = ref(false)
|
|
const paymentCheckInterval = ref<number | null>(null)
|
|
const isPayingWithWallet = ref(false)
|
|
|
|
const canPurchase = computed(() => {
|
|
return isAuthenticated.value && !isLoading.value
|
|
})
|
|
|
|
const userDisplay = computed(() => {
|
|
if (!currentUser.value) return null
|
|
|
|
return {
|
|
name: currentUser.value.username || currentUser.value.email || 'Anonymous',
|
|
id: currentUser.value.id,
|
|
shortId: currentUser.value.id.slice(0, 8) + '...' + currentUser.value.id.slice(-8)
|
|
}
|
|
})
|
|
|
|
const userWallets = computed(() => {
|
|
if (!currentUser.value) return [] as Wallet[]
|
|
return currentUser.value.wallets || []
|
|
})
|
|
|
|
const hasWalletWithBalance = computed(() => {
|
|
return userWallets.value.some((wallet: Wallet) => wallet.balance_msat > 0)
|
|
})
|
|
|
|
async function generateQRCode(bolt11: string) {
|
|
try {
|
|
const QRCode = await import('qrcode')
|
|
qrCode.value = await QRCode.toDataURL(`lightning:${bolt11}`)
|
|
} catch (err) {
|
|
console.error('Failed to generate QR code:', err)
|
|
}
|
|
}
|
|
|
|
async function payWithWallet(paymentRequest: string) {
|
|
if (!currentUser.value || !userWallets.value.length) {
|
|
throw new Error('No wallet available for payment')
|
|
}
|
|
|
|
// Find the first wallet with sufficient balance
|
|
const wallet = userWallets.value.find((w: Wallet) => w.balance_msat > 0)
|
|
if (!wallet) {
|
|
throw new Error('No wallet with sufficient balance found')
|
|
}
|
|
|
|
try {
|
|
isPayingWithWallet.value = true
|
|
const result = await payInvoiceWithWallet(paymentRequest, wallet.id, wallet.adminkey)
|
|
|
|
toast.success(`Payment successful! Fee: ${result.fee_msat} msat`)
|
|
return result
|
|
} catch (err) {
|
|
const message = err instanceof Error ? err.message : 'Failed to pay with wallet'
|
|
toast.error(message)
|
|
throw err
|
|
} finally {
|
|
isPayingWithWallet.value = false
|
|
}
|
|
}
|
|
|
|
async function purchaseTicketForEvent(eventId: string) {
|
|
if (!isAuthenticated.value) {
|
|
error.value = 'Please log in to purchase tickets'
|
|
toast.error('Please log in to purchase tickets')
|
|
return
|
|
}
|
|
|
|
try {
|
|
isLoading.value = true
|
|
error.value = ''
|
|
|
|
// Step 1: Get the invoice
|
|
const result = await purchaseTicket(eventId)
|
|
|
|
paymentHash.value = result.payment_hash
|
|
paymentRequest.value = result.payment_request
|
|
await generateQRCode(result.payment_request)
|
|
|
|
// Step 2: Try to pay with wallet if user has balance
|
|
if (hasWalletWithBalance.value) {
|
|
try {
|
|
await payWithWallet(result.payment_request)
|
|
// Payment successful, start monitoring for ticket confirmation
|
|
startPaymentStatusCheck(eventId, result.payment_hash)
|
|
return
|
|
} catch (walletPaymentError) {
|
|
console.log('Wallet payment failed, showing QR code for manual payment:', walletPaymentError)
|
|
// Fall back to QR code payment
|
|
startPaymentStatusCheck(eventId, result.payment_hash)
|
|
}
|
|
} else {
|
|
// No wallet balance, show QR code for manual payment
|
|
startPaymentStatusCheck(eventId, result.payment_hash)
|
|
}
|
|
|
|
toast.success('Payment request generated successfully')
|
|
} catch (err) {
|
|
const message = err instanceof Error ? err.message : 'Failed to generate payment request'
|
|
error.value = message
|
|
toast.error(message)
|
|
throw err
|
|
} finally {
|
|
isLoading.value = false
|
|
}
|
|
}
|
|
|
|
function startPaymentStatusCheck(eventId: string, paymentHash: string) {
|
|
isPaymentPending.value = true
|
|
|
|
// Clear any existing interval
|
|
if (paymentCheckInterval.value) {
|
|
clearInterval(paymentCheckInterval.value)
|
|
}
|
|
|
|
// Check payment status every 2 seconds
|
|
paymentCheckInterval.value = window.setInterval(async () => {
|
|
try {
|
|
const status = await checkPaymentStatus(eventId, paymentHash)
|
|
|
|
if (status.paid) {
|
|
// Payment successful
|
|
clearInterval(paymentCheckInterval.value!)
|
|
paymentCheckInterval.value = null
|
|
isPaymentPending.value = false
|
|
|
|
toast.success('Payment successful! Your ticket has been purchased.')
|
|
|
|
// Reset payment state
|
|
resetPaymentState()
|
|
|
|
return { success: true, ticketId: status.ticket_id }
|
|
}
|
|
} catch (err) {
|
|
console.error('Error checking payment status:', err)
|
|
// Don't show error to user for status checks, just log it
|
|
}
|
|
}, 2000)
|
|
}
|
|
|
|
function stopPaymentStatusCheck() {
|
|
if (paymentCheckInterval.value) {
|
|
clearInterval(paymentCheckInterval.value)
|
|
paymentCheckInterval.value = null
|
|
}
|
|
isPaymentPending.value = false
|
|
}
|
|
|
|
function resetPaymentState() {
|
|
paymentHash.value = ''
|
|
paymentRequest.value = ''
|
|
qrCode.value = ''
|
|
error.value = ''
|
|
stopPaymentStatusCheck()
|
|
}
|
|
|
|
function handleOpenLightningWallet() {
|
|
if (paymentRequest.value) {
|
|
window.location.href = `lightning:${paymentRequest.value}`
|
|
}
|
|
}
|
|
|
|
// Cleanup on unmount
|
|
function cleanup() {
|
|
stopPaymentStatusCheck()
|
|
}
|
|
|
|
return {
|
|
// State
|
|
isLoading,
|
|
error,
|
|
paymentHash,
|
|
paymentRequest,
|
|
qrCode,
|
|
isPaymentPending,
|
|
isPayingWithWallet,
|
|
canPurchase,
|
|
userDisplay,
|
|
userWallets,
|
|
hasWalletWithBalance,
|
|
|
|
// Actions
|
|
purchaseTicketForEvent,
|
|
payWithWallet,
|
|
handleOpenLightningWallet,
|
|
resetPaymentState,
|
|
cleanup,
|
|
}
|
|
}
|