Voltar ao blog
productPublicado April 28, 2026

11 Bugs in One Checkout Flow: What We Learned Pricing an AI Product

11 Bugs in One Checkout Flow: What We Learned Pricing an AI Product

We built three pricing tiers for an AI brand identity product. Then we tested every purchase path and found 11 bugs across 7 flows. The worst was a single if-statement.

The three tiers

Freeform generates AI-powered brand identities from birth data. One product, three price points:

  • Origin Spark ($47) — the entry mechanism. Brand synthesis, archetype, color palette, typography, voice guidelines, a logo, one product mockup. Two images total. A PDF of the core synthesis. No Founder Identity File.
  • Origin ($97) — the real product. Everything in Spark plus 6 AI-generated images, a 12-page brand book PDF, and the Founder Identity File.
  • Origin Alive ($97 + $19/mo) — Origin plus a living brand. Per-element regeneration, Monthly Content Brief, billing portal, ecosystem priority access.

Each tier serves a different buying intent. Spark is for someone validating a business idea for less than a dinner. Origin is for someone ready to build. Alive is for someone who wants the brand to evolve.

The Founder Identity File as upgrade gate

The Founder Identity File is a synthesis of the founder's natal chart, Human Design profile, and personality archetypes. Claude writes it during brand synthesis.

Spark does not include it. On purpose. The Founder Identity File is the thing people share. When a Spark customer sees their basic synthesis and thinks "I want the full version," the Identity File pulls them forward. The product itself creates the upgrade desire.

Stripe: mixing one-time and subscription

Alive combines a one-time $97 payment with a recurring $19/month subscription. We tried three approaches before finding the right one:

One mode: 'subscription' session with two line items. The one-time price has recurring: null. Stripe treats it as a setup fee within the subscription context.

For differential upgrades (Spark → Origin), we use inline price_data because the amount varies:

const upgrade = ORIGIN_PRICE - existingOrder.amount  // 9700 - 4700 = 5000

const session = await stripe.checkout.sessions.create({

mode: 'payment',

line_items: [{

price_data: { currency: 'usd', unit_amount: upgrade, product: ORIGIN_PRODUCT_ID },

quantity: 1,

}],

})

The 11 bugs

We wrote a systematic test script that walked through every purchase path. Seven flows. Eleven bugs.

Bug #1: The if-statement that killed every upgrade

The fulfillment function had standard deduplication at the top:

if (brand.status === 'paid') {

return { alreadyFulfilled: true }

}

On an upgrade, the brand is already status: 'paid' because the customer bought Spark. The upgrade payment succeeds, the webhook arrives, and we early-exit before updating the tier. The brand stays on Spark. The customer paid $50 for nothing.

const isUpgrade = brand.status === 'paid' && brand.tier !== tier

if (brand.status === 'paid' && !isUpgrade) {

return { alreadyFulfilled: true }

}

One line to the predicate.

Bug #2: The PDF that ignored tiers

await getGenerationQueue().add('GENERATE_BRAND_PDF', { brandId })

No tier check. Every paid purchase queued the same PDF. Spark customers got the same 12-page brand book as Origin customers.

Fix: if (tier !== 'spark') { await getGenerationQueue().add('GENERATE_BRAND_PDF', ...) }

Bug #3: The download button that checked the wrong thing

{isPaid && brand.brandBookUrl && (

<a href={/api/brand/${id}/pdf}>Download PDF</a>

)}

isPaid is true for every tier. The correct gate: hasFullAccess = isPaid && (tier === 'origin' || tier === 'alive').

The other 8

GET vs POST mismatch on the upgrade endpoint. Currency in dollars instead of cents. Missing idempotency on webhook retries. Hardcoded English in translated pages. Missing success URL parameters. Webhook signature validation disabled. Price ID environment mismatch between test and live.

Not one surfaced during happy-path testing.

The economics

| Component | Cost per brand |

|---|---|

| Claude synthesis | ~$0.26 |

| 5 AI images | ~$0.20 |

| Color panel | ~$0.04 |

| PDF | ~$0.02 |

| Total COGS | ~$0.52 |

At $97 for Origin: 99.5% gross margin on generation. Priced on value, not cost.

Alive at $19/month

At 50% revenue allocation ($9.50/month), a subscriber could regenerate 237 individual elements. Most won't regenerate more than a few times. We set caps: 10 element regens + 3 full regens per month. Maximum cost per subscriber: $1.54/month, or 8% of revenue.

The Monthly Content Brief is the retention mechanism. On the 1st of every month, Claude Haiku reads the subscriber's Founder Identity File and generates 4 Instagram post ideas. Cost: ~$0.02/month. The email reminds subscribers their brand exists and that they have credits. It previews Signal Lite, our upcoming content automation product.

The real insight

Three bugs on the brand detail page. Three one-to-three-line fixes. One root cause: we treated the tier as a label attached to a purchase, not as a product with different content, different downloads, different rules.

To make tiers real, enforce differentiation at three layers:

  • Fulfillment — what gets queued per tier. Spark queues 2 image jobs. Origin queues 6 plus PDF.
  • UI — what renders per tier. Spark clips the story at 3 lines with an upgrade overlay. Product 2, Lifestyle, Store show blurred with upgrade buttons. Every locked panel is a conversion surface.
  • API — what each tier can download. The API checks hasFullAccess, not isPaid.
  • Missing any layer turns the tier into a label.

    41 E2E tests

    We wrote tier-products.spec.ts with 41 tests: content visibility per tier, download buttons, blur overlays, upgrade CTAs, checkout-API enforcement for every legal and illegal path, download-API gates.

    await expect(page.locator('a[href*="/pdf"]')).toHaveCount(0)
    await expect(page.locator('a[href*="download-identity"]')).toHaveCount(0)

    Counting absence is the only way to catch "we gave the $47 customer a $97 asset."

    Payment flows are not one flow. They are N flows, where N = tiers × transitions × states. For 3 tiers: 7 distinct paths at minimum. Test all of them as complete user journeys.