{"slug": "bad-times-ai-lower-entry-price-one-photo-and-credit-refunds-that-build-trust", "title": "Bad Times AI: Lower Entry Price, One Photo, and Credit Refunds That Build Trust", "summary": "A developer built Bad Times AI, a Cloudflare Workers-hosted tool that turns a single person or pet photo into a roughly 15-second 9:16 quarrel or lip-sync dance video, using TanStack Start for the front end, Cloudflare D1 for credits and task records, Stripe Checkout for one-time credit packs starting at $6.90, and TryAllAPI's Seedance integration for asynchronous video generation. The build emphasizes honest paid pricing and automatic credit refunds on failed generations, with the developer arguing that restoring credits on supply, safety, or blurry-input failures avoids chargebacks and makes the low entry price feel safe.", "body_md": "*How I shipped a one-photo → quarrel / lip-sync dance product on Cloudflare Workers — honest “not free” pricing, Stripe credits, Seedance via TryAllAPI.*\n\n[badtimesai.cc](https://badtimesai.cc/?utm_source=devto&utm_medium=blog&utm_campaign=builder) takes **1 clear person or pet photo** → ~15s **9:16** quarrel / lip-sync dance MP4 with original AI ambient audio. Browser + Stripe. Independent fan tool — not affiliated with song/artists/TikTok creators named in the meme. Person templates 18+ with likeness rights.\n\nAngle: **Stripe credits + accessible entry pricing** (from **$6.90**), plus explicit “failed generations refund credits” next to the sell. Prices and model ids change; treat live site / console as source of truth.\n\nThe hero says packs from **$6.90** (one-time, no subscription) and repeats **not free**. That filters tire-kickers who only want infinite free GPU and sets expectations for a paid render.\n\nOther tiers visible in page copy while writing include **$9.90** and **$16.90**-style packs — verify live before quoting. Guest checkout path keeps friction low for “I just want one quarrel clip.”\n\nUnlike strict two-face booths, Bad Times leans **one reference** with alternate scenes (person / pet / character messaging) and a “describe any idea” adjacent path on the page. The credit system still has to:\n\nWhy refunds are marketing: face video fails for boring reasons — supply, safety, blurry input. If credits vanish silently, you get chargebacks. If credits restore automatically, the $6.90 experiment feels safe.\n\n| Layer | Choice | Role | \n|---|---|---|\n| Front / SSR | TanStack Start (Vite) | Landing, upload panel, generation status | \n| Runtime | Cloudflare Workers | Edge deploy, secrets, API routes | \n| DB | Cloudflare D1 | Credits, orders, task rows (table prefixes if DBs are shared) | \n| Payments | Stripe Checkout | One-time packs + optional larger packs; guest checkout | \n| Video upstream | TryAllAPI → Seedance | Async create + poll | \n| Analytics | GA | Funnel sanity check | \n\n```\n1 photo (+ scene choice) → quarrel / lip-sync template\n→ check credits (else Stripe)\n→ Worker creates Seedance task via TryAllAPI\n→ poll until MP4 URL → store / show\n→ on failure: refund credits\n```\n\nOperational checklist after register / top-up:\n\n`badtimes-prod` vs sandbox).\n`TRYALLAPI_API_KEY`). Never in the frontend bundle or a blog post.\nConceptually the call shape looks like this (field names follow the aggregator’s v3 contents API; always re-check current docs):\n\n```\nPOST /api/v3/contents/generations/tasks\nAuthorization: Bearer <TRYALLAPI_API_KEY>\nContent-Type: application/json\nAccept-Encoding: identity\n\nGET /api/v3/contents/generations/tasks/{id}\n```\n\nI 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.\n\nA few details that bit me across this family of sites:\n\n`duration` as an explicit integer.`resolution` and `ratio`.` generate_audio`.` queued` → `running` → `succeeded` / `failed`. Success URLs are often `supply_unavailable` or hard failures: refund credits, show a short human message, log the detailed upstream error server-side, optionally fall back to another Seedance tier.\nRough mental model for upstream cost (illustrative only — use your own `usage` fields and invoices):\n\n```\ntokens ≈ duration_sec × width × height × fps / 1024\ncost   ≈ tokens × price_per_million_tokens\n```\n\nPrice the site **after** you measure a few real runs with retry margin, Stripe fees, and failed-job refunds — not from a spreadsheet fantasy of 0% failure.\n\nKeep TryAllAPI USD as COGS; site credits as the customer meter. Rehost signed MP4 URLs. Lower entry pricing only survives if you measure real runs including refunds.\n\nDemo UIs are easy. The line between toy and product is:\n\n`task_id` in D1.\nTwo 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.\n\nWhen 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.\n\nSecrets stay in Workers: `TRYALLAPI_API_KEY`, `STRIPE_SECRET_KEY`, webhook secret, auth secrets. The browser only ever sees publishable Stripe keys if needed.\n\nTypical loop on this template family:\n\n```\npnpm install\n# wire D1 database_id, production env, custom domain in wrangler\npnpm cf:deploy\n```\n\nBefore calling a deploy “done”:\n\n`AUTH_TRUSTED_ORIGINS` and Stripe return / webhook URLs match the production HTTPS origin.\nProduction origin: `https://badtimesai.cc`. Isolate webhook metadata if Bad Times shares a Stripe account with sister dance domains.\n\nFace-based generation is not “just another SaaS CRUD app”:\n\nThose constraints belong in the product, not in a footnote you add after the first dispute email.\n\nConsumer: upload one clear authorized photo at [badtimesai.cc](https://badtimesai.cc/?utm_source=devto&utm_medium=blog&utm_campaign=builder). Builders: [TryAllAPI](https://tryallapi.com/register?aff=5A6A&utm_source=devto&utm_medium=blog&utm_campaign=builder), measure COGS, then set an entry pack that still survives refunds. **Cheap only works when failure is refundable.**", "url": "https://wpnews.pro/news/bad-times-ai-lower-entry-price-one-photo-and-credit-refunds-that-build-trust", "canonical_source": "https://dev.to/badtimes_lab/bad-times-ai-lower-entry-price-one-photo-and-credit-refunds-that-build-trust-3bpa", "published_at": "2026-10-31 06:42:22+00:00", "updated_at": "2026-10-31 06:49:35.049325+00:00", "lang": "en", "topics": ["ai-tools", "generative-ai", "ai-products", "ai-infrastructure"], "entities": ["Bad Times AI", "Cloudflare Workers", "Cloudflare D1", "Stripe", "TryAllAPI", "Seedance", "TanStack Start"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/bad-times-ai-lower-entry-price-one-photo-and-credit-refunds-that-build-trust", "markdown": "https://wpnews.pro/news/bad-times-ai-lower-entry-price-one-photo-and-credit-refunds-that-build-trust.md", "text": "https://wpnews.pro/news/bad-times-ai-lower-entry-price-one-photo-and-credit-refunds-that-build-trust.txt", "jsonld": "https://wpnews.pro/news/bad-times-ai-lower-entry-price-one-photo-and-credit-refunds-that-build-trust.jsonld"}}