@softure-ai/billing 0.0.0-stage → 0.1.6

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.
Files changed (295) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/LICENSE +21 -0
  3. package/README.md +695 -2
  4. package/dist/calendar.d.ts +26 -0
  5. package/dist/calendar.d.ts.map +1 -0
  6. package/dist/calendar.js +81 -0
  7. package/dist/calendar.js.map +1 -0
  8. package/dist/contract.d.ts +187 -0
  9. package/dist/contract.d.ts.map +1 -0
  10. package/dist/contract.js +6 -0
  11. package/dist/contract.js.map +1 -0
  12. package/dist/currency-digits.d.ts +3 -0
  13. package/dist/currency-digits.d.ts.map +1 -0
  14. package/dist/currency-digits.js +32 -0
  15. package/dist/currency-digits.js.map +1 -0
  16. package/dist/entitlement.d.ts +25 -0
  17. package/dist/entitlement.d.ts.map +1 -0
  18. package/dist/entitlement.js +75 -0
  19. package/dist/entitlement.js.map +1 -0
  20. package/dist/fields.d.ts +25 -0
  21. package/dist/fields.d.ts.map +1 -0
  22. package/dist/fields.js +27 -0
  23. package/dist/fields.js.map +1 -0
  24. package/dist/index.d.ts +274 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +73 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/invoice.d.ts +29 -0
  29. package/dist/invoice.d.ts.map +1 -0
  30. package/dist/invoice.js +35 -0
  31. package/dist/invoice.js.map +1 -0
  32. package/dist/mailing/index.d.ts +2 -0
  33. package/dist/mailing/index.d.ts.map +1 -0
  34. package/dist/mailing/index.js +4 -0
  35. package/dist/mailing/index.js.map +1 -0
  36. package/dist/mailing/reminder-mail.d.ts +50 -0
  37. package/dist/mailing/reminder-mail.d.ts.map +1 -0
  38. package/dist/mailing/reminder-mail.js +71 -0
  39. package/dist/mailing/reminder-mail.js.map +1 -0
  40. package/dist/manual.d.ts +12 -0
  41. package/dist/manual.d.ts.map +1 -0
  42. package/dist/manual.js +22 -0
  43. package/dist/manual.js.map +1 -0
  44. package/dist/messages/en.d.ts +176 -0
  45. package/dist/messages/en.d.ts.map +1 -0
  46. package/dist/messages/en.js +151 -0
  47. package/dist/messages/en.js.map +1 -0
  48. package/dist/messages/index.d.ts +359 -0
  49. package/dist/messages/index.d.ts.map +1 -0
  50. package/dist/messages/index.js +14 -0
  51. package/dist/messages/index.js.map +1 -0
  52. package/dist/messages/pl.d.ts +3 -0
  53. package/dist/messages/pl.d.ts.map +1 -0
  54. package/dist/messages/pl.js +151 -0
  55. package/dist/messages/pl.js.map +1 -0
  56. package/dist/next/access.d.ts +16 -0
  57. package/dist/next/access.d.ts.map +1 -0
  58. package/dist/next/access.js +24 -0
  59. package/dist/next/access.js.map +1 -0
  60. package/dist/next/actions.d.ts +24 -0
  61. package/dist/next/actions.d.ts.map +1 -0
  62. package/dist/next/actions.js +158 -0
  63. package/dist/next/actions.js.map +1 -0
  64. package/dist/next/context.d.ts +4 -0
  65. package/dist/next/context.d.ts.map +1 -0
  66. package/dist/next/context.js +17 -0
  67. package/dist/next/context.js.map +1 -0
  68. package/dist/next/current-entitlement.d.ts +18 -0
  69. package/dist/next/current-entitlement.d.ts.map +1 -0
  70. package/dist/next/current-entitlement.js +26 -0
  71. package/dist/next/current-entitlement.js.map +1 -0
  72. package/dist/next/index.d.ts +8 -0
  73. package/dist/next/index.d.ts.map +1 -0
  74. package/dist/next/index.js +12 -0
  75. package/dist/next/index.js.map +1 -0
  76. package/dist/next/pages.d.ts +24 -0
  77. package/dist/next/pages.d.ts.map +1 -0
  78. package/dist/next/pages.js +166 -0
  79. package/dist/next/pages.js.map +1 -0
  80. package/dist/next/pricing.d.ts +12 -0
  81. package/dist/next/pricing.d.ts.map +1 -0
  82. package/dist/next/pricing.js +20 -0
  83. package/dist/next/pricing.js.map +1 -0
  84. package/dist/next/route.d.ts +11 -0
  85. package/dist/next/route.d.ts.map +1 -0
  86. package/dist/next/route.js +56 -0
  87. package/dist/next/route.js.map +1 -0
  88. package/dist/options.d.ts +85 -0
  89. package/dist/options.d.ts.map +1 -0
  90. package/dist/options.js +121 -0
  91. package/dist/options.js.map +1 -0
  92. package/dist/payment.d.ts +61 -0
  93. package/dist/payment.d.ts.map +1 -0
  94. package/dist/payment.js +12 -0
  95. package/dist/payment.js.map +1 -0
  96. package/dist/plans.d.ts +20 -0
  97. package/dist/plans.d.ts.map +1 -0
  98. package/dist/plans.js +53 -0
  99. package/dist/plans.js.map +1 -0
  100. package/dist/price.d.ts +12 -0
  101. package/dist/price.d.ts.map +1 -0
  102. package/dist/price.js +37 -0
  103. package/dist/price.js.map +1 -0
  104. package/dist/refund.d.ts +70 -0
  105. package/dist/refund.d.ts.map +1 -0
  106. package/dist/refund.js +109 -0
  107. package/dist/refund.js.map +1 -0
  108. package/dist/reminder.d.ts +30 -0
  109. package/dist/reminder.d.ts.map +1 -0
  110. package/dist/reminder.js +34 -0
  111. package/dist/reminder.js.map +1 -0
  112. package/dist/schema.d.ts +1023 -0
  113. package/dist/schema.d.ts.map +1 -0
  114. package/dist/schema.js +77 -0
  115. package/dist/schema.js.map +1 -0
  116. package/dist/scripts/entitlement-scripts.d.ts +17 -0
  117. package/dist/scripts/entitlement-scripts.d.ts.map +1 -0
  118. package/dist/scripts/entitlement-scripts.js +167 -0
  119. package/dist/scripts/entitlement-scripts.js.map +1 -0
  120. package/dist/scripts/index.d.ts +3 -0
  121. package/dist/scripts/index.d.ts.map +1 -0
  122. package/dist/scripts/index.js +5 -0
  123. package/dist/scripts/index.js.map +1 -0
  124. package/dist/scripts/plan-scripts.d.ts +19 -0
  125. package/dist/scripts/plan-scripts.d.ts.map +1 -0
  126. package/dist/scripts/plan-scripts.js +106 -0
  127. package/dist/scripts/plan-scripts.js.map +1 -0
  128. package/dist/server/entitlements.d.ts +69 -0
  129. package/dist/server/entitlements.d.ts.map +1 -0
  130. package/dist/server/entitlements.js +202 -0
  131. package/dist/server/entitlements.js.map +1 -0
  132. package/dist/server/grants.d.ts +74 -0
  133. package/dist/server/grants.d.ts.map +1 -0
  134. package/dist/server/grants.js +174 -0
  135. package/dist/server/grants.js.map +1 -0
  136. package/dist/server/health.d.ts +3 -0
  137. package/dist/server/health.d.ts.map +1 -0
  138. package/dist/server/health.js +17 -0
  139. package/dist/server/health.js.map +1 -0
  140. package/dist/server/index.d.ts +12 -0
  141. package/dist/server/index.d.ts.map +1 -0
  142. package/dist/server/index.js +14 -0
  143. package/dist/server/index.js.map +1 -0
  144. package/dist/server/options.d.ts +20 -0
  145. package/dist/server/options.d.ts.map +1 -0
  146. package/dist/server/options.js +38 -0
  147. package/dist/server/options.js.map +1 -0
  148. package/dist/server/payments.d.ts +140 -0
  149. package/dist/server/payments.d.ts.map +1 -0
  150. package/dist/server/payments.js +339 -0
  151. package/dist/server/payments.js.map +1 -0
  152. package/dist/server/plans.d.ts +50 -0
  153. package/dist/server/plans.d.ts.map +1 -0
  154. package/dist/server/plans.js +129 -0
  155. package/dist/server/plans.js.map +1 -0
  156. package/dist/server/privacy.d.ts +77 -0
  157. package/dist/server/privacy.d.ts.map +1 -0
  158. package/dist/server/privacy.js +110 -0
  159. package/dist/server/privacy.js.map +1 -0
  160. package/dist/server/reminders.d.ts +20 -0
  161. package/dist/server/reminders.d.ts.map +1 -0
  162. package/dist/server/reminders.js +85 -0
  163. package/dist/server/reminders.js.map +1 -0
  164. package/dist/server/requests.d.ts +80 -0
  165. package/dist/server/requests.d.ts.map +1 -0
  166. package/dist/server/requests.js +155 -0
  167. package/dist/server/requests.js.map +1 -0
  168. package/dist/server/setup.d.ts +9 -0
  169. package/dist/server/setup.d.ts.map +1 -0
  170. package/dist/server/setup.js +34 -0
  171. package/dist/server/setup.js.map +1 -0
  172. package/dist/server/take-back.d.ts +80 -0
  173. package/dist/server/take-back.d.ts.map +1 -0
  174. package/dist/server/take-back.js +138 -0
  175. package/dist/server/take-back.js.map +1 -0
  176. package/dist/server/user-id.d.ts +4 -0
  177. package/dist/server/user-id.d.ts.map +1 -0
  178. package/dist/server/user-id.js +11 -0
  179. package/dist/server/user-id.js.map +1 -0
  180. package/dist/stripe-currency.d.ts +27 -0
  181. package/dist/stripe-currency.d.ts.map +1 -0
  182. package/dist/stripe-currency.js +57 -0
  183. package/dist/stripe-currency.js.map +1 -0
  184. package/dist/stripe-webhook.d.ts +101 -0
  185. package/dist/stripe-webhook.d.ts.map +1 -0
  186. package/dist/stripe-webhook.js +209 -0
  187. package/dist/stripe-webhook.js.map +1 -0
  188. package/dist/stripe.d.ts +25 -0
  189. package/dist/stripe.d.ts.map +1 -0
  190. package/dist/stripe.js +116 -0
  191. package/dist/stripe.js.map +1 -0
  192. package/dist/ui/access-badge.d.ts +16 -0
  193. package/dist/ui/access-badge.d.ts.map +1 -0
  194. package/dist/ui/access-badge.js +43 -0
  195. package/dist/ui/access-badge.js.map +1 -0
  196. package/dist/ui/access-notice.d.ts +20 -0
  197. package/dist/ui/access-notice.d.ts.map +1 -0
  198. package/dist/ui/access-notice.js +41 -0
  199. package/dist/ui/access-notice.js.map +1 -0
  200. package/dist/ui/format.d.ts +11 -0
  201. package/dist/ui/format.d.ts.map +1 -0
  202. package/dist/ui/format.js +21 -0
  203. package/dist/ui/format.js.map +1 -0
  204. package/dist/ui/grant-form.d.ts +18 -0
  205. package/dist/ui/grant-form.d.ts.map +1 -0
  206. package/dist/ui/grant-form.js +23 -0
  207. package/dist/ui/grant-form.js.map +1 -0
  208. package/dist/ui/grant-history.d.ts +39 -0
  209. package/dist/ui/grant-history.d.ts.map +1 -0
  210. package/dist/ui/grant-history.js +36 -0
  211. package/dist/ui/grant-history.js.map +1 -0
  212. package/dist/ui/index.d.ts +9 -0
  213. package/dist/ui/index.d.ts.map +1 -0
  214. package/dist/ui/index.js +12 -0
  215. package/dist/ui/index.js.map +1 -0
  216. package/dist/ui/payment-form.d.ts +23 -0
  217. package/dist/ui/payment-form.d.ts.map +1 -0
  218. package/dist/ui/payment-form.js +34 -0
  219. package/dist/ui/payment-form.js.map +1 -0
  220. package/dist/ui/payment-requests.d.ts +30 -0
  221. package/dist/ui/payment-requests.d.ts.map +1 -0
  222. package/dist/ui/payment-requests.js +31 -0
  223. package/dist/ui/payment-requests.js.map +1 -0
  224. package/dist/ui/pricing-tiles.d.ts +22 -0
  225. package/dist/ui/pricing-tiles.d.ts.map +1 -0
  226. package/dist/ui/pricing-tiles.js +40 -0
  227. package/dist/ui/pricing-tiles.js.map +1 -0
  228. package/migrations/0001_create_entitlements.sql +16 -0
  229. package/migrations/0002_create_payments.sql +30 -0
  230. package/migrations/0003_record_payment_grants.sql +25 -0
  231. package/migrations/0004_create_requests_and_grants.sql +58 -0
  232. package/migrations/0005_record_refunded_amounts.sql +18 -0
  233. package/migrations/0006_record_request_handover_and_prices.sql +38 -0
  234. package/migrations/0007_record_failed_refunds.sql +27 -0
  235. package/migrations/0008_record_request_handover_claims.sql +9 -0
  236. package/migrations/0009_record_pending_charge_states.sql +16 -0
  237. package/module.json +23 -0
  238. package/package.json +85 -4
  239. package/src/calendar.ts +90 -0
  240. package/src/contract.ts +181 -0
  241. package/src/currency-digits.ts +37 -0
  242. package/src/entitlement.ts +84 -0
  243. package/src/fields.ts +37 -0
  244. package/src/index.ts +163 -0
  245. package/src/invoice.ts +58 -0
  246. package/src/mailing/index.ts +11 -0
  247. package/src/mailing/reminder-mail.ts +108 -0
  248. package/src/manual.ts +31 -0
  249. package/src/messages/en.ts +150 -0
  250. package/src/messages/index.ts +18 -0
  251. package/src/messages/pl.ts +152 -0
  252. package/src/next/access.tsx +55 -0
  253. package/src/next/actions.ts +176 -0
  254. package/src/next/context.ts +18 -0
  255. package/src/next/current-entitlement.ts +36 -0
  256. package/src/next/index.ts +11 -0
  257. package/src/next/next-modules.d.ts +21 -0
  258. package/src/next/pages.tsx +267 -0
  259. package/src/next/pricing.tsx +39 -0
  260. package/src/next/route.ts +57 -0
  261. package/src/options.ts +128 -0
  262. package/src/payment.ts +77 -0
  263. package/src/plans.ts +63 -0
  264. package/src/price.ts +50 -0
  265. package/src/refund.ts +135 -0
  266. package/src/reminder.ts +52 -0
  267. package/src/schema.ts +86 -0
  268. package/src/scripts/entitlement-scripts.ts +188 -0
  269. package/src/scripts/index.ts +11 -0
  270. package/src/scripts/plan-scripts.ts +143 -0
  271. package/src/server/entitlements.ts +227 -0
  272. package/src/server/grants.ts +227 -0
  273. package/src/server/health.ts +18 -0
  274. package/src/server/index.ts +83 -0
  275. package/src/server/options.ts +52 -0
  276. package/src/server/payments.ts +434 -0
  277. package/src/server/plans.ts +143 -0
  278. package/src/server/privacy.ts +189 -0
  279. package/src/server/reminders.ts +113 -0
  280. package/src/server/requests.ts +201 -0
  281. package/src/server/setup.ts +37 -0
  282. package/src/server/take-back.ts +190 -0
  283. package/src/server/user-id.ts +12 -0
  284. package/src/stripe-currency.ts +65 -0
  285. package/src/stripe-webhook.ts +278 -0
  286. package/src/stripe.ts +130 -0
  287. package/src/ui/access-badge.tsx +64 -0
  288. package/src/ui/access-notice.tsx +73 -0
  289. package/src/ui/format.ts +25 -0
  290. package/src/ui/grant-form.tsx +69 -0
  291. package/src/ui/grant-history.tsx +127 -0
  292. package/src/ui/index.ts +25 -0
  293. package/src/ui/payment-form.tsx +111 -0
  294. package/src/ui/payment-requests.tsx +126 -0
  295. package/src/ui/pricing-tiles.tsx +105 -0
@@ -0,0 +1,26 @@
1
+ /** The calendar day of an instant in a time zone, as a day count; two of them subtract to days. */
2
+ export declare function getDayNumber(instant: Date, timezone: string): number;
3
+ /**
4
+ * The instant a local day starts (00:00 there). Two passes settle the offset across a DST change.
5
+ * In a zone that skips midnight itself (America/Santiago, America/Havana) 00:00 does not exist and
6
+ * the passes land in the previous day, so the result moves on by hours to the first instant of
7
+ * the day: 01:00 after a one-hour gap.
8
+ */
9
+ export declare function getStartOfDay(dayNumber: number, timezone: string): Date;
10
+ /**
11
+ * The end of a trial of `days` days begun at `start`: the start of the local day `days` days after
12
+ * the start day. The start day counts as the first day, so a 14-day trial begun on 3 October ends
13
+ * when 17 October begins. Zero days ends it at the start of the start day: no trial.
14
+ */
15
+ export declare function getTrialEnd(start: Date, days: number, timezone: string): Date;
16
+ /**
17
+ * Local calendar days of access left before `end`, today included: 1 on the last day. Assumes
18
+ * `end` is after `now`.
19
+ */
20
+ export declare function getDaysLeft(end: Date, now: Date, timezone: string): number;
21
+ /**
22
+ * The day number (as `getDayNumber` counts) of a `YYYY-MM-DD` calendar day, or null when the text
23
+ * is not one (`2026-1-1`, `2026-02-30`).
24
+ */
25
+ export declare function parseDay(day: string): number | null;
26
+ //# sourceMappingURL=calendar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"calendar.d.ts","sourceRoot":"","sources":["../src/calendar.ts"],"names":[],"mappings":"AAmCA,mGAAmG;AACnG,wBAAgB,YAAY,CAAC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEpE;AAID;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAQvE;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAE7E;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE1E;AAID;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQnD"}
@@ -0,0 +1,81 @@
1
+ // Calendar days in the app's IANA time zone, without a date library: trials end at the start of a
2
+ // local day and "days left" counts local days, as people do.
3
+ const DAY_MS = 24 * 60 * 60 * 1000;
4
+ const formatters = new Map();
5
+ function getFormatter(timezone) {
6
+ let format = formatters.get(timezone);
7
+ if (format === undefined) {
8
+ format = new Intl.DateTimeFormat("en-CA", {
9
+ timeZone: timezone,
10
+ hourCycle: "h23",
11
+ year: "numeric",
12
+ month: "2-digit",
13
+ day: "2-digit",
14
+ hour: "2-digit",
15
+ minute: "2-digit",
16
+ second: "2-digit",
17
+ });
18
+ formatters.set(timezone, format);
19
+ }
20
+ return format;
21
+ }
22
+ /** The local wall-clock time of an instant, read as if it were UTC (milliseconds). */
23
+ function getLocalWallTime(instant, timezone) {
24
+ const parts = Object.fromEntries(getFormatter(timezone)
25
+ .formatToParts(instant)
26
+ .map((part) => [part.type, part.value]));
27
+ return Date.UTC(Number(parts.year), Number(parts.month) - 1, Number(parts.day), Number(parts.hour), Number(parts.minute), Number(parts.second));
28
+ }
29
+ /** The calendar day of an instant in a time zone, as a day count; two of them subtract to days. */
30
+ export function getDayNumber(instant, timezone) {
31
+ return Math.floor(getLocalWallTime(instant.getTime(), timezone) / DAY_MS);
32
+ }
33
+ const HOUR_MS = 60 * 60 * 1000;
34
+ /**
35
+ * The instant a local day starts (00:00 there). Two passes settle the offset across a DST change.
36
+ * In a zone that skips midnight itself (America/Santiago, America/Havana) 00:00 does not exist and
37
+ * the passes land in the previous day, so the result moves on by hours to the first instant of
38
+ * the day: 01:00 after a one-hour gap.
39
+ */
40
+ export function getStartOfDay(dayNumber, timezone) {
41
+ const wallTime = dayNumber * DAY_MS;
42
+ let instant = wallTime;
43
+ for (let pass = 0; pass < 2; pass += 1)
44
+ instant = wallTime - (getLocalWallTime(instant, timezone) - instant);
45
+ for (let step = 0; step < 3 && getDayNumber(new Date(instant), timezone) < dayNumber; step += 1) {
46
+ instant = Math.floor(instant / HOUR_MS) * HOUR_MS + HOUR_MS;
47
+ }
48
+ return new Date(instant);
49
+ }
50
+ /**
51
+ * The end of a trial of `days` days begun at `start`: the start of the local day `days` days after
52
+ * the start day. The start day counts as the first day, so a 14-day trial begun on 3 October ends
53
+ * when 17 October begins. Zero days ends it at the start of the start day: no trial.
54
+ */
55
+ export function getTrialEnd(start, days, timezone) {
56
+ return getStartOfDay(getDayNumber(start, timezone) + days, timezone);
57
+ }
58
+ /**
59
+ * Local calendar days of access left before `end`, today included: 1 on the last day. Assumes
60
+ * `end` is after `now`.
61
+ */
62
+ export function getDaysLeft(end, now, timezone) {
63
+ return getDayNumber(new Date(end.getTime() - 1), timezone) - getDayNumber(now, timezone) + 1;
64
+ }
65
+ const DAY_PATTERN = /^(\d{4})-(\d{2})-(\d{2})$/;
66
+ /**
67
+ * The day number (as `getDayNumber` counts) of a `YYYY-MM-DD` calendar day, or null when the text
68
+ * is not one (`2026-1-1`, `2026-02-30`).
69
+ */
70
+ export function parseDay(day) {
71
+ const match = DAY_PATTERN.exec(day);
72
+ if (match === null)
73
+ return null;
74
+ const [year, month, date] = [Number(match[1]), Number(match[2]), Number(match[3])];
75
+ const wallTime = Date.UTC(year, month - 1, date);
76
+ const parsed = new Date(wallTime);
77
+ if (parsed.getUTCFullYear() !== year || parsed.getUTCMonth() !== month - 1 || parsed.getUTCDate() !== date)
78
+ return null;
79
+ return wallTime / DAY_MS;
80
+ }
81
+ //# sourceMappingURL=calendar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"calendar.js","sourceRoot":"","sources":["../src/calendar.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,187 @@
1
+ import type { CoreErrorCode, Locale } from "@softure-ai/core";
2
+ import type { InvoiceFieldErrorCode } from "./invoice.js";
3
+ export type BillingErrorCode =
4
+ /** The account may read but not write: its trial or its paid access ended. */
5
+ "billing.read_only"
6
+ /** No account has this id. */
7
+ | "billing.account_unknown"
8
+ /** A grant or a trial extension that ends now or earlier. */
9
+ | "billing.end_not_in_future";
10
+ /** Every code a guarded write can show: the guard's own and the generic ones. */
11
+ export type BillingFormErrorCode = BillingErrorCode | CoreErrorCode;
12
+ export declare const ENTITLEMENT_STATUSES: readonly ["trial", "paid", "read_only"];
13
+ export type EntitlementStatus = (typeof ENTITLEMENT_STATUSES)[number];
14
+ /** What is stored per account (or derived for an account without a row). */
15
+ export interface EntitlementRecord {
16
+ /** The first instant the trial no longer covers. */
17
+ readonly trialEndsAt: Date;
18
+ /**
19
+ * The first instant dated paid access no longer covers; null when never paid or revoked. Kept
20
+ * under lifetime access, so a refunded lifetime falls back to the periods bought beside it.
21
+ */
22
+ readonly paidUntil: Date | null;
23
+ /** Paid access without an end; it wins over `paidUntil`. */
24
+ readonly isLifetime: boolean;
25
+ }
26
+ /** Where an account stands at one instant: the state machine's answer. */
27
+ export type Entitlement = {
28
+ readonly status: "trial";
29
+ readonly endsAt: Date;
30
+ /** Calendar days of access left in the app's time zone, today included. */
31
+ readonly daysLeft: number;
32
+ /** Inside the trial reminder window: time to show the notice. */
33
+ readonly isEnding: boolean;
34
+ } | {
35
+ readonly status: "paid";
36
+ /** Null for lifetime access. */
37
+ readonly endsAt: Date | null;
38
+ /** Null for lifetime access. */
39
+ readonly daysLeft: number | null;
40
+ /** Inside the paid reminder window; never for lifetime access. */
41
+ readonly isEnding: boolean;
42
+ } | {
43
+ readonly status: "read_only";
44
+ /** When write access ended. */
45
+ readonly since: Date;
46
+ /** Which access ended last. */
47
+ readonly reason: "trial_ended" | "paid_ended";
48
+ };
49
+ /** A change to an account's entitlement, applied by `changeEntitlement`. */
50
+ export type EntitlementEvent =
51
+ /** Paid access until `until`; never shortens a later end already granted. */
52
+ {
53
+ readonly type: "grant";
54
+ readonly until: Date;
55
+ }
56
+ /** Paid access without an end; the dated end stays beside it. */
57
+ | {
58
+ readonly type: "grant_lifetime";
59
+ }
60
+ /** Removes all paid access (a mistaken grant), lifetime included; the trial stays as it was. */
61
+ | {
62
+ readonly type: "revoke";
63
+ }
64
+ /**
65
+ * Moves dated paid access back to end at `until` (a refunded period); never lengthens it. An end
66
+ * at or before the trial's end drops dated paid access: the account is back on its trial.
67
+ */
68
+ | {
69
+ readonly type: "shorten";
70
+ readonly until: Date;
71
+ }
72
+ /** Ends lifetime access (a refunded lifetime); dated paid access stays. */
73
+ | {
74
+ readonly type: "end_lifetime";
75
+ }
76
+ /** Moves the trial end to `until`; never shortens it. */
77
+ | {
78
+ readonly type: "extend_trial";
79
+ readonly until: Date;
80
+ }
81
+ /**
82
+ * Merges a record carried over from another system (`importEntitlement`): each end only moves
83
+ * later and lifetime only turns on, so an import never takes access away and a repeat changes
84
+ * nothing. Ends may lie in the past: an ended trial or period is recorded as it was. Null keeps
85
+ * what the account has.
86
+ */
87
+ | {
88
+ readonly type: "import";
89
+ readonly trialEndsAt: Date | null;
90
+ readonly paidUntil: Date | null;
91
+ readonly isLifetime: boolean;
92
+ };
93
+ /** What one payment of a plan added to an account, stored with the payment so a refund takes back only that. */
94
+ export type PaymentGrant =
95
+ /** A paid period from where access ended (or the payment's instant) to the period's end. */
96
+ {
97
+ readonly kind: "period";
98
+ readonly from: Date;
99
+ readonly until: Date;
100
+ } | {
101
+ readonly kind: "lifetime";
102
+ };
103
+ /** Copy per locale; a locale without its own text falls back to `en`. */
104
+ export type LocalizedText = Readonly<Partial<Record<Locale, string>>>;
105
+ export declare const PERIOD_UNITS: readonly ["day", "week", "month", "year"];
106
+ export type PeriodUnit = (typeof PERIOD_UNITS)[number];
107
+ /** How long one payment of a plan gives access: `count` units, or for good. */
108
+ export type PlanPeriod = {
109
+ readonly unit: PeriodUnit;
110
+ readonly count: number;
111
+ } | {
112
+ readonly unit: "lifetime";
113
+ };
114
+ /** A price in the currency's minor unit (cents, grosze; whole yen for JPY), as payment providers take it. */
115
+ export interface PlanPrice {
116
+ readonly amount: number;
117
+ /** ISO 4217, upper case, e.g. `PLN`, `EUR`. */
118
+ readonly currency: string;
119
+ }
120
+ /** A plan from `billing({ plans })`. */
121
+ export interface Plan {
122
+ /** Kebab-case, unique; the payment page and the payment providers name the plan by it. */
123
+ readonly id: string;
124
+ readonly name: LocalizedText;
125
+ readonly description?: LocalizedText;
126
+ readonly price: PlanPrice;
127
+ readonly period: PlanPeriod;
128
+ /** What the plan includes, one line each, in the order the tile lists them. */
129
+ readonly features: readonly LocalizedText[];
130
+ /** Drawn as the recommended plan. */
131
+ readonly isFeatured: boolean;
132
+ }
133
+ /** Why a payment could not start. */
134
+ export type PaymentErrorCode =
135
+ /** A plan id the config does not declare: a tampered or outdated form. */
136
+ "billing.plan_unknown"
137
+ /** An invoice field is missing, too long or has control characters; `fieldErrors` says which. */
138
+ | "billing.invoice_details_invalid"
139
+ /** The payment provider refused or failed; nothing was charged or requested. */
140
+ | "billing.payment_failed"
141
+ /** The account has lifetime access: there is nothing left to pay for or grant. */
142
+ | "billing.lifetime_active";
143
+ /** Every code the payment form can show, the invoice fields' own included. */
144
+ export type PaymentFormErrorCode = PaymentErrorCode | InvoiceFieldErrorCode | "security.rate_limited" | CoreErrorCode;
145
+ /** Every code the admin grant form can show. */
146
+ export type GrantFormErrorCode = "billing.plan_unknown" | "billing.account_unknown" | "billing.lifetime_active" | "auth.forbidden" | CoreErrorCode;
147
+ /** Why an admin could not act on a request or a manual grant. */
148
+ export type AdminErrorCode =
149
+ /** The request was granted or dismissed already, or never existed. */
150
+ "billing.request_closed"
151
+ /** The grant was revoked already, or never existed. */
152
+ | "billing.grant_revoked";
153
+ /** Every code the admin page's buttons and account lookup can show. */
154
+ export type AdminActionErrorCode = AdminErrorCode | GrantFormErrorCode;
155
+ /** What the payment action returns to its form (`useActionState`). */
156
+ export interface PaymentFormState {
157
+ readonly status: "idle" | "requested" | "error";
158
+ readonly error?: PaymentFormErrorCode;
159
+ /** Errors of single invoice fields, by field name. */
160
+ readonly fieldErrors?: Readonly<Record<string, PaymentFormErrorCode>>;
161
+ /** The invoice details as typed, to fill the fields again after an error. */
162
+ readonly values?: Readonly<Record<string, string>>;
163
+ }
164
+ export declare const INITIAL_PAYMENT_FORM_STATE: PaymentFormState;
165
+ /** What the admin grant action returns to its form (`useActionState`). */
166
+ export interface GrantFormState {
167
+ readonly status: "idle" | "granted" | "error";
168
+ readonly error?: GrantFormErrorCode;
169
+ /** After a grant: what the account has now, in the app's copy. */
170
+ readonly notice?: string;
171
+ /** The email and plan as sent, to fill the form again after an error. */
172
+ readonly email?: string;
173
+ readonly planId?: string;
174
+ }
175
+ export declare const INITIAL_GRANT_FORM_STATE: GrantFormState;
176
+ /** What an admin button (grant or dismiss a request, revoke a grant, find an account) returns to its form. */
177
+ export type AdminActionState = {
178
+ readonly status: "idle";
179
+ } | {
180
+ readonly status: "done";
181
+ } | {
182
+ readonly status: "error";
183
+ readonly error: AdminActionErrorCode;
184
+ readonly email?: string;
185
+ };
186
+ export declare const INITIAL_ADMIN_ACTION_STATE: AdminActionState;
187
+ //# sourceMappingURL=contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export const ENTITLEMENT_STATUSES = ["trial", "paid", "read_only"];
2
+ export const PERIOD_UNITS = ["day", "week", "month", "year"];
3
+ export const INITIAL_PAYMENT_FORM_STATE = { status: "idle" };
4
+ export const INITIAL_GRANT_FORM_STATE = { status: "idle" };
5
+ export const INITIAL_ADMIN_ACTION_STATE = { status: "idle" };
6
+ //# sourceMappingURL=contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAgBA,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,CAAU,CAAC;AA8E5E,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAU,CAAC;AAiEtE,MAAM,CAAC,MAAM,0BAA0B,GAAqB,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAa/E,MAAM,CAAC,MAAM,wBAAwB,GAAmB,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAQ3E,MAAM,CAAC,MAAM,0BAA0B,GAAqB,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC"}
@@ -0,0 +1,3 @@
1
+ /** Digits of the minor unit per currency code billing accepts, e.g. `PLN: 2`, `JPY: 0`, `KWD: 3`. */
2
+ export declare const CURRENCY_MINOR_UNIT_DIGITS: Readonly<Record<string, number>>;
3
+ //# sourceMappingURL=currency-digits.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"currency-digits.d.ts","sourceRoot":"","sources":["../src/currency-digits.ts"],"names":[],"mappings":"AA+BA,qGAAqG;AACrG,eAAO,MAAM,0BAA0B,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAIvE,CAAC"}
@@ -0,0 +1,32 @@
1
+ // The minor unit billing pins for each currency it accepts: a plan's `price.amount` counts these
2
+ // units on every runtime, whatever the runtime's `Intl` (CLDR) says. Source: ISO 4217 List One,
3
+ // published 2024-06-25, without fund codes, `N.A.` units (metals, XDR, XTS, XXX ...) and UYW (a wage
4
+ // index unit), plus the two choices below. CLDR differs from ISO on AFN, ALL, IRR, KPW, LAK, LBP,
5
+ // MMK, RSD, SOS, SYP and YER (0 instead of 2) and IQD (0 instead of 3), and its HUF and TWD digits
6
+ // have changed between Node builds; billing follows ISO, as Stripe does for all but MGA.
7
+ /** ISO 4217 codes by the digits of their minor unit. */
8
+ const CODES_BY_DIGITS = {
9
+ 0: [
10
+ "BIF", "CLP", "DJF", "GNF", "ISK", "JPY", "KMF", "KRW", "PYG", "RWF", "UGX", "VND", "VUV", "XAF", "XOF", "XPF",
11
+ // Override: ISO writes 2, but one ariary is five iraimbilanja (not a decimal subunit), and CLDR
12
+ // and Stripe both count it without a minor unit.
13
+ "MGA",
14
+ ],
15
+ 2: [
16
+ "AED", "AFN", "ALL", "AMD", "ANG", "AOA", "ARS", "AUD", "AWG", "AZN", "BAM", "BBD", "BDT", "BGN", "BMD", "BND",
17
+ "BOB", "BRL", "BSD", "BTN", "BWP", "BYN", "BZD", "CAD", "CDF", "CHF", "CNY", "COP", "CRC", "CUC", "CUP", "CVE",
18
+ "CZK", "DKK", "DOP", "DZD", "EGP", "ERN", "ETB", "EUR", "FJD", "FKP", "GBP", "GEL", "GHS", "GIP", "GMD", "GTQ",
19
+ "GYD", "HKD", "HNL", "HTG", "HUF", "IDR", "ILS", "INR", "IRR", "JMD", "KES", "KGS", "KHR", "KPW", "KYD", "KZT",
20
+ "LAK", "LBP", "LKR", "LRD", "LSL", "MAD", "MDL", "MKD", "MMK", "MNT", "MOP", "MRU", "MUR", "MVR", "MWK", "MXN",
21
+ "MYR", "MZN", "NAD", "NGN", "NIO", "NOK", "NPR", "NZD", "PAB", "PEN", "PGK", "PHP", "PKR", "PLN", "QAR", "RON",
22
+ "RSD", "RUB", "SAR", "SBD", "SCR", "SDG", "SEK", "SGD", "SHP", "SLE", "SOS", "SRD", "SSP", "STN", "SVC", "SYP",
23
+ "SZL", "THB", "TJS", "TMT", "TOP", "TRY", "TTD", "TWD", "TZS", "UAH", "USD", "UYU", "UZS", "VED", "VES", "WST",
24
+ "XCD", "YER", "ZAR", "ZMW", "ZWG",
25
+ // Addition: the Caribbean guilder, which replaced ANG on 2025-03-31, after the pinned list.
26
+ "XCG",
27
+ ],
28
+ 3: ["BHD", "IQD", "JOD", "KWD", "LYD", "OMR", "TND"],
29
+ };
30
+ /** Digits of the minor unit per currency code billing accepts, e.g. `PLN: 2`, `JPY: 0`, `KWD: 3`. */
31
+ export const CURRENCY_MINOR_UNIT_DIGITS = Object.freeze(Object.fromEntries(Object.entries(CODES_BY_DIGITS).flatMap(([digits, codes]) => codes.map((code) => [code, Number(digits)]))));
32
+ //# sourceMappingURL=currency-digits.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"currency-digits.js","sourceRoot":"","sources":["../src/currency-digits.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import { type Err, type Ok } from "@softure-ai/core";
2
+ import type { BillingErrorCode, Entitlement, EntitlementEvent, EntitlementRecord } from "./contract.js";
3
+ export interface EntitlementPolicy {
4
+ /** The app's IANA time zone: days left are counted there. */
5
+ readonly timezone: string;
6
+ /** From how many days left a trial counts as ending; 0 never. */
7
+ readonly trialReminderDays: number;
8
+ /** From how many days left paid access counts as ending; 0 never. */
9
+ readonly paidReminderDays: number;
10
+ }
11
+ /**
12
+ * The status of `record` at `now`. Paid access wins over the trial; access covers instants before
13
+ * its end, so at the end itself the account is read-only.
14
+ */
15
+ export declare function resolveEntitlement(record: EntitlementRecord, now: Date, policy: EntitlementPolicy): Entitlement;
16
+ /** Whether an account in this state may write. */
17
+ export declare function hasWriteAccess(entitlement: Entitlement): boolean;
18
+ /**
19
+ * The record after `event` at `now`. Grants and trial extensions must end after `now` and never
20
+ * shorten what the account already has; an import never shortens either, but may record past ends;
21
+ * a revoke ends paid access, lifetime included. The dated end lives on under lifetime access: a
22
+ * grant still extends it, and ending lifetime falls back to it.
23
+ */
24
+ export declare function applyEntitlementEvent(record: EntitlementRecord, event: EntitlementEvent, now: Date): Ok<EntitlementRecord> | Err<BillingErrorCode>;
25
+ //# sourceMappingURL=entitlement.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entitlement.d.ts","sourceRoot":"","sources":["../src/entitlement.ts"],"names":[],"mappings":"AAEA,OAAO,EAAW,KAAK,GAAG,EAAE,KAAK,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAE9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAExG,MAAM,WAAW,iBAAiB;IAChC,6DAA6D;IAC7D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,iEAAiE;IACjE,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,qEAAqE;IACrE,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;CACnC;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,iBAAiB,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,iBAAiB,GAAG,WAAW,CAY/G;AAED,kDAAkD;AAClD,wBAAgB,cAAc,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAEhE;AAYD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,iBAAiB,EAAE,KAAK,EAAE,gBAAgB,EAAE,GAAG,EAAE,IAAI,GAAG,EAAE,CAAC,iBAAiB,CAAC,GAAG,GAAG,CAAC,gBAAgB,CAAC,CA6BlJ"}
@@ -0,0 +1,75 @@
1
+ // The entitlement state machine, pure: a record and an instant give the status, and an event gives
2
+ // the next record. No database and no clock here, so every transition is a plain unit test.
3
+ import { err, ok } from "@softure-ai/core";
4
+ import { getDaysLeft } from "./calendar.js";
5
+ /**
6
+ * The status of `record` at `now`. Paid access wins over the trial; access covers instants before
7
+ * its end, so at the end itself the account is read-only.
8
+ */
9
+ export function resolveEntitlement(record, now, policy) {
10
+ if (record.isLifetime)
11
+ return { status: "paid", endsAt: null, daysLeft: null, isEnding: false };
12
+ if (record.paidUntil !== null && now < record.paidUntil) {
13
+ const daysLeft = getDaysLeft(record.paidUntil, now, policy.timezone);
14
+ return { status: "paid", endsAt: record.paidUntil, daysLeft, isEnding: daysLeft <= policy.paidReminderDays };
15
+ }
16
+ if (now < record.trialEndsAt) {
17
+ const daysLeft = getDaysLeft(record.trialEndsAt, now, policy.timezone);
18
+ return { status: "trial", endsAt: record.trialEndsAt, daysLeft, isEnding: daysLeft <= policy.trialReminderDays };
19
+ }
20
+ if (record.paidUntil !== null && record.paidUntil > record.trialEndsAt)
21
+ return { status: "read_only", since: record.paidUntil, reason: "paid_ended" };
22
+ return { status: "read_only", since: record.trialEndsAt, reason: "trial_ended" };
23
+ }
24
+ /** Whether an account in this state may write. */
25
+ export function hasWriteAccess(entitlement) {
26
+ return entitlement.status !== "read_only";
27
+ }
28
+ /** The later of two instants. */
29
+ function getLater(first, second) {
30
+ return first > second ? first : second;
31
+ }
32
+ /** The earlier of two instants. */
33
+ function getEarlier(first, second) {
34
+ return first < second ? first : second;
35
+ }
36
+ /**
37
+ * The record after `event` at `now`. Grants and trial extensions must end after `now` and never
38
+ * shorten what the account already has; an import never shortens either, but may record past ends;
39
+ * a revoke ends paid access, lifetime included. The dated end lives on under lifetime access: a
40
+ * grant still extends it, and ending lifetime falls back to it.
41
+ */
42
+ export function applyEntitlementEvent(record, event, now) {
43
+ switch (event.type) {
44
+ case "grant": {
45
+ if (event.until <= now)
46
+ return err("billing.end_not_in_future");
47
+ const paidUntil = record.paidUntil === null ? event.until : getLater(record.paidUntil, event.until);
48
+ return ok({ ...record, paidUntil });
49
+ }
50
+ case "grant_lifetime":
51
+ return ok({ ...record, isLifetime: true });
52
+ case "revoke":
53
+ return ok({ ...record, paidUntil: null, isLifetime: false });
54
+ case "shorten": {
55
+ if (record.paidUntil === null)
56
+ return ok(record);
57
+ const paidUntil = getEarlier(record.paidUntil, event.until);
58
+ // Paid access that would end inside the trial adds nothing: the account is back on its trial.
59
+ return ok({ ...record, paidUntil: paidUntil > record.trialEndsAt ? paidUntil : null });
60
+ }
61
+ case "end_lifetime":
62
+ return ok({ ...record, isLifetime: false });
63
+ case "extend_trial":
64
+ if (event.until <= now)
65
+ return err("billing.end_not_in_future");
66
+ return ok({ ...record, trialEndsAt: getLater(record.trialEndsAt, event.until) });
67
+ case "import":
68
+ return ok({
69
+ trialEndsAt: event.trialEndsAt === null ? record.trialEndsAt : getLater(record.trialEndsAt, event.trialEndsAt),
70
+ paidUntil: event.paidUntil === null || (record.paidUntil !== null && record.paidUntil >= event.paidUntil) ? record.paidUntil : event.paidUntil,
71
+ isLifetime: record.isLifetime || event.isLifetime,
72
+ });
73
+ }
74
+ }
75
+ //# sourceMappingURL=entitlement.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entitlement.js","sourceRoot":"","sources":["../src/entitlement.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ export declare const PLAN_FIELD = "plan";
2
+ export declare const EMAIL_FIELD = "email";
3
+ export declare const INVOICE_FIELDS: {
4
+ readonly name: "invoiceName";
5
+ readonly taxId: "invoiceTaxId";
6
+ readonly address: "invoiceAddress";
7
+ };
8
+ export type InvoiceField = (typeof INVOICE_FIELDS)[keyof typeof INVOICE_FIELDS];
9
+ export declare const INVOICE_LIMITS: {
10
+ readonly name: 200;
11
+ readonly taxId: 32;
12
+ readonly address: 500;
13
+ };
14
+ /** The limit of the invoice field with this form name, for the too-long message. */
15
+ export declare function getInvoiceFieldLimit(field: string): number | undefined;
16
+ /** The payment page's parameter a hosted checkout returns with: `?checkout=success` or `?checkout=cancelled`. */
17
+ export declare const CHECKOUT_PARAM = "checkout";
18
+ export declare const CHECKOUT_RESULTS: readonly ["success", "cancelled"];
19
+ export type CheckoutResult = (typeof CHECKOUT_RESULTS)[number];
20
+ /** The admin page's hidden fields: the request a button grants or dismisses, the grant one revokes. */
21
+ export declare const REQUEST_FIELD = "request";
22
+ export declare const GRANT_FIELD = "grant";
23
+ /** The admin page's parameter that names the account whose history it shows (its id, never its email). */
24
+ export declare const ACCOUNT_PARAM = "account";
25
+ //# sourceMappingURL=fields.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fields.d.ts","sourceRoot":"","sources":["../src/fields.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,UAAU,SAAS,CAAC;AACjC,eAAO,MAAM,WAAW,UAAU,CAAC;AAEnC,eAAO,MAAM,cAAc;;;;CAIjB,CAAC;AAEX,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,OAAO,cAAc,CAAC,CAAC;AAEhF,eAAO,MAAM,cAAc;;;;CAIjB,CAAC;AAEX,oFAAoF;AACpF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGtE;AAED,iHAAiH;AACjH,eAAO,MAAM,cAAc,aAAa,CAAC;AAEzC,eAAO,MAAM,gBAAgB,mCAAoC,CAAC;AAElE,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE/D,uGAAuG;AACvG,eAAO,MAAM,aAAa,YAAY,CAAC;AACvC,eAAO,MAAM,WAAW,UAAU,CAAC;AAEnC,0GAA0G;AAC1G,eAAO,MAAM,aAAa,YAAY,CAAC"}
package/dist/fields.js ADDED
@@ -0,0 +1,27 @@
1
+ // Form field names shared by the forms (`/ui`) and the actions that read them (`/next`).
2
+ export const PLAN_FIELD = "plan";
3
+ export const EMAIL_FIELD = "email";
4
+ export const INVOICE_FIELDS = {
5
+ name: "invoiceName",
6
+ taxId: "invoiceTaxId",
7
+ address: "invoiceAddress",
8
+ };
9
+ export const INVOICE_LIMITS = {
10
+ name: 200,
11
+ taxId: 32,
12
+ address: 500,
13
+ };
14
+ /** The limit of the invoice field with this form name, for the too-long message. */
15
+ export function getInvoiceFieldLimit(field) {
16
+ const key = Object.keys(INVOICE_FIELDS).find((name) => INVOICE_FIELDS[name] === field);
17
+ return key === undefined ? undefined : INVOICE_LIMITS[key];
18
+ }
19
+ /** The payment page's parameter a hosted checkout returns with: `?checkout=success` or `?checkout=cancelled`. */
20
+ export const CHECKOUT_PARAM = "checkout";
21
+ export const CHECKOUT_RESULTS = ["success", "cancelled"];
22
+ /** The admin page's hidden fields: the request a button grants or dismisses, the grant one revokes. */
23
+ export const REQUEST_FIELD = "request";
24
+ export const GRANT_FIELD = "grant";
25
+ /** The admin page's parameter that names the account whose history it shows (its id, never its email). */
26
+ export const ACCOUNT_PARAM = "account";
27
+ //# sourceMappingURL=fields.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fields.js","sourceRoot":"","sources":["../src/fields.ts"],"names":[],"mappings":"AAAA,yFAAyF;AACzF,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC;AACjC,MAAM,CAAC,MAAM,WAAW,GAAG,OAAO,CAAC;AAEnC,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,IAAI,EAAE,aAAa;IACnB,KAAK,EAAE,cAAc;IACrB,OAAO,EAAE,gBAAgB;CACjB,CAAC;AAIX,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,IAAI,EAAE,GAAG;IACT,KAAK,EAAE,EAAE;IACT,OAAO,EAAE,GAAG;CACJ,CAAC;AAEX,oFAAoF;AACpF,MAAM,UAAU,oBAAoB,CAAC,KAAa;IAChD,MAAM,GAAG,GAAI,MAAM,CAAC,IAAI,CAAC,cAAc,CAAqC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,CAAC;IAC5H,OAAO,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;AAC7D,CAAC;AAED,iHAAiH;AACjH,MAAM,CAAC,MAAM,cAAc,GAAG,UAAU,CAAC;AAEzC,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,SAAS,EAAE,WAAW,CAAU,CAAC;AAIlE,uGAAuG;AACvG,MAAM,CAAC,MAAM,aAAa,GAAG,SAAS,CAAC;AACvC,MAAM,CAAC,MAAM,WAAW,GAAG,OAAO,CAAC;AAEnC,0GAA0G;AAC1G,MAAM,CAAC,MAAM,aAAa,GAAG,SAAS,CAAC"}