feat: operator settings, guest booking list, card checkout #22

Merged
padreug merged 4 commits from feat/fiat-checkout into main 2026-09-21 09:12:17 +00:00
Owner

Three slices, stacked:

  • Per-operator settings (m003): house rules (check-in/out times, cancellation
    policy) and accept_fiat move off the instance row onto
    chatelet.operator_settings keyed by user id. Public room views and the
    kind:30402 listing carry house_rules / payment_methods so a generic Nostr
    client can render them without our RPC. GET/PUT /api/v1/operator plus RPC
    twins; admin UI gets a per-operator card.
  • GET /api/v1/bookings/mine and RPC chatelet_booking_list_mine: a guest's own
    stays, newest check-in first, minus the Lightning/Nostr plumbing.
  • Card rail via LNbits fiat providers (Stripe). Offered only when the operator
    opted in, the room is fiat-priced and core has a provider for that user
    (settings.get_fiat_providers_for_user — the seam lnbits#67 plugs into;
    chatelet stores no credentials). Both rails go through
    create_payment_request; the Stripe webhook settles onto the same invoice
    queue so on_invoice_paid confirms card bookings unchanged. frontend_url is
    allow-listed against trusted origins, ported from events.

min_lnbits_version → 1.4.1. docs/data-model.md and event-flow.md updated.
The webapp's chatelet my-bookings slice depends on this.

Three slices, stacked: - Per-operator settings (m003): house rules (check-in/out times, cancellation policy) and accept_fiat move off the instance row onto chatelet.operator_settings keyed by user id. Public room views and the kind:30402 listing carry house_rules / payment_methods so a generic Nostr client can render them without our RPC. GET/PUT /api/v1/operator plus RPC twins; admin UI gets a per-operator card. - GET /api/v1/bookings/mine and RPC chatelet_booking_list_mine: a guest's own stays, newest check-in first, minus the Lightning/Nostr plumbing. - Card rail via LNbits fiat providers (Stripe). Offered only when the operator opted in, the room is fiat-priced and core has a provider for that user (settings.get_fiat_providers_for_user — the seam lnbits#67 plugs into; chatelet stores no credentials). Both rails go through create_payment_request; the Stripe webhook settles onto the same invoice queue so on_invoice_paid confirms card bookings unchanged. frontend_url is allow-listed against trusted origins, ported from events. min_lnbits_version → 1.4.1. docs/data-model.md and event-flow.md updated. The webapp's chatelet my-bookings slice depends on this.
Chatelet is multi-tenant: any LNbits user can host rooms. What an operator
decides for all their rooms now lives in chatelet.operator_settings, keyed
by user id and created lazily (m003, which also indexes bookings by guest):
check-in/out times, cancellation policy, and accept_fiat.

Guests see it: the public room view (both doors) gains house_rules and
payment_methods, and the kind:30402 listing carries payment_methods,
checkin_time and checkout_time tags so a generic Nostr client can render
the right pay buttons and rules without our RPC. The check-in DM reads the
room owner's rules instead of the instance row.

Card is offered only when the operator opted in, the room is fiat-priced,
and LNbits core has a fiat provider for that user — resolved through
settings.get_fiat_providers_for_user(owner), the one seam lnbits#67's
per-user Stripe credentials will plug into; chatelet never sees creds.

Operator endpoints: GET/PUT /api/v1/operator (admin key → wallet user) and
RPC twins chatelet_operator_get/update (AUTH_WALLET); saving re-publishes
the owner's active listings. Admin UI moves the house-rule inputs into a
per-operator card with the card toggle and a provider hint. The old
house-rule columns on settings stay for old rows but are no longer read.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
GET /api/v1/bookings/mine (LNbits account auth; identity = the account's
Nostr pubkey, the same value the booking request carried) and RPC twin
chatelet_booking_list_mine (scoped by the signed sender_pubkey). Rows come
back newest check-in first via the m003 guest index, as guest_booking_dict:
the guest's own contact and counts, minus the Lightning/Nostr plumbing.
Declared ahead of /bookings/{booking_id} so 'mine' is not read as an id.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A guest may pay a fiat-priced room by card when its owner has opted in and
LNbits core has a fiat provider for that user — resolved through
settings.get_fiat_providers_for_user(owner), the seam lnbits#67's per-user
Stripe Connect credentials will plug into; chatelet stores no credentials.

Both rails now go through create_payment_request: Lightning unchanged
(sats, deposit_sat), card charges the same deposit share of the fiat price
in the room's currency with extra.checkout parameterising the hosted
Stripe page — success/cancel return to {frontend}/chatelet/{room}?checkout=…
&booking=<id>, customer_email, line item, metadata. frontend_url is
allow-listed against the instance's trusted origins (ported from events)
and resolved before the hold so a refused rail never leaves a dead hold.
Core settles the Stripe webhook onto the same invoice queue, so
tasks.on_invoice_paid confirms card bookings unchanged.

BookingRequestData gains payment_method / fiat_provider / frontend_url;
BookingQuote gains fiat_payment_request / fiat_provider / is_fiat and a
nullable payment_request. RPC chatelet_booking_request passes the fields
through. min_lnbits_version → 1.4.1 (events' floor for these APIs).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
padreug deleted branch feat/fiat-checkout 2026-09-21 09:12:17 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
aiolabs/chatelet!22
No description provided.