diff --git a/docs/event-flow.md b/docs/event-flow.md index 8fce44a..93b33bf 100644 --- a/docs/event-flow.md +++ b/docs/event-flow.md @@ -39,10 +39,7 @@ scoped, so the *operator* can stream booking settlements (`subscribe_payments({tag:"chatelet", link_id:})`, wired via `register_link_owner_resolver`). A **guest** can't subscribe to the operator's wallet, so the guest confirms by polling `chatelet_booking_get` until -`confirmed` (a guest push would need a NIP-17 DM — issue #5). The HTTP door -has the same read as keyless `GET /api/v1/public/bookings/{id}` (issue #18): -the booking id from the quote is the capability, and the response is the -same identity-stripped shape (`public_booking_dict`) on both doors. +`confirmed` (a guest push would need a NIP-17 DM — issue #5). > **Custom kinds vs the RPC — training wheels, not redundancy:** the > `chatelet_availability` RPC is what ships first, but the ephemeral diff --git a/models.py b/models.py index be6f22d..6fd20a8 100644 --- a/models.py +++ b/models.py @@ -208,25 +208,6 @@ def public_room_dict(room: Room) -> dict: class AvailabilityQuery(BaseModel): room_id: str -def public_booking_dict(booking: "Booking") -> dict: - """A Booking as public JSON for the guest who holds its id — lifecycle + - money + dates only. Strips the guest's own identity/contact (so the id - alone can't be turned into PII) and the internal Lightning/Nostr - plumbing. The 10-char id from the quote is the capability here, the same - trust level as the RPC door's chatelet_booking_get; chatelet_booking_get - additionally scopes by sender_pubkey, which HTTP can't.""" - d = json.loads(booking.json()) - for k in ( - "guest_pubkey", - "guest_contact", - "payment_hash", - "request_event_id", - "reservation_event_id", - ): - d.pop(k, None) - return d - - check_in: str # YYYY-MM-DD inclusive check_out: str # YYYY-MM-DD exclusive diff --git a/tests/test_public_endpoints.py b/tests/test_public_endpoints.py index 8150b9a..363ae7c 100644 --- a/tests/test_public_endpoints.py +++ b/tests/test_public_endpoints.py @@ -7,13 +7,7 @@ import pytest from fastapi import HTTPException from .. import crud, views_api -from ..models import ( - Booking, - BookingStatus, - RoomStatus, - public_booking_dict, - public_room_dict, -) +from ..models import RoomStatus, public_room_dict from .conftest import make_room @@ -64,66 +58,3 @@ def test_public_room_returns_stripped_when_active(monkeypatch): assert out["id"] == "a" assert "checkin_instructions" not in out assert "wallet" not in out - - -def _booking(**overrides) -> Booking: - base = { - "id": "bk_1234567", - "room_id": "a", - "guest_pubkey": "ab" * 32, - "guest_contact": "guest@example.com", - "check_in": "2026-10-05", - "check_out": "2026-10-07", - "nights": 2, - "num_guests": 1, - "currency": "EUR", - "price_fiat": 200.0, - "amount_sat": 300000, - "deposit_sat": 300000, - "status": BookingStatus.awaiting_payment, - "payment_hash": "ph_1", - "request_event_id": "req_ev", - "reservation_event_id": "res_ev", - } - base.update(overrides) - return Booking(**base) - - -def test_public_booking_dict_strips_identity_and_plumbing(): - d = public_booking_dict(_booking()) - for private in ( - "guest_pubkey", - "guest_contact", - "payment_hash", - "request_event_id", - "reservation_event_id", - ): - assert private not in d - # What a guest client needs to render "waiting" / "booked" / "expired". - assert d["id"] == "bk_1234567" - assert d["status"] == "awaiting_payment" - assert d["check_in"] == "2026-10-05" - assert d["nights"] == 2 - assert d["deposit_sat"] == 300000 - assert "expires_at" in d - - -def test_public_booking_404_when_missing(monkeypatch): - async def gb(_): - return None - - monkeypatch.setattr(crud, "get_booking", gb) - with pytest.raises(HTTPException) as e: - asyncio.run(views_api.api_public_booking("nope")) - assert e.value.status_code == 404 - - -def test_public_booking_returns_stripped(monkeypatch): - async def gb(_): - return _booking(status=BookingStatus.confirmed) - - monkeypatch.setattr(crud, "get_booking", gb) - out = asyncio.run(views_api.api_public_booking("bk_1234567")) - assert out["status"] == "confirmed" - assert "guest_contact" not in out - assert "payment_hash" not in out diff --git a/views_api.py b/views_api.py index cc45519..7b28cde 100644 --- a/views_api.py +++ b/views_api.py @@ -24,7 +24,6 @@ from .models import ( CreateRoomData, Room, RoomStatus, - public_booking_dict, public_room_dict, ) from .nostr import service as nostr @@ -244,15 +243,3 @@ async def api_get_booking( if not booking: raise HTTPException(404, "Booking not found") return booking - - -@chatelet_api_router.get("/api/v1/public/bookings/{booking_id}") -async def api_public_booking(booking_id: str) -> dict: - """Guest read-back of their own booking (keyless). The guest can't - subscribe to the operator's wallet, so this is how a client waits for - `awaiting_payment` -> `confirmed` (or sees `expired` / `declined`) - without a key — the HTTP twin of the RPC door's chatelet_booking_get.""" - booking = await crud.get_booking(booking_id) - if not booking: - raise HTTPException(404, "Booking not found") - return public_booking_dict(booking)