v0.1.0:49 — multi-currency pricing functional end-to-end
Bump version with release notes covering Phases 2-6 of the multi- currency design (admin UI write path, buy page fiat rendering, rate fetcher, invoice rate recording, currency-aware discount codes). Operators can list products in USD/EUR and accept BTC; the daemon converts at invoice creation and pins the rate. Test count: 37. Straight drop-in upgrade from :48.
This commit is contained in:
@@ -9,8 +9,30 @@
|
|||||||
import { VersionInfo } from '@start9labs/start-sdk'
|
import { VersionInfo } from '@start9labs/start-sdk'
|
||||||
|
|
||||||
export const v0_1_0 = VersionInfo.of({
|
export const v0_1_0 = VersionInfo.of({
|
||||||
version: '0.1.0:48',
|
version: '0.1.0:49',
|
||||||
releaseNotes: [
|
releaseNotes: [
|
||||||
|
`Alpha-iteration revision 49 of v0.1.0 — Multi-currency pricing is now functional end-to-end. Operators can list products in USD or EUR and accept BTC; the daemon converts at invoice creation and pins the rate on the invoice row for audit. SAT-priced products are unchanged.`,
|
||||||
|
``,
|
||||||
|
`**Phase 2: admin UI write path.** The Create Product form on the admin Products page now has a currency picker (sats / USD / EUR). Picking USD/EUR swaps the input units in place — decimal entry like \`$49.00\` is converted to cents (4900) on the way out. The Products table now renders prices via a currency-aware formatter: SAT products show "50,000 sats", USD products show "$49.00" with an optional "≈ 75k sats" hint once the first invoice has pinned a sat amount. Backend accepts both the legacy \`price_sats: N\` form (for older clients) and the new \`price_currency + price_value\` form; mismatched mixes return 400.`,
|
||||||
|
``,
|
||||||
|
`**Phase 3: buy page renders fiat.** The server-rendered initial price on \`/buy/<slug>\` now respects \`product.price_currency\`. USD-priced products show "49.00 USD" instead of "75,000 sats". The cents-to-dollars conversion happens server-side. Tier-picker JS still formats per-tier prices in sats — that's a v0.3 polish; most operators ship single-policy products initially so the static initial render is the high-leverage piece.`,
|
||||||
|
``,
|
||||||
|
`**Phase 4: BTC/fiat rate fetcher.** New module at \`src/rates.rs\` with a 60s in-memory TTL cache and a 3-source fallback chain: Kraken → Coinbase → CoinGecko. Kraken first because BTCPay also defaults to Kraken — when both quote from Kraken they agree on the BTC amount we hand the buyer. The settings table key \`manual_rate_pin_<CCY>\` (e.g. \`manual_rate_pin_USD = 65000\`) overrides the chain entirely; useful for maintenance windows where a fetcher glitch is producing weird quotes.`,
|
||||||
|
``,
|
||||||
|
`Two new admin endpoints expose the cache:`,
|
||||||
|
` - \`GET /v1/admin/rates\` returns what's currently cached (currency, units_per_btc, source, fetched_at_secs_ago)`,
|
||||||
|
` - \`POST /v1/admin/rates/refresh\` forces a fresh fetch for a given currency. Audit-logged.`,
|
||||||
|
``,
|
||||||
|
`**Phase 5: invoice records the rate.** When a buyer purchases a USD-priced product, the daemon calls the rate fetcher to convert to sats, then stores the listed currency, listed value, exchange_rate_centibps (rate × 10,000), and source on the local invoice row. The audit trail answers: "What did the buyer SEE? What rate did the daemon QUOTE? What source did the rate come from? What sat amount were they actually charged?" — all four available for any invoice forever.`,
|
||||||
|
``,
|
||||||
|
`**Phase 6: currency-aware discount codes.** \`POST /v1/admin/discount-codes\` accepts an optional \`discount_currency\` field ('SAT' default, 'USD', 'EUR'). For \`fixed_sats\` and \`set_price\` codes the currency determines the unit; for \`percent\` codes it's recorded for audit but the basis points apply to whatever currency the product is in. Existing sat-only codes keep working unchanged.`,
|
||||||
|
``,
|
||||||
|
`**Test count: 37** (was 33; +4 covering the manual-pin path, admin rates endpoint, USD product creation forms, and the end-to-end USD purchase that pins listed_currency + rate to the invoice row).`,
|
||||||
|
``,
|
||||||
|
`**Upgrade path.** v0.1.0:48 → v0.1.0:49 is a straight drop-in. No new SQL migrations beyond 0010 (which already shipped in :48). Existing operators continue to see sat prices everywhere; the currency picker shows up the next time they create or edit a product.`,
|
||||||
|
``,
|
||||||
|
`**What's NOT yet shipped.** Discount-code admin UI doesn't yet have a currency picker (just JSON API). Tier picker on the buy page still formats per-tier prices in sats. Recurring subscriptions in non-SAT currencies (Phase 7 of MULTI_CURRENCY_DESIGN.md) needs the recurring-billing scaffolding from v0.3 / Zaprite first. None of these are blockers for an operator who wants to start pricing single products in USD today.`,
|
||||||
|
``,
|
||||||
`Alpha-iteration revision 48 of v0.1.0 — Multi-currency schema foundation. Daemon now stores native currency + value on products, policies, invoices, and discount codes. Behaviour is unchanged for existing operators (everything backfills as 'SAT'). Lands the storage shape so the v0.3 admin UI work for fiat pricing has somewhere to write. The admin SPA, buy page, and discount-code form continue to render sat-only views in this release.`,
|
`Alpha-iteration revision 48 of v0.1.0 — Multi-currency schema foundation. Daemon now stores native currency + value on products, policies, invoices, and discount codes. Behaviour is unchanged for existing operators (everything backfills as 'SAT'). Lands the storage shape so the v0.3 admin UI work for fiat pricing has somewhere to write. The admin SPA, buy page, and discount-code form continue to render sat-only views in this release.`,
|
||||||
``,
|
``,
|
||||||
`Migration 0010 adds:`,
|
`Migration 0010 adds:`,
|
||||||
|
|||||||
Reference in New Issue
Block a user