+ Your LNbits wallet is empty — pay with an external wallet
+ using the QR or "Open in wallet" above.
+
+
+
+
+
+
+ Waiting for payment…
+
+
+
+ Confirmation lands automatically — no need to refresh.
+
diff --git a/src/modules/activities/composables/useTicketPurchase.ts b/src/modules/activities/composables/useTicketPurchase.ts
index 0145fd9..edeb6da 100644
--- a/src/modules/activities/composables/useTicketPurchase.ts
+++ b/src/modules/activities/composables/useTicketPurchase.ts
@@ -75,7 +75,15 @@ export function useTicketPurchase() {
}
}
- async function purchaseTicketForEvent(eventId: string) {
+ /** The event id this composable is currently driving — kept so
+ * `payCurrentInvoiceWithWallet` and `startPaymentStatusCheck` don't
+ * have to take it as an argument from the UI. */
+ const currentEventId = ref(null)
+
+ async function purchaseTicketForEvent(
+ eventId: string,
+ options: { quantity?: number } = {},
+ ) {
if (!canPurchase.value || !currentUser.value) {
throw new Error('User must be authenticated to purchase tickets')
}
@@ -88,6 +96,7 @@ export function useTicketPurchase() {
ticketQRCode.value = null
purchasedTicketId.value = null
showTicketQR.value = false
+ currentEventId.value = eventId
// Get the invoice via TicketApiService
const lnbitsAPI = injectService(SERVICE_TOKENS.LNBITS_API) as any
@@ -96,7 +105,8 @@ export function useTicketPurchase() {
const invoice = await ticketApi.requestTicket(
eventId,
currentUser.value!.id,
- accessToken
+ accessToken,
+ { quantity: options.quantity },
)
// Backend now returns either a Lightning invoice or a fiat
@@ -119,18 +129,12 @@ export function useTicketPurchase() {
// Generate QR code for payment
await generateQRCode(bolt11)
- // Try to pay with wallet if available
- if (hasWalletWithBalance.value) {
- try {
- await payWithWallet(bolt11)
- await startPaymentStatusCheck(eventId, invoice.paymentHash)
- } catch (walletError) {
- console.log('Wallet payment failed, falling back to manual payment:', walletError)
- await startPaymentStatusCheck(eventId, invoice.paymentHash)
- }
- } else {
- await startPaymentStatusCheck(eventId, invoice.paymentHash)
- }
+ // Restaurant-style: don't auto-pay. Surface the QR + amount and
+ // let the buyer pick "Pay with my LNbits wallet" vs "Open in
+ // external wallet" on the same screen. The composable just
+ // starts polling so when payment lands (from any path) the UI
+ // advances to the ticket-QR success state.
+ await startPaymentStatusCheck(eventId, invoice.paymentHash)
return invoice
}, {
@@ -138,6 +142,19 @@ export function useTicketPurchase() {
})
}
+ /**
+ * Trigger LNbits-wallet payment of the invoice this composable is
+ * currently displaying. Called when the buyer clicks the "Pay from
+ * my LNbits wallet" button on the invoice screen.
+ */
+ async function payCurrentInvoiceWithWallet(): Promise {
+ if (!paymentRequest.value) return
+ await payWithWallet(paymentRequest.value)
+ // Polling is already running from purchaseTicketForEvent — when
+ // the payment lands, it advances to showTicketQR. No need to
+ // restart it here.
+ }
+
async function startPaymentStatusCheck(eventId: string, hash: string) {
isPaymentPending.value = true
let checkInterval: number | null = null
@@ -219,6 +236,7 @@ export function useTicketPurchase() {
// Actions
purchaseTicketForEvent,
+ payCurrentInvoiceWithWallet,
handleOpenLightningWallet,
resetPaymentState,
cleanup,
diff --git a/src/modules/activities/services/TicketApiService.ts b/src/modules/activities/services/TicketApiService.ts
index b3174a1..6086d51 100644
--- a/src/modules/activities/services/TicketApiService.ts
+++ b/src/modules/activities/services/TicketApiService.ts
@@ -75,6 +75,8 @@ export class TicketApiService {
promoCode?: string
refundAddress?: string
nostrIdentifier?: string
+ /** Number of tickets to buy on this invoice. Backend caps at 10. */
+ quantity?: number
} = {},
): Promise {
const body: CreateTicketRequest = { user_id: userId }
@@ -83,6 +85,7 @@ export class TicketApiService {
if (options.promoCode) body.promo_code = options.promoCode
if (options.refundAddress) body.refund_address = options.refundAddress
if (options.nostrIdentifier) body.nostr_identifier = options.nostrIdentifier
+ if (options.quantity && options.quantity > 1) body.quantity = options.quantity
const data = await this.request(
`/events/api/v1/tickets/${eventId}`,
diff --git a/src/modules/activities/types/ticket.ts b/src/modules/activities/types/ticket.ts
index bd1985c..85ead95 100644
--- a/src/modules/activities/types/ticket.ts
+++ b/src/modules/activities/types/ticket.ts
@@ -169,4 +169,6 @@ export interface CreateTicketRequest {
nostr_identifier?: string
payment_method?: PaymentMethod
fiat_provider?: string
+ /** Number of tickets on this invoice (backend bounds 1..10). */
+ quantity?: number
}