206 lines
5.8 KiB
Markdown
206 lines
5.8 KiB
Markdown
# 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
|