@openrails/billing-ui 0.0.0-stage → 0.230.0
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/CHANGELOG.md +257 -0
- package/LICENSE +21 -0
- package/README.md +211 -2
- package/dist/account/account-billing.d.ts +19 -0
- package/dist/account/cancel-dialog.d.ts +18 -0
- package/dist/account/change-card-dialog.d.ts +14 -0
- package/dist/account/format.d.ts +25 -0
- package/dist/account/notice.d.ts +3 -0
- package/dist/account/payment-history.d.ts +8 -0
- package/dist/account/payment-methods-panel.d.ts +19 -0
- package/dist/account/section.d.ts +25 -0
- package/dist/account/status-badge.d.ts +8 -0
- package/dist/account/subscriptions-panel.d.ts +19 -0
- package/dist/appearance.d.ts +23 -0
- package/dist/authenticate.d.ts +8 -0
- package/dist/checkout-frame.d.ts +17 -0
- package/dist/checkout-page.d.ts +16 -0
- package/dist/checkout.d.ts +24 -0
- package/dist/client/client.d.ts +167 -0
- package/dist/client/errors.d.ts +26 -0
- package/dist/client/generated/openrails-routes.d.ts +2 -0
- package/dist/client/index.d.ts +4 -0
- package/dist/client/types.d.ts +754 -0
- package/dist/client-DhG2jv34.js +771 -0
- package/dist/client-DhG2jv34.js.map +1 -0
- package/dist/client.js +3 -0
- package/dist/components/billing-fields.d.ts +39 -0
- package/dist/components/card-brands.d.ts +6 -0
- package/dist/components/card-fields.d.ts +29 -0
- package/dist/components/ccbill-fields.d.ts +8 -0
- package/dist/components/methods.d.ts +13 -0
- package/dist/components/pay-button.d.ts +6 -0
- package/dist/components/qr.d.ts +6 -0
- package/dist/components/saved-methods.d.ts +9 -0
- package/dist/components/states.d.ts +10 -0
- package/dist/components/stripe-card.d.ts +17 -0
- package/dist/components/summary.d.ts +12 -0
- package/dist/components/ui/alert-dialog.d.ts +18 -0
- package/dist/components/ui/badge.d.ts +7 -0
- package/dist/components/ui/button.d.ts +8 -0
- package/dist/components/ui/card.d.ts +11 -0
- package/dist/components/ui/dialog.d.ts +17 -0
- package/dist/components/ui/input.d.ts +3 -0
- package/dist/components/ui/label.d.ts +3 -0
- package/dist/components/ui/radio-group.d.ts +5 -0
- package/dist/components/ui/separator.d.ts +3 -0
- package/dist/components/ui/skeleton.d.ts +2 -0
- package/dist/components/ui/spinner.d.ts +3 -0
- package/dist/components/ui/textarea.d.ts +3 -0
- package/dist/dist-CslWupfi.js +3311 -0
- package/dist/dist-CslWupfi.js.map +1 -0
- package/dist/errors-T-PRm3vV.js +54 -0
- package/dist/errors-T-PRm3vV.js.map +1 -0
- package/dist/fixtures.d.ts +9 -0
- package/dist/frame.d.ts +47 -0
- package/dist/hooks-DLFF-zx5.js +285 -0
- package/dist/hooks-DLFF-zx5.js.map +1 -0
- package/dist/i18n/context.d.ts +4 -0
- package/dist/i18n/index.d.ts +2 -0
- package/dist/i18n/messages.d.ts +58 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +4182 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/billing.d.ts +25 -0
- package/dist/lib/card-brands.d.ts +2 -0
- package/dist/lib/card-entry.d.ts +46 -0
- package/dist/lib/ccbill.d.ts +8 -0
- package/dist/lib/collect.d.ts +51 -0
- package/dist/lib/money.d.ts +9 -0
- package/dist/lib/period.d.ts +15 -0
- package/dist/lib/rail-meta.d.ts +15 -0
- package/dist/lib/redirect.d.ts +2 -0
- package/dist/lib/stripe-appearance.d.ts +2 -0
- package/dist/lib/stripe.d.ts +2 -0
- package/dist/locales/de.d.ts +2 -0
- package/dist/locales/de.js +204 -0
- package/dist/locales/de.js.map +1 -0
- package/dist/locales/en.d.ts +201 -0
- package/dist/locales/en.js +204 -0
- package/dist/locales/en.js.map +1 -0
- package/dist/locales/es.d.ts +2 -0
- package/dist/locales/es.js +204 -0
- package/dist/locales/es.js.map +1 -0
- package/dist/locales/ja.d.ts +2 -0
- package/dist/locales/ja.js +195 -0
- package/dist/locales/ja.js.map +1 -0
- package/dist/locales/ko.d.ts +2 -0
- package/dist/locales/ko.js +195 -0
- package/dist/locales/ko.js.map +1 -0
- package/dist/locales/zh.d.ts +2 -0
- package/dist/locales/zh.js +195 -0
- package/dist/locales/zh.js.map +1 -0
- package/dist/modal.d.ts +12 -0
- package/dist/provider.d.ts +18 -0
- package/dist/psp.d.ts +46 -0
- package/dist/react/context.d.ts +38 -0
- package/dist/react/hooks.d.ts +76 -0
- package/dist/react/index.d.ts +3 -0
- package/dist/react/provider.d.ts +18 -0
- package/dist/react/remote.d.ts +11 -0
- package/dist/react.js +37 -0
- package/dist/react.js.map +1 -0
- package/dist/save-payment-method.d.ts +26 -0
- package/dist/scope-context.d.ts +17 -0
- package/dist/scope.d.ts +6 -0
- package/dist/source.d.ts +14 -0
- package/dist/styles.css +3 -0
- package/dist/tokenized-card-form.d.ts +24 -0
- package/dist/types.d.ts +191 -0
- package/docs/1055-nmi-host-integration.md +28 -0
- package/docs/payment-form-contract.md +153 -0
- package/package.json +112 -3
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { o as e } from "./errors-T-PRm3vV.js";
|
|
2
|
+
import { createContext as t, useCallback as n, useContext as r, useEffect as i, useRef as a, useState as o } from "react";
|
|
3
|
+
//#region src/react/context.ts
|
|
4
|
+
var s = t(null);
|
|
5
|
+
function c() {
|
|
6
|
+
let e = r(s);
|
|
7
|
+
if (!e) throw Error("billing-ui: wrap the tree in <BillingProvider>");
|
|
8
|
+
return e;
|
|
9
|
+
}
|
|
10
|
+
var l = () => c().client, u = () => c().refresh, d = () => r(s);
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/react/remote.ts
|
|
13
|
+
function f(t, r) {
|
|
14
|
+
let [s, c] = o(0), l = `${t}|${s}`, [u, d] = o({
|
|
15
|
+
request: null,
|
|
16
|
+
data: null,
|
|
17
|
+
error: null
|
|
18
|
+
}), f = a(r);
|
|
19
|
+
i(() => {
|
|
20
|
+
f.current = r;
|
|
21
|
+
}), i(() => {
|
|
22
|
+
let t = new AbortController();
|
|
23
|
+
return f.current(t.signal).then((e) => d({
|
|
24
|
+
request: l,
|
|
25
|
+
data: e,
|
|
26
|
+
error: null
|
|
27
|
+
}), (n) => {
|
|
28
|
+
t.signal.aborted || d((t) => ({
|
|
29
|
+
request: l,
|
|
30
|
+
data: t.data,
|
|
31
|
+
error: e(n)
|
|
32
|
+
}));
|
|
33
|
+
}), () => t.abort();
|
|
34
|
+
}, [l]);
|
|
35
|
+
let p = n(() => c((e) => e + 1), []), m = n((e) => d((t) => t.data === null ? t : {
|
|
36
|
+
...t,
|
|
37
|
+
data: e(t.data)
|
|
38
|
+
}), []);
|
|
39
|
+
return {
|
|
40
|
+
data: u.data,
|
|
41
|
+
loading: u.request !== l,
|
|
42
|
+
error: u.request === l ? u.error : null,
|
|
43
|
+
refetch: p,
|
|
44
|
+
replace: m
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
var p = (e) => new Promise((t) => setTimeout(t, e)), m = (e) => !!e.cancel_scheduled || e.status === "cancelled" || !!e.resumable, h = (e) => !e.cancel_scheduled && e.status !== "cancelled";
|
|
48
|
+
function g() {
|
|
49
|
+
let [e, t] = o({});
|
|
50
|
+
return [e, n((e, n) => {
|
|
51
|
+
t((t) => {
|
|
52
|
+
let r = { ...t };
|
|
53
|
+
return n ? r[e] = n : delete r[e], r;
|
|
54
|
+
});
|
|
55
|
+
}, [])];
|
|
56
|
+
}
|
|
57
|
+
function _(t = {}) {
|
|
58
|
+
let { client: r, version: i, notify: a, settle: o } = c(), s = t.status ?? "all", l = t.limit ?? 100, u = f(`${s}|${l}|${i}`, (e) => r.listSubscriptions({
|
|
59
|
+
status: s,
|
|
60
|
+
limit: l,
|
|
61
|
+
signal: e
|
|
62
|
+
})), [d, _] = g(), { replace: v } = u, y = n(async (e, t) => {
|
|
63
|
+
for (let n = 0; n < o.attempts; n++) {
|
|
64
|
+
let n = await r.getSubscription(e).catch(() => null);
|
|
65
|
+
if (n && t(n)) return v((t) => ({
|
|
66
|
+
...t,
|
|
67
|
+
data: t.data.map((t) => t.id === e ? n : t)
|
|
68
|
+
})), !0;
|
|
69
|
+
await p(o.intervalMs);
|
|
70
|
+
}
|
|
71
|
+
return !1;
|
|
72
|
+
}, [
|
|
73
|
+
r,
|
|
74
|
+
v,
|
|
75
|
+
o.attempts,
|
|
76
|
+
o.intervalMs
|
|
77
|
+
]), b = n(async (t, n, r, i) => {
|
|
78
|
+
_(t, n);
|
|
79
|
+
try {
|
|
80
|
+
let e = await r();
|
|
81
|
+
return i?.(e !== !1), null;
|
|
82
|
+
} catch (t) {
|
|
83
|
+
return e(t);
|
|
84
|
+
} finally {
|
|
85
|
+
_(t, null);
|
|
86
|
+
}
|
|
87
|
+
}, [_]), x = n((e, t) => b(e, "cancel", async () => (await r.cancelSubscription(e, { feedback: t }), y(e, m)), (t) => a({
|
|
88
|
+
type: "subscription.cancelled",
|
|
89
|
+
subscriptionId: e,
|
|
90
|
+
settled: t
|
|
91
|
+
})), [
|
|
92
|
+
b,
|
|
93
|
+
r,
|
|
94
|
+
a,
|
|
95
|
+
y
|
|
96
|
+
]), S = n((e, t) => b(e, "preparing", async () => (await r.cancelSubscriptionOnChain(e, t, (t) => _(e, t)), y(e, m)), (t) => a({
|
|
97
|
+
type: "subscription.cancelled",
|
|
98
|
+
subscriptionId: e,
|
|
99
|
+
settled: t
|
|
100
|
+
})), [
|
|
101
|
+
b,
|
|
102
|
+
r,
|
|
103
|
+
_,
|
|
104
|
+
a,
|
|
105
|
+
y
|
|
106
|
+
]), C = n((e) => b(e, "resume", async () => (await r.resumeSubscription(e), y(e, h)), (t) => a({
|
|
107
|
+
type: "subscription.resumed",
|
|
108
|
+
subscriptionId: e,
|
|
109
|
+
settled: t
|
|
110
|
+
})), [
|
|
111
|
+
b,
|
|
112
|
+
r,
|
|
113
|
+
a,
|
|
114
|
+
y
|
|
115
|
+
]), w = n((e, t) => b(e, "payment_method", async () => {
|
|
116
|
+
await r.setSubscriptionPaymentMethod(e, t), v((n) => ({
|
|
117
|
+
...n,
|
|
118
|
+
data: n.data.map((n) => n.id === e ? {
|
|
119
|
+
...n,
|
|
120
|
+
payment_method_id: t
|
|
121
|
+
} : n)
|
|
122
|
+
}));
|
|
123
|
+
}, () => a({
|
|
124
|
+
type: "subscription.payment_method_changed",
|
|
125
|
+
subscriptionId: e,
|
|
126
|
+
paymentMethodId: t
|
|
127
|
+
})), [
|
|
128
|
+
b,
|
|
129
|
+
r,
|
|
130
|
+
a,
|
|
131
|
+
v
|
|
132
|
+
]), T = n(async (t, n) => {
|
|
133
|
+
_(t, "change_tier");
|
|
134
|
+
try {
|
|
135
|
+
let e = await r.changeTier(t, n);
|
|
136
|
+
return a({
|
|
137
|
+
type: "subscription.tier_changed",
|
|
138
|
+
subscriptionId: t,
|
|
139
|
+
change: e
|
|
140
|
+
}), e;
|
|
141
|
+
} catch (t) {
|
|
142
|
+
return e(t);
|
|
143
|
+
} finally {
|
|
144
|
+
_(t, null);
|
|
145
|
+
}
|
|
146
|
+
}, [
|
|
147
|
+
r,
|
|
148
|
+
_,
|
|
149
|
+
a
|
|
150
|
+
]);
|
|
151
|
+
return {
|
|
152
|
+
subscriptions: u.data?.data ?? null,
|
|
153
|
+
total: u.data?.total ?? null,
|
|
154
|
+
loading: u.loading,
|
|
155
|
+
error: u.error,
|
|
156
|
+
refetch: u.refetch,
|
|
157
|
+
pending: d,
|
|
158
|
+
cancel: x,
|
|
159
|
+
cancelOnChain: S,
|
|
160
|
+
resume: C,
|
|
161
|
+
setPaymentMethod: w,
|
|
162
|
+
changeTier: T
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
function v(e = {}) {
|
|
166
|
+
let { client: t } = c(), n = e.limit ?? 100, r = f(`${n}`, (e) => t.listProducts({
|
|
167
|
+
limit: n,
|
|
168
|
+
signal: e
|
|
169
|
+
}));
|
|
170
|
+
return {
|
|
171
|
+
products: r.data?.data ?? null,
|
|
172
|
+
total: r.data?.total ?? null,
|
|
173
|
+
loading: r.loading,
|
|
174
|
+
error: r.error,
|
|
175
|
+
refetch: r.refetch
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
function y() {
|
|
179
|
+
let { client: t, version: r, notify: i } = c(), a = f(`${r}`, (e) => t.listPaymentMethods({ signal: e })), [s, l] = g(), [u, d] = o(!1), { replace: p } = a, m = n(async (n) => {
|
|
180
|
+
d(!0);
|
|
181
|
+
try {
|
|
182
|
+
let e = await t.addPaymentMethod(n);
|
|
183
|
+
return p((t) => ({
|
|
184
|
+
...t,
|
|
185
|
+
data: [...t.data, e]
|
|
186
|
+
})), i({
|
|
187
|
+
type: "payment_method.added",
|
|
188
|
+
paymentMethodId: e.id
|
|
189
|
+
}), null;
|
|
190
|
+
} catch (t) {
|
|
191
|
+
return e(t);
|
|
192
|
+
} finally {
|
|
193
|
+
d(!1);
|
|
194
|
+
}
|
|
195
|
+
}, [
|
|
196
|
+
t,
|
|
197
|
+
i,
|
|
198
|
+
p
|
|
199
|
+
]), h = n(async (n) => {
|
|
200
|
+
l(n, "remove");
|
|
201
|
+
try {
|
|
202
|
+
return await t.removePaymentMethod(n), p((e) => ({
|
|
203
|
+
...e,
|
|
204
|
+
data: e.data.filter((e) => e.id !== n)
|
|
205
|
+
})), i({
|
|
206
|
+
type: "payment_method.removed",
|
|
207
|
+
paymentMethodId: n
|
|
208
|
+
}), null;
|
|
209
|
+
} catch (t) {
|
|
210
|
+
return e(t);
|
|
211
|
+
} finally {
|
|
212
|
+
l(n, null);
|
|
213
|
+
}
|
|
214
|
+
}, [
|
|
215
|
+
t,
|
|
216
|
+
l,
|
|
217
|
+
i,
|
|
218
|
+
p
|
|
219
|
+
]), _ = n(async (n, r) => {
|
|
220
|
+
l(n, "default");
|
|
221
|
+
try {
|
|
222
|
+
await t.setDefaultPaymentMethod({
|
|
223
|
+
currency: r,
|
|
224
|
+
paymentMethodId: n
|
|
225
|
+
});
|
|
226
|
+
let e = r.toUpperCase();
|
|
227
|
+
return p((t) => ({
|
|
228
|
+
...t,
|
|
229
|
+
data: t.data.map((t) => {
|
|
230
|
+
let r = (t.collection_default_currencies ?? []).filter((t) => t !== e);
|
|
231
|
+
return {
|
|
232
|
+
...t,
|
|
233
|
+
collection_default_currencies: t.id === n ? [...r, e] : r
|
|
234
|
+
};
|
|
235
|
+
})
|
|
236
|
+
})), i({
|
|
237
|
+
type: "payment_method.default_changed",
|
|
238
|
+
paymentMethodId: n
|
|
239
|
+
}), null;
|
|
240
|
+
} catch (t) {
|
|
241
|
+
return e(t);
|
|
242
|
+
} finally {
|
|
243
|
+
l(n, null);
|
|
244
|
+
}
|
|
245
|
+
}, [
|
|
246
|
+
t,
|
|
247
|
+
l,
|
|
248
|
+
i,
|
|
249
|
+
p
|
|
250
|
+
]);
|
|
251
|
+
return {
|
|
252
|
+
methods: a.data?.data ?? null,
|
|
253
|
+
loading: a.loading,
|
|
254
|
+
error: a.error,
|
|
255
|
+
refetch: a.refetch,
|
|
256
|
+
pending: s,
|
|
257
|
+
adding: u,
|
|
258
|
+
add: m,
|
|
259
|
+
remove: h,
|
|
260
|
+
setDefault: _
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
function b(e = {}) {
|
|
264
|
+
let { client: t, version: r } = c(), i = e.pageSize ?? 20, [a, s] = o(0), l = f(`${i}|${e.rail ?? ""}|${a}|${r}`, (n) => t.listPayments({
|
|
265
|
+
limit: i,
|
|
266
|
+
offset: a * i,
|
|
267
|
+
rail: e.rail,
|
|
268
|
+
signal: n
|
|
269
|
+
})), u = l.data, d = u?.total ?? null, p = u?.has_more ?? (d === null ? u?.data.length === i : (a + 1) * i < d);
|
|
270
|
+
return {
|
|
271
|
+
payments: u?.data ?? null,
|
|
272
|
+
page: a,
|
|
273
|
+
hasMore: !!p,
|
|
274
|
+
total: d,
|
|
275
|
+
loading: l.loading,
|
|
276
|
+
error: l.error,
|
|
277
|
+
next: n(() => s((e) => e + 1), []),
|
|
278
|
+
previous: n(() => s((e) => Math.max(0, e - 1)), []),
|
|
279
|
+
refetch: l.refetch
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
//#endregion
|
|
283
|
+
export { s as a, u as c, _ as i, d as l, b as n, l as o, v as r, c as s, y as t };
|
|
284
|
+
|
|
285
|
+
//# sourceMappingURL=hooks-DLFF-zx5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks-DLFF-zx5.js","names":[],"sources":["../src/react/context.ts","../src/react/remote.ts","../src/react/hooks.ts"],"sourcesContent":["import { createContext, useContext } from \"react\"\n\nimport type { BillingClient } from \"../client/client\"\nimport type { TierChange } from \"../client/types\"\n\nexport type BillingChange =\n | {\n type: \"subscription.cancelled\" | \"subscription.resumed\"\n subscriptionId: string\n /** False when the server had not applied the queued change yet. */\n settled: boolean\n }\n | {\n type: \"subscription.payment_method_changed\"\n subscriptionId: string\n paymentMethodId: string\n }\n | {\n type: \"subscription.tier_changed\"\n subscriptionId: string\n /** `status` may still be `processing` or `requires_action`. */\n change: TierChange\n }\n | {\n type:\n | \"payment_method.added\"\n | \"payment_method.removed\"\n | \"payment_method.default_changed\"\n paymentMethodId: string\n }\n\nexport interface BillingContextValue {\n client: BillingClient\n /** Bumped after every mutation; hooks refetch on change. */\n version: number\n notify(change: BillingChange): void\n /** Refetch every hook after a host-side change such as a plan switch. */\n refresh(): void\n settle: { intervalMs: number; attempts: number }\n}\n\nexport const BillingContext = createContext<BillingContextValue | null>(null)\n\nexport function useBillingContext(): BillingContextValue {\n const ctx = useContext(BillingContext)\n if (!ctx) throw new Error(\"billing-ui: wrap the tree in <BillingProvider>\")\n return ctx\n}\n\nexport const useBillingClient = (): BillingClient => useBillingContext().client\n\nexport const useBillingRefresh = (): (() => void) => useBillingContext().refresh\n\n/** The billing context when a BillingProvider is present, else null. */\nexport const useOptionalBillingContext = (): BillingContextValue | null =>\n useContext(BillingContext)\n","import { useCallback, useEffect, useRef, useState } from \"react\"\n\nimport { toBillingError, type BillingError } from \"../client/errors\"\n\nexport interface RemoteState<T> {\n data: T | null\n loading: boolean\n error: BillingError | null\n refetch: () => void\n}\n\n// Keyed fetch that keeps the previous data while a newer key loads, so a\n// refetch after a mutation never flashes an empty list.\nexport function useRemote<T>(\n key: string,\n load: (signal: AbortSignal) => Promise<T>\n): RemoteState<T> & { replace: (update: (data: T) => T) => void } {\n const [nonce, setNonce] = useState(0)\n const request = `${key}|${nonce}`\n const [state, setState] = useState<{\n request: string | null\n data: T | null\n error: BillingError | null\n }>({ request: null, data: null, error: null })\n const loadRef = useRef(load)\n useEffect(() => {\n loadRef.current = load\n })\n\n useEffect(() => {\n const ctl = new AbortController()\n loadRef.current(ctl.signal).then(\n (data) => setState({ request, data, error: null }),\n (err: unknown) => {\n if (!ctl.signal.aborted)\n setState((s) => ({\n request,\n data: s.data,\n error: toBillingError(err),\n }))\n }\n )\n return () => ctl.abort()\n }, [request])\n\n const refetch = useCallback(() => setNonce((n) => n + 1), [])\n const replace = useCallback(\n (update: (data: T) => T) =>\n setState((s) => (s.data === null ? s : { ...s, data: update(s.data) })),\n []\n )\n return {\n data: state.data,\n loading: state.request !== request,\n error: state.request === request ? state.error : null,\n refetch,\n replace,\n }\n}\n\nexport const sleep = (ms: number) =>\n new Promise<void>((resolve) => setTimeout(resolve, ms))\n","import { useCallback, useState } from \"react\"\n\nimport type { SendSolanaTransaction, SolanaCancelStage } from \"../client/client\"\nimport { toBillingError, type BillingError } from \"../client/errors\"\nimport type {\n NewCard,\n Payment,\n PaymentMethod,\n Product,\n Subscription,\n TierChange,\n} from \"../client/types\"\nimport { useBillingContext } from \"./context\"\nimport { sleep, useRemote } from \"./remote\"\n\n// Actions never throw: they resolve to null on success or the error.\nexport type ActionResult = Promise<BillingError | null>\n\nexport type SubscriptionAction =\n \"cancel\" | \"resume\" | \"payment_method\" | \"change_tier\" | SolanaCancelStage\n\nexport interface SubscriptionsOptions {\n /** Server filter; default `all`. */\n status?: string\n limit?: number\n}\n\nexport interface SubscriptionsState {\n subscriptions: Subscription[] | null\n total: number | null\n loading: boolean\n error: BillingError | null\n refetch: () => void\n /** In-flight action per subscription id. */\n pending: Readonly<Record<string, SubscriptionAction>>\n cancel: (subscriptionId: string, feedback: string) => ActionResult\n /** Solana rail: the wallet signs the server-prepared cancel transaction. */\n cancelOnChain: (\n subscriptionId: string,\n sendTransaction: SendSolanaTransaction\n ) => ActionResult\n resume: (subscriptionId: string) => ActionResult\n setPaymentMethod: (\n subscriptionId: string,\n paymentMethodId: string\n ) => ActionResult\n /**\n * Card rails: `client.changeTier`. Resolves to the change, whose `status`\n * may still be `processing` or `requires_action`, or to the error.\n */\n changeTier: (\n subscriptionId: string,\n input: { priceId: string; idempotencyKey: string }\n ) => Promise<TierChange | BillingError>\n}\n\nconst cancelApplied = (s: Subscription) =>\n !!s.cancel_scheduled || s.status === \"cancelled\" || !!s.resumable\nconst resumeApplied = (s: Subscription) =>\n !s.cancel_scheduled && s.status !== \"cancelled\"\n\nfunction usePending<A extends string>() {\n const [pending, setPending] = useState<Readonly<Record<string, A>>>({})\n const mark = useCallback((id: string, action: A | null) => {\n setPending((p) => {\n const next = { ...p }\n if (action) next[id] = action\n else delete next[id]\n return next\n })\n }, [])\n return [pending, mark] as const\n}\n\nexport function useSubscriptions(\n options: SubscriptionsOptions = {}\n): SubscriptionsState {\n const { client, version, notify, settle } = useBillingContext()\n const status = options.status ?? \"all\"\n const limit = options.limit ?? 100\n const remote = useRemote(`${status}|${limit}|${version}`, (signal) =>\n client.listSubscriptions({ status, limit, signal })\n )\n const [pending, mark] = usePending<SubscriptionAction>()\n const { replace } = remote\n\n // Re-reads a queued change until it shows, then patches the row.\n const settleRow = useCallback(\n async (id: string, applied: (s: Subscription) => boolean) => {\n for (let i = 0; i < settle.attempts; i++) {\n const next = await client.getSubscription(id).catch(() => null)\n if (next && applied(next)) {\n replace((page) => ({\n ...page,\n data: page.data.map((s) => (s.id === id ? next : s)),\n }))\n return true\n }\n await sleep(settle.intervalMs)\n }\n return false\n },\n [client, replace, settle.attempts, settle.intervalMs]\n )\n\n const act = useCallback(\n async (\n id: string,\n action: SubscriptionAction,\n run: () => Promise<boolean | void>,\n done?: (settled: boolean) => void\n ): ActionResult => {\n mark(id, action)\n try {\n const settled = await run()\n done?.(settled !== false)\n return null\n } catch (err) {\n return toBillingError(err)\n } finally {\n mark(id, null)\n }\n },\n [mark]\n )\n\n const cancel = useCallback(\n (id: string, feedback: string) =>\n act(\n id,\n \"cancel\",\n async () => {\n await client.cancelSubscription(id, { feedback })\n return settleRow(id, cancelApplied)\n },\n (settled) =>\n notify({\n type: \"subscription.cancelled\",\n subscriptionId: id,\n settled,\n })\n ),\n [act, client, notify, settleRow]\n )\n\n // A declined wallet prompt resolves to a `wallet_rejected` error.\n const cancelOnChain = useCallback(\n (id: string, sendTransaction: SendSolanaTransaction) =>\n act(\n id,\n \"preparing\",\n async () => {\n await client.cancelSubscriptionOnChain(id, sendTransaction, (stage) =>\n mark(id, stage)\n )\n return settleRow(id, cancelApplied)\n },\n (settled) =>\n notify({\n type: \"subscription.cancelled\",\n subscriptionId: id,\n settled,\n })\n ),\n [act, client, mark, notify, settleRow]\n )\n\n const resume = useCallback(\n (id: string) =>\n act(\n id,\n \"resume\",\n async () => {\n await client.resumeSubscription(id)\n return settleRow(id, resumeApplied)\n },\n (settled) =>\n notify({ type: \"subscription.resumed\", subscriptionId: id, settled })\n ),\n [act, client, notify, settleRow]\n )\n\n const setPaymentMethod = useCallback(\n (id: string, paymentMethodId: string) =>\n act(\n id,\n \"payment_method\",\n async () => {\n await client.setSubscriptionPaymentMethod(id, paymentMethodId)\n replace((page) => ({\n ...page,\n data: page.data.map((s) =>\n s.id === id ? { ...s, payment_method_id: paymentMethodId } : s\n ),\n }))\n },\n () =>\n notify({\n type: \"subscription.payment_method_changed\",\n subscriptionId: id,\n paymentMethodId,\n })\n ),\n [act, client, notify, replace]\n )\n\n // An upgrade may open a successor subscription: notify refetches the list.\n const changeTier = useCallback(\n async (id: string, input: { priceId: string; idempotencyKey: string }) => {\n mark(id, \"change_tier\")\n try {\n const change = await client.changeTier(id, input)\n notify({\n type: \"subscription.tier_changed\",\n subscriptionId: id,\n change,\n })\n return change\n } catch (err) {\n return toBillingError(err)\n } finally {\n mark(id, null)\n }\n },\n [client, mark, notify]\n )\n\n return {\n subscriptions: remote.data?.data ?? null,\n total: remote.data?.total ?? null,\n loading: remote.loading,\n error: remote.error,\n refetch: remote.refetch,\n pending,\n cancel,\n cancelOnChain,\n resume,\n setPaymentMethod,\n changeTier,\n }\n}\n\nexport interface ProductsState {\n products: Product[] | null\n total: number | null\n loading: boolean\n error: BillingError | null\n refetch: () => void\n}\n\n/** The catalog: active products with their active prices. */\nexport function useProducts(options: { limit?: number } = {}): ProductsState {\n const { client } = useBillingContext()\n const limit = options.limit ?? 100\n const remote = useRemote(`${limit}`, (signal) =>\n client.listProducts({ limit, signal })\n )\n return {\n products: remote.data?.data ?? null,\n total: remote.data?.total ?? null,\n loading: remote.loading,\n error: remote.error,\n refetch: remote.refetch,\n }\n}\n\nexport type PaymentMethodAction = \"remove\" | \"default\"\n\nexport interface PaymentMethodsState {\n methods: PaymentMethod[] | null\n loading: boolean\n error: BillingError | null\n refetch: () => void\n pending: Readonly<Record<string, PaymentMethodAction>>\n adding: boolean\n add: (card: NewCard) => ActionResult\n remove: (paymentMethodId: string) => ActionResult\n /** Default collector for one currency's invoices. */\n setDefault: (paymentMethodId: string, currency: string) => ActionResult\n}\n\nexport function usePaymentMethods(): PaymentMethodsState {\n const { client, version, notify } = useBillingContext()\n const remote = useRemote(`${version}`, (signal) =>\n client.listPaymentMethods({ signal })\n )\n const [pending, mark] = usePending<PaymentMethodAction>()\n const [adding, setAdding] = useState(false)\n const { replace } = remote\n\n const add = useCallback(\n async (card: NewCard): ActionResult => {\n setAdding(true)\n try {\n const method = await client.addPaymentMethod(card)\n replace((page) => ({ ...page, data: [...page.data, method] }))\n notify({ type: \"payment_method.added\", paymentMethodId: method.id })\n return null\n } catch (err) {\n return toBillingError(err)\n } finally {\n setAdding(false)\n }\n },\n [client, notify, replace]\n )\n\n const remove = useCallback(\n async (id: string): ActionResult => {\n mark(id, \"remove\")\n try {\n await client.removePaymentMethod(id)\n replace((page) => ({\n ...page,\n data: page.data.filter((m) => m.id !== id),\n }))\n notify({ type: \"payment_method.removed\", paymentMethodId: id })\n return null\n } catch (err) {\n return toBillingError(err)\n } finally {\n mark(id, null)\n }\n },\n [client, mark, notify, replace]\n )\n\n const setDefault = useCallback(\n async (id: string, currency: string): ActionResult => {\n mark(id, \"default\")\n try {\n await client.setDefaultPaymentMethod({ currency, paymentMethodId: id })\n const code = currency.toUpperCase()\n replace((page) => ({\n ...page,\n data: page.data.map((m) => {\n const others = (m.collection_default_currencies ?? []).filter(\n (c) => c !== code\n )\n return {\n ...m,\n collection_default_currencies:\n m.id === id ? [...others, code] : others,\n }\n }),\n }))\n notify({ type: \"payment_method.default_changed\", paymentMethodId: id })\n return null\n } catch (err) {\n return toBillingError(err)\n } finally {\n mark(id, null)\n }\n },\n [client, mark, notify, replace]\n )\n\n return {\n methods: remote.data?.data ?? null,\n loading: remote.loading,\n error: remote.error,\n refetch: remote.refetch,\n pending,\n adding,\n add,\n remove,\n setDefault,\n }\n}\n\nexport interface PaymentsOptions {\n pageSize?: number\n /** Filter by rail. */\n rail?: string\n}\n\nexport interface PaymentsState {\n payments: Payment[] | null\n /** Zero-based. */\n page: number\n hasMore: boolean\n total: number | null\n loading: boolean\n error: BillingError | null\n next: () => void\n previous: () => void\n refetch: () => void\n}\n\nexport function usePayments(options: PaymentsOptions = {}): PaymentsState {\n const { client, version } = useBillingContext()\n const pageSize = options.pageSize ?? 20\n const [page, setPage] = useState(0)\n const remote = useRemote(\n `${pageSize}|${options.rail ?? \"\"}|${page}|${version}`,\n (signal) =>\n client.listPayments({\n limit: pageSize,\n offset: page * pageSize,\n rail: options.rail,\n signal,\n })\n )\n const data = remote.data\n const total = data?.total ?? null\n const hasMore =\n data?.has_more ??\n (total !== null\n ? (page + 1) * pageSize < total\n : data?.data.length === pageSize)\n return {\n payments: data?.data ?? null,\n page,\n hasMore: !!hasMore,\n total,\n loading: remote.loading,\n error: remote.error,\n next: useCallback(() => setPage((p) => p + 1), []),\n previous: useCallback(() => setPage((p) => Math.max(0, p - 1)), []),\n refetch: remote.refetch,\n }\n}\n"],"mappings":";;;AAyCA,IAAa,IAAiB,EAA0C,IAAI;AAE5E,SAAgB,IAAyC;CACvD,IAAM,IAAM,EAAW,CAAc;CACrC,IAAI,CAAC,GAAK,MAAU,MAAM,gDAAgD;CAC1E,OAAO;AACT;AAEA,IAAa,UAAwC,EAAkB,CAAC,CAAC,QAE5D,UAAwC,EAAkB,CAAC,CAAC,SAG5D,UACX,EAAW,CAAc;;;AC1C3B,SAAgB,EACd,GACA,GACgE;CAChE,IAAM,CAAC,GAAO,KAAY,EAAS,CAAC,GAC9B,IAAU,GAAG,EAAI,GAAG,KACpB,CAAC,GAAO,KAAY,EAIvB;EAAE,SAAS;EAAM,MAAM;EAAM,OAAO;CAAK,CAAC,GACvC,IAAU,EAAO,CAAI;CAK3B,AAJA,QAAgB;EACd,EAAQ,UAAU;CACpB,CAAC,GAED,QAAgB;EACd,IAAM,IAAM,IAAI,gBAAgB;EAYhC,OAXA,EAAQ,QAAQ,EAAI,MAAM,CAAC,CAAC,MACzB,MAAS,EAAS;GAAE;GAAS;GAAM,OAAO;EAAK,CAAC,IAChD,MAAiB;GAChB,AAAK,EAAI,OAAO,WACd,GAAU,OAAO;IACf;IACA,MAAM,EAAE;IACR,OAAO,EAAe,CAAG;GAC3B,EAAE;EACN,CACF,SACa,EAAI,MAAM;CACzB,GAAG,CAAC,CAAO,CAAC;CAEZ,IAAM,IAAU,QAAkB,GAAU,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC,GACtD,IAAU,GACb,MACC,GAAU,MAAO,EAAE,SAAS,OAAO,IAAI;EAAE,GAAG;EAAG,MAAM,EAAO,EAAE,IAAI;CAAE,CAAE,GACxE,CAAC,CACH;CACA,OAAO;EACL,MAAM,EAAM;EACZ,SAAS,EAAM,YAAY;EAC3B,OAAO,EAAM,YAAY,IAAU,EAAM,QAAQ;EACjD;EACA;CACF;AACF;AAEA,IAAa,KAAS,MACpB,IAAI,SAAe,MAAY,WAAW,GAAS,CAAE,CAAC,GCLlD,KAAiB,MACrB,CAAC,CAAC,EAAE,oBAAoB,EAAE,WAAW,eAAe,CAAC,CAAC,EAAE,WACpD,KAAiB,MACrB,CAAC,EAAE,oBAAoB,EAAE,WAAW;AAEtC,SAAS,IAA+B;CACtC,IAAM,CAAC,GAAS,KAAc,EAAsC,CAAC,CAAC;CAStE,OAAO,CAAC,GARK,GAAa,GAAY,MAAqB;EACzD,GAAY,MAAM;GAChB,IAAM,IAAO,EAAE,GAAG,EAAE;GAGpB,OAFI,IAAQ,EAAK,KAAM,IAClB,OAAO,EAAK,IACV;EACT,CAAC;CACH,GAAG,CAAC,CACa,CAAI;AACvB;AAEA,SAAgB,EACd,IAAgC,CAAC,GACb;CACpB,IAAM,EAAE,WAAQ,YAAS,WAAQ,cAAW,EAAkB,GACxD,IAAS,EAAQ,UAAU,OAC3B,IAAQ,EAAQ,SAAS,KACzB,IAAS,EAAU,GAAG,EAAO,GAAG,EAAM,GAAG,MAAY,MACzD,EAAO,kBAAkB;EAAE;EAAQ;EAAO;CAAO,CAAC,CACpD,GACM,CAAC,GAAS,KAAQ,EAA+B,GACjD,EAAE,eAAY,GAGd,IAAY,EAChB,OAAO,GAAY,MAA0C;EAC3D,KAAK,IAAI,IAAI,GAAG,IAAI,EAAO,UAAU,KAAK;GACxC,IAAM,IAAO,MAAM,EAAO,gBAAgB,CAAE,CAAC,CAAC,YAAY,IAAI;GAC9D,IAAI,KAAQ,EAAQ,CAAI,GAKtB,OAJA,GAAS,OAAU;IACjB,GAAG;IACH,MAAM,EAAK,KAAK,KAAK,MAAO,EAAE,OAAO,IAAK,IAAO,CAAE;GACrD,EAAE,GACK;GAET,MAAM,EAAM,EAAO,UAAU;EAC/B;EACA,OAAO;CACT,GACA;EAAC;EAAQ;EAAS,EAAO;EAAU,EAAO;CAAU,CACtD,GAEM,IAAM,EACV,OACE,GACA,GACA,GACA,MACiB;EACjB,EAAK,GAAI,CAAM;EACf,IAAI;GACF,IAAM,IAAU,MAAM,EAAI;GAE1B,OADA,IAAO,MAAY,EAAK,GACjB;EACT,SAAS,GAAK;GACZ,OAAO,EAAe,CAAG;EAC3B,UAAU;GACR,EAAK,GAAI,IAAI;EACf;CACF,GACA,CAAC,CAAI,CACP,GAEM,IAAS,GACZ,GAAY,MACX,EACE,GACA,UACA,aACE,MAAM,EAAO,mBAAmB,GAAI,EAAE,YAAS,CAAC,GACzC,EAAU,GAAI,CAAa,KAEnC,MACC,EAAO;EACL,MAAM;EACN,gBAAgB;EAChB;CACF,CAAC,CACL,GACF;EAAC;EAAK;EAAQ;EAAQ;CAAS,CACjC,GAGM,IAAgB,GACnB,GAAY,MACX,EACE,GACA,aACA,aACE,MAAM,EAAO,0BAA0B,GAAI,IAAkB,MAC3D,EAAK,GAAI,CAAK,CAChB,GACO,EAAU,GAAI,CAAa,KAEnC,MACC,EAAO;EACL,MAAM;EACN,gBAAgB;EAChB;CACF,CAAC,CACL,GACF;EAAC;EAAK;EAAQ;EAAM;EAAQ;CAAS,CACvC,GAEM,IAAS,GACZ,MACC,EACE,GACA,UACA,aACE,MAAM,EAAO,mBAAmB,CAAE,GAC3B,EAAU,GAAI,CAAa,KAEnC,MACC,EAAO;EAAE,MAAM;EAAwB,gBAAgB;EAAI;CAAQ,CAAC,CACxE,GACF;EAAC;EAAK;EAAQ;EAAQ;CAAS,CACjC,GAEM,IAAmB,GACtB,GAAY,MACX,EACE,GACA,kBACA,YAAY;EAEV,AADA,MAAM,EAAO,6BAA6B,GAAI,CAAe,GAC7D,GAAS,OAAU;GACjB,GAAG;GACH,MAAM,EAAK,KAAK,KAAK,MACnB,EAAE,OAAO,IAAK;IAAE,GAAG;IAAG,mBAAmB;GAAgB,IAAI,CAC/D;EACF,EAAE;CACJ,SAEE,EAAO;EACL,MAAM;EACN,gBAAgB;EAChB;CACF,CAAC,CACL,GACF;EAAC;EAAK;EAAQ;EAAQ;CAAO,CAC/B,GAGM,IAAa,EACjB,OAAO,GAAY,MAAuD;EACxE,EAAK,GAAI,aAAa;EACtB,IAAI;GACF,IAAM,IAAS,MAAM,EAAO,WAAW,GAAI,CAAK;GAMhD,OALA,EAAO;IACL,MAAM;IACN,gBAAgB;IAChB;GACF,CAAC,GACM;EACT,SAAS,GAAK;GACZ,OAAO,EAAe,CAAG;EAC3B,UAAU;GACR,EAAK,GAAI,IAAI;EACf;CACF,GACA;EAAC;EAAQ;EAAM;CAAM,CACvB;CAEA,OAAO;EACL,eAAe,EAAO,MAAM,QAAQ;EACpC,OAAO,EAAO,MAAM,SAAS;EAC7B,SAAS,EAAO;EAChB,OAAO,EAAO;EACd,SAAS,EAAO;EAChB;EACA;EACA;EACA;EACA;EACA;CACF;AACF;AAWA,SAAgB,EAAY,IAA8B,CAAC,GAAkB;CAC3E,IAAM,EAAE,cAAW,EAAkB,GAC/B,IAAQ,EAAQ,SAAS,KACzB,IAAS,EAAU,GAAG,MAAU,MACpC,EAAO,aAAa;EAAE;EAAO;CAAO,CAAC,CACvC;CACA,OAAO;EACL,UAAU,EAAO,MAAM,QAAQ;EAC/B,OAAO,EAAO,MAAM,SAAS;EAC7B,SAAS,EAAO;EAChB,OAAO,EAAO;EACd,SAAS,EAAO;CAClB;AACF;AAiBA,SAAgB,IAAyC;CACvD,IAAM,EAAE,WAAQ,YAAS,cAAW,EAAkB,GAChD,IAAS,EAAU,GAAG,MAAY,MACtC,EAAO,mBAAmB,EAAE,UAAO,CAAC,CACtC,GACM,CAAC,GAAS,KAAQ,EAAgC,GAClD,CAAC,GAAQ,KAAa,EAAS,EAAK,GACpC,EAAE,eAAY,GAEd,IAAM,EACV,OAAO,MAAgC;EACrC,EAAU,EAAI;EACd,IAAI;GACF,IAAM,IAAS,MAAM,EAAO,iBAAiB,CAAI;GAGjD,OAFA,GAAS,OAAU;IAAE,GAAG;IAAM,MAAM,CAAC,GAAG,EAAK,MAAM,CAAM;GAAE,EAAE,GAC7D,EAAO;IAAE,MAAM;IAAwB,iBAAiB,EAAO;GAAG,CAAC,GAC5D;EACT,SAAS,GAAK;GACZ,OAAO,EAAe,CAAG;EAC3B,UAAU;GACR,EAAU,EAAK;EACjB;CACF,GACA;EAAC;EAAQ;EAAQ;CAAO,CAC1B,GAEM,IAAS,EACb,OAAO,MAA6B;EAClC,EAAK,GAAI,QAAQ;EACjB,IAAI;GAOF,OANA,MAAM,EAAO,oBAAoB,CAAE,GACnC,GAAS,OAAU;IACjB,GAAG;IACH,MAAM,EAAK,KAAK,QAAQ,MAAM,EAAE,OAAO,CAAE;GAC3C,EAAE,GACF,EAAO;IAAE,MAAM;IAA0B,iBAAiB;GAAG,CAAC,GACvD;EACT,SAAS,GAAK;GACZ,OAAO,EAAe,CAAG;EAC3B,UAAU;GACR,EAAK,GAAI,IAAI;EACf;CACF,GACA;EAAC;EAAQ;EAAM;EAAQ;CAAO,CAChC,GAEM,IAAa,EACjB,OAAO,GAAY,MAAmC;EACpD,EAAK,GAAI,SAAS;EAClB,IAAI;GACF,MAAM,EAAO,wBAAwB;IAAE;IAAU,iBAAiB;GAAG,CAAC;GACtE,IAAM,IAAO,EAAS,YAAY;GAelC,OAdA,GAAS,OAAU;IACjB,GAAG;IACH,MAAM,EAAK,KAAK,KAAK,MAAM;KACzB,IAAM,KAAU,EAAE,iCAAiC,CAAC,EAAA,CAAG,QACpD,MAAM,MAAM,CACf;KACA,OAAO;MACL,GAAG;MACH,+BACE,EAAE,OAAO,IAAK,CAAC,GAAG,GAAQ,CAAI,IAAI;KACtC;IACF,CAAC;GACH,EAAE,GACF,EAAO;IAAE,MAAM;IAAkC,iBAAiB;GAAG,CAAC,GAC/D;EACT,SAAS,GAAK;GACZ,OAAO,EAAe,CAAG;EAC3B,UAAU;GACR,EAAK,GAAI,IAAI;EACf;CACF,GACA;EAAC;EAAQ;EAAM;EAAQ;CAAO,CAChC;CAEA,OAAO;EACL,SAAS,EAAO,MAAM,QAAQ;EAC9B,SAAS,EAAO;EAChB,OAAO,EAAO;EACd,SAAS,EAAO;EAChB;EACA;EACA;EACA;EACA;CACF;AACF;AAqBA,SAAgB,EAAY,IAA2B,CAAC,GAAkB;CACxE,IAAM,EAAE,WAAQ,eAAY,EAAkB,GACxC,IAAW,EAAQ,YAAY,IAC/B,CAAC,GAAM,KAAW,EAAS,CAAC,GAC5B,IAAS,EACb,GAAG,EAAS,GAAG,EAAQ,QAAQ,GAAG,GAAG,EAAK,GAAG,MAC5C,MACC,EAAO,aAAa;EAClB,OAAO;EACP,QAAQ,IAAO;EACf,MAAM,EAAQ;EACd;CACF,CAAC,CACL,GACM,IAAO,EAAO,MACd,IAAQ,GAAM,SAAS,MACvB,IACJ,GAAM,aACL,MAAU,OAEP,GAAM,KAAK,WAAW,KADrB,IAAO,KAAK,IAAW;CAE9B,OAAO;EACL,UAAU,GAAM,QAAQ;EACxB;EACA,SAAS,CAAC,CAAC;EACX;EACA,SAAS,EAAO;EAChB,OAAO,EAAO;EACd,MAAM,QAAkB,GAAS,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC;EACjD,UAAU,QAAkB,GAAS,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;EAClE,SAAS,EAAO;CAClB;AACF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { useMessages } from './context.ts';
|
|
2
|
+
export { createTranslator, defaultMessages, defineMessages, interpolate, resolveMessages, type BillingUiMessageBundle, type BillingUiMessages, type BillingUiTranslate, type MessageKey, type MessageVars, type PluralKey, type PluralMessage, type Translator, } from './messages.ts';
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { en } from '../locales/en.ts';
|
|
2
|
+
/**
|
|
3
|
+
* A count-dependent message: CLDR plural categories for the locale, with
|
|
4
|
+
* `other` required. An exact-count key (`"1"`) wins over the category, like
|
|
5
|
+
* ICU's `=1`, for locales whose single form differs from `{count}` (毎日).
|
|
6
|
+
*/
|
|
7
|
+
export type PluralMessage = {
|
|
8
|
+
[K in Intl.LDMLPluralRule | "1"]?: string;
|
|
9
|
+
} & {
|
|
10
|
+
other: string;
|
|
11
|
+
};
|
|
12
|
+
type Widen<T> = {
|
|
13
|
+
[K in keyof T]: T[K] extends string ? string : T[K] extends PluralMessage ? PluralMessage : Widen<T[K]>;
|
|
14
|
+
};
|
|
15
|
+
/** The complete message tree; English is the reference shape. */
|
|
16
|
+
export type BillingUiMessages = Widen<typeof en>;
|
|
17
|
+
type DeepPartial<T> = {
|
|
18
|
+
[K in keyof T]?: T[K] extends string ? string : T[K] extends PluralMessage ? Partial<PluralMessage> : DeepPartial<T[K]>;
|
|
19
|
+
};
|
|
20
|
+
/** A locale bundle or host override: any subset of the tree. */
|
|
21
|
+
export type BillingUiMessageBundle = DeepPartial<BillingUiMessages>;
|
|
22
|
+
type Paths<T, P extends string = ""> = {
|
|
23
|
+
[K in keyof T & string]: T[K] extends string ? `${P}${K}` : T[K] extends PluralMessage ? never : Paths<T[K], `${P}${K}.`>;
|
|
24
|
+
}[keyof T & string];
|
|
25
|
+
type PluralPaths<T, P extends string = ""> = {
|
|
26
|
+
[K in keyof T & string]: T[K] extends string ? never : T[K] extends PluralMessage ? `${P}${K}` : PluralPaths<T[K], `${P}${K}.`>;
|
|
27
|
+
}[keyof T & string];
|
|
28
|
+
/** Dotted key of any message, e.g. `subscriptions.title`. */
|
|
29
|
+
export type MessageKey = Paths<BillingUiMessages>;
|
|
30
|
+
/** Dotted key of a count-dependent message, e.g. `interval.every.day`. */
|
|
31
|
+
export type PluralKey = PluralPaths<BillingUiMessages>;
|
|
32
|
+
export type MessageVars = Record<string, string | number>;
|
|
33
|
+
/**
|
|
34
|
+
* Host translation hook. Receives the bare key (`subscriptions.title`,
|
|
35
|
+
* `errors.not_found`); return nothing or the key itself to use ours.
|
|
36
|
+
*/
|
|
37
|
+
export type BillingUiTranslate = (key: string, vars?: MessageVars) => string | null | undefined;
|
|
38
|
+
/** Types a locale bundle or override against the message tree. */
|
|
39
|
+
export declare function defineMessages(bundle: BillingUiMessageBundle): BillingUiMessageBundle;
|
|
40
|
+
export declare const defaultMessages: BillingUiMessages;
|
|
41
|
+
/** Layers bundles over English; later bundles win, missing keys fall back. */
|
|
42
|
+
export declare function resolveMessages(bundles?: BillingUiMessageBundle | readonly BillingUiMessageBundle[]): BillingUiMessages;
|
|
43
|
+
export declare function interpolate(template: string, vars?: MessageVars): string;
|
|
44
|
+
export interface Translator {
|
|
45
|
+
messages: BillingUiMessages;
|
|
46
|
+
/** BCP 47 tag used for plural rules and numbers; the runtime's when unset. */
|
|
47
|
+
locale?: string;
|
|
48
|
+
t(key: MessageKey, vars?: MessageVars): string;
|
|
49
|
+
/**
|
|
50
|
+
* Count-dependent message: tries `<key>.<count>`, then the locale's plural
|
|
51
|
+
* category, then `<key>.other`. `{count}` is locale-formatted.
|
|
52
|
+
*/
|
|
53
|
+
plural(key: PluralKey, count: number, vars?: MessageVars): string;
|
|
54
|
+
/** Message for an billing error, error code, or thrown value. */
|
|
55
|
+
error(error: unknown, vars?: MessageVars): string;
|
|
56
|
+
}
|
|
57
|
+
export declare function createTranslator(messages: BillingUiMessages, hostT?: BillingUiTranslate, locale?: string): Translator;
|
|
58
|
+
export {};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export { BillingUiProvider, type BillingUiProviderProps } from './provider';
|
|
2
|
+
export { BillingUiRoot } from './scope';
|
|
3
|
+
export type { Navigate } from './scope-context';
|
|
4
|
+
export { AccountBilling, type AccountBillingProps, } from './account/account-billing';
|
|
5
|
+
export { SubscriptionsPanel, type SubscriptionsPanelProps, } from './account/subscriptions-panel';
|
|
6
|
+
export { CancelSubscriptionDialog, type CancelSubscriptionDialogProps, } from './account/cancel-dialog';
|
|
7
|
+
export { PaymentMethodsPanel, type PaymentMethodsPanelProps, } from './account/payment-methods-panel';
|
|
8
|
+
export { SavePaymentMethod, type SavePaymentMethodProps, } from './save-payment-method';
|
|
9
|
+
export { authenticatePayment } from './authenticate';
|
|
10
|
+
export { canAuthenticatePayment, canSavePaymentMethod, cardSetupDriver, checkoutPsps, checkoutRails, isCardRail, pspConfigSchema, savedMethodsFor, type CardSetupDriver, type CheckoutRailOffer, type PspConfig, } from './psp';
|
|
11
|
+
export { PaymentHistory, type PaymentHistoryProps, } from './account/payment-history';
|
|
12
|
+
export { BillingStatusBadge, type BillingStatusBadgeProps, } from './account/status-badge';
|
|
13
|
+
export { statusTone, type StatusTone } from './account/format';
|
|
14
|
+
export { createTranslator, defaultMessages, defineMessages, interpolate, resolveMessages, useMessages, type BillingUiMessageBundle, type BillingUiMessages, type BillingUiTranslate, type MessageKey, type PluralKey, type PluralMessage, type MessageVars, type Translator, } from './i18n';
|
|
15
|
+
export { Checkout, type CheckoutLayout, type CheckoutProps } from './checkout';
|
|
16
|
+
export { CheckoutModal, type CheckoutModalProps } from './modal';
|
|
17
|
+
export { CheckoutPage, type CheckoutPageProps } from './checkout-page';
|
|
18
|
+
export { CheckoutFrame, type CheckoutFrameProps } from './checkout-frame';
|
|
19
|
+
export type { CheckoutFrameTheme } from './frame';
|
|
20
|
+
export { CardBrandPlate } from './components/card-brands';
|
|
21
|
+
export { resolveCardBrand, type CardBrand } from './lib/card-brands';
|
|
22
|
+
export { createHttpSource, CheckoutSourceError, type CheckoutSource, } from './source';
|
|
23
|
+
export { createFixtureSource, fixtureSession } from './fixtures';
|
|
24
|
+
export { browserCountry, initialCountry } from './lib/billing';
|
|
25
|
+
export { addAmounts, amountToDecimal, formatAmount, isAmount, type Amount, } from './lib/money';
|
|
26
|
+
export { type CheckoutAppearance, type CheckoutAppearance as BillingAppearance, type CheckoutTheme, type CheckoutVariables, } from './appearance';
|
|
27
|
+
export type { CheckoutLineItem, CheckoutPhase, CheckoutPlan, CheckoutSession, CheckoutSessionStatus, PaymentFailure, PaymentRailOption, PayRequest, PayResult, SavedPaymentMethod, } from './types';
|
|
28
|
+
export { amountSchema, checkoutLineItemSchema, checkoutPlanSchema, checkoutSessionSchema, checkoutSessionStatusSchema, paymentFailureSchema, payRequestSchema, payResultSchema, paymentRailOptionSchema, savedPaymentMethodSchema, unitDecimalsSchema, } from './types';
|
|
29
|
+
export { TokenizedCardForm, type TokenizedCardFormProps, type TokenizedCardData, } from './tokenized-card-form';
|