@eifi1/ui-kit 0.39.0 → 0.39.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/billing/billing-overview.d.ts +2 -2
- package/dist/billing/pay-page.js +1 -1
- package/dist/billing/pay-page.js.map +1 -1
- package/dist/bin/eifi1-pay-page.mjs +1 -1
- package/dist/eslint/index.js +1 -1
- package/dist/pay/pay.js +1 -1
- package/package.json +1 -1
- package/src/billing/billing-overview.ts +2 -2
- package/src/billing/pay-page.ts +1 -1
|
@@ -70,9 +70,9 @@ export interface PlanSwitchPreview {
|
|
|
70
70
|
* charges. */
|
|
71
71
|
now_minor: number;
|
|
72
72
|
/** The next regular charge, in minor units. */
|
|
73
|
-
next_minor
|
|
73
|
+
next_minor?: number | null;
|
|
74
74
|
/** When it is charged; null when Paddle names no date. */
|
|
75
|
-
next_billed_at
|
|
75
|
+
next_billed_at?: string | null;
|
|
76
76
|
/** The plan and period switched to. */
|
|
77
77
|
plan: string;
|
|
78
78
|
interval: BillingInterval;
|
package/dist/billing/pay-page.js
CHANGED
|
@@ -64,7 +64,7 @@ function appAt(name, site, returnPath) {
|
|
|
64
64
|
}
|
|
65
65
|
const PAY_PAGE_APPS = Object.freeze({
|
|
66
66
|
keksdose: Object.freeze(appAt("Keksdose", "https://keksdose.app", "/settings/subscription")),
|
|
67
|
-
kastlan: Object.freeze(appAt("
|
|
67
|
+
kastlan: Object.freeze(appAt("Kastlan", "https://kastlan.app", "/admin/billing")),
|
|
68
68
|
kurvenschmiede: Object.freeze(appAt("Kurvenschmiede", "https://kurvenschmiede.app", "/settings/subscription"))
|
|
69
69
|
});
|
|
70
70
|
const TOKEN_PREFIX = { sandbox: "test_", live: "live_" };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/billing/pay-page.ts"],"sourcesContent":["import type { UiKitLabels } from \"../i18n/kit-labels\";\n\n/**\n * The kit's pay page (docs/billing-harmonization.md §14.4, decision 19 as amended by\n * §15.2): a standalone static page that ui-kit ships as `dist/pay/`, and that each app\n * serves on its own pay host, `pay.<app domain>` — a separate origin, approved in Paddle,\n * where Paddle.js shares the origin with nothing: no sign-in, no app code, no storage.\n * Since 0.39 every pay page serves ANY app's transaction: it names the app from the\n * transaction's custom data, from the kit's map {@link PAY_PAGE_APPS}, not from its\n * domain — Paddle's single default payment link reaches one host for all three apps.\n *\n * Paddle's create-transaction answers `checkout.url`: the pay page's URL plus\n * `?_ptxn=<txn>`, and Paddle.js on that page opens the transaction by itself. The app\n * sends the buyer there with {@link payPageUrl}, which adds the buyer's language; the\n * page maps it to Paddle's checkout locale ({@link paddleLocale}). Everything else the\n * page needs — the client-side token, sandbox or live, the host's app and the map of\n * apps with their ways back — is the host's `pay-config.json`, written at its web build\n * by the `eifi1-pay-page` script.\n *\n * Pure, and React-free: the page's own script (`pay-page/pay.ts`) and that build script\n * bundle this module. The barrel exports the link's parts and the app map ({@link PAY_PAGE_APPS},\n * {@link PayPageApp}); the exports marked @internal are theirs.\n */\n\n/** Paddle's two environments: the sandbox's client-side tokens start `test_`, live\n * ones `live_`. */\nexport type PaddleEnvironment = \"sandbox\" | \"live\";\n\n/** The pay page's language parameter: `?lang=<kit locale>`. */\nexport const PAY_PAGE_LANG_PARAM = \"lang\";\n\n/** The kit's seven languages (`KitLanguageCode`), as the page reads them. */\nconst PAY_PAGE_LANGUAGES = [\"de-CH\", \"en\", \"es\", \"fr\", \"it\", \"hu\", \"zh\"] as const;\n\n/** One of the kit's languages, as the page speaks it. */\nexport type PayPageLanguage = (typeof PAY_PAGE_LANGUAGES)[number];\n\n/**\n * The kit's language → Paddle's checkout locale (`Paddle.Checkout.open`,\n * `settings.locale`, read 2026-10-09: ar, zh-Hans, zh-TW, da, nl, en, fr, de, it, ja, ko,\n * no, pl, pt, pt-BR, tr, ru, es, sv). Paddle has no Hungarian: a Hungarian buyer gets\n * English always, not Paddle's guess from the browser. The page's own lines stay in the\n * kit's language, Hungarian included.\n */\nconst PADDLE_LOCALES: Readonly<Record<PayPageLanguage, string>> = {\n en: \"en\",\n \"de-CH\": \"de\",\n fr: \"fr\",\n it: \"it\",\n es: \"es\",\n zh: \"zh-Hans\",\n hu: \"en\",\n};\n\n/** Paddle's transaction parameter on a pay page link. @internal */\nexport const PAY_PAGE_TRANSACTION_PARAM = \"_ptxn\";\n\n/**\n * The kit language one tag names, or undefined — `resolveLanguage`'s rule\n * (src/i18n/languages.ts), restated here because that module loads the seven catalogues\n * and the page must stay a few kilobytes: exact first, ignoring case and reading `_` as\n * `-`; then by language, so every German is `de-CH`, every Chinese `zh` (Simplified),\n * and any regional form its base (`fr-CA` → `fr`).\n */\nfunction kitLanguageOf(tag: unknown): PayPageLanguage | undefined {\n if (typeof tag !== \"string\") return undefined;\n const clean = tag.trim().replace(/_/g, \"-\");\n if (!clean) return undefined;\n const lower = clean.toLowerCase();\n const exact = PAY_PAGE_LANGUAGES.find((code) => code.toLowerCase() === lower);\n if (exact) return exact;\n const language = lower.split(\"-\")[0];\n return PAY_PAGE_LANGUAGES.find((code) => code.split(\"-\")[0].toLowerCase() === language);\n}\n\n/**\n * The kit's locale → Paddle's checkout locale: `de-CH` → `de`, `zh` → `zh-Hans`, `hu` →\n * `en` (Paddle has no Hungarian), the others as they are. Any tag is read as the kit\n * reads it (`de-AT` is the one German, `zh-TW` the one Chinese); one the kit doesn't\n * ship is English, and so is `undefined` (0.33.1) — `useKitLocale()` with no provider\n * locale.\n */\nexport function paddleLocale(kitLocale: string | undefined): string {\n return PADDLE_LOCALES[kitLanguageOf(kitLocale) ?? \"en\"];\n}\n\n/**\n * The checkout's `{url}` with the buyer's language: `lang=<kit locale>` on a pay page\n * link (one that carries `_ptxn`); any other URL — a hosted checkout's, which takes its\n * own `locale` — unchanged, as is a locale the kit doesn't ship.\n *\n * const { url } = await api.checkout({ plan, interval, currency });\n * noteCheckoutStarted(user.id, overview);\n * window.location.assign(payPageUrl(url, locale)); // locale = useKitLocale()\n *\n * `locale` may be `undefined` (0.33.1, keksdose): `useKitLocale()` answers that with no\n * provider locale, the runtime's language. The link then goes without `lang`, and the\n * page speaks the browser's language, else English — what a missing or wrong `lang`\n * already did: it costs nothing but the language.\n */\nexport function payPageUrl(checkoutUrl: string, locale: string | undefined): string {\n const language = kitLanguageOf(locale);\n if (!language) return checkoutUrl;\n let url: URL;\n try {\n url = new URL(checkoutUrl);\n } catch {\n return checkoutUrl;\n }\n if (!url.searchParams.has(PAY_PAGE_TRANSACTION_PARAM)) return checkoutUrl;\n url.searchParams.set(PAY_PAGE_LANG_PARAM, language);\n return url.toString();\n}\n\n/**\n * The language the page speaks: its `lang` parameter, else the browser's languages in\n * their order, each matched to the kit's; else English. @internal\n */\nexport function payPageLanguage(\n lang: string | null | undefined,\n browserLanguages: readonly string[] = [],\n): PayPageLanguage {\n for (const candidate of [lang, ...browserLanguages]) {\n const found = kitLanguageOf(candidate);\n if (found) return found;\n }\n return \"en\";\n}\n\n/* ── The page's words ──────────────────────────────────────────────────────── */\n\n/**\n * The page's words in one language — built into `pay.js` at the kit's build from the\n * seven catalogues, since the page has no React and no provider. @internal\n */\nexport interface PayPageStrings {\n /** `billing.payOpening`, while the checkout opens. */\n opening: string;\n /** `billing.payNothing`: no `_ptxn`, or no token (billing off). */\n nothing: string;\n /** `billing.payFailed`: Paddle.js didn't load, or the configuration is wrong. */\n failed: string;\n /** `billing.payBack`: the buyer closed the checkout. */\n closed: string;\n /** `billing.payDone` (0.39): paid, for a transaction that names no app the page\n * knows — there is no way back to offer. */\n done: string;\n /** `common.back`: the link to the app's subscription page. */\n back: string;\n /** `legal.links.terms` and `legal.links.privacy`: the footer's links. */\n terms: string;\n privacy: string;\n /** `legal.navLabel`: the footer's `<nav>`. */\n legal: string;\n}\n\n/** The page's words, read from one of the kit's catalogues. @internal */\nexport function payPageStrings(labels: UiKitLabels): PayPageStrings {\n return {\n opening: labels.billing.payOpening,\n nothing: labels.billing.payNothing,\n failed: labels.billing.payFailed,\n closed: labels.billing.payBack,\n done: labels.billing.payDone,\n back: labels.common.back,\n terms: labels.legal.links.terms,\n privacy: labels.legal.links.privacy,\n legal: labels.legal.navLabel,\n };\n}\n\n/* ── pay-config.json ───────────────────────────────────────────────────────── */\n\n/** The configuration file beside the page, written at each app's web build. @internal */\nexport const PAY_CONFIG_FILE = \"pay-config.json\";\n\n/**\n * One app a pay page serves (docs/billing-harmonization.md §15.2.6): its name, the way\n * back and its Terms and Privacy. Every pay page knows every app, because Paddle's links\n * — its single default payment link, a payment-method update from its e-mails — reach\n * whichever page the account names, whatever app the transaction is for.\n */\nexport interface PayPageApp {\n /** The app's display name: the footer's line and the page's title. */\n name: string;\n /** The app's subscription page. Once paid, the page goes to\n * `checkoutReturnUrl(returnUrl)`; \"Back\" goes here. */\n returnUrl: string;\n termsUrl: string;\n privacyUrl: string;\n}\n\n/** An app's tag: server-kit's `billing_app` pattern, the key of {@link PAY_PAGE_APPS}\n * and the `app` of a transaction's custom data (§14.3). */\nconst APP_TAG = /^[a-z][a-z0-9_-]{0,63}$/;\n\n/** An app on its site: the name, the Terms and Privacy at `/terms` and `/privacy`, and\n * its subscription page. */\nfunction appAt(name: string, site: string, returnPath: string): PayPageApp {\n return { name, returnUrl: `${site}${returnPath}`, termsUrl: `${site}/terms`, privacyUrl: `${site}/privacy` };\n}\n\n/**\n * The apps the kit's pay pages serve, by their `billing_app` tag (§15.2.6) — one source,\n * so the three pay hosts can't drift. A new app, or a page that moves, is a kit release.\n * The bin writes it into `pay-config.json`; `--app-url` moves the host's own app to\n * another site (a local stack, a dev deploy) and `--apps <file>` merges a map over it.\n */\nexport const PAY_PAGE_APPS: Readonly<Record<string, Readonly<PayPageApp>>> = Object.freeze({\n keksdose: Object.freeze(appAt(\"Keksdose\", \"https://keksdose.app\", \"/settings/subscription\")),\n kastlan: Object.freeze(appAt(\"kastlan\", \"https://kastlan.app\", \"/admin/billing\")),\n kurvenschmiede: Object.freeze(appAt(\"Kurvenschmiede\", \"https://kurvenschmiede.app\", \"/settings/subscription\")),\n});\n\n/**\n * `pay-config.json`: what the page needs that differs per deployment (§15.2.6). Read by\n * the page from its own origin — never from the query, where Paddle's own links (it\n * puts only `_ptxn` there) would lack it and a return URL would be an open redirect on\n * an approved payment domain. @internal\n */\nexport interface PayPageConfig {\n /** Paddle's client-side token (`test_…` for the sandbox, `live_…` for live); null\n * while billing is off, and the page says `payNothing`. */\n token: string | null;\n environment: PaddleEnvironment | null;\n /** The host's own app, a key of `apps`: the page shows it until the checkout loads,\n * and its Terms and Privacy stay for a transaction that names no app. */\n app: string;\n /** Every app the page serves, by tag. The transaction's `custom_data.app` picks one;\n * a return URL only ever comes from here. */\n apps: Record<string, PayPageApp>;\n}\n\n/** Paddle's client-side token prefixes, per environment. */\nconst TOKEN_PREFIX: Readonly<Record<PaddleEnvironment, string>> = { sandbox: \"test_\", live: \"live_\" };\n\n/** `https://`, or `http://` on this machine only (`localhost`, `127.0.0.1`, `[::1]`). @internal */\nexport function isPayPageUrl(value: unknown): boolean {\n if (typeof value !== \"string\") return false;\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n return false;\n }\n if (url.protocol === \"https:\") return true;\n return url.protocol === \"http:\" && [\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(url.hostname);\n}\n\n/** Whether `tag` is a tag of `apps`, its own key — never `__proto__` or `constructor`. */\nfunction hasApp(apps: unknown, tag: unknown): tag is string {\n return (\n typeof tag === \"string\" &&\n APP_TAG.test(tag) &&\n typeof apps === \"object\" &&\n apps !== null &&\n Object.prototype.hasOwnProperty.call(apps, tag)\n );\n}\n\n/**\n * The app a transaction's custom data names (`custom_data.app`, §14.3), from the page's\n * map — or undefined for none, or a tag the map doesn't know. @internal\n */\nexport function payPageAppFor(config: Pick<PayPageConfig, \"apps\">, tag: unknown): PayPageApp | undefined {\n return hasApp(config.apps, tag) ? config.apps[tag] : undefined;\n}\n\n/** The problems of one app of the map. */\nfunction appProblems(tag: string, app: unknown): string[] {\n if (typeof app !== \"object\" || app === null) return [`apps.${tag} is not an object`];\n const a = app as Record<string, unknown>;\n const problems: string[] = [];\n if (typeof a.name !== \"string\" || a.name.trim() === \"\") problems.push(`apps.${tag}.name is missing`);\n for (const key of [\"returnUrl\", \"termsUrl\", \"privacyUrl\"] as const) {\n if (!isPayPageUrl(a[key])) problems.push(`apps.${tag}.${key} must be an https:// URL (http:// for localhost only)`);\n }\n return problems;\n}\n\n/**\n * What is wrong with a `pay-config.json`, one line each — `[]` when nothing is. The\n * build script refuses to write a file with any (so a wrong pairing fails the build,\n * not a buyer), and the page says `payFailed` for one it reads:\n *\n * - a token whose prefix disagrees with the environment (`test_` ↔ sandbox, `live_` ↔\n * live), or a token without an environment;\n * - an app tag that isn't a `billing_app` tag (`^[a-z][a-z0-9_-]{0,63}$`), an app\n * without a name, or a return, terms or privacy URL that isn't `https://` (`http://`\n * for localhost only);\n * - no `app`, or one that isn't in `apps`. @internal\n */\nexport function payPageConfigProblems(config: unknown): string[] {\n if (typeof config !== \"object\" || config === null) return [\"the configuration is not an object\"];\n const c = config as Record<string, unknown>;\n const problems: string[] = [];\n const { token, environment } = c;\n if (environment !== null && environment !== undefined && environment !== \"sandbox\" && environment !== \"live\") {\n problems.push(`environment must be \"sandbox\" or \"live\", not ${JSON.stringify(environment)}`);\n }\n if (token !== null && token !== undefined) {\n if (typeof token !== \"string\" || token === \"\") {\n problems.push(\"token must be Paddle's client-side token, or null while billing is off\");\n } else if (environment !== \"sandbox\" && environment !== \"live\") {\n problems.push(\"a token needs its environment: sandbox or live\");\n } else if (!token.startsWith(TOKEN_PREFIX[environment]) || token.length === TOKEN_PREFIX[environment].length) {\n const other = environment === \"sandbox\" ? \"live\" : \"sandbox\";\n problems.push(\n token.startsWith(TOKEN_PREFIX[other])\n ? `the token is a ${other} token (${TOKEN_PREFIX[other]}…), but the environment is ${environment}`\n : `a ${environment} token starts with ${TOKEN_PREFIX[environment]}`,\n );\n }\n }\n const { apps, app } = c;\n if (typeof apps !== \"object\" || apps === null || Array.isArray(apps) || Object.keys(apps).length === 0) {\n problems.push(\"apps must map each app's tag to its name and URLs\");\n } else {\n for (const [tag, entry] of Object.entries(apps)) {\n if (!APP_TAG.test(tag)) problems.push(`apps: ${JSON.stringify(tag)} is not an app tag (a-z, 0-9, _ and -)`);\n else problems.push(...appProblems(tag, entry));\n }\n }\n if (typeof app !== \"string\" || app === \"\") problems.push(\"app is missing: the host's own app tag\");\n else if (!hasApp(apps, app)) {\n const known = typeof apps === \"object\" && apps !== null ? Object.keys(apps).join(\", \") : \"\";\n problems.push(`app ${JSON.stringify(app)} is not in apps${known ? ` (${known})` : \"\"}`);\n }\n return problems;\n}\n"],"mappings":";AA6BO,MAAM,sBAAsB;AAGnC,MAAM,qBAAqB,CAAC,SAAS,MAAM,MAAM,MAAM,MAAM,MAAM,IAAI;AAYvE,MAAM,iBAA4D;AAAA,EAChE,IAAI;AAAA,EACJ,SAAS;AAAA,EACT,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN;AAGO,MAAM,6BAA6B;AAS1C,SAAS,cAAc,KAA2C;AAChE,MAAI,OAAO,QAAQ,SAAU,QAAO;AACpC,QAAM,QAAQ,IAAI,KAAK,EAAE,QAAQ,MAAM,GAAG;AAC1C,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,QAAQ,MAAM,YAAY;AAChC,QAAM,QAAQ,mBAAmB,KAAK,CAAC,SAAS,KAAK,YAAY,MAAM,KAAK;AAC5E,MAAI,MAAO,QAAO;AAClB,QAAM,WAAW,MAAM,MAAM,GAAG,EAAE,CAAC;AACnC,SAAO,mBAAmB,KAAK,CAAC,SAAS,KAAK,MAAM,GAAG,EAAE,CAAC,EAAE,YAAY,MAAM,QAAQ;AACxF;AASO,SAAS,aAAa,WAAuC;AAClE,SAAO,eAAe,cAAc,SAAS,KAAK,IAAI;AACxD;AAgBO,SAAS,WAAW,aAAqB,QAAoC;AAClF,QAAM,WAAW,cAAc,MAAM;AACrC,MAAI,CAAC,SAAU,QAAO;AACtB,MAAI;AACJ,MAAI;AACF,UAAM,IAAI,IAAI,WAAW;AAAA,EAC3B,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,CAAC,IAAI,aAAa,IAAI,0BAA0B,EAAG,QAAO;AAC9D,MAAI,aAAa,IAAI,qBAAqB,QAAQ;AAClD,SAAO,IAAI,SAAS;AACtB;AAMO,SAAS,gBACd,MACA,mBAAsC,CAAC,GACtB;AACjB,aAAW,aAAa,CAAC,MAAM,GAAG,gBAAgB,GAAG;AACnD,UAAM,QAAQ,cAAc,SAAS;AACrC,QAAI,MAAO,QAAO;AAAA,EACpB;AACA,SAAO;AACT;AA8BO,SAAS,eAAe,QAAqC;AAClE,SAAO;AAAA,IACL,SAAS,OAAO,QAAQ;AAAA,IACxB,SAAS,OAAO,QAAQ;AAAA,IACxB,QAAQ,OAAO,QAAQ;AAAA,IACvB,QAAQ,OAAO,QAAQ;AAAA,IACvB,MAAM,OAAO,QAAQ;AAAA,IACrB,MAAM,OAAO,OAAO;AAAA,IACpB,OAAO,OAAO,MAAM,MAAM;AAAA,IAC1B,SAAS,OAAO,MAAM,MAAM;AAAA,IAC5B,OAAO,OAAO,MAAM;AAAA,EACtB;AACF;AAKO,MAAM,kBAAkB;AAoB/B,MAAM,UAAU;AAIhB,SAAS,MAAM,MAAc,MAAc,YAAgC;AACzE,SAAO,EAAE,MAAM,WAAW,GAAG,IAAI,GAAG,UAAU,IAAI,UAAU,GAAG,IAAI,UAAU,YAAY,GAAG,IAAI,WAAW;AAC7G;AAQO,MAAM,gBAAgE,OAAO,OAAO;AAAA,EACzF,UAAU,OAAO,OAAO,MAAM,YAAY,wBAAwB,wBAAwB,CAAC;AAAA,EAC3F,SAAS,OAAO,OAAO,MAAM,WAAW,uBAAuB,gBAAgB,CAAC;AAAA,EAChF,gBAAgB,OAAO,OAAO,MAAM,kBAAkB,8BAA8B,wBAAwB,CAAC;AAC/G,CAAC;AAsBD,MAAM,eAA4D,EAAE,SAAS,SAAS,MAAM,QAAQ;AAG7F,SAAS,aAAa,OAAyB;AACpD,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI;AACJ,MAAI;AACF,UAAM,IAAI,IAAI,KAAK;AAAA,EACrB,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,IAAI,aAAa,SAAU,QAAO;AACtC,SAAO,IAAI,aAAa,WAAW,CAAC,aAAa,aAAa,OAAO,EAAE,SAAS,IAAI,QAAQ;AAC9F;AAGA,SAAS,OAAO,MAAe,KAA6B;AAC1D,SACE,OAAO,QAAQ,YACf,QAAQ,KAAK,GAAG,KAChB,OAAO,SAAS,YAChB,SAAS,QACT,OAAO,UAAU,eAAe,KAAK,MAAM,GAAG;AAElD;AAMO,SAAS,cAAc,QAAqC,KAAsC;AACvG,SAAO,OAAO,OAAO,MAAM,GAAG,IAAI,OAAO,KAAK,GAAG,IAAI;AACvD;AAGA,SAAS,YAAY,KAAa,KAAwB;AACxD,MAAI,OAAO,QAAQ,YAAY,QAAQ,KAAM,QAAO,CAAC,QAAQ,GAAG,mBAAmB;AACnF,QAAM,IAAI;AACV,QAAM,WAAqB,CAAC;AAC5B,MAAI,OAAO,EAAE,SAAS,YAAY,EAAE,KAAK,KAAK,MAAM,GAAI,UAAS,KAAK,QAAQ,GAAG,kBAAkB;AACnG,aAAW,OAAO,CAAC,aAAa,YAAY,YAAY,GAAY;AAClE,QAAI,CAAC,aAAa,EAAE,GAAG,CAAC,EAAG,UAAS,KAAK,QAAQ,GAAG,IAAI,GAAG,uDAAuD;AAAA,EACpH;AACA,SAAO;AACT;AAcO,SAAS,sBAAsB,QAA2B;AAC/D,MAAI,OAAO,WAAW,YAAY,WAAW,KAAM,QAAO,CAAC,oCAAoC;AAC/F,QAAM,IAAI;AACV,QAAM,WAAqB,CAAC;AAC5B,QAAM,EAAE,OAAO,YAAY,IAAI;AAC/B,MAAI,gBAAgB,QAAQ,gBAAgB,UAAa,gBAAgB,aAAa,gBAAgB,QAAQ;AAC5G,aAAS,KAAK,gDAAgD,KAAK,UAAU,WAAW,CAAC,EAAE;AAAA,EAC7F;AACA,MAAI,UAAU,QAAQ,UAAU,QAAW;AACzC,QAAI,OAAO,UAAU,YAAY,UAAU,IAAI;AAC7C,eAAS,KAAK,wEAAwE;AAAA,IACxF,WAAW,gBAAgB,aAAa,gBAAgB,QAAQ;AAC9D,eAAS,KAAK,gDAAgD;AAAA,IAChE,WAAW,CAAC,MAAM,WAAW,aAAa,WAAW,CAAC,KAAK,MAAM,WAAW,aAAa,WAAW,EAAE,QAAQ;AAC5G,YAAM,QAAQ,gBAAgB,YAAY,SAAS;AACnD,eAAS;AAAA,QACP,MAAM,WAAW,aAAa,KAAK,CAAC,IAChC,kBAAkB,KAAK,WAAW,aAAa,KAAK,CAAC,mCAA8B,WAAW,KAC9F,KAAK,WAAW,sBAAsB,aAAa,WAAW,CAAC;AAAA,MACrE;AAAA,IACF;AAAA,EACF;AACA,QAAM,EAAE,MAAM,IAAI,IAAI;AACtB,MAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,MAAM,QAAQ,IAAI,KAAK,OAAO,KAAK,IAAI,EAAE,WAAW,GAAG;AACtG,aAAS,KAAK,mDAAmD;AAAA,EACnE,OAAO;AACL,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,GAAG;AAC/C,UAAI,CAAC,QAAQ,KAAK,GAAG,EAAG,UAAS,KAAK,SAAS,KAAK,UAAU,GAAG,CAAC,wCAAwC;AAAA,UACrG,UAAS,KAAK,GAAG,YAAY,KAAK,KAAK,CAAC;AAAA,IAC/C;AAAA,EACF;AACA,MAAI,OAAO,QAAQ,YAAY,QAAQ,GAAI,UAAS,KAAK,wCAAwC;AAAA,WACxF,CAAC,OAAO,MAAM,GAAG,GAAG;AAC3B,UAAM,QAAQ,OAAO,SAAS,YAAY,SAAS,OAAO,OAAO,KAAK,IAAI,EAAE,KAAK,IAAI,IAAI;AACzF,aAAS,KAAK,OAAO,KAAK,UAAU,GAAG,CAAC,kBAAkB,QAAQ,KAAK,KAAK,MAAM,EAAE,EAAE;AAAA,EACxF;AACA,SAAO;AACT;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/billing/pay-page.ts"],"sourcesContent":["import type { UiKitLabels } from \"../i18n/kit-labels\";\n\n/**\n * The kit's pay page (docs/billing-harmonization.md §14.4, decision 19 as amended by\n * §15.2): a standalone static page that ui-kit ships as `dist/pay/`, and that each app\n * serves on its own pay host, `pay.<app domain>` — a separate origin, approved in Paddle,\n * where Paddle.js shares the origin with nothing: no sign-in, no app code, no storage.\n * Since 0.39 every pay page serves ANY app's transaction: it names the app from the\n * transaction's custom data, from the kit's map {@link PAY_PAGE_APPS}, not from its\n * domain — Paddle's single default payment link reaches one host for all three apps.\n *\n * Paddle's create-transaction answers `checkout.url`: the pay page's URL plus\n * `?_ptxn=<txn>`, and Paddle.js on that page opens the transaction by itself. The app\n * sends the buyer there with {@link payPageUrl}, which adds the buyer's language; the\n * page maps it to Paddle's checkout locale ({@link paddleLocale}). Everything else the\n * page needs — the client-side token, sandbox or live, the host's app and the map of\n * apps with their ways back — is the host's `pay-config.json`, written at its web build\n * by the `eifi1-pay-page` script.\n *\n * Pure, and React-free: the page's own script (`pay-page/pay.ts`) and that build script\n * bundle this module. The barrel exports the link's parts and the app map ({@link PAY_PAGE_APPS},\n * {@link PayPageApp}); the exports marked @internal are theirs.\n */\n\n/** Paddle's two environments: the sandbox's client-side tokens start `test_`, live\n * ones `live_`. */\nexport type PaddleEnvironment = \"sandbox\" | \"live\";\n\n/** The pay page's language parameter: `?lang=<kit locale>`. */\nexport const PAY_PAGE_LANG_PARAM = \"lang\";\n\n/** The kit's seven languages (`KitLanguageCode`), as the page reads them. */\nconst PAY_PAGE_LANGUAGES = [\"de-CH\", \"en\", \"es\", \"fr\", \"it\", \"hu\", \"zh\"] as const;\n\n/** One of the kit's languages, as the page speaks it. */\nexport type PayPageLanguage = (typeof PAY_PAGE_LANGUAGES)[number];\n\n/**\n * The kit's language → Paddle's checkout locale (`Paddle.Checkout.open`,\n * `settings.locale`, read 2026-10-09: ar, zh-Hans, zh-TW, da, nl, en, fr, de, it, ja, ko,\n * no, pl, pt, pt-BR, tr, ru, es, sv). Paddle has no Hungarian: a Hungarian buyer gets\n * English always, not Paddle's guess from the browser. The page's own lines stay in the\n * kit's language, Hungarian included.\n */\nconst PADDLE_LOCALES: Readonly<Record<PayPageLanguage, string>> = {\n en: \"en\",\n \"de-CH\": \"de\",\n fr: \"fr\",\n it: \"it\",\n es: \"es\",\n zh: \"zh-Hans\",\n hu: \"en\",\n};\n\n/** Paddle's transaction parameter on a pay page link. @internal */\nexport const PAY_PAGE_TRANSACTION_PARAM = \"_ptxn\";\n\n/**\n * The kit language one tag names, or undefined — `resolveLanguage`'s rule\n * (src/i18n/languages.ts), restated here because that module loads the seven catalogues\n * and the page must stay a few kilobytes: exact first, ignoring case and reading `_` as\n * `-`; then by language, so every German is `de-CH`, every Chinese `zh` (Simplified),\n * and any regional form its base (`fr-CA` → `fr`).\n */\nfunction kitLanguageOf(tag: unknown): PayPageLanguage | undefined {\n if (typeof tag !== \"string\") return undefined;\n const clean = tag.trim().replace(/_/g, \"-\");\n if (!clean) return undefined;\n const lower = clean.toLowerCase();\n const exact = PAY_PAGE_LANGUAGES.find((code) => code.toLowerCase() === lower);\n if (exact) return exact;\n const language = lower.split(\"-\")[0];\n return PAY_PAGE_LANGUAGES.find((code) => code.split(\"-\")[0].toLowerCase() === language);\n}\n\n/**\n * The kit's locale → Paddle's checkout locale: `de-CH` → `de`, `zh` → `zh-Hans`, `hu` →\n * `en` (Paddle has no Hungarian), the others as they are. Any tag is read as the kit\n * reads it (`de-AT` is the one German, `zh-TW` the one Chinese); one the kit doesn't\n * ship is English, and so is `undefined` (0.33.1) — `useKitLocale()` with no provider\n * locale.\n */\nexport function paddleLocale(kitLocale: string | undefined): string {\n return PADDLE_LOCALES[kitLanguageOf(kitLocale) ?? \"en\"];\n}\n\n/**\n * The checkout's `{url}` with the buyer's language: `lang=<kit locale>` on a pay page\n * link (one that carries `_ptxn`); any other URL — a hosted checkout's, which takes its\n * own `locale` — unchanged, as is a locale the kit doesn't ship.\n *\n * const { url } = await api.checkout({ plan, interval, currency });\n * noteCheckoutStarted(user.id, overview);\n * window.location.assign(payPageUrl(url, locale)); // locale = useKitLocale()\n *\n * `locale` may be `undefined` (0.33.1, keksdose): `useKitLocale()` answers that with no\n * provider locale, the runtime's language. The link then goes without `lang`, and the\n * page speaks the browser's language, else English — what a missing or wrong `lang`\n * already did: it costs nothing but the language.\n */\nexport function payPageUrl(checkoutUrl: string, locale: string | undefined): string {\n const language = kitLanguageOf(locale);\n if (!language) return checkoutUrl;\n let url: URL;\n try {\n url = new URL(checkoutUrl);\n } catch {\n return checkoutUrl;\n }\n if (!url.searchParams.has(PAY_PAGE_TRANSACTION_PARAM)) return checkoutUrl;\n url.searchParams.set(PAY_PAGE_LANG_PARAM, language);\n return url.toString();\n}\n\n/**\n * The language the page speaks: its `lang` parameter, else the browser's languages in\n * their order, each matched to the kit's; else English. @internal\n */\nexport function payPageLanguage(\n lang: string | null | undefined,\n browserLanguages: readonly string[] = [],\n): PayPageLanguage {\n for (const candidate of [lang, ...browserLanguages]) {\n const found = kitLanguageOf(candidate);\n if (found) return found;\n }\n return \"en\";\n}\n\n/* ── The page's words ──────────────────────────────────────────────────────── */\n\n/**\n * The page's words in one language — built into `pay.js` at the kit's build from the\n * seven catalogues, since the page has no React and no provider. @internal\n */\nexport interface PayPageStrings {\n /** `billing.payOpening`, while the checkout opens. */\n opening: string;\n /** `billing.payNothing`: no `_ptxn`, or no token (billing off). */\n nothing: string;\n /** `billing.payFailed`: Paddle.js didn't load, or the configuration is wrong. */\n failed: string;\n /** `billing.payBack`: the buyer closed the checkout. */\n closed: string;\n /** `billing.payDone` (0.39): paid, for a transaction that names no app the page\n * knows — there is no way back to offer. */\n done: string;\n /** `common.back`: the link to the app's subscription page. */\n back: string;\n /** `legal.links.terms` and `legal.links.privacy`: the footer's links. */\n terms: string;\n privacy: string;\n /** `legal.navLabel`: the footer's `<nav>`. */\n legal: string;\n}\n\n/** The page's words, read from one of the kit's catalogues. @internal */\nexport function payPageStrings(labels: UiKitLabels): PayPageStrings {\n return {\n opening: labels.billing.payOpening,\n nothing: labels.billing.payNothing,\n failed: labels.billing.payFailed,\n closed: labels.billing.payBack,\n done: labels.billing.payDone,\n back: labels.common.back,\n terms: labels.legal.links.terms,\n privacy: labels.legal.links.privacy,\n legal: labels.legal.navLabel,\n };\n}\n\n/* ── pay-config.json ───────────────────────────────────────────────────────── */\n\n/** The configuration file beside the page, written at each app's web build. @internal */\nexport const PAY_CONFIG_FILE = \"pay-config.json\";\n\n/**\n * One app a pay page serves (docs/billing-harmonization.md §15.2.6): its name, the way\n * back and its Terms and Privacy. Every pay page knows every app, because Paddle's links\n * — its single default payment link, a payment-method update from its e-mails — reach\n * whichever page the account names, whatever app the transaction is for.\n */\nexport interface PayPageApp {\n /** The app's display name: the footer's line and the page's title. */\n name: string;\n /** The app's subscription page. Once paid, the page goes to\n * `checkoutReturnUrl(returnUrl)`; \"Back\" goes here. */\n returnUrl: string;\n termsUrl: string;\n privacyUrl: string;\n}\n\n/** An app's tag: server-kit's `billing_app` pattern, the key of {@link PAY_PAGE_APPS}\n * and the `app` of a transaction's custom data (§14.3). */\nconst APP_TAG = /^[a-z][a-z0-9_-]{0,63}$/;\n\n/** An app on its site: the name, the Terms and Privacy at `/terms` and `/privacy`, and\n * its subscription page. */\nfunction appAt(name: string, site: string, returnPath: string): PayPageApp {\n return { name, returnUrl: `${site}${returnPath}`, termsUrl: `${site}/terms`, privacyUrl: `${site}/privacy` };\n}\n\n/**\n * The apps the kit's pay pages serve, by their `billing_app` tag (§15.2.6) — one source,\n * so the three pay hosts can't drift. A new app, or a page that moves, is a kit release.\n * The bin writes it into `pay-config.json`; `--app-url` moves the host's own app to\n * another site (a local stack, a dev deploy) and `--apps <file>` merges a map over it.\n */\nexport const PAY_PAGE_APPS: Readonly<Record<string, Readonly<PayPageApp>>> = Object.freeze({\n keksdose: Object.freeze(appAt(\"Keksdose\", \"https://keksdose.app\", \"/settings/subscription\")),\n kastlan: Object.freeze(appAt(\"Kastlan\", \"https://kastlan.app\", \"/admin/billing\")),\n kurvenschmiede: Object.freeze(appAt(\"Kurvenschmiede\", \"https://kurvenschmiede.app\", \"/settings/subscription\")),\n});\n\n/**\n * `pay-config.json`: what the page needs that differs per deployment (§15.2.6). Read by\n * the page from its own origin — never from the query, where Paddle's own links (it\n * puts only `_ptxn` there) would lack it and a return URL would be an open redirect on\n * an approved payment domain. @internal\n */\nexport interface PayPageConfig {\n /** Paddle's client-side token (`test_…` for the sandbox, `live_…` for live); null\n * while billing is off, and the page says `payNothing`. */\n token: string | null;\n environment: PaddleEnvironment | null;\n /** The host's own app, a key of `apps`: the page shows it until the checkout loads,\n * and its Terms and Privacy stay for a transaction that names no app. */\n app: string;\n /** Every app the page serves, by tag. The transaction's `custom_data.app` picks one;\n * a return URL only ever comes from here. */\n apps: Record<string, PayPageApp>;\n}\n\n/** Paddle's client-side token prefixes, per environment. */\nconst TOKEN_PREFIX: Readonly<Record<PaddleEnvironment, string>> = { sandbox: \"test_\", live: \"live_\" };\n\n/** `https://`, or `http://` on this machine only (`localhost`, `127.0.0.1`, `[::1]`). @internal */\nexport function isPayPageUrl(value: unknown): boolean {\n if (typeof value !== \"string\") return false;\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n return false;\n }\n if (url.protocol === \"https:\") return true;\n return url.protocol === \"http:\" && [\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(url.hostname);\n}\n\n/** Whether `tag` is a tag of `apps`, its own key — never `__proto__` or `constructor`. */\nfunction hasApp(apps: unknown, tag: unknown): tag is string {\n return (\n typeof tag === \"string\" &&\n APP_TAG.test(tag) &&\n typeof apps === \"object\" &&\n apps !== null &&\n Object.prototype.hasOwnProperty.call(apps, tag)\n );\n}\n\n/**\n * The app a transaction's custom data names (`custom_data.app`, §14.3), from the page's\n * map — or undefined for none, or a tag the map doesn't know. @internal\n */\nexport function payPageAppFor(config: Pick<PayPageConfig, \"apps\">, tag: unknown): PayPageApp | undefined {\n return hasApp(config.apps, tag) ? config.apps[tag] : undefined;\n}\n\n/** The problems of one app of the map. */\nfunction appProblems(tag: string, app: unknown): string[] {\n if (typeof app !== \"object\" || app === null) return [`apps.${tag} is not an object`];\n const a = app as Record<string, unknown>;\n const problems: string[] = [];\n if (typeof a.name !== \"string\" || a.name.trim() === \"\") problems.push(`apps.${tag}.name is missing`);\n for (const key of [\"returnUrl\", \"termsUrl\", \"privacyUrl\"] as const) {\n if (!isPayPageUrl(a[key])) problems.push(`apps.${tag}.${key} must be an https:// URL (http:// for localhost only)`);\n }\n return problems;\n}\n\n/**\n * What is wrong with a `pay-config.json`, one line each — `[]` when nothing is. The\n * build script refuses to write a file with any (so a wrong pairing fails the build,\n * not a buyer), and the page says `payFailed` for one it reads:\n *\n * - a token whose prefix disagrees with the environment (`test_` ↔ sandbox, `live_` ↔\n * live), or a token without an environment;\n * - an app tag that isn't a `billing_app` tag (`^[a-z][a-z0-9_-]{0,63}$`), an app\n * without a name, or a return, terms or privacy URL that isn't `https://` (`http://`\n * for localhost only);\n * - no `app`, or one that isn't in `apps`. @internal\n */\nexport function payPageConfigProblems(config: unknown): string[] {\n if (typeof config !== \"object\" || config === null) return [\"the configuration is not an object\"];\n const c = config as Record<string, unknown>;\n const problems: string[] = [];\n const { token, environment } = c;\n if (environment !== null && environment !== undefined && environment !== \"sandbox\" && environment !== \"live\") {\n problems.push(`environment must be \"sandbox\" or \"live\", not ${JSON.stringify(environment)}`);\n }\n if (token !== null && token !== undefined) {\n if (typeof token !== \"string\" || token === \"\") {\n problems.push(\"token must be Paddle's client-side token, or null while billing is off\");\n } else if (environment !== \"sandbox\" && environment !== \"live\") {\n problems.push(\"a token needs its environment: sandbox or live\");\n } else if (!token.startsWith(TOKEN_PREFIX[environment]) || token.length === TOKEN_PREFIX[environment].length) {\n const other = environment === \"sandbox\" ? \"live\" : \"sandbox\";\n problems.push(\n token.startsWith(TOKEN_PREFIX[other])\n ? `the token is a ${other} token (${TOKEN_PREFIX[other]}…), but the environment is ${environment}`\n : `a ${environment} token starts with ${TOKEN_PREFIX[environment]}`,\n );\n }\n }\n const { apps, app } = c;\n if (typeof apps !== \"object\" || apps === null || Array.isArray(apps) || Object.keys(apps).length === 0) {\n problems.push(\"apps must map each app's tag to its name and URLs\");\n } else {\n for (const [tag, entry] of Object.entries(apps)) {\n if (!APP_TAG.test(tag)) problems.push(`apps: ${JSON.stringify(tag)} is not an app tag (a-z, 0-9, _ and -)`);\n else problems.push(...appProblems(tag, entry));\n }\n }\n if (typeof app !== \"string\" || app === \"\") problems.push(\"app is missing: the host's own app tag\");\n else if (!hasApp(apps, app)) {\n const known = typeof apps === \"object\" && apps !== null ? Object.keys(apps).join(\", \") : \"\";\n problems.push(`app ${JSON.stringify(app)} is not in apps${known ? ` (${known})` : \"\"}`);\n }\n return problems;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -17,7 +17,7 @@ function appAt(name, site, returnPath) {
|
|
|
17
17
|
}
|
|
18
18
|
var PAY_PAGE_APPS = Object.freeze({
|
|
19
19
|
keksdose: Object.freeze(appAt("Keksdose", "https://keksdose.app", "/settings/subscription")),
|
|
20
|
-
kastlan: Object.freeze(appAt("
|
|
20
|
+
kastlan: Object.freeze(appAt("Kastlan", "https://kastlan.app", "/admin/billing")),
|
|
21
21
|
kurvenschmiede: Object.freeze(appAt("Kurvenschmiede", "https://kurvenschmiede.app", "/settings/subscription"))
|
|
22
22
|
});
|
|
23
23
|
var TOKEN_PREFIX = { sandbox: "test_", live: "live_" };
|
package/dist/eslint/index.js
CHANGED
|
@@ -47,7 +47,7 @@ const roleUtilities = {
|
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
49
|
};
|
|
50
|
-
const VERSION = true ? "0.39.
|
|
50
|
+
const VERSION = true ? "0.39.1" : "0.0.0-source";
|
|
51
51
|
const recommended = {
|
|
52
52
|
name: "@eifi1/ui-kit/recommended",
|
|
53
53
|
// Not the tests, as check:tokens skips the kit's: a test names the old class to assert it
|
package/dist/pay/pay.js
CHANGED
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
}
|
|
44
44
|
var PAY_PAGE_APPS = Object.freeze({
|
|
45
45
|
keksdose: Object.freeze(appAt("Keksdose", "https://keksdose.app", "/settings/subscription")),
|
|
46
|
-
kastlan: Object.freeze(appAt("
|
|
46
|
+
kastlan: Object.freeze(appAt("Kastlan", "https://kastlan.app", "/admin/billing")),
|
|
47
47
|
kurvenschmiede: Object.freeze(appAt("Kurvenschmiede", "https://kurvenschmiede.app", "/settings/subscription"))
|
|
48
48
|
});
|
|
49
49
|
var TOKEN_PREFIX = { sandbox: "test_", live: "live_" };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eifi1/ui-kit",
|
|
3
|
-
"version": "0.39.
|
|
3
|
+
"version": "0.39.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "App-agnostic shared design system: domain-free React components, theme and tokens.",
|
|
6
6
|
"_sideEffects": "The barrel (src/index.ts) re-exports EVERY component, including components/chart, which statically imports recharts. Without this field a bundler must assume each of those modules has side effects, so it cannot drop the unused ones: importing a Button from '@eifi1/ui-kit' dragged all 433KB of recharts into the consumer's ENTRY chunk, and Keksdose was preloading it on every page load, login included. Nothing in src/ imports for side effects (no bare imports, no CSS imports from JS, no module-level global writes) — the token stylesheet is its own './tokens.css' export — so this is safe. CSS is still listed so any future stylesheet survives. Re-checked when `flag-icons` left `dependencies` and tsup started emitting a `\"use client\"` banner: neither touches this. The flag stylesheet was never imported from JS in the first place (that was the finding), and a directive prologue is a string literal every bundler lifts out before it decides whether a module is inert.",
|
|
@@ -76,9 +76,9 @@ export interface PlanSwitchPreview {
|
|
|
76
76
|
* charges. */
|
|
77
77
|
now_minor: number;
|
|
78
78
|
/** The next regular charge, in minor units. */
|
|
79
|
-
next_minor
|
|
79
|
+
next_minor?: number | null;
|
|
80
80
|
/** When it is charged; null when Paddle names no date. */
|
|
81
|
-
next_billed_at
|
|
81
|
+
next_billed_at?: string | null;
|
|
82
82
|
/** The plan and period switched to. */
|
|
83
83
|
plan: string;
|
|
84
84
|
interval: BillingInterval;
|
package/src/billing/pay-page.ts
CHANGED
|
@@ -208,7 +208,7 @@ function appAt(name: string, site: string, returnPath: string): PayPageApp {
|
|
|
208
208
|
*/
|
|
209
209
|
export const PAY_PAGE_APPS: Readonly<Record<string, Readonly<PayPageApp>>> = Object.freeze({
|
|
210
210
|
keksdose: Object.freeze(appAt("Keksdose", "https://keksdose.app", "/settings/subscription")),
|
|
211
|
-
kastlan: Object.freeze(appAt("
|
|
211
|
+
kastlan: Object.freeze(appAt("Kastlan", "https://kastlan.app", "/admin/billing")),
|
|
212
212
|
kurvenschmiede: Object.freeze(appAt("Kurvenschmiede", "https://kurvenschmiede.app", "/settings/subscription")),
|
|
213
213
|
});
|
|
214
214
|
|