Turn your pet's photo into a magical Christmas portrait in seconds, powered by AI.
- About
- Screenshots
- Features
- Tech Stack
- Architecture
- Getting Started
- Environment Variables
- Project Structure
- Available Scripts
- Deployment
- Contributing
- Security
- License
Pets Santa is a full-stack AI portrait studio that lets pet owners upload a photo of their dog, cat, or any furry friend and instantly generate festive, high-quality Christmas portraits — Santa suits, elf costumes, cozy fireplaces, snowy forests, and more — using generative AI.
The project is built end-to-end on the Next.js App Router, with real user authentication, a credit-based billing system backed by Stripe, persistent image storage via Vercel Blob, a Supabase (PostgreSQL) database managed through Drizzle ORM, and AI image generation powered by the Kie.ai (Nano Banana Pro) API.
This repository is a great reference if you're building a production-style AI SaaS product: auth, credits, payments, async AI job processing, and a polished marketing landing page, all wired together.
Pick an outfit, a background, and a furry model (or upload your own pet), then fine-tune stickers, text, and decorations live on the canvas.
Every generation is saved to your personal gallery with an original-vs-AI-result comparison and one-click download.
Track remaining credits, purchase history, and a full ledger of every credit earned and spent.
| Feature | Description |
|---|---|
| 🎨 6 Christmas outfits | Santa Suit, Elf Costume, Reindeer Hoodie, Cozy Sweater, Winter Wonderland, Gift Box Surprise |
| 🖼️ 6 holiday scenes | Cozy fireplace, snowy forest, warm lights, starry night, candy-cane red, vintage gold |
| 🐶 Flexible pet input | Upload your own pet or try instant presets |
| ✨ Interactive sticker studio | Drag, scale, rotate, and place holiday accessories live on the canvas |
| ⚡ Async AI generation pipeline | Live polling and webhook callbacks for reliable task completion |
| 🔐 Authentication | Email/password and Google OAuth (Better Auth) |
| 💳 Credit-based billing | Stripe Checkout with signature-verified webhooks |
| 📦 My Creations gallery | Original vs. AI-result comparisons, saved per user |
| 🌗 Light / dark theme | Full theme support out of the box |
| 🔒 Privacy-first | Uploaded photos are stored securely and only used for generation |
| Layer | Technology |
|---|---|
| Framework | Next.js 16 (App Router, Turbopack) |
| Language | TypeScript |
| UI | Tailwind CSS v4, shadcn/ui, Radix UI, lucide-react |
| Authentication | Better Auth (email/password + Google OAuth, username plugin) |
| Database | Supabase (PostgreSQL) via Drizzle ORM |
| File Storage | Vercel Blob |
| Payments | Stripe (Checkout Sessions + Webhooks) |
| AI Image Generation | Kie.ai — Nano Banana Pro model |
| Forms & Validation | React Hook Form + Zod |
| Notifications | Sonner (toast) |
┌──────────────────────┐
│ Browser / UI │
│ Portrait Studio (Next)│
└──────────┬───────────┘
│ 1. Upload photo
▼
/api/upload ──────────────► Vercel Blob
│
│ 2. Create generation task
▼
/api/generate ──────────────► Kie.ai (Nano Banana Pro)
│ │
│ 3. Poll / callback │
▼ ▼
/api/generate/[taskId] ◄────── /api/callback
│
▼
Supabase (PostgreSQL via Drizzle)
- generation_task
- credit_transaction
▲
│ Checkout & webhook
│
/api/checkout ────────────► Stripe Checkout
/api/webhook ◄──────────── Stripe Webhook (signed)
- The user uploads a pet photo, which is stored in Vercel Blob.
/api/generatevalidates the user's credit balance, builds a prompt, and creates an async task on Kie.ai.- The task is resolved either via Kie.ai's callback or by the client polling
/api/generate/[taskId]. - Successful generations deduct credits and are persisted to Supabase through Drizzle ORM.
- Credit top-ups go through Stripe Checkout; the signed webhook (
/api/webhook) is the single source of truth that grants credits, guaranteeing idempotency even if Stripe retries delivery.
- Node.js ≥ 18.18
- pnpm (recommended) or bun / npm
- A Supabase project (PostgreSQL connection string)
- A Stripe account (test mode is fine for local dev)
- A Vercel Blob store / read-write token
- A Kie.ai API key
- (Optional) Google OAuth credentials for social login
git clone https://github.com/<your-username>/<your-repo>.git
cd <your-repo>pnpm install
# or: bun install / npm installThis project reads environment variables from .env.local (see drizzle.config.ts). Create the file at the project root:
touch .env.localSee the Environment Variables section below for the full list of required keys.
Push the Drizzle schema to your Supabase Postgres instance:
pnpm db:pushOr generate and run migrations instead:
pnpm db:generate
pnpm db:migrateYou can also open Drizzle Studio to inspect data visually:
pnpm db:studiopnpm devOpen http://localhost:3000 to see the app.
Use the Stripe CLI to forward events to your local server:
stripe listen --forward-to localhost:3000/api/webhookCopy the printed signing secret into STRIPE_WEBHOOK_SECRET.
All variables are read from .env.local at the project root. Never commit this file.
| Variable | Description | Required |
|---|---|---|
DATABASE_URL |
Supabase PostgreSQL connection string used by the app at runtime | ✅ |
DIRECT_URL |
Direct (non-pooled) Supabase Postgres connection string, used by Drizzle Kit for migrations | ✅ |
NEXT_PUBLIC_BASE_URL |
Public base URL of the deployment, e.g. http://localhost:3000 or your production domain |
✅ |
GOOGLE_CLIENT_ID |
Google OAuth client ID for social login | Optional |
GOOGLE_CLIENT_SECRET |
Google OAuth client secret | Optional |
KIE_AI_API_KEY |
API key for Kie.ai (Nano Banana Pro image generation) | ✅ |
STRIPE_SECRET_KEY |
Stripe secret key (server-side) | ✅ |
STRIPE_WEBHOOK_SECRET |
Signing secret used to verify Stripe webhook events | ✅ |
PRICE_ID |
Stripe Price ID for the credit pack used at checkout | ✅ |
BLOB_READ_WRITE_TOKEN |
Vercel Blob read/write token for storing uploaded pet photos and generated portraits | ✅ |
💡 In production,
NEXT_PUBLIC_BASE_URLis also used to build the Kie.ai callback URL (/api/callback) — make sure it points to a publicly reachable HTTPS domain.
src/
├── app/
│ ├── (routes)/
│ │ ├── (auth)/ # Sign in / sign up pages
│ │ ├── (home)/ # Landing page
│ │ ├── billing/ # Billing & credit history
│ │ ├── creations/ # "My Creations" gallery
│ │ └── pricing/ # Pricing page
│ └── api/
│ ├── auth/[...all]/ # Better Auth handler
│ ├── billing/ # Credit & payment data
│ ├── callback/ # Kie.ai generation callback
│ ├── checkout/ # Stripe Checkout session creation
│ ├── creations/ # Creations list endpoint
│ ├── generate/ # Create & poll generation tasks
│ ├── upload/ # Vercel Blob photo upload
│ └── webhook/ # Stripe webhook handler (signed)
├── components/
│ ├── pets-santa/ # Landing page, Portrait Studio, pricing, etc.
│ └── ui/ # shadcn/ui primitives
├── db/
│ ├── schema/ # Drizzle schema: auth, billing, generation
│ └── index.ts # Drizzle client
├── lib/
│ ├── auth/ # Better Auth client/server config
│ ├── billing/ # Stripe & credit logic
│ ├── generation/ # Generation task orchestration
│ └── kie/ # Kie.ai API client & prompt builder
└── proxy.ts # Route protection middleware
| Command | Description |
|---|---|
pnpm dev |
Start the dev server with Turbopack |
pnpm build |
Build for production |
pnpm start |
Start the production server |
pnpm lint |
Run ESLint |
pnpm db:generate |
Generate Drizzle migration files |
pnpm db:migrate |
Apply migrations to the database |
pnpm db:push |
Push the schema directly to the database (no migration files) |
pnpm db:studio |
Open Drizzle Studio |
This project is designed to deploy seamlessly on Vercel, since it natively uses Vercel Blob for storage.
- Push your repository to GitHub.
- Import the project on Vercel.
- Add all variables from the Environment Variables table to your Vercel project settings.
- Set
NEXT_PUBLIC_BASE_URLto your production domain. - Add a Stripe webhook endpoint pointing to
https://<your-domain>/api/webhookand copy its signing secret intoSTRIPE_WEBHOOK_SECRET. - Deploy 🚀
Contributions, issues, and feature requests are welcome! Please read CONTRIBUTING.md before opening a pull request, and make sure you follow our Code of Conduct.
If you discover a security vulnerability, please do not open a public issue. See SECURITY.md for our responsible disclosure process.
This project is licensed under the MIT License — see the LICENSE file for details.
Made with ❄️ and 🎄 for pet lovers everywhere.


