Skip to content

feat(billing): polar-sync creates the Migrate-with-Studio bundle and yearly products - #411

Merged
ABB65 merged 1 commit into
mainfrom
feat/polar-yearly-bundle-products
Oct 4, 2026
Merged

ABB65 merged 1 commit into
mainfrom
feat/polar-yearly-bundle-products

Conversation

@ABB65

@ABB65 ABB65 commented Oct 4, 2026

Copy link
Copy Markdown
Member

What

pnpm polar:sync gets a third step that creates the four products Migrate with Studio sells through: per plan a bundle product (year 1 at 20% off) and a yearly list-price product. The Polar plugin already reads them via NUXT_POLAR_{STARTER,PRO}_{BUNDLE,YEARLY}_PRODUCT_ID.

  • Prices come from shared/utils/migrate-bundle.ts (Starter $72 / $90, Pro $392 / $490), not a second copy.
  • Products carry contentrain_variant; a product with no tag is monthly, so the existing Starter/Pro keep matching and are untouched.
  • Create-if-missing, drift warns (exit 1) like the monthly products.
  • Not in this PR: metered prices and meter credits on the yearly/bundle products. A yearly period grants credits per billing cycle, so the units are a pricing decision.

Evidence (sandbox)

  • Dry run first: monthly products in sync, 4 products "would be created". Then --apply on sandbox only (NUXT_POLAR_SERVER=sandbox).
  • A checkout opened the way createBundleCheckout does for 49000 (Migrate fee) + 7200 (Starter year 1): amount 56200, total 56200, bundle product, yearly interval. Paying it is not done here.
  • Production: no product changes; env names and products ship with v0.4.8 on founder approval.

Reviewer: t6

…yearly products

A third sync step creates, per plan, the bundle product (year 1 at 20% off)
and the yearly list-price product the Polar plugin already reads through
the NUXT_POLAR_{STARTER,PRO}_{BUNDLE,YEARLY}_PRODUCT_ID env names.
Prices come from shared/utils/migrate-bundle.ts. Products are matched by a
new contentrain_variant tag (no tag means monthly, so the existing
products keep matching). Metered prices and meter credits are not
attached yet: a yearly period grants credits per billing cycle.
@ABB65

ABB65 commented Oct 4, 2026

Copy link
Copy Markdown
Member Author

ONAY on 2742319 (local checks).

  • Monthly products untouched: findExistingProduct now also requires (contentrain_variant ?? 'monthly') === 'monthly', so the two existing untagged products keep matching and the new tagged ones never match the monthly path; the new step 3 is create-if-missing only (an existing variant product only gets a price-drift warning, never a write).
  • Env names printed (STARTER_BUNDLE, STARTER_YEARLY, PRO_BUNDLE, PRO_YEARLY → NUXT_POLAR_*_PRODUCT_ID) match nuxt.config.ts, .env.example and the keys the Polar plugin reads (starter#bundle, pro#bundle). Amounts come from migrate-bundle.ts (starter 7200/9000, pro 39200/49000 cents), matching the doc.
  • Sandbox/prod: the target is NUXT_POLAR_SERVER (default sandbox, pnpm polar:sync pins .env); a production --apply is refused without --server=production; this PR adds no path around that. Nothing was run against Polar in review, so prod gets nothing without the founder's explicit go.
  • pnpm lint 0 errors (7 existing warnings, none in polar-sync), pnpm typecheck clean, unit project 2037 passed; integration/nuxt projects not run: the diff is one script plus a doc that no integration or nuxt test imports.

@ABB65
ABB65 merged commit 05fdfb8 into main Oct 4, 2026
2 checks passed
@ABB65
ABB65 deleted the feat/polar-yearly-bundle-products branch October 4, 2026 15:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant