diff --git a/docs/API_MANUAL.md b/docs/API_MANUAL.md new file mode 100644 index 0000000..872a1a7 --- /dev/null +++ b/docs/API_MANUAL.md @@ -0,0 +1,622 @@ +# Trisolaris Server API Manual (Manual, No Script) + +Last updated: 2026-02-04 +Source: current Kotlin controllers in this repo. + +## Global rules + +- Base API domain: `api.hoteltrisolaris.in` +- Auth: Firebase bearer token (`MyPrincipal`) for all non-public endpoints. +- Super admin bypass: property membership/role checks are bypassed by `superAdmin=true` (via `PropertyAccess`). +- Global API error shape: + ```json + { + "timestamp": "...", + "status": 400, + "error": "Bad Request", + "message": "...", + "path": "/..." + } + ``` +- Common auth/permission errors: + - `401 Unauthorized`: missing principal / user not found + - `403 Forbidden`: missing membership/role + +## Public endpoints (no auth required) + +- `GET /` +- `GET /health` +- `GET /auth/*` +- `GET /properties/{propertyId}/rooms/available` +- `GET /properties/{propertyId}/rooms/by-type/{roomTypeCode}` +- `GET /properties/{propertyId}/room-types` +- `GET /properties/{propertyId}/room-types/{roomTypeCode}/images` +- `GET /properties/{propertyId}/rooms/{roomId}/images` +- `GET /properties/{propertyId}/rooms/{roomId}/images/{imageId}/file` +- `GET /properties/{propertyId}/guests/{guestId}/documents/{documentId}/file` (or token-based access) +- `GET /properties/{propertyId}/cancellation-policy` +- `GET /image-tags` +- `GET /icons/png` +- `GET /icons/png/{filename}` +- `POST /properties/{propertyId}/razorpay/webhook` +- `POST /properties/{propertyId}/razorpay/return/success` +- `POST /properties/{propertyId}/razorpay/return/failure` + +## Core storage map + +- Property + config: `property`, `property_email_address`, `property_email_alias`, `property_transport_mode` +- Users + membership: `app_user`, `property_user`, `property_user_role`, `property_access_code`, `property_access_code_role` +- Room inventory: `room_type`, `room_type_alias`, `room_type_amenity_link`, `room`, `room_amenity` +- Stays + cards: `room_stay`, `room_stay_audit_log`, `issued_card`, `property_card_counter` +- Booking + billing: `booking`, `booking_room_request`, `booking_billing_policy_audit_log`, `charge`, `payment` +- Guest data: `guest`, `guest_vehicle`, `guest_rating`, `guest_document` +- Rates: `rate_plan`, `rate_calendar` +- Email ingest: `inbound_email` +- Room media: `room_image`, `room_image_tag`, `room_image_tag_link` +- Razorpay: `razorpay_settings`, `razorpay_qr_request`, `razorpay_payment_link_request`, `razorpay_payment_attempt`, `razorpay_webhook_log` +- File storage roots: + - documents/signatures: `/home/androidlover5842/docs` + - emails: `/home/androidlover5842/docs/emails` + - room images: `/home/androidlover5842/docs/rooms` + - icons: `/home/androidlover5842/docs/icons/png` + +--- + +## 1) Auth APIs + +- `POST /auth/verify` + - What it does: resolves Firebase principal (creates app user if missing), returns user + property memberships. + - Access: public. + - Stores data: may insert/update `app_user`; reads `property_user`. +- `GET /auth/me` + - What it does: same shape as verify. + - Access: public. + - Stores data: may create `app_user` if resolver is used. +- `PUT /auth/me` + - Request: `{ "name": "..." }` + - What it does: updates current user name. + - Access: authenticated user. + - Stores data: updates `app_user.name`. + +## 2) System + Static assets + +- `GET /health` / `GET /` + - What it does: health payload with build identifier. + - Access: public. + - Stores data: none. +- `GET /icons/png` + - What it does: lists PNG filenames in icon root. + - Access: public. + - Stores data: reads filesystem only. +- `GET /icons/png/{filename}` + - What it does: serves icon PNG. + - Access: public. + - Stores data: reads filesystem only. + +## 3) Property + User management + +- `POST /properties` + - Request: `PropertyCreateRequest`. + - Access: authenticated user. + - Stores data: inserts `property` (+ email/alias/transport collections); inserts `property_user` with `ADMIN`. +- `GET /properties` + - What it does: list properties (all for super admin, memberships for others). + - Access: authenticated user. + - Stores data: reads `property`, `property_user`. +- `GET /properties/{propertyId}/code` + - Access: property member. + - Stores data: reads `property.code`. +- `PUT /properties/{propertyId}` + - Request: `PropertyUpdateRequest`. + - Access: `ADMIN`. + - Stores data: updates `property` and its collection tables. +- `GET /properties/{propertyId}/billing-policy` + - Access: property member. + - Stores data: reads `property.billing_checkin_time`, `property.billing_checkout_time`. +- `PUT /properties/{propertyId}/billing-policy` + - Request: `PropertyBillingPolicyRequest`. + - Access: `ADMIN`. + - Stores data: updates `property` billing policy. +- `GET /properties/{propertyId}/users` + - Access: `ADMIN`/`MANAGER`. + - Stores data: reads `property_user`, `app_user`. +- `PUT /properties/{propertyId}/users/{userId}/roles` + - Request: `PropertyUserRoleRequest`. + - Access: super admin / `ADMIN` / `MANAGER` (role-limited by hierarchy). + - Stores data: upserts `property_user` + `property_user_role`. +- `PUT /properties/{propertyId}/users/{userId}/disabled` + - Request: `PropertyUserDisableRequest`. + - Access: hierarchy-based. + - Stores data: updates `property_user.is_disabled`. +- `DELETE /properties/{propertyId}/users/{userId}` + - Access: `ADMIN`. + - Stores data: deletes `property_user` membership. + +### Access codes + directory + +- `POST /properties/{propertyId}/access-codes` + - Request: `PropertyAccessCodeCreateRequest` (`roles`, no `ADMIN`). + - Access: `ADMIN`. + - Stores data: inserts `property_access_code` + `property_access_code_role` (1-minute expiry). +- `POST /properties/access-codes/join` + - Request: `PropertyAccessCodeJoinRequest` (`propertyCode` + `code`). + - Access: authenticated. + - Stores data: inserts `property_user`; marks `property_access_code.used_at/used_by`. +- `GET /users` + - Query: optional `phone`. + - Access: super admin only. + - Stores data: reads `app_user`. +- `GET /properties/{propertyId}/users/search` + - Query: optional `phone`. + - Access: `ADMIN`. + - Stores data: reads `property_user`, `app_user`. + +### Cancellation policy + transport modes + +- `GET /properties/{propertyId}/cancellation-policy` + - Access: public. + - Stores data: reads `property_cancellation_policy` (or default response). +- `PUT /properties/{propertyId}/cancellation-policy` + - Request: `CancellationPolicyUpsertRequest`. + - Access: `ADMIN`. + - Stores data: upserts `property_cancellation_policy`. +- `GET /properties/{propertyId}/transport-modes` + - Access: property member. + - Stores data: reads `property.allowedTransportModes`. + +## 4) Room amenities + image tags + +- `GET /amenities` + - Access: authenticated. + - Stores data: reads `room_amenity`. +- `POST /amenities` + - Request: `AmenityUpsertRequest`. + - Access: super admin only. + - Stores data: inserts `room_amenity`. +- `PUT /amenities/{amenityId}` + - Request: `AmenityUpsertRequest`. + - Access: super admin only. + - Stores data: updates `room_amenity`. +- `DELETE /amenities/{amenityId}` + - Access: super admin only. + - Stores data: deletes `room_amenity`; removes links from `room_type_amenity_link`. + +- `GET /image-tags` + - Access: public. + - Stores data: reads `room_image_tag`. +- `POST /image-tags` + - Request: `RoomImageTagUpsertRequest`. + - Access: super admin only. + - Stores data: inserts `room_image_tag`. +- `PUT /image-tags/{tagId}` + - Request: `RoomImageTagUpsertRequest`. + - Access: super admin only. + - Stores data: updates `room_image_tag`. +- `DELETE /image-tags/{tagId}` + - Access: super admin only. + - Stores data: deletes links from `room_image_tag_link`, deletes `room_image_tag`. + +## 5) Room types + +- `GET /properties/{propertyId}/room-types` + - Access: public (or member when auth present). + - Stores data: reads `room_type` + alias/amenity links. +- `POST /properties/{propertyId}/room-types` + - Request: `RoomTypeUpsertRequest`. + - Access: `ADMIN`/`MANAGER`. + - Stores data: inserts `room_type`, `room_type_alias`, `room_type_amenity_link`. +- `PUT /properties/{propertyId}/room-types/{roomTypeId}` + - Request: `RoomTypeUpsertRequest`. + - Access: `ADMIN`/`MANAGER`. + - Stores data: updates `room_type` and links. +- `DELETE /properties/{propertyId}/room-types/{roomTypeId}` + - Access: `ADMIN`/`MANAGER`. + - Stores data: soft delete by setting `room_type.is_active=false`. +- `GET /properties/{propertyId}/room-types/{roomTypeCode}/rate?date=...&ratePlanCode=...` + - Access: public/member. + - Stores data: reads `room_type`, `rate_plan`, `rate_calendar`. + +## 6) Rooms + availability + board + +- `GET /properties/{propertyId}/rooms` + - Access: property member (agents see only free sellable rooms). + - Stores data: reads `room`, active stays in `room_stay`, temp cards in `issued_card`. +- `POST /properties/{propertyId}/rooms` + - Request: `RoomUpsertRequest`. + - Access: `ADMIN`. + - Stores data: inserts `room`. +- `PUT /properties/{propertyId}/rooms/{roomId}` + - Request: `RoomUpsertRequest`. + - Access: `ADMIN`. + - Stores data: updates `room`. +- `DELETE /properties/{propertyId}/rooms/{roomId}` + - Access: `ADMIN`/`MANAGER`. + - Stores data: deletes room image files + `room_image` rows, then deletes `room` (blocked if any `room_stay` exists). + +- `GET /properties/{propertyId}/rooms/board` + - Access: member (agent filtered to FREE). + - Stores data: reads `room`, active occupancy from `room_stay`. +- `GET /properties/{propertyId}/rooms/board/stream` + - Access: member. + - Stores data: no writes; SSE from derived room board events. +- `GET /properties/{propertyId}/rooms/availability` + - Access: member. + - Stores data: reads `room`, `room_stay`. +- `GET /properties/{propertyId}/rooms/available` + - Access: public. + - Stores data: reads `room`, `room_stay`, `issued_card`. +- `GET /properties/{propertyId}/rooms/by-type/{roomTypeCode}?availableOnly=true|false` + - Access: public. + - Stores data: reads `room_type`, `room`, `room_stay`, `issued_card`. +- `GET /properties/{propertyId}/rooms/availability-range?from=YYYY-MM-DD&to=YYYY-MM-DD` + - Access: member. + - Stores data: reads `room`, `room_stay`, `property.timezone`. +- `GET /properties/{propertyId}/rooms/available-range-with-rate?from=...&to=...&ratePlanCode=...` + - Access: member. + - Stores data: reads `room`, `room_stay`, `rate_plan`, `rate_calendar`, `property`. + +## 7) Room images + +- `GET /properties/{propertyId}/rooms/{roomId}/images` + - Access: public. + - Stores data: reads `room_image`; deletes missing-file rows; reads filesystem. +- `POST /properties/{propertyId}/rooms/{roomId}/images` + - Multipart: file + optional `tagIds`. + - Access: `ADMIN`/`MANAGER`. + - Stores data: writes files under `/docs/rooms`; inserts `room_image` + `room_image_tag_link`. +- `DELETE /properties/{propertyId}/rooms/{roomId}/images/{imageId}` + - Access: `ADMIN`/`MANAGER`. + - Stores data: deletes files + `room_image`; reorders sort fields. +- `PUT /properties/{propertyId}/rooms/{roomId}/images/{imageId}/tags` + - Request: `RoomImageTagUpdateRequest`. + - Access: `ADMIN`/`MANAGER`. + - Stores data: updates `room_image_tag_link`. +- `PUT /properties/{propertyId}/rooms/{roomId}/images/reorder-room` + - Request: `RoomImageReorderRequest`. + - Access: `ADMIN`/`MANAGER`. + - Stores data: updates `room_image.sort_order`. +- `PUT /properties/{propertyId}/rooms/{roomId}/images/reorder-room-type` + - Request: `RoomImageReorderRequest`. + - Access: `ADMIN`/`MANAGER`. + - Stores data: updates `room_image.room_type_sort_order`. +- `GET /properties/{propertyId}/rooms/{roomId}/images/{imageId}/file?size=full|thumb` + - Access: public/member. + - Stores data: reads filesystem + `room_image` metadata. +- `GET /properties/{propertyId}/room-types/{roomTypeCode}/images` + - Access: public. + - Stores data: reads `room_image`; prunes missing-file rows. + +## 8) Room stays + cards + +- `GET /properties/{propertyId}/room-stays/active` + - Access: member except AGENT-only users. + - Stores data: reads `room_stay`, `booking`, `guest`, `room`. +- `POST /properties/{propertyId}/room-stays/{roomStayId}/void` + - Request: `RoomStayVoidRequest`. + - Access: `ADMIN`/`MANAGER`/`STAFF` (staff blocked after first booking payment). + - Stores data: updates `room_stay` (`is_voided`, `to_at`), inserts `room_stay_audit_log`, may update `booking` to `CHECKED_OUT`. + +### Issued cards (room stay) + +- `POST /properties/{propertyId}/room-stays/{roomStayId}/cards/prepare` + - Request: `CardPrepareRequest`. + - Access: `ADMIN`/`MANAGER`. + - Stores data: increments `property_card_counter`; returns encoded payload only. +- `POST /properties/{propertyId}/room-stays/{roomStayId}/cards` + - Request: `IssueCardRequest`. + - Access: `ADMIN`/`MANAGER`. + - Stores data: inserts `issued_card`. +- `GET /properties/{propertyId}/room-stays/{roomStayId}/cards` + - Access: `STAFF`/`ADMIN`/`MANAGER`/`SUPERVISOR`. + - Stores data: reads `issued_card`. +- `POST /properties/{propertyId}/room-stays/cards/{cardIndex}/revoke` + - Access: ADMIN for regular cards; ADMIN/MANAGER for temp cards. + - Stores data: updates `issued_card.revoked_at` and `expires_at`. +- `GET /properties/{propertyId}/room-stays/cards/{cardIndex}` + - Access: `ADMIN`/`MANAGER`. + - Stores data: reads `issued_card`. + +### Temporary room cards + +- `POST /properties/{propertyId}/rooms/{roomId}/cards/prepare-temp` + - Access: `ADMIN`/`MANAGER`. + - Stores data: increments `property_card_counter`; returns payload only. +- `POST /properties/{propertyId}/rooms/{roomId}/cards/temp` + - Request: `IssueTempCardRequest`. + - Access: `ADMIN`/`MANAGER`. + - Stores data: inserts temp `issued_card` (7-minute expiry). + +## 9) Booking APIs + +- `POST /properties/{propertyId}/bookings` + - Request: `BookingCreateRequest`. + - Access: `ADMIN`/`MANAGER`/`STAFF`. + - Stores data: inserts `booking`; creates placeholder `guest` if needed. +- `GET /properties/{propertyId}/bookings?status=...` + - Access: `ADMIN`/`MANAGER`/`STAFF`/`HOUSEKEEPING`/`FINANCE`. + - Stores data: reads `booking`, `room_stay`, `charge`, `payment`. +- `GET /properties/{propertyId}/bookings/{bookingId}` + - Access: same as list. + - Stores data: reads booking snapshot from `booking`, `guest`, `room_stay`, `guest_vehicle`, `charge`, `payment`. +- `GET /properties/{propertyId}/bookings/{bookingId}/stream` + - Access: same as list. + - Stores data: no writes; SSE booking event stream. +- `POST /properties/{propertyId}/bookings/{bookingId}/link-guest` + - Request: `BookingLinkGuestRequest`. + - Access: property member. + - Stores data: updates `booking.primary_guest_id`; may delete placeholder `guest` when safe. +- `POST /properties/{propertyId}/bookings/{bookingId}/check-in/bulk` + - Request: `BookingBulkCheckInRequest`. + - Access: `ADMIN`/`MANAGER`/`STAFF`. + - Stores data: inserts `room_stay`; updates `booking` to `CHECKED_IN`; may fulfill `booking_room_request`. +- `POST /properties/{propertyId}/bookings/{bookingId}/expected-dates` + - Request: `BookingExpectedDatesUpdateRequest`. + - Access: `ADMIN`/`MANAGER`/`STAFF`. + - Stores data: updates `booking.expected_checkin_at` / `booking.expected_checkout_at`. +- `POST /properties/{propertyId}/bookings/{bookingId}/billing-policy` + - Request: `BookingBillingPolicyUpdateRequest`. + - Access: `ADMIN`/`MANAGER`/`STAFF`. + - Stores data: updates `booking.billing_*`; inserts `booking_billing_policy_audit_log` when changed. +- `POST /properties/{propertyId}/bookings/{bookingId}/check-out` + - Request: `BookingCheckOutRequest`. + - Access: `ADMIN`/`MANAGER`/`STAFF`. + - Stores data: closes active `room_stay` rows (`to_at`), inserts `room_stay_audit_log`, updates `booking` to `CHECKED_OUT`. +- `POST /properties/{propertyId}/bookings/{bookingId}/room-stays/{roomStayId}/check-out` + - Request: `BookingCheckOutRequest`. + - Access: `ADMIN`/`MANAGER`/`STAFF`. + - Stores data: closes specific `room_stay`, inserts audit log, may auto-close `booking`. +- `POST /properties/{propertyId}/bookings/{bookingId}/cancel` + - Request: `BookingCancelRequest`. + - Access: `ADMIN`/`MANAGER`/`STAFF`. + - Stores data: updates `booking.status=CANCELLED`; may insert penalty row in `charge`. +- `POST /properties/{propertyId}/bookings/{bookingId}/no-show` + - Request: `BookingNoShowRequest`. + - Access: `ADMIN`/`MANAGER`/`STAFF`. + - Stores data: updates `booking.status=NO_SHOW`; may insert penalty row in `charge`. + +### Room requests + booking balance + +- `POST /properties/{propertyId}/bookings/{bookingId}/room-requests` + - Request: `BookingRoomRequestCreateRequest`. + - Access: `ADMIN`/`MANAGER`/`STAFF`. + - Stores data: inserts `booking_room_request`. +- `GET /properties/{propertyId}/bookings/{bookingId}/room-requests` + - Access: `ADMIN`/`MANAGER`/`STAFF`/`HOUSEKEEPING`/`FINANCE`. + - Stores data: reads `booking_room_request`. +- `DELETE /properties/{propertyId}/bookings/{bookingId}/room-requests/{requestId}` + - Access: `ADMIN`/`MANAGER`/`STAFF`. + - Stores data: sets `booking_room_request.status=CANCELLED`. +- `GET /properties/{propertyId}/bookings/{bookingId}/balance` + - Access: property member. + - Stores data: reads `room_stay`, `charge`, `payment` and returns computed ledger balance. + +## 10) Charges + Payments APIs + +- `POST /properties/{propertyId}/bookings/{bookingId}/charges` + - Request: `ChargeCreateRequest`. + - Access: `ADMIN`/`MANAGER`/`FINANCE`. + - Stores data: inserts `charge`. +- `GET /properties/{propertyId}/bookings/{bookingId}/charges` + - Access: property member. + - Stores data: reads `charge`. + +- `POST /properties/{propertyId}/bookings/{bookingId}/payments` + - Request: `PaymentCreateRequest`. + - Access: `ADMIN`/`MANAGER`/`STAFF`. + - Stores data: inserts `payment`. +- `GET /properties/{propertyId}/bookings/{bookingId}/payments` + - Access: property member. + - Stores data: reads `payment`. +- `DELETE /properties/{propertyId}/bookings/{bookingId}/payments/{paymentId}` + - Access: `ADMIN` (super admin allowed through bypass). + - Stores data: deletes `payment` only when method is CASH and booking is OPEN/CHECKED_IN. + +## 11) Guest APIs + +- `PUT /properties/{propertyId}/guests/{guestId}` + - Request: `GuestUpdateRequest`. + - Access: property member. + - Stores data: updates `guest`. +- `GET /properties/{propertyId}/guests/search?phone=...|vehicleNumber=...` + - Access: property member. + - Stores data: reads `guest`, `guest_vehicle`, `guest_rating`. +- `GET /properties/{propertyId}/guests/{guestId}` + - Access: property member. + - Stores data: reads `guest`, `guest_vehicle`, `guest_rating`. +- `GET /properties/{propertyId}/guests/visit-count?phone=...` + - Access: property member. + - Stores data: reads `guest`, booking count from `booking`. +- `POST /properties/{propertyId}/guests/{guestId}/vehicles` + - Request: `GuestVehicleRequest`. + - Access: property member. + - Stores data: inserts/updates `guest_vehicle`. +- `POST /properties/{propertyId}/guests/{guestId}/signature` + - Multipart SVG file. + - Access: `ADMIN`/`MANAGER`. + - Stores data: writes file under documents root; updates `guest.signature_path`. +- `GET /properties/{propertyId}/guests/{guestId}/signature/file` + - Access: property member. + - Stores data: reads signature file path from `guest`. + +### Guest documents + +- `POST /properties/{propertyId}/guests/{guestId}/documents?bookingId=...` + - Multipart file. + - Access: `ADMIN`/`MANAGER`. + - Stores data: writes document file under docs root; inserts `guest_document`; queues AI extraction and updates `guest_document.extracted_data`. +- `GET /properties/{propertyId}/guests/{guestId}/documents` + - Access: `ADMIN`/`MANAGER`. + - Stores data: reads `guest_document`. +- `GET /properties/{propertyId}/guests/{guestId}/documents/stream` + - Access: `ADMIN`/`MANAGER`. + - Stores data: no writes; SSE extraction updates. +- `GET /properties/{propertyId}/guests/{guestId}/documents/{documentId}/file` + - Access: public with token OR `ADMIN`/`MANAGER`. + - Stores data: reads `guest_document` + filesystem. +- `DELETE /properties/{propertyId}/guests/{guestId}/documents/{documentId}` + - Access: `ADMIN`/`MANAGER`. + - Stores data: deletes file + `guest_document` row (only OPEN/CHECKED_IN booking). + +### Guest ratings + +- `POST /properties/{propertyId}/guests/{guestId}/ratings` + - Request: `GuestRatingCreateRequest`. + - Access: property member. + - Stores data: inserts `guest_rating`. +- `GET /properties/{propertyId}/guests/{guestId}/ratings` + - Access: property member. + - Stores data: reads `guest_rating`. + +## 12) Inbound Email APIs + +- `GET /properties/{propertyId}/inbound-emails/{emailId}/file` + - Access: `ADMIN`/`MANAGER`. + - Stores data: reads `inbound_email.raw_pdf_path` and PDF file. +- `POST /properties/{propertyId}/inbound-emails/manual` + - Multipart PDF. + - Access: `ADMIN`/`MANAGER`. + - Stores data: writes PDF under `/docs/emails`; inserts `inbound_email`; triggers ingestion workflow. + +## 13) Rate plan APIs + +- `POST /properties/{propertyId}/rate-plans` + - Request: `RatePlanCreateRequest`. + - Access: `ADMIN`/`MANAGER`. + - Stores data: inserts `rate_plan`. +- `GET /properties/{propertyId}/rate-plans?roomTypeCode=...` + - Access: property member. + - Stores data: reads `rate_plan`. +- `PUT /properties/{propertyId}/rate-plans/{ratePlanId}` + - Request: `RatePlanUpdateRequest`. + - Access: `ADMIN`/`MANAGER`. + - Stores data: updates `rate_plan`. +- `DELETE /properties/{propertyId}/rate-plans/{ratePlanId}` + - Access: `ADMIN`/`MANAGER`. + - Stores data: deletes `rate_calendar` rows for plan, then deletes `rate_plan`. +- `POST /properties/{propertyId}/rate-plans/{ratePlanId}/calendar` + - Request: `RateCalendarRangeUpsertRequest`. + - Access: `ADMIN`/`MANAGER`. + - Stores data: upserts `rate_calendar` rows in date range. +- `GET /properties/{propertyId}/rate-plans/{ratePlanId}/calendar?from=...&to=...` + - Access: property member. + - Stores data: reads `rate_calendar` + `rate_plan`; returns average. +- `DELETE /properties/{propertyId}/rate-plans/{ratePlanId}/calendar/{rateDate}` + - Access: `ADMIN`/`MANAGER`. + - Stores data: deletes one `rate_calendar` override row. + +## 14) Razorpay APIs + +### Settings + return + webhook + +- `GET /properties/{propertyId}/razorpay-settings` + - Access: `ADMIN`/`MANAGER`. + - Stores data: reads `razorpay_settings`. +- `PUT /properties/{propertyId}/razorpay-settings` + - Request: `RazorpaySettingsUpsertRequest`. + - Access: `ADMIN`. + - Stores data: inserts/updates `razorpay_settings`. +- `POST /properties/{propertyId}/razorpay/webhook` + - Access: public (signature validated). + - Stores data: inserts `razorpay_webhook_log`, inserts `razorpay_payment_attempt`, updates `razorpay_qr_request` / `razorpay_payment_link_request`, inserts `payment` for capture/refund events. +- `POST /properties/{propertyId}/razorpay/return/success` + - Access: public. + - Stores data: none. +- `POST /properties/{propertyId}/razorpay/return/failure` + - Access: public. + - Stores data: none. + +### QR + links + request close + refund + +- `POST /properties/{propertyId}/bookings/{bookingId}/payments/razorpay/qr` + - Request: `RazorpayQrGenerateRequest`. + - Access: `ADMIN`/`MANAGER`/`STAFF`. + - Stores data: inserts `razorpay_qr_request` (or returns active existing). +- `GET /properties/{propertyId}/bookings/{bookingId}/payments/razorpay/qr/active` + - Access: `ADMIN`/`MANAGER`/`STAFF`. + - Stores data: reads `razorpay_qr_request`. +- `POST /properties/{propertyId}/bookings/{bookingId}/payments/razorpay/qr/close` + - Access: `ADMIN`/`MANAGER`/`STAFF`. + - Stores data: updates latest active `razorpay_qr_request.status`. +- `POST /properties/{propertyId}/bookings/{bookingId}/payments/razorpay/qr/{qrId}/close` + - Access: `ADMIN`/`MANAGER`/`STAFF`. + - Stores data: updates specific `razorpay_qr_request.status`. +- `GET /properties/{propertyId}/bookings/{bookingId}/payments/razorpay/qr/{qrId}/events` + - Access: `ADMIN`/`MANAGER`/`STAFF`. + - Stores data: reads `razorpay_webhook_log`. +- `GET /properties/{propertyId}/bookings/{bookingId}/payments/razorpay/qr/{qrId}/events/stream` + - Access: `ADMIN`/`MANAGER`/`STAFF`. + - Stores data: SSE stream only. +- `GET /properties/{propertyId}/bookings/{bookingId}/payments/razorpay/qr` + - Access: `ADMIN`/`MANAGER`/`STAFF`. + - Stores data: reads `razorpay_qr_request`. + +- `POST /properties/{propertyId}/bookings/{bookingId}/payments/razorpay/payment-link` + - Request: `RazorpayPaymentLinkCreateRequest`. + - Access: `ADMIN`/`MANAGER`/`STAFF`. + - Stores data: inserts `razorpay_payment_link_request` (or returns existing open one). +- `GET /properties/{propertyId}/bookings/{bookingId}/payments/razorpay/requests` + - Access: `ADMIN`/`MANAGER`/`STAFF`. + - Stores data: reads open `razorpay_qr_request` + `razorpay_payment_link_request`. +- `POST /properties/{propertyId}/bookings/{bookingId}/payments/razorpay/close` + - Request: `RazorpayPaymentRequestCloseRequest`. + - Access: `ADMIN`/`MANAGER`/`STAFF`. + - Stores data: updates closed status in corresponding Razorpay request row. +- `POST /properties/{propertyId}/bookings/{bookingId}/payments/razorpay/refund` + - Request: `RazorpayRefundRequest`. + - Access: `ADMIN`/`MANAGER`/`FINANCE`. + - Stores data: no local insert here; refund call is external and webhook later records ledger entry. + +--- + +## Detailed example (as requested format) + +### Expected checkout planning API + +**Expected checkout API is:** + +`POST /properties/{propertyId}/bookings/{bookingId}/expected-dates` + +**What it does:** + +- Updates planned dates on booking (`expectedCheckinAt`, `expectedCheckoutAt`), not actual checkout. +- For `OPEN` bookings: can update both expected check-in and expected check-out. +- For `CHECKED_IN`: can update only `expectedCheckOutAt` (check-in change is blocked). +- For `CHECKED_OUT` / `CANCELLED` / `NO_SHOW`: returns conflict (`Booking closed`). +- Validates range: if both are present, checkout must be after checkin. +- Returns `204 No Content`. +- Emits booking SSE update event. + +**Request body (`BookingExpectedDatesUpdateRequest`):** + +```json +{ + "expectedCheckInAt": "2026-02-05T12:00:00+05:30", + "expectedCheckOutAt": "2026-02-06T11:00:00+05:30" +} +``` + +**Allowed roles:** + +- `ADMIN`, `MANAGER`, `STAFF` (property-scoped) +- `superAdmin` can also access (bypasses property membership/role checks) +- Requires authenticated Firebase principal (`MyPrincipal`) + +**Data store:** + +- Updates row in `booking` table (`expected_checkin_at`, `expected_checkout_at`, `updated_at`) + +**Error codes:** + +- `400 Bad Request` + - Invalid timestamp format (`"Invalid timestamp"`) for `expectedCheckInAt` / `expectedCheckOutAt` + - Invalid range (`"Invalid date range"`) when checkout is not after check-in +- `401 Unauthorized` + - Missing auth principal / user not found +- `403 Forbidden` + - Authenticated but not a property member or missing role (`ADMIN`, `MANAGER`, `STAFF`) +- `404 Not Found` + - Booking not found, or booking does not belong to that property +- `409 Conflict` + - `"Cannot change expected check-in after check-in"` + - `"Booking closed"` for `CHECKED_OUT`, `CANCELLED`, `NO_SHOW` +