Free events (price_per_ticket == 0) tried to mint a 0-amount Lightning
invoice via create_payment_request — an invoice that can't settle, and
which the invoice listener would never mark paid, so the ticket never
became scannable.
api_ticket_create now short-circuits when the final charge is 0 (a free
event or a 100%-off promo, computed after promo + quantity) before any
invoice / fiat-provider logic: _issue_free_tickets creates the N rows and
runs each through the existing set_ticket_paid — the same path
on_invoice_paid drives for a settled payment (flip paid, bump
sold/available under the per-event lock, republish the NIP-52 event) —
plus the ticket notification. The response carries a new
TicketPaymentRequest.paid=True with no payment_request so the client
skips the QR / payment-poll and goes straight to the ticket QRs.
No invoice means sats_paid=0, so free tickets are naturally skipped by
refund_tickets. All rows in a batch share one synthetic payment_hash —
the join key the poll / WebSocket / My-Tickets lookups use — mirroring
the paid multi-ticket path.
Self-service forfeit (#28), abuse/identity limits (#29) and
pay-what-you-want/donation tickets (#30) are tracked as follow-ups.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>