Source layout
apps/api/src/
├── index.ts # entry (serve Hono)
├── app.ts # mount routes, global error handler
├── routes/
│ ├── packages.route.ts
│ ├── bookings.route.ts
│ ├── payments.route.ts
│ ├── users.route.ts
│ ├── stalls.route.ts
│ ├── water-orders.route.ts
│ ├── reviews.route.ts
│ └── webhooks.route.ts # POST /v1/webhooks/stripe
├── controllers/
├── services/
│ ├── bookings.service.ts
│ ├── payments.service.ts # Stripe only
│ ├── email.service.ts # Resend
│ └── storage.service.ts # R2
├── middleware/
│ ├── auth.middleware.ts
│ ├── cors.middleware.ts
│ ├── rate-limit.middleware.ts
│ └── logger.middleware.ts
└── lib/
├── db.ts # import from @chalo-kumbh/db
├── stripe.ts
├── resend.ts
└── r2.ts
API versioning
Public REST surface under /v1/…. Breaking changes require
/v2 or additive fields only with backward compatibility.
Stripe webhooks
- Verify
Stripe-Signaturewith the webhook secret. - Handle events idempotently (e.g. store Stripe event id or payment external id before mutating booking).
-
On success: set payment row + transition booking to
confirmedwhen business rules satisfied. - Trigger Resend confirmation + queue WhatsApp send.
Server-enforced business rules
- Pilgrim details required before confirming paid booking.
- Pind Pradanam: ancestor records required when package type demands it.
- Shahi Snan: service date must be one of fixed calendar rows.
- Group puja: 10–30 pilgrims; personal puja: 1–5.
Environment variables (illustrative)
DATABASE_URL=
STRIPE_SECRET_KEY=
STRIPE_WEBHOOK_SECRET=
RESEND_API_KEY=
R2_ACCOUNT_ID= R2_ACCESS_KEY_ID= R2_SECRET_ACCESS_KEY= R2_BUCKET=
BETTER_AUTH_SECRET= (and provider-specific vars)
PUBLIC_APP_URL=