Enhance PaymentService and useTicketPurchase composable for improved wallet handling
- Introduced asynchronous methods in PaymentService for retrieving user wallets and checking wallet balances, allowing for dual authentication detection. - Updated getUserWalletsAsync, hasWalletWithBalanceAsync, and getWalletWithBalanceAsync methods to streamline wallet access and balance checks. - Refactored useTicketPurchase composable to load user wallets asynchronously on component mount, improving user experience during ticket purchases. - Enhanced error handling and logging for wallet loading and payment processes. These changes improve the reliability and responsiveness of wallet interactions within the payment flow.
This commit is contained in:
parent
5a899d1501
commit
5633aa154b
3 changed files with 123 additions and 16 deletions
|
|
@ -52,21 +52,81 @@ export class PaymentService extends BaseService {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get user wallets from authenticated user
|
* Get global auth composable
|
||||||
|
*/
|
||||||
|
private async getGlobalAuth() {
|
||||||
|
try {
|
||||||
|
// Use async dynamic import to avoid circular dependencies
|
||||||
|
const { auth } = await import('@/composables/useAuth')
|
||||||
|
return auth
|
||||||
|
} catch (error) {
|
||||||
|
this.debug('Could not access global auth:', error)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get user wallets from authenticated user (using dual auth detection)
|
||||||
|
*/
|
||||||
|
async getUserWalletsAsync() {
|
||||||
|
// Check both injected auth service AND global auth composable
|
||||||
|
const hasAuthService = this.authService?.user?.value?.wallets
|
||||||
|
const globalAuth = await this.getGlobalAuth()
|
||||||
|
const hasGlobalAuth = globalAuth?.currentUser?.value?.wallets
|
||||||
|
|
||||||
|
const wallets = hasAuthService || hasGlobalAuth || []
|
||||||
|
|
||||||
|
this.debug('Getting user wallets:', {
|
||||||
|
hasAuthService: !!hasAuthService,
|
||||||
|
hasGlobalAuth: !!hasGlobalAuth,
|
||||||
|
walletsCount: Array.isArray(wallets) ? wallets.length : 0,
|
||||||
|
walletsSource: hasAuthService ? 'authService' : (hasGlobalAuth ? 'globalAuth' : 'none')
|
||||||
|
})
|
||||||
|
|
||||||
|
return wallets
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get user wallets from authenticated user (synchronous fallback)
|
||||||
*/
|
*/
|
||||||
get userWallets() {
|
get userWallets() {
|
||||||
|
// Fallback to just auth service for synchronous access
|
||||||
return this.authService?.user?.value?.wallets || []
|
return this.authService?.user?.value?.wallets || []
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if user has any wallet with balance
|
* Check if user has any wallet with balance (async version)
|
||||||
|
*/
|
||||||
|
async hasWalletWithBalanceAsync(): Promise<boolean> {
|
||||||
|
const wallets = await this.getUserWalletsAsync()
|
||||||
|
return wallets.some((wallet: any) => wallet.balance_msat > 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if user has any wallet with balance (synchronous fallback)
|
||||||
*/
|
*/
|
||||||
get hasWalletWithBalance(): boolean {
|
get hasWalletWithBalance(): boolean {
|
||||||
return this.userWallets.some((wallet: any) => wallet.balance_msat > 0)
|
return this.userWallets.some((wallet: any) => wallet.balance_msat > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find wallet with sufficient balance for payment
|
* Find wallet with sufficient balance for payment (async version)
|
||||||
|
*/
|
||||||
|
async getWalletWithBalanceAsync(requiredAmountSats?: number): Promise<any | null> {
|
||||||
|
const wallets = await this.getUserWalletsAsync()
|
||||||
|
if (!wallets.length) return null
|
||||||
|
|
||||||
|
if (requiredAmountSats) {
|
||||||
|
// Convert sats to msat for comparison
|
||||||
|
const requiredMsat = requiredAmountSats * 1000
|
||||||
|
return wallets.find((wallet: any) => wallet.balance_msat >= requiredMsat)
|
||||||
|
}
|
||||||
|
|
||||||
|
return wallets.find((wallet: any) => wallet.balance_msat > 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find wallet with sufficient balance for payment (synchronous fallback)
|
||||||
*/
|
*/
|
||||||
getWalletWithBalance(requiredAmountSats?: number): any | null {
|
getWalletWithBalance(requiredAmountSats?: number): any | null {
|
||||||
const wallets = this.userWallets
|
const wallets = this.userWallets
|
||||||
|
|
@ -137,14 +197,26 @@ export class PaymentService extends BaseService {
|
||||||
requiredAmountSats?: number,
|
requiredAmountSats?: number,
|
||||||
options: PaymentOptions = {}
|
options: PaymentOptions = {}
|
||||||
): Promise<PaymentResult> {
|
): Promise<PaymentResult> {
|
||||||
// Check authentication
|
// Check authentication using dual auth detection
|
||||||
if (!this.authService?.isAuthenticated?.value || !this.authService?.user?.value) {
|
const hasAuthService = this.authService?.isAuthenticated?.value && this.authService?.user?.value
|
||||||
|
const globalAuth = await this.getGlobalAuth()
|
||||||
|
const hasGlobalAuth = globalAuth?.isAuthenticated?.value && globalAuth?.currentUser?.value
|
||||||
|
|
||||||
|
if (!hasAuthService && !hasGlobalAuth) {
|
||||||
|
this.debug('Payment failed - user not authenticated:', {
|
||||||
|
hasAuthService: !!hasAuthService,
|
||||||
|
hasGlobalAuth: !!hasGlobalAuth
|
||||||
|
})
|
||||||
throw new Error('User must be authenticated to pay with wallet')
|
throw new Error('User must be authenticated to pay with wallet')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find suitable wallet
|
// Find suitable wallet using async version for proper dual auth detection
|
||||||
const wallet = this.getWalletWithBalance(requiredAmountSats)
|
const wallet = await this.getWalletWithBalanceAsync(requiredAmountSats)
|
||||||
if (!wallet) {
|
if (!wallet) {
|
||||||
|
this.debug('No wallet with sufficient balance found:', {
|
||||||
|
requiredAmountSats,
|
||||||
|
walletsAvailable: (await this.getUserWalletsAsync()).length
|
||||||
|
})
|
||||||
throw new Error('No wallet with sufficient balance found')
|
throw new Error('No wallet with sufficient balance found')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -235,8 +307,8 @@ export class PaymentService extends BaseService {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try wallet payment first if user has balance
|
// Try wallet payment first if user has balance (use async check for proper dual auth detection)
|
||||||
if (this.hasWalletWithBalance) {
|
if (await this.hasWalletWithBalanceAsync()) {
|
||||||
try {
|
try {
|
||||||
return await this.payWithWallet(
|
return await this.payWithWallet(
|
||||||
paymentRequest,
|
paymentRequest,
|
||||||
|
|
@ -247,6 +319,8 @@ export class PaymentService extends BaseService {
|
||||||
this.debug('Wallet payment failed, offering external wallet option:', error)
|
this.debug('Wallet payment failed, offering external wallet option:', error)
|
||||||
// Don't throw here, continue to external wallet option
|
// Don't throw here, continue to external wallet option
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
this.debug('No wallet with balance available, skipping wallet payment')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback to external wallet
|
// Fallback to external wallet
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<!-- eslint-disable vue/multi-word-component-names -->
|
<!-- eslint-disable vue/multi-word-component-names -->
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onUnmounted } from 'vue'
|
import { onUnmounted, watch } from 'vue'
|
||||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from '@/components/ui/dialog'
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from '@/components/ui/dialog'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { Badge } from '@/components/ui/badge'
|
import { Badge } from '@/components/ui/badge'
|
||||||
|
|
@ -44,7 +44,8 @@ const {
|
||||||
cleanup,
|
cleanup,
|
||||||
ticketQRCode,
|
ticketQRCode,
|
||||||
purchasedTicketId,
|
purchasedTicketId,
|
||||||
showTicketQR
|
showTicketQR,
|
||||||
|
loadWallets
|
||||||
} = useTicketPurchase()
|
} = useTicketPurchase()
|
||||||
|
|
||||||
async function handlePurchase() {
|
async function handlePurchase() {
|
||||||
|
|
@ -62,6 +63,13 @@ function handleClose() {
|
||||||
resetPaymentState()
|
resetPaymentState()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reload wallets when dialog opens
|
||||||
|
watch(() => props.isOpen, async (newVal) => {
|
||||||
|
if (newVal && isAuthenticated.value) {
|
||||||
|
await loadWallets()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// Cleanup on unmount
|
// Cleanup on unmount
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
cleanup()
|
cleanup()
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { ref, computed, onUnmounted } from 'vue'
|
import { ref, computed, onUnmounted, onMounted } from 'vue'
|
||||||
import { purchaseTicket, checkPaymentStatus } from '@/lib/api/events'
|
import { purchaseTicket, checkPaymentStatus } from '@/lib/api/events'
|
||||||
import { useAuth } from '@/composables/useAuth'
|
import { useAuth } from '@/composables/useAuth'
|
||||||
import { toast } from 'vue-sonner'
|
import { toast } from 'vue-sonner'
|
||||||
|
|
@ -23,6 +23,10 @@ export function useTicketPurchase() {
|
||||||
const ticketQRCode = ref<string | null>(null)
|
const ticketQRCode = ref<string | null>(null)
|
||||||
const purchasedTicketId = ref<string | null>(null)
|
const purchasedTicketId = ref<string | null>(null)
|
||||||
const showTicketQR = ref(false)
|
const showTicketQR = ref(false)
|
||||||
|
|
||||||
|
// Wallet state (loaded asynchronously)
|
||||||
|
const userWallets = ref<any[]>([])
|
||||||
|
const hasWalletWithBalance = ref(false)
|
||||||
|
|
||||||
// Computed properties
|
// Computed properties
|
||||||
const canPurchase = computed(() => isAuthenticated.value && currentUser.value)
|
const canPurchase = computed(() => isAuthenticated.value && currentUser.value)
|
||||||
|
|
@ -34,10 +38,30 @@ export function useTicketPurchase() {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Delegate to PaymentService
|
// Delegate to PaymentService for processing status
|
||||||
const userWallets = computed(() => paymentService.userWallets) // getter method
|
|
||||||
const hasWalletWithBalance = computed(() => paymentService.hasWalletWithBalance) // getter method
|
|
||||||
const isPayingWithWallet = computed(() => paymentService.isProcessingPayment.value) // computed ref
|
const isPayingWithWallet = computed(() => paymentService.isProcessingPayment.value) // computed ref
|
||||||
|
|
||||||
|
// Load wallets asynchronously
|
||||||
|
async function loadWallets() {
|
||||||
|
try {
|
||||||
|
const wallets = await paymentService.getUserWalletsAsync()
|
||||||
|
userWallets.value = wallets
|
||||||
|
hasWalletWithBalance.value = await paymentService.hasWalletWithBalanceAsync()
|
||||||
|
console.log('Loaded wallets for ticket purchase:', {
|
||||||
|
count: wallets.length,
|
||||||
|
hasBalance: hasWalletWithBalance.value
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to load wallets:', error)
|
||||||
|
userWallets.value = []
|
||||||
|
hasWalletWithBalance.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load wallets on mount
|
||||||
|
onMounted(() => {
|
||||||
|
loadWallets()
|
||||||
|
})
|
||||||
|
|
||||||
// Generate QR code for Lightning payment - delegate to PaymentService
|
// Generate QR code for Lightning payment - delegate to PaymentService
|
||||||
async function generateQRCode(bolt11: string) {
|
async function generateQRCode(bolt11: string) {
|
||||||
|
|
@ -217,6 +241,7 @@ export function useTicketPurchase() {
|
||||||
handleOpenLightningWallet,
|
handleOpenLightningWallet,
|
||||||
resetPaymentState,
|
resetPaymentState,
|
||||||
cleanup,
|
cleanup,
|
||||||
generateTicketQRCode
|
generateTicketQRCode,
|
||||||
|
loadWallets
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue