@openrails/billing-ui 0.0.0-stage → 0.233.2

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 (113) hide show
  1. package/CHANGELOG.md +351 -0
  2. package/LICENSE +21 -0
  3. package/README.md +260 -2
  4. package/dist/account/account-billing.d.ts +19 -0
  5. package/dist/account/cancel-dialog.d.ts +18 -0
  6. package/dist/account/change-card-dialog.d.ts +14 -0
  7. package/dist/account/format.d.ts +25 -0
  8. package/dist/account/notice.d.ts +3 -0
  9. package/dist/account/payment-history.d.ts +8 -0
  10. package/dist/account/payment-methods-panel.d.ts +17 -0
  11. package/dist/account/section.d.ts +27 -0
  12. package/dist/account/status-badge.d.ts +8 -0
  13. package/dist/account/subscriptions-panel.d.ts +19 -0
  14. package/dist/appearance.d.ts +23 -0
  15. package/dist/authenticate.d.ts +8 -0
  16. package/dist/checkout-frame.d.ts +17 -0
  17. package/dist/checkout-page.d.ts +21 -0
  18. package/dist/checkout.d.ts +24 -0
  19. package/dist/client/client.d.ts +181 -0
  20. package/dist/client/errors.d.ts +26 -0
  21. package/dist/client/generated/currencies.d.ts +1 -0
  22. package/dist/client/generated/error-codes.d.ts +7 -0
  23. package/dist/client/generated/routes.d.ts +775 -0
  24. package/dist/client/generated/wire.d.ts +682 -0
  25. package/dist/client/index.d.ts +4 -0
  26. package/dist/client/types.d.ts +800 -0
  27. package/dist/client-DXUPzA8C.js +889 -0
  28. package/dist/client-DXUPzA8C.js.map +1 -0
  29. package/dist/client.js +2 -0
  30. package/dist/components/billing-fields.d.ts +39 -0
  31. package/dist/components/card-brands.d.ts +6 -0
  32. package/dist/components/card-fields.d.ts +29 -0
  33. package/dist/components/ccbill-fields.d.ts +8 -0
  34. package/dist/components/methods.d.ts +13 -0
  35. package/dist/components/pay-button.d.ts +6 -0
  36. package/dist/components/qr.d.ts +6 -0
  37. package/dist/components/saved-methods.d.ts +9 -0
  38. package/dist/components/states.d.ts +10 -0
  39. package/dist/components/stripe-card.d.ts +17 -0
  40. package/dist/components/summary.d.ts +12 -0
  41. package/dist/components/ui/alert-dialog.d.ts +18 -0
  42. package/dist/components/ui/badge.d.ts +7 -0
  43. package/dist/components/ui/button.d.ts +8 -0
  44. package/dist/components/ui/card.d.ts +11 -0
  45. package/dist/components/ui/dialog.d.ts +17 -0
  46. package/dist/components/ui/input.d.ts +3 -0
  47. package/dist/components/ui/label.d.ts +3 -0
  48. package/dist/components/ui/radio-group.d.ts +5 -0
  49. package/dist/components/ui/separator.d.ts +3 -0
  50. package/dist/components/ui/skeleton.d.ts +2 -0
  51. package/dist/components/ui/spinner.d.ts +3 -0
  52. package/dist/components/ui/textarea.d.ts +3 -0
  53. package/dist/config-BQELtBh4.js +341 -0
  54. package/dist/config-BQELtBh4.js.map +1 -0
  55. package/dist/dist-CslWupfi.js +3311 -0
  56. package/dist/dist-CslWupfi.js.map +1 -0
  57. package/dist/fixtures.d.ts +9 -0
  58. package/dist/frame.d.ts +47 -0
  59. package/dist/i18n/context.d.ts +4 -0
  60. package/dist/i18n/index.d.ts +2 -0
  61. package/dist/i18n/messages.d.ts +58 -0
  62. package/dist/index.d.ts +29 -0
  63. package/dist/index.js +4090 -0
  64. package/dist/index.js.map +1 -0
  65. package/dist/lib/billing.d.ts +39 -0
  66. package/dist/lib/card-brands.d.ts +2 -0
  67. package/dist/lib/card-entry.d.ts +46 -0
  68. package/dist/lib/ccbill.d.ts +8 -0
  69. package/dist/lib/collect.d.ts +51 -0
  70. package/dist/lib/money.d.ts +10 -0
  71. package/dist/lib/period.d.ts +13 -0
  72. package/dist/lib/rail-meta.d.ts +15 -0
  73. package/dist/lib/redirect.d.ts +2 -0
  74. package/dist/lib/stripe-appearance.d.ts +2 -0
  75. package/dist/lib/stripe.d.ts +2 -0
  76. package/dist/locales/de.d.ts +2 -0
  77. package/dist/locales/de.js +207 -0
  78. package/dist/locales/de.js.map +1 -0
  79. package/dist/locales/en.d.ts +204 -0
  80. package/dist/locales/en.js +207 -0
  81. package/dist/locales/en.js.map +1 -0
  82. package/dist/locales/es.d.ts +2 -0
  83. package/dist/locales/es.js +207 -0
  84. package/dist/locales/es.js.map +1 -0
  85. package/dist/locales/ja.d.ts +2 -0
  86. package/dist/locales/ja.js +198 -0
  87. package/dist/locales/ja.js.map +1 -0
  88. package/dist/locales/ko.d.ts +2 -0
  89. package/dist/locales/ko.js +198 -0
  90. package/dist/locales/ko.js.map +1 -0
  91. package/dist/locales/zh.d.ts +2 -0
  92. package/dist/locales/zh.js +198 -0
  93. package/dist/locales/zh.js.map +1 -0
  94. package/dist/modal.d.ts +12 -0
  95. package/dist/provider.d.ts +18 -0
  96. package/dist/psp.d.ts +35 -0
  97. package/dist/react/config.d.ts +34 -0
  98. package/dist/react/context.d.ts +35 -0
  99. package/dist/react/hooks.d.ts +82 -0
  100. package/dist/react/index.d.ts +4 -0
  101. package/dist/react/provider.d.ts +10 -0
  102. package/dist/react/remote.d.ts +10 -0
  103. package/dist/react.js +33 -0
  104. package/dist/react.js.map +1 -0
  105. package/dist/save-payment-method.d.ts +26 -0
  106. package/dist/scope-context.d.ts +17 -0
  107. package/dist/scope.d.ts +6 -0
  108. package/dist/source.d.ts +5 -0
  109. package/dist/styles.css +3 -0
  110. package/dist/tokenized-card-form.d.ts +24 -0
  111. package/dist/types.d.ts +250 -0
  112. package/docs/payment-form-contract.md +136 -0
  113. package/package.json +112 -3
@@ -0,0 +1,250 @@
1
+ import { z } from 'zod';
2
+ export declare const amountSchema: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
3
+ export declare const unitDecimalsSchema: z.ZodNumber & z.ZodType<number, number, z.core.$ZodTypeInternals<number, number>>;
4
+ export declare const checkoutSessionStatusSchema: z.ZodEnum<{
5
+ canceled: "canceled";
6
+ succeeded: "succeeded";
7
+ failed: "failed";
8
+ expired: "expired";
9
+ created: "created";
10
+ requires_action: "requires_action";
11
+ processing: "processing";
12
+ blocked: "blocked";
13
+ }>;
14
+ export type CheckoutSessionStatus = z.infer<typeof checkoutSessionStatusSchema>;
15
+ export declare const paymentFailureSchema: z.ZodObject<{
16
+ reason: z.ZodString;
17
+ message: z.ZodString;
18
+ field: z.ZodOptional<z.ZodNullable<z.ZodString>>;
19
+ }, z.core.$strip>;
20
+ export type PaymentFailure = z.infer<typeof paymentFailureSchema>;
21
+ export declare const checkoutOperationSchema: z.ZodObject<{
22
+ id: z.ZodString;
23
+ status: z.ZodString;
24
+ }, z.core.$strip>;
25
+ export declare const paymentOptionSchema: z.ZodObject<{
26
+ id: z.ZodString;
27
+ psp_id: z.ZodOptional<z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>>;
28
+ rail: z.ZodString;
29
+ mode: z.ZodEnum<{
30
+ subscription: "subscription";
31
+ one_off: "one_off";
32
+ }>;
33
+ driver: z.ZodEnum<{
34
+ card: "card";
35
+ collect_js: "collect_js";
36
+ stripe_elements: "stripe_elements";
37
+ redirect: "redirect";
38
+ solana_pay: "solana_pay";
39
+ }>;
40
+ psp_key: z.ZodOptional<z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>>;
41
+ public_config: z.ZodOptional<z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodString>>>, z.ZodTransform<Record<string, string> | undefined, Record<string, string> | null | undefined>>>;
42
+ }, z.core.$strip>;
43
+ export type PaymentOption = z.infer<typeof paymentOptionSchema>;
44
+ export declare const nextActionSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
45
+ type: z.ZodLiteral<"redirect_to_url">;
46
+ url: z.ZodString;
47
+ }, z.core.$strip>, z.ZodObject<{
48
+ type: z.ZodLiteral<"solana_pay">;
49
+ url: z.ZodString;
50
+ }, z.core.$strip>], "type">;
51
+ export type NextAction = z.infer<typeof nextActionSchema>;
52
+ export declare const savedPaymentMethodSchema: z.ZodObject<{
53
+ id: z.ZodString;
54
+ option_id: z.ZodString;
55
+ rail: z.ZodString;
56
+ card: z.ZodOptional<z.ZodNullable<z.ZodObject<{
57
+ brand: z.ZodOptional<z.ZodNullable<z.ZodString>>;
58
+ last4: z.ZodOptional<z.ZodNullable<z.ZodString>>;
59
+ exp_month: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
60
+ exp_year: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
61
+ }, z.core.$strip>>>;
62
+ }, z.core.$strip>;
63
+ export type SavedPaymentMethod = z.infer<typeof savedPaymentMethodSchema>;
64
+ export declare const checkoutLineItemSchema: z.ZodObject<{
65
+ label: z.ZodString;
66
+ sublabel: z.ZodOptional<z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>>;
67
+ amount: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
68
+ }, z.core.$strip>;
69
+ export type CheckoutLineItem = z.infer<typeof checkoutLineItemSchema>;
70
+ export declare const checkoutPlanSchema: z.ZodObject<{
71
+ auto_renew: z.ZodBoolean;
72
+ display_name: z.ZodString;
73
+ unit_amount: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
74
+ currency: z.ZodString;
75
+ unit_decimals: z.ZodNumber & z.ZodType<number, number, z.core.$ZodTypeInternals<number, number>>;
76
+ billing_interval_hours: z.ZodOptional<z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodNumber>>, z.ZodTransform<number | undefined, number | null | undefined>>>;
77
+ access_duration_hours: z.ZodOptional<z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodNumber>>, z.ZodTransform<number | undefined, number | null | undefined>>>;
78
+ }, z.core.$strip>;
79
+ export type CheckoutPlan = z.infer<typeof checkoutPlanSchema>;
80
+ export declare const checkoutSessionSchema: z.ZodObject<{
81
+ id: z.ZodString;
82
+ status: z.ZodEnum<{
83
+ canceled: "canceled";
84
+ succeeded: "succeeded";
85
+ failed: "failed";
86
+ expired: "expired";
87
+ created: "created";
88
+ requires_action: "requires_action";
89
+ processing: "processing";
90
+ blocked: "blocked";
91
+ }>;
92
+ merchant: z.ZodObject<{
93
+ display_name: z.ZodString;
94
+ }, z.core.$strip>;
95
+ plan: z.ZodObject<{
96
+ auto_renew: z.ZodBoolean;
97
+ display_name: z.ZodString;
98
+ unit_amount: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
99
+ currency: z.ZodString;
100
+ unit_decimals: z.ZodNumber & z.ZodType<number, number, z.core.$ZodTypeInternals<number, number>>;
101
+ billing_interval_hours: z.ZodOptional<z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodNumber>>, z.ZodTransform<number | undefined, number | null | undefined>>>;
102
+ access_duration_hours: z.ZodOptional<z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodNumber>>, z.ZodTransform<number | undefined, number | null | undefined>>>;
103
+ }, z.core.$strip>;
104
+ line_items: z.ZodOptional<z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodObject<{
105
+ label: z.ZodString;
106
+ sublabel: z.ZodOptional<z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>>;
107
+ amount: z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>;
108
+ }, z.core.$strip>>>>, z.ZodTransform<{
109
+ label: string;
110
+ amount: string;
111
+ sublabel?: string | undefined;
112
+ }[] | undefined, {
113
+ label: string;
114
+ amount: string;
115
+ sublabel?: string | undefined;
116
+ }[] | null | undefined>>>;
117
+ tax: z.ZodOptional<z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>>>, z.ZodTransform<string | undefined, string | null | undefined>>>;
118
+ due_today: z.ZodOptional<z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString & z.ZodType<string, string, z.core.$ZodTypeInternals<string, string>>>>, z.ZodTransform<string | undefined, string | null | undefined>>>;
119
+ options: z.ZodArray<z.ZodObject<{
120
+ id: z.ZodString;
121
+ psp_id: z.ZodOptional<z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>>;
122
+ rail: z.ZodString;
123
+ mode: z.ZodEnum<{
124
+ subscription: "subscription";
125
+ one_off: "one_off";
126
+ }>;
127
+ driver: z.ZodEnum<{
128
+ card: "card";
129
+ collect_js: "collect_js";
130
+ stripe_elements: "stripe_elements";
131
+ redirect: "redirect";
132
+ solana_pay: "solana_pay";
133
+ }>;
134
+ psp_key: z.ZodOptional<z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>>;
135
+ public_config: z.ZodOptional<z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodString>>>, z.ZodTransform<Record<string, string> | undefined, Record<string, string> | null | undefined>>>;
136
+ }, z.core.$strip>>;
137
+ saved_methods: z.ZodOptional<z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodObject<{
138
+ id: z.ZodString;
139
+ option_id: z.ZodString;
140
+ rail: z.ZodString;
141
+ card: z.ZodOptional<z.ZodNullable<z.ZodObject<{
142
+ brand: z.ZodOptional<z.ZodNullable<z.ZodString>>;
143
+ last4: z.ZodOptional<z.ZodNullable<z.ZodString>>;
144
+ exp_month: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
145
+ exp_year: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
146
+ }, z.core.$strip>>>;
147
+ }, z.core.$strip>>>>, z.ZodTransform<{
148
+ id: string;
149
+ option_id: string;
150
+ rail: string;
151
+ card?: {
152
+ brand?: string | null | undefined;
153
+ last4?: string | null | undefined;
154
+ exp_month?: number | null | undefined;
155
+ exp_year?: number | null | undefined;
156
+ } | null | undefined;
157
+ }[] | undefined, {
158
+ id: string;
159
+ option_id: string;
160
+ rail: string;
161
+ card?: {
162
+ brand?: string | null | undefined;
163
+ last4?: string | null | undefined;
164
+ exp_month?: number | null | undefined;
165
+ exp_year?: number | null | undefined;
166
+ } | null | undefined;
167
+ }[] | null | undefined>>>;
168
+ next_action: z.ZodOptional<z.ZodNullable<z.ZodDiscriminatedUnion<[z.ZodObject<{
169
+ type: z.ZodLiteral<"redirect_to_url">;
170
+ url: z.ZodString;
171
+ }, z.core.$strip>, z.ZodObject<{
172
+ type: z.ZodLiteral<"solana_pay">;
173
+ url: z.ZodString;
174
+ }, z.core.$strip>], "type">>>;
175
+ payment_id: z.ZodOptional<z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>>;
176
+ subscription_id: z.ZodOptional<z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>>;
177
+ failure_message: z.ZodOptional<z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>>;
178
+ failure: z.ZodOptional<z.ZodNullable<z.ZodObject<{
179
+ reason: z.ZodString;
180
+ message: z.ZodString;
181
+ field: z.ZodOptional<z.ZodNullable<z.ZodString>>;
182
+ }, z.core.$strip>>>;
183
+ operation: z.ZodOptional<z.ZodNullable<z.ZodObject<{
184
+ id: z.ZodString;
185
+ status: z.ZodString;
186
+ }, z.core.$strip>>>;
187
+ success_url: z.ZodOptional<z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>>;
188
+ embed_origin: z.ZodOptional<z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>>;
189
+ expires_at: z.ZodOptional<z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>>;
190
+ }, z.core.$strip>;
191
+ export type CheckoutSession = z.infer<typeof checkoutSessionSchema>;
192
+ export declare const payRequestSchema: z.ZodObject<{
193
+ option_id: z.ZodString;
194
+ payment_token: z.ZodOptional<z.ZodString>;
195
+ payment_method_id: z.ZodOptional<z.ZodString>;
196
+ card: z.ZodOptional<z.ZodObject<{
197
+ number: z.ZodString;
198
+ exp_month: z.ZodNumber;
199
+ exp_year: z.ZodNumber;
200
+ cvc: z.ZodString;
201
+ }, z.core.$strip>>;
202
+ billing_details: z.ZodOptional<z.ZodObject<{
203
+ name: z.ZodOptional<z.ZodString>;
204
+ email: z.ZodOptional<z.ZodString>;
205
+ phone: z.ZodOptional<z.ZodString>;
206
+ address: z.ZodOptional<z.ZodObject<{
207
+ line1: z.ZodOptional<z.ZodString>;
208
+ line2: z.ZodOptional<z.ZodString>;
209
+ city: z.ZodOptional<z.ZodString>;
210
+ state: z.ZodOptional<z.ZodString>;
211
+ postal_code: z.ZodOptional<z.ZodString>;
212
+ country: z.ZodOptional<z.ZodString>;
213
+ }, z.core.$strip>>;
214
+ }, z.core.$strip>>;
215
+ token_symbol: z.ZodOptional<z.ZodString>;
216
+ }, z.core.$strip>;
217
+ export type PayRequest = z.infer<typeof payRequestSchema>;
218
+ export declare const payResultSchema: z.ZodObject<{
219
+ status: z.ZodEnum<{
220
+ canceled: "canceled";
221
+ succeeded: "succeeded";
222
+ failed: "failed";
223
+ expired: "expired";
224
+ created: "created";
225
+ requires_action: "requires_action";
226
+ processing: "processing";
227
+ blocked: "blocked";
228
+ }>;
229
+ next_action: z.ZodOptional<z.ZodNullable<z.ZodDiscriminatedUnion<[z.ZodObject<{
230
+ type: z.ZodLiteral<"redirect_to_url">;
231
+ url: z.ZodString;
232
+ }, z.core.$strip>, z.ZodObject<{
233
+ type: z.ZodLiteral<"solana_pay">;
234
+ url: z.ZodString;
235
+ }, z.core.$strip>], "type">>>;
236
+ operation: z.ZodOptional<z.ZodNullable<z.ZodObject<{
237
+ id: z.ZodString;
238
+ status: z.ZodString;
239
+ }, z.core.$strip>>>;
240
+ payment_id: z.ZodOptional<z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>>;
241
+ subscription_id: z.ZodOptional<z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>>;
242
+ failure_message: z.ZodOptional<z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | undefined, string | null | undefined>>>;
243
+ failure: z.ZodOptional<z.ZodNullable<z.ZodObject<{
244
+ reason: z.ZodString;
245
+ message: z.ZodString;
246
+ field: z.ZodOptional<z.ZodNullable<z.ZodString>>;
247
+ }, z.core.$strip>>>;
248
+ }, z.core.$strip>;
249
+ export type PayResult = z.infer<typeof payResultSchema>;
250
+ export type CheckoutPhase = "loading" | "ready" | "processing" | "succeeded" | "failed" | "blocked" | "expired" | "unavailable" | "error";
@@ -0,0 +1,136 @@
1
+ # Payment form contract
2
+
3
+ `Checkout` renders one real `<form autocomplete="on">` and submits through its
4
+ native submit event. Every host-owned billing field has a stable `id`, `name`,
5
+ label, and standards-based autocomplete token.
6
+
7
+ ## Money
8
+
9
+ The session document (`GET /billing/v1/checkout-sessions/{id}`) carries every amount
10
+ as an exact signed int64 decimal string of `plan.currency`'s native unit, and
11
+ `plan.unit_decimals` is that currency's registered scale:
12
+
13
+ ```json
14
+ {
15
+ "plan": {
16
+ "display_name": "Premium Membership",
17
+ "unit_amount": "99000000",
18
+ "currency": "USD",
19
+ "unit_decimals": 6,
20
+ "auto_renew": true,
21
+ "billing_interval_hours": 720,
22
+ "access_duration_hours": 720
23
+ },
24
+ "line_items": [{ "label": "Premium Membership", "amount": "99000000" }],
25
+ "tax": "0",
26
+ "due_today": "99000000"
27
+ }
28
+ ```
29
+
30
+ `unit_amount`, `line_items[].amount`, `tax` and `due_today` share the plan's
31
+ currency and scale. A JSON number, a decimal point, a value outside int64, or
32
+ a missing `unit_decimals` fails schema validation and the session is
33
+ unavailable; the package never assumes a scale. Hosts take the scale from
34
+ OpenRails' currency registry (`billing.LookupCurrency`, the same table as
35
+ the `currencies` of `GET /v1/config`); OpenRails serves the session at
36
+ `GET /v1/checkout-sessions/{id}`; its canonical fixture
37
+ (`testdata/wire/checkout_session.json`) is decoded by this package's tests.
38
+
39
+ Rendering scales with `BigInt` and hands Intl the exact major-unit decimal;
40
+ `formatAmount`, `amountToDecimal` and `addAmounts` are exported for hosts that
41
+ show the same figures outside the component. An amount the engine cannot show
42
+ exactly is refused with a visible notice, never rounded. `due_today`, when
43
+ absent, is the exact sum of the line items and `tax`.
44
+
45
+ ## NMI new cards
46
+
47
+ The host collects only:
48
+
49
+ - the cardholder's name: one visible “Name on card” input with `autocomplete="cc-name"`;
50
+ - the country: a native ISO-3166 country select with `autocomplete="billing country"`;
51
+ - the postal code: a country-aware input with `autocomplete="billing postal-code"`.
52
+
53
+ Postal code is optional for the 53 ISO-3166 alpha-2 countries and territories
54
+ in the Universal Postal Union's September 2025
55
+ [list of countries which do not require postal codes](https://www.upu.int/UPU/media/upu/documents/PostCode/General-Addressing-Issues.pdf).
56
+ The UI keeps a postal code when the customer supplies one. It does not collect
57
+ street, city, or state for NMI.
58
+
59
+ Card number, expiration, and CVC remain inside NMI Collect.js cross-origin
60
+ iframes. NMI owns those inner inputs, including their browser-autofill behavior;
61
+ host markup cannot assign `cc-number`, `cc-exp`, or `cc-csc` to them. The
62
+ Collect.js configuration supplies the supported field titles and placeholders,
63
+ and the outer host page provides stable labelled containers. Verify saved-card
64
+ autofill against the real gateway in each supported browser because a DOM unit
65
+ test cannot inspect or control the cross-origin fields.
66
+
67
+ A PSP that takes cards on OpenRails itself (`card_entry: server`, driver
68
+ `card`) gets plain inputs instead, with `autocomplete` `cc-number`, `cc-exp`
69
+ and `cc-csc`, the same layout and field errors, and no gateway script. Its
70
+ request carries `card: {number, exp_month, exp_year, cvc}` in place of
71
+ `payment_token`; the inputs are cleared once the card is sent.
72
+
73
+ A new-card payment request includes the one-time `payment_token` plus
74
+ `billing_details`: `name`, and `address` with the uppercase ISO `country` and
75
+ `postal_code`. Paying with an
76
+ existing saved method sends only `payment_method_id`; it does not overwrite the
77
+ stored billing identity with empty form values.
78
+
79
+ The package never shows separate first- and last-name inputs; OpenRails
80
+ projects the one name onto provider-specific fields at the rail boundary.
81
+
82
+ ## Card subscriptions
83
+
84
+ `Subscribe` is one action: paying the session with a `payment_token` (or a
85
+ `payment_method_id`) saves a new card, accepts the displayed recurring terms
86
+ and charges it. A decline answers `failed` and keeps no card.
87
+
88
+ ## Solana Pay
89
+
90
+ The package never assumes which token a Solana option settles in. OpenRails
91
+ binds the token on the advertised option's `public_config` (hosts copy it):
92
+
93
+ - `token_symbol` — required; the SPL mint symbol the price is bound to
94
+ (`USDC`, `USD1`, …). It is sent back verbatim (uppercased) as the pay
95
+ request's `token_symbol`. An option without it is not offered at all.
96
+ - `token_name` — optional buyer-facing name (`USD Coin`), shown next to the
97
+ symbol in the method list.
98
+ - `network` — optional Solana cluster (`mainnet-beta`, `devnet`, `testnet`).
99
+ Any network other than `mainnet-beta` is named in the method hint
100
+ (“USD Coin (USDC) on Solana devnet”) so a test-network payment is never
101
+ mistaken for a real one.
102
+
103
+ ## Pending payment outcomes
104
+
105
+ `processing` means an accepted outcome is unresolved. Checkout polls the same
106
+ source's `getSession` without calling `pay` or Collect.js again. The optional
107
+ `expires_at` may be null for an accepted pending attempt; a local display timer
108
+ never releases or fails that attempt. The host must return verified terminal
109
+ state, not infer payment from navigation or a timeout. A thrown error after
110
+ `pay` starts is treated as unknown and remains pending, rather than enabling
111
+ a fresh card token and payment. Tokenization/field validation errors before
112
+ submission remain editable. Explicit `failed` results are definitive host
113
+ responses; they must not represent an unknown provider result.
114
+
115
+ `client.checkoutSource(id)` is the source of every checkout: it reads and pays
116
+ the OpenRails session. `getSession` is a read, never another payment. With the
117
+ session id alone it sends no bearer, so it pays with a new card only. A
118
+ signed-in customer pays with a saved card through a customer surface:
119
+ `client.checkoutSource(id, { customerBase: "/billing/v1/me" })` reads and pays
120
+ at that surface's `/checkout-sessions/{id}` with the client's credential.
121
+
122
+ ## Separate NMI card setup
123
+
124
+ `TokenizedCardForm` reuses the same hosted fields and emits `payment_token`,
125
+ `name_on_card`, `country` and `zip` to its async `onTokenized` callback. The host
126
+ provides explicit save-card consent and uses the callback to invoke its native
127
+ authenticated save-card endpoint. Set `disabled` until consent is given. The
128
+ form never creates a zero-dollar checkout or announces a completed payment.
129
+ After token submission it locks the form; a failed/unknown save must be checked
130
+ against the host's saved-method state before mounting a new attempt.
131
+
132
+ Saving a card does not authorize a recurring charge. The host separately obtains
133
+ and displays the immutable membership quote, then asks the signed-in customer
134
+ to confirm that agreement.
135
+
136
+ Hosts must key `TokenizedCardForm` by authenticated customer and provider identity. A customer change must unmount the old form. The form also discards an in-flight token if its tokenization configuration changes or save consent is withdrawn before dispatch.
package/package.json CHANGED
@@ -1,6 +1,115 @@
1
1
  {
2
2
  "name": "@openrails/billing-ui",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.233.2",
4
+ "description": "Embeddable OpenRails checkout and account-billing UI: client, React hooks and styled components.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/open-rails/openrails.git",
10
+ "directory": "sdk/billing-ui"
11
+ },
12
+ "homepage": "https://github.com/open-rails/openrails/tree/master/sdk/billing-ui#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/open-rails/openrails/issues"
15
+ },
16
+ "engines": {
17
+ "node": ">=22.12.0"
18
+ },
19
+ "files": [
20
+ "dist",
21
+ "docs",
22
+ "CHANGELOG.md",
23
+ "LICENSE",
24
+ "README.md"
25
+ ],
26
+ "sideEffects": [
27
+ "./dist/index.js",
28
+ "**/*.css"
29
+ ],
30
+ "exports": {
31
+ ".": {
32
+ "types": "./dist/index.d.ts",
33
+ "import": "./dist/index.js"
34
+ },
35
+ "./client": {
36
+ "types": "./dist/client/index.d.ts",
37
+ "import": "./dist/client.js"
38
+ },
39
+ "./react": {
40
+ "types": "./dist/react/index.d.ts",
41
+ "import": "./dist/react.js"
42
+ },
43
+ "./locales/*": {
44
+ "types": "./dist/locales/*.d.ts",
45
+ "import": "./dist/locales/*.js"
46
+ },
47
+ "./styles.css": "./dist/styles.css",
48
+ "./package.json": "./package.json"
49
+ },
50
+ "main": "./dist/index.js",
51
+ "module": "./dist/index.js",
52
+ "types": "./dist/index.d.ts",
53
+ "style": "./dist/styles.css",
54
+ "publishConfig": {
55
+ "access": "public",
56
+ "provenance": false
57
+ },
58
+ "peerDependencies": {
59
+ "react": ">=19 <20",
60
+ "react-dom": ">=19 <20"
61
+ },
62
+ "dependencies": {
63
+ "@base-ui/react": "^1.7.0",
64
+ "@hugeicons/core-free-icons": "^4.2.3",
65
+ "@hugeicons/react": "^1.1.9",
66
+ "@stripe/stripe-js": "^9.17.0",
67
+ "class-variance-authority": "^0.7.1",
68
+ "cn": "0.4.0",
69
+ "zod": "^4.4.3"
70
+ },
71
+ "devDependencies": {
72
+ "@eslint/js": "^10.0.1",
73
+ "@microsoft/api-extractor": "^7.58.12",
74
+ "@playwright/test": "1.60.0",
75
+ "@solana/kit": "^6.10.0",
76
+ "@solana/pay": "^1.0.26",
77
+ "@solana/sysvars": "^5.5.1",
78
+ "@tailwindcss/vite": "^4.3.3",
79
+ "@testing-library/jest-dom": "^7.0.0",
80
+ "@testing-library/react": "^16.3.2",
81
+ "@types/node": "^26.2.0",
82
+ "@types/react": "^19.2.18",
83
+ "@types/react-dom": "^19.2.4",
84
+ "@vitejs/plugin-react": "^6.0.5",
85
+ "eslint": "^10.8.1",
86
+ "eslint-plugin-react-hooks": "^7.1.1",
87
+ "eslint-plugin-react-refresh": "^0.5.3",
88
+ "globals": "^17.9.0",
89
+ "jsdom": "^30.0.1",
90
+ "postcss": "8.5.26",
91
+ "prettier": "^3.9.6",
92
+ "prettier-plugin-tailwindcss": "^0.8.1",
93
+ "react": "^19.2.8",
94
+ "react-dom": "^19.2.8",
95
+ "tailwindcss": "^4.3.3",
96
+ "tw-animate-css": "^1.4.0",
97
+ "typescript": "^5.9.3",
98
+ "typescript-eslint": "^8.66.0",
99
+ "vite": "^8.2.1",
100
+ "vite-plugin-dts": "^5.0.3",
101
+ "vitest": "^4.1.11"
102
+ },
103
+ "scripts": {
104
+ "build": "vite build",
105
+ "typecheck": "tsc --noEmit",
106
+ "lint": "eslint .",
107
+ "test": "vitest run",
108
+ "test:browser": "playwright test",
109
+ "test:e2e": "playwright test -c playwright.e2e.config.ts",
110
+ "contract": "cd ../.. && go run ./scripts/contracts -write",
111
+ "contract:check": "cd ../.. && go run ./scripts/contracts",
112
+ "format": "prettier --write \"**/*.{css,ts,tsx,json,md}\"",
113
+ "check": "pnpm typecheck && pnpm lint && pnpm test && pnpm build"
114
+ }
6
115
  }