Pricing currency: CHF base, 30 CHF/seat/year, localized display from the first pageview #23

Closed
opened 2026-09-05 23:23:58 +00:00 by Cordy · 4 comments
Owner

Decisions from Nikola (2026-09-06): switch pricing from USD to CHF and set the yearly seat price to 30 CHF (currently 32.40). Open question researched below: global CHF vs. local currency display.

What Paddle supports (researched)

  • Automatic currency conversion (account-level): base price in one currency, checkout charges the visitor's local currency automatically by region; can be enabled for all or selected currencies. CHF is a supported Paddle currency.
  • Country-specific price overrides: manual price+currency per country, taking precedence over auto-conversion — the tool for "clean" prices (29 € instead of 27.83 €) in key markets later. Both mechanisms combine; overrides win where set.
  • On-site localized display: Paddle.js PricePreview returns the localized price for the visitor before checkout — exactly the "they see Euro from the first pageview instead of discovering it at checkout" behaviour Nikola prefers.

Recommendation

Base price 30 CHF/seat/year in Paddle; enable automatic currency conversion; render the pricing section and account seat-change previews via PricePreview so a German visitor sees EUR from the start (fallback to CHF when the preview call fails). Add clean-price EUR override later once the market bracket is settled (see the pricing-research issue).

Touchpoints

  • Paddle catalog: price entity currency + amount (sandbox first, per the v0.7 dress-rehearsal plan).
  • index.html pricing section (currently hardcoded USD figures) → PricePreview-driven with static CHF fallback.
  • account.html seat-change priced previews + invoices already come from Paddle/license-server — verify currency flows through end to end.
  • cairn-license-server: webhook amounts are informational; verify nothing assumes USD.
  • Docs briefs mentioning prices.

Sources: Paddle: Localize prices, Paddle: enable local currencies, Boathouse: clean international prices with Paddle.

Decisions from Nikola (2026-09-06): switch pricing from USD to **CHF** and set the yearly seat price to **30 CHF** (currently 32.40). Open question researched below: global CHF vs. local currency display. ## What Paddle supports (researched) - **Automatic currency conversion** (account-level): base price in one currency, checkout charges the visitor's local currency automatically by region; can be enabled for all or selected currencies. CHF is a supported Paddle currency. - **Country-specific price overrides**: manual price+currency per country, taking precedence over auto-conversion — the tool for "clean" prices (29 € instead of 27.83 €) in key markets later. Both mechanisms combine; overrides win where set. - **On-site localized display**: Paddle.js `PricePreview` returns the localized price for the visitor before checkout — exactly the "they see Euro from the first pageview instead of discovering it at checkout" behaviour Nikola prefers. ## Recommendation Base price 30 CHF/seat/year in Paddle; enable automatic currency conversion; render the pricing section and account seat-change previews via `PricePreview` so a German visitor sees EUR from the start (fallback to CHF when the preview call fails). Add clean-price EUR override later once the market bracket is settled (see the pricing-research issue). ## Touchpoints - Paddle catalog: price entity currency + amount (sandbox first, per the v0.7 dress-rehearsal plan). - `index.html` pricing section (currently hardcoded USD figures) → PricePreview-driven with static CHF fallback. - `account.html` seat-change priced previews + invoices already come from Paddle/license-server — verify currency flows through end to end. - `cairn-license-server`: webhook amounts are informational; verify nothing assumes USD. - Docs briefs mentioning prices. Sources: [Paddle: Localize prices](https://developer.paddle.com/build/products/offer-localized-pricing/), [Paddle: enable local currencies](https://www.paddle.com/help/grow/localisation/how-do-i-enable-local-currencies), [Boathouse: clean international prices with Paddle](https://www.boathouse.co/knowledge/setting-clean-prices-for-international-markets-using-paddle).
Author
Owner

Unblocked by the #24 decision (2026-09-06). Confirmed figures for the build:

  • Monthly: 3 CHF/seat/month (unchanged amount, currency switches from USD)
  • Yearly: 30 CHF/seat/year (was 32.40) — ten months' worth, ~17% annual incentive
  • First 50 seats of every licence free, no seat minimum (unchanged)

Build order when picked up: Paddle sandbox catalog first (CHF prices, auto currency conversion on), then the site surfaces (index pricing card, account Buy-seats/seat-change previews) driven by Paddle.PricePreview with static CHF fallback, then verify the webhook → licence issuance path end-to-end on sandbox — which doubles as a slice of the v0.7 licensing dress rehearsal. Live Paddle swap stays a launch-time act (Cairn #26).

Unblocked by the #24 decision (2026-09-06). Confirmed figures for the build: - **Monthly: 3 CHF/seat/month** (unchanged amount, currency switches from USD) - **Yearly: 30 CHF/seat/year** (was 32.40) — ten months' worth, ~17% annual incentive - First 50 seats of every licence free, no seat minimum (unchanged) Build order when picked up: Paddle **sandbox** catalog first (CHF prices, auto currency conversion on), then the site surfaces (index pricing card, account Buy-seats/seat-change previews) driven by `Paddle.PricePreview` with static CHF fallback, then verify the webhook → licence issuance path end-to-end on sandbox — which doubles as a slice of the v0.7 licensing dress rehearsal. Live Paddle swap stays a launch-time act (Cairn #26).
Author
Owner

Progress + verification (2026-09-06):

  • Paddle sandbox catalog switched to CHF in place (same price ids): monthly 3.00, yearly 30.00. Checkout/webhook wiring untouched.
  • Automatic currency conversion was already enabled — Nikola confirmed in the dashboard: Business account → Currencies shows 33 active conversions (CHF, EUR, GBP, USD, …). Nothing to toggle.
  • Site shipped (PR #30, deployed): CHF everywhere (fmt() de-CH, cycle buttons, demo fixtures, landing card + bullets incl. "ten months' worth"), plus the token-gated PricePreview block on the landing — strict no-op until PADDLE_CLIENT_TOKEN is set at launch.
  • Conversion verified empirically via pricing-preview against the live sandbox: CH → 3.00/30.00 CHF · DE → €3.19/€31.89 · GB → £2.74/£27.40 · US → $3.70/$37.04. Conversion applies at checkout and in the preview APIs (location-driven); totals are tax-inclusive (CH contains 8.1% VAT → net 27.75; DE 19%; US untaxed).

Notes: FX puts the US yearly at ~$37 (above the old $32.40) — a clean-price unit_price_override for the US is a one-liner if ever wanted. Remaining before close: set the client token at launch (#26/#27 window) and one full sandbox purchase (checkout → webhook → licence issued in CHF) as the dress-rehearsal slice.

Progress + verification (2026-09-06): - **Paddle sandbox catalog switched to CHF in place** (same price ids): monthly 3.00, yearly 30.00. Checkout/webhook wiring untouched. - **Automatic currency conversion was already enabled** — Nikola confirmed in the dashboard: Business account → Currencies shows 33 active conversions (CHF, EUR, GBP, USD, …). Nothing to toggle. - **Site shipped** (PR #30, deployed): CHF everywhere (`fmt()` de-CH, cycle buttons, demo fixtures, landing card + bullets incl. "ten months' worth"), plus the token-gated PricePreview block on the landing — strict no-op until `PADDLE_CLIENT_TOKEN` is set at launch. - **Conversion verified empirically** via `pricing-preview` against the live sandbox: CH → 3.00/30.00 CHF · DE → €3.19/€31.89 · GB → £2.74/£27.40 · US → $3.70/$37.04. Conversion applies at checkout and in the preview APIs (location-driven); totals are tax-inclusive (CH contains 8.1% VAT → net 27.75; DE 19%; US untaxed). Notes: FX puts the US yearly at ~$37 (above the old $32.40) — a clean-price `unit_price_override` for the US is a one-liner if ever wanted. Remaining before close: set the client token at launch (#26/#27 window) and one full sandbox purchase (checkout → webhook → licence issued in CHF) as the dress-rehearsal slice.
Author
Owner

Closing — everything substantive is shipped and verified:

  • Sandbox catalog: CHF 3/mo, 30/yr, same price ids.
  • Auto currency conversion: already active (33 currencies, dashboard-confirmed).
  • Site: CHF everywhere; landing card localizes via PricePreview.
  • Sandbox client token minted via the Paddle MCP and embedded (public value): ctkn_01m1t5056qzb7eff6ppf8nad6a. Site-driven sandbox checkout is enabled again.
  • Live-verified on www: Paddle.js loads, PricePreview returns localized totals, card renders them (CHF from a Swiss visitor; DE/GB/US previews verified API-side at €3.19/€31.89, £2.74/£27.40, $3.70/$37.04).

Per Nikola: the sandbox re-purchase is NOT required for the currency change (webhook issuance is quantity-based and currency-agnostic; two prior purchases prove the path) — the full purchase→renewal→seat-change pass stays in the v0.7 dress rehearsal on its own merits. The single remaining act, sandbox→live token+environment swap, moves to the #27 launch checklist. FX note stands: US yearly lands ~$37; a clean-price unit_price_override is a one-liner if ever wanted.

Closing — everything substantive is shipped and verified: - Sandbox catalog: CHF 3/mo, 30/yr, same price ids. - Auto currency conversion: already active (33 currencies, dashboard-confirmed). - Site: CHF everywhere; landing card localizes via PricePreview. - Sandbox client token minted via the Paddle MCP and embedded (public value): `ctkn_01m1t5056qzb7eff6ppf8nad6a`. Site-driven sandbox checkout is enabled again. - **Live-verified on www**: Paddle.js loads, PricePreview returns localized totals, card renders them (CHF from a Swiss visitor; DE/GB/US previews verified API-side at €3.19/€31.89, £2.74/£27.40, $3.70/$37.04). Per Nikola: the sandbox re-purchase is NOT required for the currency change (webhook issuance is quantity-based and currency-agnostic; two prior purchases prove the path) — the full purchase→renewal→seat-change pass stays in the v0.7 dress rehearsal on its own merits. The single remaining act, sandbox→live token+environment swap, moves to the #27 launch checklist. FX note stands: US yearly lands ~$37; a clean-price `unit_price_override` is a one-liner if ever wanted.
Cordy closed this issue 2026-09-06 01:24:44 +00:00
Author
Owner

Post-close follow-ups from Nikola's sandbox checkout walkthrough (2026-09-06), for the record:

  • Three missed $ literals in the buy-view total block → CHF (PR #32, deployed). The earlier "clean" grep was vacuous (\$ became an end-of-line anchor); regex-verified now.
  • Paddle price display names renamed for checkout clarity: "Paid seats beyond the 50 free — monthly/yearly, per seat" — the quantity in the overlay now explains itself.
  • Checkout behaviour confirmed: initial currency is IP-based (Swiss IP → CHF), recalculates to the customer's currency once the country is set (DE → €9.57 incl. 19% VAT); consent checkbox = opt-in to marketing email (Paddle-owned copy, can be disabled in dashboard checkout settings).
  • Still Nikola's, tracked on #27: rename the Paddle business name "Swiss Cairn Sandbox" → "Swisscairn" (dashboard-only, sandbox and live separately).
Post-close follow-ups from Nikola's sandbox checkout walkthrough (2026-09-06), for the record: - Three missed `$` literals in the buy-view total block → CHF (PR #32, deployed). The earlier "clean" grep was vacuous (`\$` became an end-of-line anchor); regex-verified now. - Paddle price display names renamed for checkout clarity: "Paid seats beyond the 50 free — monthly/yearly, per seat" — the quantity in the overlay now explains itself. - Checkout behaviour confirmed: initial currency is IP-based (Swiss IP → CHF), recalculates to the customer's currency once the country is set (DE → €9.57 incl. 19% VAT); consent checkbox = opt-in to marketing email (Paddle-owned copy, can be disabled in dashboard checkout settings). - Still Nikola's, tracked on #27: rename the Paddle business name "Swiss Cairn Sandbox" → "Swisscairn" (dashboard-only, sandbox and live separately).
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: Cordy/cairn-site#23
No description provided.