hotellobby (LobbyDuo): UX Notes on Preview-Before-Pay AI Booth Videos
How I shipped a two-photo → Hotel Lobby booth duo on Cloudflare Workers — optimizing framing preview before Stripe, with Seedance via TryAllAPI. Trend video products usually fail at the same moment: the user pays, then
How I shipped a two-photo → Hotel Lobby booth duo on Cloudflare Workers — optimizing framing preview before Stripe, with Seedance via TryAllAPI.
Trend video products usually fail at the same moment: the user pays, then discovers their faces are cropped wrong. For tryhotellobby.app (product name hotellobby / LobbyDuo) I optimized the UX before the charge, not another prompt textarea.
Upload two adult face photos → ~15s orange COLORS-style booth duo (9:16 / 16:9 / 21:9, 480p or 720p) with original/AI booth audio. Independent tool — not affiliated with any artist group. This post is the builder + UX walkthrough. Prices and model ids change; treat live site / console as source of truth.
UX thesis: preview framing, then unlock Create
The homepage copy is blunt: free scene preview before you pay — upload two photos, check framing in the orange booth layout, then unlock Create with a one-time pack.
That sequence matters more than fancy motion poetry:
- Photo 1 → left of the booth duo.
- Photo 2 → right.
- Local/layout preview of how faces sit in the booth.
- Only then: Stripe → credits → Seedance render.
Users who can see “my face is too small / off-center” before paying convert better and refund less. Free-form “make a hotel video” prompts do the opposite: ambiguity first, charge second, regret third.
Live packs verified on-site while writing: Single $8.88 (≈ 500 credits / one 720p), 3-pack $16.99, plus larger packs (and occasional cheaper 480p messaging). Guest Stripe — no account for a single video. Always re-check the create CTA before you screenshot a price for a friend.
Why left/right roles beat a single “upload faces” dump
Booth memes are spatial. If the UI does not say who stands where, users invent their own convention and then complain the sample “looks different.” Fixed seats:
- Remove “who is who?” support tickets.
- Stabilize the locked template prompt sent upstream.
- Make demos honest — the sample matches the seats.
Aspect ratio is a first-class control (9:16 default for Reels/TikTok; 16:9 / 21:9 for other cuts). Original/AI booth audio means the download is shareable without hunting a track.
Stack overview
| Layer | Choice | Role |
|---|---|---|
| Front / SSR | TanStack Start (Vite) | Landing, upload panel, generation status |
| Runtime | Cloudflare Workers | Edge deploy, secrets, API routes |
| DB | Cloudflare D1 | Credits, orders, task rows (table prefixes if DBs are shared) |
| Payments | Stripe Checkout | One-time packs + optional larger packs; guest checkout |
| Video upstream | TryAllAPI → Seedance | Async create + poll |
| Analytics | GA | Funnel sanity check |
two faces → booth layout preview
→ check credits (else Stripe)
→ Worker creates Seedance task via TryAllAPI
→ poll until MP4 URL → store / show
→ on failure: refund credits
Demo clips are self-hosted. Do not hotlink someone else’s sample MP4 for marketing.
Why a video API aggregator
Operational checklist after register / top-up:
- Confirm available USD balance in the console (not only the checkout success page).
- Create a token in pay-as-you-go style billing.
- Enable the video / Seedance-related channel group on that token. Wrong group → “no available channel” even when the model id looks right.
- Prefer separate keys per product (
hotellobby-prodvs sandbox). - Put the key only in Workers Secrets (for example
TRYALLAPI_API_KEY). Never in the frontend bundle or a blog post.
Conceptually the call shape looks like this (field names follow the aggregator’s v3 contents API; always re-check current docs):
POST /api/v3/contents/generations/tasks
Authorization: Bearer <TRYALLAPI_API_KEY>
Content-Type: application/json
Accept-Encoding: identity
GET /api/v3/contents/generations/tasks/{id}
I send Accept-Encoding: identity from the Worker because some gateways behave badly with transparent decompression. Model ids used in production look like Seedance 2.x family strings — treat console ids as source of truth, not this paragraph.
Seedance parameters that matter for booth duos
A few details that bit me across this family of sites:
-
Pass
durationas an explicit integer. Some models default to “smart length.” Omitting duration can silently lengthen the clip and the bill. -
resolutionandratio. Cost tracks resolution tier and duration more than portrait vs landscape. Match whatever the create UI offers (often 480p/720p and 9:16 for Shorts). -
generate_audio. When the product ships “finished” downloads, leave audio on; creators who want a trending sound can mute later. -
Status machine. Expect something like
queued→running→succeeded/failed. Success URLs are often short-lived signed links — download/rehost on your side before handing the file to the user. -
Decent failure. On
supply_unavailableor hard failures: refund credits, show a short human message, log the detailed upstream error server-side, optionally fall back to another Seedance tier.
Rough mental model for upstream cost (illustrative only — use your own usage fields and invoices):
tokens ≈ duration_sec × width × height × fps / 1024
cost ≈ tokens × price_per_million_tokens
Price the site after you measure a few real 720p runs with retry margin, Stripe fees, and failed-job refunds — not from a spreadsheet fantasy of 0% failure.
Booth-specific: map two reference images to left/right seats consistently; same-person clones in both seats look wrong — the UI should say so.
Credits, Stripe, and guest checkout
Demo UIs are easy. The line between toy and product is:
- User hits Create → check credit balance.
- If short → Stripe Checkout Session (guest allowed).
- Webhook (plus a return-URL sync check) grants credits.
- Pre-debit / lock credits, create upstream task, persist
task_idin D1. - Poll until done; succeed → show MP4; fail → refund credits.
Two books of account stay separate: USD balance at TryAllAPI (your cost) vs site credits (what you sold). Mixing them in one mental ledger is how you underprice or invent “mysterious” losses.
When several products share one Stripe account, isolate webhooks by endpoint or by metadata (site / product). Callbacks pointed at the wrong apex domain are a classic multi-site footgun.
Secrets stay in Workers: TRYALLAPI_API_KEY, STRIPE_SECRET_KEY, webhook secret, auth secrets. The browser only ever sees publishable Stripe keys if needed.
Deploy notes (Workers + D1)
Typical loop on this template family:
pnpm install
# wire D1 database_id, production env, custom domain in wrangler
pnpm cf:deploy
Before calling a deploy “done”:
- D1 binding is correct; shared DBs use table prefixes, not a new database per micro-idea (free-tier DB counts are limited).
-
AUTH_TRUSTED_ORIGINSand Stripe return / webhook URLs match the production HTTPS origin. - GA id is set for a coarse funnel (land → upload → pay → wait → download).
- One real end-to-end path on production or a staging Worker: upload → pay → render → download.
Production origin for Checkout / auth trust: https://tryhotellobby.app (alternate brand hostnames may exist — keep allowlists in sync with the URL users actually pay on).
Product rules that are not optional
Face-based generation is not “just another SaaS CRUD app”:
- Require an explicit 18+ / rights-to-use confirmation on upload.
- Block minors and refuse celebrity / stranger misuse in copy and policy.
- Expect upstream safety refusals; refund and explain.
- Keep privacy policy honest about retention; do not train on user media by default.
- Market as an independent fan-style tool for a trend format — not as an official tie-in unless you actually have that deal.
- Use your own sample renders in marketing.
Those constraints belong in the product, not in a footnote you add after the first dispute email.
What the UX taught me
- Preview-before-pay reduces “bad crop” chargebacks more than shaving another dollar off the Single pack.
- Locked booth layout beats free-form hotel prompts.
- Guest Single pack (~$8.88) matches “I need one clip tonight.”
- Credits with automatic refund on failed generation is table stakes.
- Measure land → upload → pay → wait → download before rewriting marketing adjectives.
Consumer path: tryhotellobby.app — watch the sample, upload two authorized adult faces, preview framing, pay once. Builder path: TryAllAPI, top up, prove create+poll, then polish the preview UX. Sell confidence in framing first; sell GPU second.
Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.

