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
This commit is contained in:
parent
89a1fcb030
commit
cbd4159f72
2 changed files with 83 additions and 0 deletions
76
docs/02-modules/events-module/guest-checkout.md
Normal file
76
docs/02-modules/events-module/guest-checkout.md
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
# Events — guest checkout, fiat return flow and ticket delivery
|
||||
|
||||
Buyers can purchase **paid** tickets without an LNbits account. This note is
|
||||
the wire contract between the webapp and the `aiolabs/events` extension
|
||||
(≥ v1.6.1-aio.8) for that flow. Free tickets still require a login
|
||||
(events#29: no anonymous free issuance).
|
||||
|
||||
## Identity on `POST /events/api/v1/tickets/{event_id}`
|
||||
|
||||
| Buyer | Body fields | Auth header |
|
||||
|---|---|---|
|
||||
| Logged in | `user_id` + `email` (delivery address, prefilled from the account, editable) | `Authorization: Bearer <token>` |
|
||||
| Guest | `name` + `email` | none (the endpoint is anonymous; the instance `X-API-KEY` is always sent) |
|
||||
|
||||
Every purchase also sends `frontend_url` — this app's root
|
||||
(`window.location.origin + import.meta.env.BASE_URL`, no trailing slash;
|
||||
`src/modules/events/lib/frontendRoot.ts`). The extension only accepts it when
|
||||
its origin is on the LNbits `LNBITS_CORS_ALLOWED_ORIGINS` allowlist (or the
|
||||
LNbits base / custom-frontend origin); otherwise the request fails with
|
||||
`frontend_url origin is not allowed.` and the dialog shows that message.
|
||||
|
||||
`payment_method` is `lightning` or `fiat`. The event's enabled rails come from
|
||||
the NIP-52 tag `tickets_payment_methods` (`"lightning,fiat"`); when the tag is
|
||||
absent the legacy rule applies (Lightning always, fiat iff
|
||||
`tickets_allow_fiat`). See `lib/paymentMethods.ts`. A rail that is off is
|
||||
rejected by the backend with `Payment method not enabled for this event.`
|
||||
|
||||
## Return from the fiat provider
|
||||
|
||||
The extension composes both Stripe URLs under `frontend_url`:
|
||||
|
||||
| Purpose | URL |
|
||||
|---|---|
|
||||
| success | `<root>/events/{event_id}?checkout=success&tickets=<id1,id2>` |
|
||||
| cancel | `<root>/events/{event_id}?checkout=cancelled` |
|
||||
|
||||
Before redirecting (same tab, `window.location.assign`), the purchase dialog
|
||||
writes `localStorage['events:pending-checkout']` =
|
||||
`{ eventId, paymentHash, ticketIds, email, ts }` (`lib/pendingCheckout.ts`).
|
||||
`EventDetailPage` strips the query on arrival, then:
|
||||
|
||||
- `success` → opens `CheckoutReturnDialog`, which polls the anonymous
|
||||
`GET /events/api/v1/tickets/{ticket_id}` for every id every 2 s (60 s budget,
|
||||
retry button) until all are `paid`, then renders one QR per ticket, the
|
||||
"emailed to" note (email from the localStorage record) and a link to the
|
||||
ticket page. Logged-in buyers also get `useOwnedTickets().refresh()`.
|
||||
- `cancelled` → info toast, record cleared. Nothing was charged; the
|
||||
extension purges unpaid rows after 24 h.
|
||||
|
||||
## Public ticket page
|
||||
|
||||
`/events/ticket/:ticketId` (`views/TicketPage.vue`, `requiresAuth: false`) is
|
||||
where the emailed link lands. It reads the `PublicTicket` (no id / email /
|
||||
user_id in the payload; the id in the URL is the door credential, exactly as
|
||||
in the LNbits UI), resolves the event name (Nostr store → scoped relay query
|
||||
→ public REST, which 410s on sold-out events and is treated as non-fatal) and
|
||||
renders the QR.
|
||||
|
||||
## QR rendering
|
||||
|
||||
All `ticket://<id>` QRs (purchase success, return dialog, ticket page, My
|
||||
Tickets) go through `lib/ticketQr.ts`: error-correction level H with the
|
||||
brand logo (`@brand/logo.png`) centred at ≤ 20 % width on a white pad, so the
|
||||
in-app image matches the PNG the extension embeds in the ticket email
|
||||
(`GET /events/api/v1/qr/{ticket_id}`). Canvas failures fall back to the plain
|
||||
QR.
|
||||
|
||||
## Organizer side
|
||||
|
||||
`CreateEventDialog` publishes `extra.payment_methods` (Lightning / Card
|
||||
checkboxes; at least one) and keeps `allow_fiat` in sync for older readers.
|
||||
Card is disabled with a tooltip when the organizer's LNbits user has no fiat
|
||||
provider (`useFiatProviders().hasAnyProvider`).
|
||||
|
||||
`resendTicketEmail` returns the structured `TicketResendResult`
|
||||
(`{ ticket, email: {attempted, sent, error}, nostr: {...} }`).
|
||||
Loading…
Add table
Add a link
Reference in a new issue