feat(events): guest checkout with email delivery, Stripe return flow, public ticket page #144

Merged
padreug merged 7 commits from feat/events-guest-checkout into dev 2026-09-08 10:25:57 +00:00

7 commits

Author SHA1 Message Date
cbd4159f72 docs(events): guest checkout, fiat return flow and ticket delivery contract
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYwoAkZZmXMMmaBp4WGUBo
2026-09-06 19:46:16 +02:00
89a1fcb030 feat(events): use the shared logo QR renderer in My Tickets
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYwoAkZZmXMMmaBp4WGUBo
2026-09-06 19:43:11 +02:00
5d3ebcc5e4 feat(events): per-event payment methods in the create/edit dialog
Replace the "Also accept fiat" switch with a Lightning / Card checkbox
group so an organizer can switch Lightning off for a card-only sale.
Sends extra.payment_methods (upstream v2 shape) and keeps allow_fiat in
sync (fiat in list ⇒ allow_fiat=true) for older readers. Card stays
disabled with a tooltip when the organizer has no fiat provider; the
fiat-currency picker appears when Card is on and the price is in sats.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYwoAkZZmXMMmaBp4WGUBo
2026-09-06 19:43:10 +02:00
b853fbce4b feat(events): public ticket page at /events/ticket/:ticketId
Target of the emailed ticket link the extension now builds under the
webapp root. Anonymous, like the LNbits UI's own /events/ticket/{id}:
reads the PublicTicket, resolves the event name (Nostr store → scoped
relay query → public REST as a last resort), and renders the logo QR
with paid / pending / checked-in state.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYwoAkZZmXMMmaBp4WGUBo
2026-09-06 19:39:42 +02:00
bd78e3227d feat(events): handle the return from fiat checkout on the event page
Stripe now brings the buyer back to the event page with
?checkout=success&tickets=<ids> or ?checkout=cancelled. The page strips
the query, then opens CheckoutReturnDialog, which polls the anonymous
ticket endpoint until every row is paid (60 s budget with a retry), and
renders one logo QR per ticket with a link to its public ticket page and
the "emailed to" note from the pending-checkout record. Logged-in buyers
also get their owned tickets refreshed and a My Tickets shortcut.

The login toast on "Buy ticket" now only fires for free events; paid
events open the dialog for guests.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYwoAkZZmXMMmaBp4WGUBo
2026-09-06 19:37:52 +02:00
891b5072f8 feat(events): guest checkout in the purchase dialog with delivery email
Buyers no longer need an account to buy a paid ticket. The dialog gains a
"Your details" block (name for guests, email + confirm-email for everyone,
prefilled from the account email) as a vee-validate/zod form, and builds
its rail buttons from the event's published payment methods so an
organizer can run card-only events.

- Fiat: same-tab redirect to the provider right after the POST, with the
  pending checkout (ticket ids, email) remembered in localStorage for the
  return page; the two-step "open in new tab" panel is gone.
- Lightning: guests get the bolt11 QR + "Open in wallet" and settle via
  the existing poll; the LNbits-wallet button stays login-only.
- Free tickets remain login-gated (events#29).
- Guest success state renders the ticket QRs inline with the "emailed
  to" note, since guests have no My Tickets.

i18n keys added under events.purchase / checkout / ticket (en, es, fr).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYwoAkZZmXMMmaBp4WGUBo
2026-09-06 19:35:45 +02:00
f85848a50b feat(events): guest identity, frontend_url and public ticket read in the ticket API
Prepare the webapp for account-less ticket purchases against events ext
v1.6.1-aio.8:

- requestTicket takes an options object: user_id (+ optional email) for
  logged-in buyers, name + email for guests, Authorization only when a
  token exists, and frontend_url so the backend builds the Stripe return
  URLs and the emailed ticket link under this app's root.
- getTicket (anonymous PublicTicket) and getPublicEvent for the return
  dialog and the public ticket page.
- resendTicketEmail maps the structured TicketResendResult (legacy bare
  ticket tolerated).
- NIP-52 tickets_payment_methods tag parsed into ticketInfo.paymentMethods;
  effectivePaymentMethods() mirrors the backend rule.
- renderTicketQr: level-H ticket:// QR with the brand logo centred, shared
  by every ticket surface; useTicketPurchase accepts a guest contact.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYwoAkZZmXMMmaBp4WGUBo
2026-09-06 19:30:47 +02:00