Tarot reader — booking, payments, video shop
Paid bookings and a video shop where each recorded reading reaches only the person who bought it.
- Astro
- React
- Cloudflare Workers
- Cloudflare D1
- Cloudflare R2
- Stripe
- Workers AI
- Claude API
- ≈27API endpoints for booking, admin, payments, video shop and chunked upload
- 20 minphone videos uploaded in resumable chunks
- 3months of live updates on the client's real requests
Cards, payments and videos only the buyer can open
A night-sky site with a card game, a booking engine with a shared pool of seats, payment before every slot or video, and phone uploads that become a private library.
Tap a block to see what it does.
- Input
- People
- Logic
- AI
- Infrastructure
- Output
Input
Public site
Astro SSR on Workers. React islands for the booking wizard, a draw-a-card game and a FAQ chat; Lenis for smooth scroll.
- Astro
- React
- Lenis
Hands to Booking engine

People
Phone upload
From the phone admin, a 10 to 20 minute video goes to R2 as a multipart upload that resumes from the last part. She can add a short voice note about it.
- Cloudflare R2
Hands to Transcript and listing, Web version
Logic
Booking engine
Reused shared engine: slots, time zone, emails, .ics. Recorded readings share one 10-seat pool with delivery within 72 hours.
- TypeScript
- Cloudflare D1
Gets from Public siteHands to Stripe Checkout, Reminder cron
AI
Transcript and listing
Whisper on Workers AI transcribes her voice note, then Claude drafts the product description and tags. Without a key it does nothing and she writes them by hand.
- Workers AI
- Whisper
- Claude API
Gets from Phone uploadHands to Buyer's library
Infrastructure
Web version
GitHub Actions job every two hours by day: HEVC HDR at about 12 Mbit/s becomes H.264 720p at about 2 Mbit/s, uploaded back in 40 MB parts.
- GitHub Actions
- ffmpeg
Gets from Phone uploadHands to Buyer's library
Infrastructure
Reminder cron
Separate worker every 15 minutes: email reminder one hour before each consultation, and seats held by unfinished payments released.
- Cron Worker
- Resend
Gets from Booking engine
Logic
Stripe Checkout
Payment before the slot or the video. The webhook signature is checked with Web Crypto, no SDK; the webhook confirms the booking or unlocks the purchase.
- Stripe
- Web Crypto
Gets from Booking engineHands to Buyer's library
Output
Buyer's library
Login by a single-use emailed link valid 30 minutes, then a signed 90-day session. Video bytes stream from R2 only for a paid purchase, never by public link.
- HMAC
- PWA
- HTTP range
Gets from Stripe Checkout, Transcript and listing, Web version
The problem
A tarot reader gave readings over video calls and handled her agenda and payments by hand. She also wanted to sell recorded readings. The site had to take bookings with limited seats, charge in advance and deliver each video only to its buyer.
What I built
- I forked the booking engine from an earlier trainer project into its own repo and extended it with shared seat pools and agenda openings and closures.
- I added Stripe Checkout with a webhook whose signature I verify with Web Crypto, without the SDK; with no keys the site stays in demo mode.
- I built the video shop: catalogue, free preview, single purchase, and a personal library opened by an emailed link that installs as a mini-app.
- I made uploads from her phone go to R2 in parts that resume after an interruption; a short voice note about the video becomes a draft description and tags (Whisper, then Claude) that she can edit.
- I gave the home a draw-a-card game, a FAQ chat and ambient sound, as React islands.
The hard part
Long phone videos. Her 10 to 20 minute recordings arrive as HDR HEVC at about 12 Mbit/s: they stalled every few minutes and did not play in many browsers. Uploads go to R2 in resumable parts, and a scheduled job makes a 720p H.264 copy at about 2 Mbit/s that the player serves instead, keeping the original intact.
Result
Live on the client's domain and updated for three months on her real requests, such as agenda openings and closures and a shared pool of 10 seats. Production bugs, like a blank booking page caused by an expired date limit, were fixed in the same loop.
My role
Solo client project. I handled the client and turned her requests into tasks, configured Stripe, R2, domain and secrets, and tested payments and uploads on a phone; Claude Code wrote 71 of the 72 commits.