Initial commit: MSD backend API, docs, mockups, README
This commit is contained in:
@@ -0,0 +1,205 @@
|
||||
# Moore Sunny Daze — Reservation System with Stripe
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
```
|
||||
Guest Browser → Landing Page → Check Availability → Book → Stripe Checkout → Confirmation
|
||||
↑
|
||||
Admin Dashboard (Tim)
|
||||
```
|
||||
|
||||
## Components
|
||||
|
||||
### 1. Public Booking Flow
|
||||
```
|
||||
Landing Page (mooresunnydaze.com)
|
||||
→ "Check Availability" button
|
||||
→ Availability calendar (fetches from API)
|
||||
→ Select dates → guest count → price quote
|
||||
→ Guest details form (name, email, phone, # guests, pet?)
|
||||
→ Stripe Checkout (embedded or redirect)
|
||||
→ Confirmation page + email
|
||||
```
|
||||
|
||||
### 2. Backend API (FastAPI on Core/app1)
|
||||
| Endpoint | Method | Purpose |
|
||||
|----------|--------|---------|
|
||||
| `/api/availability` | GET | Get booked dates for a month |
|
||||
| `/api/quote` | POST | Calculate price for selected dates |
|
||||
| `/api/booking` | POST | Create booking (pending payment) |
|
||||
| `/api/booking/{id}/confirm` | POST | Confirm after Stripe payment |
|
||||
| `/api/booking/{id}` | GET | View booking details |
|
||||
| `/api/guest/{email}` | GET | Lookup returning guest |
|
||||
|
||||
### 3. Database Schema (SQLite)
|
||||
```sql
|
||||
-- Guests
|
||||
CREATE TABLE guests (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
email TEXT UNIQUE NOT NULL,
|
||||
phone TEXT,
|
||||
notes TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- Bookings
|
||||
CREATE TABLE bookings (
|
||||
id TEXT PRIMARY KEY,
|
||||
guest_id TEXT REFERENCES guests(id),
|
||||
check_in DATE NOT NULL,
|
||||
check_out DATE NOT NULL,
|
||||
guests_count INTEGER NOT NULL,
|
||||
nightly_rate REAL NOT NULL,
|
||||
cleaning_fee REAL DEFAULT 150,
|
||||
total REAL NOT NULL,
|
||||
status TEXT DEFAULT 'pending', -- pending, confirmed, cancelled, completed
|
||||
stripe_session_id TEXT,
|
||||
stripe_payment_intent TEXT,
|
||||
notes TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- Blocked Dates
|
||||
CREATE TABLE blocked_dates (
|
||||
date DATE PRIMARY KEY,
|
||||
reason TEXT -- 'booking_XYZ', 'maintenance', 'owner'
|
||||
);
|
||||
|
||||
-- Rate Rules
|
||||
CREATE TABLE rate_rules (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT,
|
||||
start_date DATE,
|
||||
end_date DATE,
|
||||
nightly_rate REAL,
|
||||
min_nights INTEGER DEFAULT 3
|
||||
);
|
||||
```
|
||||
|
||||
## Stripe Integration
|
||||
|
||||
### Setup
|
||||
1. Tim creates Stripe account (stripe.com)
|
||||
2. Connects business bank account
|
||||
3. We get API keys from Stripe Dashboard
|
||||
4. Store in `.env` on server
|
||||
|
||||
### Payment Flow
|
||||
```python
|
||||
# 1. Create Stripe Checkout Session
|
||||
session = stripe.checkout.Session.create(
|
||||
payment_method_types=['card'],
|
||||
line_items=[{
|
||||
'price_data': {
|
||||
'currency': 'usd',
|
||||
'product_data': {
|
||||
'name': f'Moore Sunny Daze — {nights} nights',
|
||||
'description': f'{check_in} to {check_out}, {guests} guests'
|
||||
},
|
||||
'unit_amount': int(total * 100), # cents
|
||||
},
|
||||
'quantity': 1
|
||||
}],
|
||||
mode='payment',
|
||||
success_url='https://mooresunnydaze.com/booking/confirmed?session_id={CHECKOUT_SESSION_ID}',
|
||||
cancel_url='https://mooresunnydaze.com/booking/cancelled',
|
||||
customer_email=guest_email,
|
||||
metadata={
|
||||
'booking_id': booking_id,
|
||||
'property': 'moore-sunny-daze'
|
||||
}
|
||||
)
|
||||
|
||||
# 2. Redirect to session.url
|
||||
# 3. Stripe webhook → mark booking confirmed
|
||||
```
|
||||
|
||||
### Webhook Handling
|
||||
```python
|
||||
@app.post("/stripe-webhook")
|
||||
async def stripe_webhook(request: Request):
|
||||
payload = await request.body()
|
||||
sig = request.headers.get('stripe-signature')
|
||||
event = stripe.Webhook.construct_event(payload, sig, WEBHOOK_SECRET)
|
||||
|
||||
if event['type'] == 'checkout.session.completed':
|
||||
session = event['data']['object']
|
||||
booking_id = session['metadata']['booking_id']
|
||||
mark_booking_confirmed(booking_id)
|
||||
send_confirmation_email(booking_id)
|
||||
add_blocked_dates(booking_id)
|
||||
|
||||
return {"status": "ok"}
|
||||
```
|
||||
|
||||
## Deployment
|
||||
|
||||
### Server
|
||||
- Deploy on Core (152.53.33.195) as systemd service
|
||||
- Port: 8910 (internal)
|
||||
- Caddy reverse proxy: `mooresunnydaze.com/api/* → localhost:8910`
|
||||
|
||||
### Filesystem
|
||||
```
|
||||
/opt/mooresunnydaze/
|
||||
├── server.py # FastAPI app
|
||||
├── db.sqlite # SQLite database
|
||||
├── .env # STRIPE_SECRET_KEY, etc
|
||||
├── requirements.txt # fastapi, uvicorn, stripe, aiosqlite
|
||||
└── templates/ # Email templates
|
||||
```
|
||||
|
||||
### systemd Service
|
||||
```ini
|
||||
# /etc/systemd/system/mooresunnydaze.service
|
||||
[Unit]
|
||||
Description=Moore Sunny Daze Booking API
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
WorkingDirectory=/opt/mooresunnydaze
|
||||
ExecStart=/opt/mooresunnydaze/venv/bin/uvicorn server:app --host 127.0.0.1 --port 8910
|
||||
Restart=always
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
## Admin Features
|
||||
1. **Calendar view** — booked/available/blocked dates
|
||||
2. **Booking management** — confirm, cancel, view details
|
||||
3. **Guest directory** — past guests, contact info, stay history
|
||||
4. **Rate management** — seasonal pricing, minimum stays
|
||||
5. **Block dates** — maintenance, owner use
|
||||
6. **Revenue reports** — monthly/annual totals
|
||||
7. **Email templates** — confirmation, pre-arrival, post-stay
|
||||
|
||||
## Pricing Integration
|
||||
Tim's property is currently listed at ~$295/night (peak season, from Airbnb). We'd implement:
|
||||
- **Base rate:** $295/night peak, $225/night off-peak
|
||||
- **Cleaning fee:** $150 flat
|
||||
- **Pet fee:** $50 (if applicable)
|
||||
- **Minimum stay:** 3 nights
|
||||
- **Holiday premium:** +20% for major holidays
|
||||
|
||||
## Comparison: Direct vs VRBO
|
||||
| | VRBO (current) | Direct (our system) |
|
||||
|---|---|---|
|
||||
| 3-night stay @ $295 | $885 + VRBO fee (~$130) | $885 (no fee) |
|
||||
| Guest pays | ~$1,015 | $885 |
|
||||
| Owner receives | ~$885 - VRBO 5% host fee | $885 - Stripe 2.9% |
|
||||
| Owner net | ~$840 | ~$859 |
|
||||
| **Savings to guest** | — | **$130 (13%)** |
|
||||
| **Savings to owner** | — | **~$19/stay + guest relationship** |
|
||||
|
||||
## Next Steps
|
||||
1. ✅ Design complete
|
||||
2. Set up Stripe account for Tim
|
||||
3. Build FastAPI backend
|
||||
4. Deploy on Core
|
||||
5. Wire up booking widget on landing page
|
||||
6. Test end-to-end booking flow
|
||||
7. Go live
|
||||
Reference in New Issue
Block a user