@openrails/billing-ui 0.237.0 → 0.239.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 CHANGED
@@ -1,5 +1,62 @@
1
1
  # Changelog
2
2
 
3
+ ## Public products and staff route groups
4
+
5
+ - Breaking: public `listProducts()` now reads `GET /products`. Catalog
6
+ management uses `/catalog`, merchant configuration `/merchant`, and metrics
7
+ `/metrics`; customer support stays under `/admin`.
8
+
9
+ ## Provider operations removed
10
+
11
+ - Breaking: `OpenRailsErrorCode` and `OPENRAILS_ERROR_CODES` lose
12
+ `provider_operation_not_found`, `provider_operation_conflict`,
13
+ `provider_operation_not_open`, `provider_operation_has_billing_evidence`,
14
+ `provider_operation_refused`, `provider_operation_not_refused` and
15
+ `provider_billing_observation_conflict`.
16
+
17
+ ## Usage report
18
+
19
+ - `getUsage({ currency, groupBy, from, to, limit, cursor })` reads a page of
20
+ the customer's usage (`GET /me/usage`): `Usage`, its `rows` by total
21
+ amount, largest first, and `next_cursor` (null on the last page). A page
22
+ holds at most 1,000 groups (default 100); a cursor reads only the report it
23
+ came from.
24
+
25
+ ## Public reads name their merchant; token source
26
+
27
+ - Breaking: `listProducts({ merchant, keys })`, `useProducts({ merchant, keys
28
+ })` and `<BuyButton merchant>` take the merchant's id, and `<Offers>` takes
29
+ it with `keys`: `GET /catalog/products?merchant=<id>&keys=…`. The read sends
30
+ no bearer, signed in or out.
31
+ - Breaking: `getConfig()` and `useConfig()` are the deployment's part alone
32
+ (`capabilities`, `currencies`, `captcha`, `network`); `PublicConfig` loses
33
+ `payment` and `merchant`. `getCustomerConfig()` and `useCustomerConfig()`
34
+ read the customer's merchant (`GET /me/config`, `CustomerConfig`: `payment`
35
+ and `merchant`); `<OrderCheckout>`, `<AccountBilling>` and
36
+ `<PaymentMethodsPanel>` take their PSPs from it.
37
+ - Breaking: `createConfigStore(read)` takes the read it shares, and
38
+ `ConfigStore` and `ConfigState` name what they hold (`ConfigStore<T>`).
39
+ - `tokenSource({ fetchToken, skew = 60 })` is a `getToken` that keeps the
40
+ customer's access token in memory (never in browser storage), replaces it
41
+ `skew` seconds before it expires with one request in flight, and has
42
+ `invalidate()`. With one, the client sends a call answered 401
43
+ `credential_expired` once more with a new token.
44
+ - `redeemAssertion({ tokenEndpoint, assertion, fetch })` redeems the
45
+ assertion a merchant's backend signed for its customer (RFC 7523,
46
+ `scope=openrails:self`) with the fetch it is given, so a DPoP fetch binds
47
+ the token to the browser's key.
48
+
49
+ ## Payment operations removed
50
+
51
+ - Breaking: `getPaymentAuthentication`, `confirmPaymentAuthentication`,
52
+ `authenticatePayment`, `PaymentOperation` and `PaymentAuthentication` are
53
+ removed, with `operation_id` on `SubscriptionChange` and `operation` on
54
+ `PaymentRecovery`. A change the issuer challenges answers an `authenticate`
55
+ `next_action`; `authenticateChange(client, id, change, psp)` completes it
56
+ and confirms the change (`confirmSubscriptionChange`).
57
+ - `NextAction` is one shape for orders, changes and subscriptions (`type`,
58
+ `url`, `psp_id`, `transactions`, `payload`); `OrderNextAction` is gone.
59
+
3
60
  ## Running charges
4
61
 
5
62
  - `Balance` gains `accrued_amount`, `accrual_rate_per_hour`, `as_of` and
@@ -235,15 +292,6 @@
235
292
  product on sale with its current prices. Filter by currency, product or
236
293
  renewal in the browser.
237
294
 
238
- ## Paying on a customer surface
239
-
240
- - `client.checkoutSource(id, { customerBase })` reads and pays a checkout
241
- session as its signed-in customer at a customer surface's
242
- `/checkout-sessions/{id}` (for example a host's
243
- `/api/v1/merchants/acme/billing/me`), so its saved cards pay. Without
244
- `customerBase` the session id alone pays at `/checkout-sessions/{id}` as
245
- before. `<CheckoutPage customerBase>` passes it through.
246
-
247
295
  ## 1.0.0
248
296
 
249
297
  Ships with OpenRails v1.0.0; use the two at the same version. Everything a host
package/README.md CHANGED
@@ -42,11 +42,33 @@ const billing = createBillingClient({ baseUrl: "/billing/v1", fetch: auth.authFe
42
42
  </BillingProvider>
43
43
  ```
44
44
 
45
+ Where your backend mints the customer's token, `tokenSource` keeps it: in
46
+ memory only, replaced `skew` seconds (60) before it expires with one request
47
+ however many calls wait, and once more when a call answers 401
48
+ `credential_expired`.
49
+
50
+ ```ts
51
+ import { createBillingClient, redeemAssertion, tokenSource } from "@openrails/billing-ui"
52
+
53
+ const billing = createBillingClient({
54
+ baseUrl: "https://billing.example/v1",
55
+ getToken: tokenSource({
56
+ // { access_token, expires_in } from your backend, or its signed assertion redeemed here.
57
+ fetchToken: async () => {
58
+ const { assertion } = await (await fetch("/api/billing/assertion", { method: "POST" })).json()
59
+ return redeemAssertion({ tokenEndpoint: "https://billing.example/oauth2/token", assertion })
60
+ },
61
+ }),
62
+ })
63
+ ```
64
+
45
65
  `<BuyButton>` buys one price, `<Offers>` the products your server names by
46
66
  key (a course, its bundle and the membership that also unlocks it; the Go
47
67
  `Client.ListOffers` finds the products granting an entitlement): a
48
68
  `BuyButton` per price ("$4.99", "Rent for 3 days, $1.99",
49
- "$10.00 every 30 days"). The purchase is theirs from start to payment: a
69
+ "$10.00 every 30 days"). Both look prices up in the public catalog of the
70
+ merchant whose id you pass as `merchant`, signed in or out. The purchase is
71
+ theirs from start to payment: a
50
72
  button creates the customer's order and pays it in a dialog
51
73
  (`<OrderCheckout>`); a second click before it is paid opens the same order.
52
74
  Signing in is the host's: with `signedIn={false}`, or when OpenRails answers
@@ -56,6 +78,7 @@ granting anything.
56
78
 
57
79
  ```tsx
58
80
  <BuyButton
81
+ merchant={merchantId} // your merchant's id, from your server
59
82
  product="course-101"
60
83
  price="rent" // labelled from the catalog unless you pass label
61
84
  signedIn={signedIn}
@@ -64,6 +87,7 @@ granting anything.
64
87
  />
65
88
 
66
89
  <Offers
90
+ merchant={merchantId}
67
91
  keys={["course-101", "course-bundle", "channel-membership"]} // from your server, or products={...} from its Client.ListOffers
68
92
  signedIn={signedIn}
69
93
  onSignInRequired={() => openSignIn()}
@@ -87,7 +111,7 @@ const amount = decimalToAmount(enteredAmount, currency.decimals)
87
111
  const bounds = depositPrice.customer_amount
88
112
  const valid = amount && bounds && BigInt(amount) >= BigInt(bounds.min_amount) && BigInt(amount) <= BigInt(bounds.max_amount)
89
113
 
90
- <BuyButton product="api-credits" price="deposit" amount={valid ? amount : undefined} label="Add credits" {...handlers} />
114
+ <BuyButton merchant={merchantId} product="api-credits" price="deposit" amount={valid ? amount : undefined} label="Add credits" {...handlers} />
91
115
  ```
92
116
 
93
117
  OpenRails validates the bounds and whole currency minor units before accepting
@@ -140,23 +164,25 @@ const billing = createBillingClient({ baseUrl: "/billing/v1", fetch: auth.authFe
140
164
  </BillingProvider>
141
165
  ```
142
166
 
143
- The panels read OpenRails' public configuration (`GET /config`) themselves,
144
- once per `BillingProvider`: "Add card" offers each PSP of its `payment` that
145
- saves a card in the page, and says so while the configuration fails or its
146
- card PSPs are temporarily unavailable; amounts use its currency registry over
147
- the pinned copy. `useConfig()` and `useCurrencyScales()` read the same copy
148
- for host components.
167
+ The panels read OpenRails' configuration themselves, once per
168
+ `BillingProvider`: the customer's merchant's (`GET /me/config`), where "Add
169
+ card" offers each PSP of its `payment` that saves a card in the page, and says
170
+ so while the read fails or its card PSPs are temporarily unavailable; and the
171
+ deployment's (`GET /config`), whose currency registry amounts use over the
172
+ pinned copy. `useCustomerConfig()`, `useConfig()` and `useCurrencyScales()`
173
+ read the same copies for host components.
149
174
 
150
175
  ## Catalog and plan changes
151
176
 
152
177
  The client also reads the public catalog and changes a subscription's plan.
153
- The catalog read is a public route, served only when the host mounts
154
- OpenRails' checkout routes; `GET /config` is always served.
178
+ The catalog read and `GET /config` are public routes: the client sends them
179
+ no bearer.
155
180
 
156
181
  | Call | Route |
157
182
  | --------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- |
158
- | `listProducts({ keys })`: the products on sale named by `keys` (1 to 100 product keys), each with its prices, in one page; `useProducts` reads the same | `GET /catalog/products` |
159
- | `getConfig()` (capabilities, `currencies`, `payment` with PSPs, `solana.network` and tokens; `client.currencies` is the pinned registry) | `GET /config` |
183
+ | `listProducts({ merchant, keys })`: the products `merchant` (its id) has on sale named by `keys` (1 to 100 product keys), each with its prices, in one page; `useProducts` reads the same | `GET /products` |
184
+ | `getConfig()` (capabilities, `currencies`, `captcha`; `client.currencies` is the pinned registry) | `GET /config` |
185
+ | `getCustomerConfig()` (the customer's merchant: `payment` with PSPs, `solana.network` and tokens, and `merchant`, its name) | `GET /me/config` |
160
186
  | `previewSubscriptionChange(id, { priceId?, quantity? })` | `POST /me/subscriptions/{id}/change/preview` |
161
187
  | `changeSubscription(id, { priceId?, quantity?, idempotencyKey, signature? })` | `POST /me/subscriptions/{id}/change` |
162
188
 
@@ -173,10 +199,11 @@ const change = await billing.changeSubscription(sub.id, {
173
199
  charge now; fewer seats and a downgrade apply at the next renewal.
174
200
  - `changeSubscription` is a money write. Mint one `idempotencyKey` per attempt and
175
201
  reuse it until the change resolves (`status: "processing"`, a
176
- `subscription_change_in_flight` refusal naming `metadata.operation_id`, a 5xx or a
177
- lost response), so OpenRails replays the stored result instead of charging
178
- twice. `requires_action` carries `operation_id` for `authenticatePayment`;
179
- replay the same key afterwards.
202
+ `subscription_change_in_flight` refusal, a 5xx or a lost response), so
203
+ OpenRails replays the stored result instead of charging twice. A change the
204
+ card issuer challenges answers `requires_action` with an `authenticate`
205
+ `next_action`: `authenticateChange(client, id, change, psp)` runs it in the
206
+ page and confirms the change (`confirmSubscriptionChange`).
180
207
  - A rail that needs the customer's own step answers `requires_action` with a
181
208
  `next_action`. Solana's is `solana_sign_transactions`:
182
209
  `signWalletAction(change.next_action, send)` signs and sends its
@@ -188,7 +215,7 @@ const change = await billing.changeSubscription(sub.id, {
188
215
 
189
216
  ## Provider-neutral flows
190
217
 
191
- OpenRails's browser PSP configs (`GET /config`'s `payment.psps`) pick the
218
+ OpenRails's browser PSP configs (`GET /me/config`'s `payment.psps`) pick the
192
219
  browser flow from each PSP's `flow` and public `config`
193
220
  (Collect.js, native card inputs, Stripe Elements, redirect, wallet). Hosts never branch on a
194
221
  provider:
@@ -208,8 +235,6 @@ provider:
208
235
  page (needs `BillingProvider`); `canSavePaymentMethod(psp)` filters PSPs
209
236
  that support it. After an off-page verification the provider returns to
210
237
  `returnURL(setupId)`; confirm with `client.confirmCardSetup(id)`.
211
- - `authenticatePayment(client, operationId, psp)` completes a pending
212
- payment's 3-D Secure challenge when `canAuthenticatePayment(psp)`.
213
238
  - `defaultCountry` (SavePaymentMethod, AccountBilling)
214
239
  preselects the billing country, else the browser locale's region.
215
240
 
@@ -263,3 +288,7 @@ pnpm contract:check # fail if a generated contract file is stale
263
288
  `BILLING_UI_SCREENSHOTS=<dir>` to capture light, dark and mobile screenshots.
264
289
  Card deletion calls NMI, which the harness has no credentials for; jsdom tests
265
290
  cover it.
291
+
292
+ ## License
293
+
294
+ MIT; see [LICENSE](LICENSE). Only this package is MIT: the rest of the OpenRails repository is under the Functional Source License (FSL-1.1-ALv2).
@@ -13,6 +13,6 @@ export interface PaymentMethodsPanelProps {
13
13
  }
14
14
  /**
15
15
  * The customer's saved cards. "Add card" offers each PSP of the merchant's
16
- * payment setup (`GET /config`) that saves a card in the page.
16
+ * payment setup (`GET /me/config`) that saves a card in the page.
17
17
  */
18
18
  export declare function PaymentMethodsPanel({ defaultCurrency, defaultCountry, appearance, className, }: PaymentMethodsPanelProps): React.JSX.Element;
@@ -1,8 +1,9 @@
1
1
  import { BillingClient } from './client/client';
2
+ import { SubscriptionChange } from './client/types';
2
3
  import { PspConfig } from './psp';
3
4
  /**
4
- * Completes the provider challenge (3-D Secure) a pending payment operation
5
- * waits on. "not_required" when the provider has none outstanding; the
6
- * operation's own status remains the result.
5
+ * Completes the issuer's authentication (3-D Secure) a subscription change
6
+ * waits on, then confirms the change and answers it. A change with no
7
+ * `authenticate` next action is answered as it is.
7
8
  */
8
- export declare function authenticatePayment(client: BillingClient, operationId: string, psp: PspConfig): Promise<"authenticated" | "not_required">;
9
+ export declare function authenticateChange(client: BillingClient, subscriptionId: string, change: SubscriptionChange, psp: PspConfig): Promise<SubscriptionChange>;
@@ -2,6 +2,8 @@ import { CheckoutAppearance } from './appearance';
2
2
  import { Order } from './client/types';
3
3
  import * as React from "react";
4
4
  export interface BuyButtonProps {
5
+ /** The id of the merchant that sells the product. */
6
+ merchant: string;
5
7
  /** The product's catalog key. */
6
8
  product: string;
7
9
  /** The price's key within the product. */
@@ -30,8 +32,8 @@ export interface BuyButtonProps {
30
32
  appearance?: CheckoutAppearance;
31
33
  className?: string;
32
34
  }
33
- export declare function BuyButton(props: BuyButtonProps): React.JSX.Element;
34
- export type PurchaseProps = Omit<BuyButtonProps, "product" | "price"> & {
35
+ export declare function BuyButton({ merchant, product, price: priceKey, ...props }: BuyButtonProps): React.JSX.Element;
36
+ export type PurchaseProps = Omit<BuyButtonProps, "merchant" | "product" | "price"> & {
35
37
  /** The price to buy; the button waits while it is unknown. */
36
38
  priceId?: string;
37
39
  label: string;
@@ -1,5 +1,5 @@
1
1
  import { BillingError } from './errors';
2
- import { Account, CustomerLink, NetworkLinkChallenge, PublicConfig, PaymentAuthentication, CurrencyScales, ListPage, NewCard, NextAction, Cart, Order, OrderPaymentInput, Page, Payment, PaymentMethod, Product, Subscription, SubscriptionChange, SubscriptionChangePreview } from './types';
2
+ import { Account, CustomerConfig, CustomerLink, NetworkLinkChallenge, PublicConfig, CurrencyScales, ListPage, NewCard, NextAction, Cart, Order, OrderPaymentInput, Page, Payment, PaymentMethod, Product, Subscription, SubscriptionChange, SubscriptionChangePreview, Usage } from './types';
3
3
  export interface BillingClientOptions {
4
4
  /** OpenRails embedded mount. Default "/billing/v1". */
5
5
  baseUrl?: string;
@@ -8,7 +8,12 @@ export interface BillingClientOptions {
8
8
  * or combine the default with `getToken`.
9
9
  */
10
10
  fetch?: (input: string, init: RequestInit) => Promise<Response>;
11
- /** Bearer for each request; omit when `fetch` authenticates. */
11
+ /**
12
+ * The customer's access token, sent as the bearer of every request but
13
+ * the two public reads (`getConfig`, `listProducts`); omit when `fetch`
14
+ * authenticates. A `tokenSource` keeps it fresh: a call answered 401
15
+ * `credential_expired` is then retried once with a new token.
16
+ */
12
17
  getToken?: () => string | null | undefined | Promise<string | null | undefined>;
13
18
  /** Sent as Accept-Language. */
14
19
  language?: () => string | null | undefined;
@@ -19,10 +24,11 @@ export interface BillingClientOptions {
19
24
  currencies?: CurrencyScales;
20
25
  }
21
26
  /**
22
- * A public catalog lookup: the products named by `keys` (at most 100 product
23
- * keys), in one page.
27
+ * A public catalog lookup: the products of `merchant` (its id) named by
28
+ * `keys` (at most 100 product keys), in one page.
24
29
  */
25
30
  export interface ProductListOptions {
31
+ merchant: string;
26
32
  keys: string[];
27
33
  signal?: AbortSignal;
28
34
  }
@@ -47,6 +53,18 @@ export interface CursorOptions {
47
53
  cursor?: string | null;
48
54
  signal?: AbortSignal;
49
55
  }
56
+ /**
57
+ * A page of the customer's usage report in `currency`, grouped by `groupBy`
58
+ * (event type by default). `from` and `to` are RFC 3339 or a date; left out,
59
+ * the month before now, or with `cursor` the window of the page it came from.
60
+ * `limit` is at most 1,000 groups (default 100).
61
+ */
62
+ export interface UsageOptions extends CursorOptions {
63
+ currency: string;
64
+ groupBy?: "event_type" | "resource" | "invoker";
65
+ from?: string;
66
+ to?: string;
67
+ }
50
68
  /**
51
69
  * Signs and submits a base64 transaction with the customer's wallet and
52
70
  * returns its signature. Throw `WalletRejectedError` when the user declines.
@@ -99,6 +117,12 @@ export declare function createBillingClient(options?: BillingClientOptions): {
99
117
  setSubscriptionPaymentMethod(subscriptionId: string, paymentMethodId: string | null): Promise<Subscription>;
100
118
  /** What `changeSubscription` would charge now and at the next renewal. */
101
119
  previewSubscriptionChange(subscriptionId: string, change: SubscriptionChangeInput, signal?: AbortSignal): Promise<SubscriptionChangePreview>;
120
+ /**
121
+ * Reads the provider for a change awaiting the customer, after they
122
+ * completed its `next_action` (the issuer's authentication), and answers
123
+ * the change as it now stands.
124
+ */
125
+ confirmSubscriptionChange(subscriptionId: string): Promise<SubscriptionChange>;
102
126
  /**
103
127
  * Moves the subscription to another price of its tier group, to other
104
128
  * seats of a per-seat price, or both. An upgrade and more seats charge
@@ -106,7 +130,8 @@ export declare function createBillingClient(options?: BillingClientOptions): {
106
130
  * Reuse `idempotencyKey` until the change resolves (`processing`, a
107
131
  * `subscription_change_in_flight` refusal or a lost response) so the
108
132
  * stored result replays instead of charging twice. A Solana wallet next
109
- * action is completed with `signWalletAction`, then repeated with `signature`.
133
+ * action is completed with `signWalletAction`, then repeated with
134
+ * `signature`; an `authenticate` one with `authenticateChange`.
110
135
  */
111
136
  changeSubscription(subscriptionId: string, input: SubscriptionChangeInput & {
112
137
  idempotencyKey: string;
@@ -180,9 +205,6 @@ export declare function createBillingClient(options?: BillingClientOptions): {
180
205
  * script answered its `next_action`.
181
206
  */
182
207
  confirmPaymentMethod(paymentMethodId: string): Promise<PaymentMethod>;
183
- /** The provider challenge a pending payment operation is waiting on. */
184
- getPaymentAuthentication(operationId: string): Promise<PaymentAuthentication>;
185
- confirmPaymentAuthentication(operationId: string): Promise<void>;
186
208
  /** `pending` when the provider is still converging (202). */
187
209
  removePaymentMethod(paymentMethodId: string): Promise<"removed" | "pending">;
188
210
  /**
@@ -203,8 +225,14 @@ export declare function createBillingClient(options?: BillingClientOptions): {
203
225
  */
204
226
  getAccount(signal?: AbortSignal): Promise<Account>;
205
227
  /**
206
- * The products on sale named by `keys`, each with its current prices.
207
- * No keys name no products: nothing is fetched.
228
+ * A page of the customer's usage: its groups by total amount, largest
229
+ * first. Pass the answer's `next_cursor` as `cursor` for the next page.
230
+ */
231
+ getUsage(opts: UsageOptions): Promise<Usage>;
232
+ /**
233
+ * The products `merchant` has on sale named by `keys`, each with its
234
+ * current prices: a public read, the same signed in or out. No keys
235
+ * name no products: nothing is fetched.
208
236
  */
209
237
  listProducts(opts: ProductListOptions): Promise<Page<Product>>;
210
238
  /**
@@ -221,11 +249,17 @@ export declare function createBillingClient(options?: BillingClientOptions): {
221
249
  idToken: string;
222
250
  }): Promise<CustomerLink>;
223
251
  /**
224
- * The deployment's public configuration: what the mount serves, the
225
- * currency registry and the merchant's payment setup. React components
226
- * share one copy through `useConfig()`.
252
+ * The deployment's configuration, a public read: what the mount serves,
253
+ * the currency registry and the captcha. React components share one
254
+ * copy through `useConfig()`.
227
255
  */
228
256
  getConfig(signal?: AbortSignal): Promise<PublicConfig>;
257
+ /**
258
+ * The customer's merchant: its payment setup (PSPs, Solana network and
259
+ * tokens) and how it presents itself. React components share one copy
260
+ * through `useCustomerConfig()`.
261
+ */
262
+ getCustomerConfig(signal?: AbortSignal): Promise<CustomerConfig>;
229
263
  /** Currency code (upper case) to native-unit decimals. */
230
264
  currencies: Readonly<Record<string, number>>;
231
265
  };
@@ -1,4 +1,4 @@
1
- export type OpenRailsErrorCode = "admission_captured" | "admission_not_found" | "allowance_meter_not_found" | "allowance_source_in_use" | "allowance_source_invalid" | "already_owned" | "application_required" | "as_of_required" | "authentication_required" | "authentication_unavailable" | "authorization_unavailable" | "billing_archive_integrity" | "billing_archive_invalid_artifact" | "billing_archive_merchant_mismatch" | "billing_archive_not_empty" | "billing_archive_unavailable" | "billing_archive_unsupported_state" | "billing_policy_not_found" | "cancel_unsupported_on_rail" | "captcha_invalid" | "captcha_required" | "card_attempts_blocked" | "card_declined" | "card_not_saved" | "card_requires_https" | "catalog_benefit_overlap" | "catalog_revision_conflict" | "checkout_attempt_closed" | "checkout_attempt_expired" | "checkout_attempts_exhausted" | "checkout_embed_origins_required" | "checkout_expired" | "checkout_not_found" | "credential_expired" | "credential_revoked" | "credit_grant_held" | "credit_grant_not_found" | "credit_grant_unavailable" | "csrf_origin_denied" | "currency_unsupported" | "custodian_capture_unavailable" | "customer_action_required" | "customer_email_required" | "customer_not_found" | "customer_payment_unsupported" | "customer_session_required" | "dashboard_invalid" | "database_busy" | "default_payment_method_invalid" | "default_payment_method_required" | "default_rate_card_not_found" | "default_rate_card_required" | "finding_action_failed" | "finding_not_actionable" | "hold_not_found" | "host_event_not_found" | "host_principal_invalid" | "hosted_checkout_unavailable" | "idempotency_key_in_progress" | "idempotency_key_in_use" | "idempotency_key_required" | "idempotency_key_reused" | "insufficient_credits" | "insufficient_funds" | "internal_error" | "invalid_cursor" | "invalid_customer_id" | "invalid_host_event_request" | "invalid_param" | "invalid_payment_method" | "invalid_psp_reference" | "invalid_query" | "invalid_request_body" | "invalid_settlement_status_request" | "invoker_scoped_principal" | "merchant_binding_mismatch" | "merchant_config_read_only" | "merchant_directory_unavailable" | "merchant_not_found" | "merchant_selector_invalid" | "merchant_unresolved" | "meter_in_use" | "meter_rate_card_conflict" | "method_not_allowed" | "metrics_query_invalid" | "model_unavailable" | "network_account_linked" | "network_link_challenge_invalid" | "network_link_conflict" | "network_link_in_use" | "network_terms_required" | "order_action_not_allowed" | "order_has_recurring_line" | "order_line_unavailable" | "order_not_cancelable" | "order_not_payable" | "order_payment_in_progress" | "order_total_changed" | "payment_duplicate_refused" | "payment_exceeds_due" | "payment_failed" | "payment_idempotency_conflict" | "payment_in_progress" | "payment_method_delete_failed" | "payment_method_delete_unsupported" | "payment_method_not_psp_vaulted" | "payment_method_not_usable" | "payment_method_psp_mismatch" | "payment_method_required" | "payment_method_same_vault" | "payment_method_stale" | "payment_method_update_failed" | "payment_method_update_unsupported" | "payment_not_found" | "payment_not_refundable" | "payment_not_retryable" | "payment_option_unavailable" | "payment_provider_rejected" | "permission_required" | "price_change_currency_mismatch" | "price_change_target_archived" | "price_increase_notice_too_short" | "price_key_cadence_conflict" | "price_key_not_found" | "price_migration_not_found" | "price_not_found" | "price_not_sellable" | "product_not_found" | "product_tier_group_conflict" | "product_tier_group_in_use" | "provider_billing_observation_conflict" | "provider_cancel_held" | "provider_operation_conflict" | "provider_operation_has_billing_evidence" | "provider_operation_not_found" | "provider_operation_not_open" | "provider_operation_not_refused" | "provider_operation_refused" | "provider_outcome_unknown" | "psp_claim_requires_proof" | "psp_credentials_rejected" | "psp_exists" | "psp_key_taken" | "psp_last_active" | "psp_not_found" | "purchase_review_resolved" | "quantity_not_allowed" | "rate_card_currency_mismatch" | "rate_card_has_overrides" | "rate_card_product_not_found" | "rate_limit_exceeded" | "rebill_terms_committed" | "refund_failed" | "refund_rail_unavailable" | "refund_unsupported" | "request_body_too_large" | "resource_access_denied" | "resource_conflict" | "resource_not_found" | "revision_mismatch" | "route_not_found" | "running_charge_not_found" | "scheduled_change_exists" | "service_credential_customer_scope_denied" | "service_unavailable" | "solana_rpc_unavailable" | "solana_transaction_refused" | "step_up_required" | "step_up_unavailable" | "stored_credential_required" | "subscription_change_already_scheduled" | "subscription_change_cadence_unsupported" | "subscription_change_credit_exceeds_price" | "subscription_change_cycle_unknown" | "subscription_change_idempotency_conflict" | "subscription_change_idempotency_key_required" | "subscription_change_in_flight" | "subscription_change_period_unknown" | "subscription_change_provider_conflict" | "subscription_change_refused" | "subscription_change_renewal_due" | "subscription_change_requires_linked_plan" | "subscription_change_target_inactive" | "subscription_change_unsupported_on_rail" | "subscription_not_active" | "subscription_not_found" | "subscription_paid_through" | "subscription_resumable" | "trial_unsupported_on_rail" | "unknown_field" | "unsupported_media_type" | "usage_meter_invalid" | "usage_meter_not_found" | "usage_rate_card_invalid" | "webhook_account_mismatch" | "webhook_invalid" | "widget_generation_invalid";
1
+ export type OpenRailsErrorCode = "admission_captured" | "admission_not_found" | "allowance_meter_not_found" | "allowance_source_in_use" | "allowance_source_invalid" | "already_owned" | "application_required" | "as_of_required" | "authentication_required" | "authentication_unavailable" | "authorization_unavailable" | "billing_archive_integrity" | "billing_archive_invalid_artifact" | "billing_archive_merchant_mismatch" | "billing_archive_not_empty" | "billing_archive_unavailable" | "billing_archive_unsupported_state" | "billing_policy_not_found" | "cancel_unsupported_on_rail" | "captcha_invalid" | "captcha_required" | "card_attempts_blocked" | "card_declined" | "card_not_saved" | "card_requires_https" | "catalog_benefit_overlap" | "catalog_revision_conflict" | "checkout_attempt_closed" | "checkout_attempt_expired" | "checkout_attempts_exhausted" | "checkout_embed_origins_required" | "checkout_expired" | "checkout_not_found" | "credential_expired" | "credential_revoked" | "credit_grant_held" | "credit_grant_not_found" | "credit_grant_unavailable" | "csrf_origin_denied" | "currency_unsupported" | "custodian_capture_unavailable" | "customer_action_required" | "customer_email_required" | "customer_issuer_mismatch" | "customer_not_found" | "customer_payment_unsupported" | "customer_session_required" | "dashboard_invalid" | "database_busy" | "default_payment_method_invalid" | "default_payment_method_required" | "default_rate_card_not_found" | "finding_action_failed" | "finding_not_actionable" | "hold_not_found" | "host_event_not_found" | "host_principal_invalid" | "hosted_checkout_unavailable" | "idempotency_key_in_progress" | "idempotency_key_in_use" | "idempotency_key_required" | "idempotency_key_reused" | "insufficient_credits" | "insufficient_funds" | "internal_error" | "invalid_cursor" | "invalid_customer_id" | "invalid_host_event_request" | "invalid_param" | "invalid_payment_method" | "invalid_psp_reference" | "invalid_query" | "invalid_request_body" | "invalid_settlement_status_request" | "invoker_scoped_principal" | "merchant_config_read_only" | "merchant_unresolved" | "meter_in_use" | "meter_rate_card_conflict" | "method_not_allowed" | "metrics_query_invalid" | "model_unavailable" | "network_account_linked" | "network_link_challenge_invalid" | "network_link_conflict" | "network_link_in_use" | "network_terms_required" | "order_action_not_allowed" | "order_has_recurring_line" | "order_line_unavailable" | "order_not_cancelable" | "order_not_payable" | "order_payment_in_progress" | "order_total_changed" | "payment_duplicate_refused" | "payment_exceeds_due" | "payment_failed" | "payment_idempotency_conflict" | "payment_in_progress" | "payment_method_delete_failed" | "payment_method_delete_unsupported" | "payment_method_not_psp_vaulted" | "payment_method_not_usable" | "payment_method_psp_mismatch" | "payment_method_required" | "payment_method_same_vault" | "payment_method_stale" | "payment_method_update_failed" | "payment_method_update_unsupported" | "payment_not_found" | "payment_not_refundable" | "payment_not_retryable" | "payment_option_unavailable" | "payment_provider_rejected" | "permission_required" | "price_change_currency_mismatch" | "price_change_target_archived" | "price_increase_notice_too_short" | "price_key_cadence_conflict" | "price_key_not_found" | "price_migration_not_found" | "price_not_found" | "price_not_sellable" | "product_not_found" | "product_tier_group_conflict" | "product_tier_group_in_use" | "provider_cancel_held" | "provider_outcome_unknown" | "psp_claim_requires_proof" | "psp_credentials_rejected" | "psp_exists" | "psp_key_taken" | "psp_last_active" | "psp_not_found" | "purchase_review_resolved" | "quantity_not_allowed" | "rate_card_product_not_found" | "rate_limit_exceeded" | "rebill_terms_committed" | "refund_failed" | "refund_rail_unavailable" | "refund_unsupported" | "request_body_too_large" | "resource_access_denied" | "resource_conflict" | "resource_not_found" | "revision_mismatch" | "route_not_found" | "running_charge_not_found" | "scheduled_change_exists" | "service_credential_customer_scope_denied" | "service_unavailable" | "solana_rpc_unavailable" | "solana_transaction_refused" | "step_up_required" | "step_up_unavailable" | "stored_credential_required" | "subscription_change_already_scheduled" | "subscription_change_cadence_unsupported" | "subscription_change_credit_exceeds_price" | "subscription_change_cycle_unknown" | "subscription_change_idempotency_conflict" | "subscription_change_idempotency_key_required" | "subscription_change_in_flight" | "subscription_change_period_unknown" | "subscription_change_provider_conflict" | "subscription_change_refused" | "subscription_change_renewal_due" | "subscription_change_requires_linked_plan" | "subscription_change_target_inactive" | "subscription_change_unsupported_on_rail" | "subscription_not_active" | "subscription_not_found" | "subscription_paid_through" | "subscription_resumable" | "trial_unsupported_on_rail" | "unknown_field" | "unsupported_media_type" | "usage_meter_invalid" | "usage_meter_not_found" | "usage_rate_card_invalid" | "webhook_account_mismatch" | "webhook_invalid";
2
2
  export type OpenRailsErrorType = "api_error" | "authentication_error" | "authorization_error" | "card_error" | "invalid_request_error" | "rate_limit_error";
3
3
  export declare const OPENRAILS_ERROR_CODES: Readonly<Record<OpenRailsErrorCode, {
4
4
  status: number;
@@ -3,7 +3,10 @@ export type OpenRailsRoute = {
3
3
  path: string;
4
4
  group: string;
5
5
  auth: string;
6
- permission: string | null;
6
+ permission: {
7
+ merchant: string | null;
8
+ root: string;
9
+ } | null;
7
10
  sensitive: boolean;
8
11
  mountedWhen: string | null;
9
12
  request: string | null;
@@ -53,6 +56,20 @@ export declare const OPENRAILS_ROUTES: readonly [{
53
56
  };
54
57
  readonly errors: readonly [];
55
58
  readonly errorSets: readonly ["tier:public"];
59
+ }, {
60
+ readonly method: "GET";
61
+ readonly path: "/v1/me/config";
62
+ readonly group: "customer";
63
+ readonly auth: "customer";
64
+ readonly permission: null;
65
+ readonly sensitive: false;
66
+ readonly mountedWhen: null;
67
+ readonly request: null;
68
+ readonly responses: {
69
+ readonly 200: "CustomerConfig";
70
+ };
71
+ readonly errors: readonly ["service_unavailable"];
72
+ readonly errorSets: readonly ["tier:customer"];
56
73
  }, {
57
74
  readonly method: "POST";
58
75
  readonly path: "/v1/me/orders/preview";
@@ -224,7 +241,7 @@ export declare const OPENRAILS_ROUTES: readonly [{
224
241
  readonly errorSets: readonly ["tier:customer"];
225
242
  }, {
226
243
  readonly method: "GET";
227
- readonly path: "/v1/catalog/products";
244
+ readonly path: "/v1/products";
228
245
  readonly group: "checkout";
229
246
  readonly auth: "public";
230
247
  readonly permission: null;
@@ -234,7 +251,7 @@ export declare const OPENRAILS_ROUTES: readonly [{
234
251
  readonly responses: {
235
252
  readonly 200: "ListPage<Product>";
236
253
  };
237
- readonly errors: readonly ["invalid_query"];
254
+ readonly errors: readonly ["invalid_query", "resource_not_found"];
238
255
  readonly errorSets: readonly ["tier:public", "request"];
239
256
  }, {
240
257
  readonly method: "POST";
@@ -336,6 +353,21 @@ export declare const OPENRAILS_ROUTES: readonly [{
336
353
  };
337
354
  readonly errors: readonly ["authentication_required", "card_declined", "customer_action_required", "customer_email_required", "invalid_param", "payment_duplicate_refused", "payment_method_stale", "payment_provider_rejected", "price_change_currency_mismatch", "price_not_found", "product_not_found", "quantity_not_allowed", "rebill_terms_committed", "resource_conflict", "resource_not_found", "scheduled_change_exists", "service_unavailable", "solana_rpc_unavailable", "solana_transaction_refused", "subscription_change_already_scheduled", "subscription_change_credit_exceeds_price", "subscription_change_cycle_unknown", "subscription_change_idempotency_conflict", "subscription_change_idempotency_key_required", "subscription_change_in_flight", "subscription_change_period_unknown", "subscription_change_provider_conflict", "subscription_change_refused", "subscription_change_renewal_due", "subscription_change_requires_linked_plan", "subscription_change_target_inactive", "subscription_change_unsupported_on_rail", "subscription_not_active", "subscription_not_found"];
338
355
  readonly errorSets: readonly ["tier:customer", "request"];
356
+ }, {
357
+ readonly method: "POST";
358
+ readonly path: "/v1/me/subscriptions/{id}/confirm";
359
+ readonly group: "customer";
360
+ readonly auth: "customer";
361
+ readonly permission: null;
362
+ readonly sensitive: false;
363
+ readonly mountedWhen: null;
364
+ readonly request: null;
365
+ readonly responses: {
366
+ readonly 200: "SubscriptionChange";
367
+ readonly 202: "SubscriptionChange";
368
+ };
369
+ readonly errors: readonly ["authentication_required", "card_declined", "customer_session_required", "invalid_param", "payment_not_found", "payment_provider_rejected", "resource_conflict", "resource_not_found", "service_unavailable", "subscription_change_refused", "subscription_not_found"];
370
+ readonly errorSets: readonly ["tier:customer"];
339
371
  }, {
340
372
  readonly method: "POST";
341
373
  readonly path: "/v1/me/subscriptions/{id}/change/preview";
@@ -404,36 +436,8 @@ export declare const OPENRAILS_ROUTES: readonly [{
404
436
  readonly responses: {
405
437
  readonly 200: "Usage";
406
438
  };
407
- readonly errors: readonly ["currency_unsupported"];
439
+ readonly errors: readonly ["currency_unsupported", "invalid_cursor"];
408
440
  readonly errorSets: readonly ["tier:customer", "request"];
409
- }, {
410
- readonly method: "GET";
411
- readonly path: "/v1/me/payment-operations/{id}/authentication";
412
- readonly group: "customer";
413
- readonly auth: "customer";
414
- readonly permission: null;
415
- readonly sensitive: false;
416
- readonly mountedWhen: null;
417
- readonly request: null;
418
- readonly responses: {
419
- readonly 200: "StripeEngineAuthentication";
420
- };
421
- readonly errors: readonly ["card_attempts_blocked", "card_declined", "card_not_saved", "custodian_capture_unavailable", "customer_session_required", "idempotency_key_reused", "insufficient_funds", "invalid_param", "payment_method_required", "payment_method_stale", "payment_not_found", "payment_provider_rejected", "resource_access_denied", "resource_conflict", "resource_not_found", "service_unavailable"];
422
- readonly errorSets: readonly ["tier:customer"];
423
- }, {
424
- readonly method: "POST";
425
- readonly path: "/v1/me/payment-operations/{id}/authentication/confirm";
426
- readonly group: "customer";
427
- readonly auth: "customer";
428
- readonly permission: null;
429
- readonly sensitive: false;
430
- readonly mountedWhen: null;
431
- readonly request: null;
432
- readonly responses: {
433
- readonly 200: "PaymentOperation";
434
- };
435
- readonly errors: readonly ["card_attempts_blocked", "card_declined", "card_not_saved", "custodian_capture_unavailable", "customer_session_required", "idempotency_key_reused", "insufficient_funds", "invalid_param", "payment_method_required", "payment_method_stale", "payment_not_found", "payment_provider_rejected", "resource_access_denied", "resource_conflict", "resource_not_found", "service_unavailable"];
436
- readonly errorSets: readonly ["tier:customer"];
437
441
  }, {
438
442
  readonly method: "GET";
439
443
  readonly path: "/v1/me/payments";
@@ -547,6 +551,34 @@ export declare const OPENRAILS_ROUTES: readonly [{
547
551
  };
548
552
  readonly errors: readonly ["invalid_param", "resource_not_found", "route_not_found"];
549
553
  readonly errorSets: readonly ["tier:customer"];
554
+ }, {
555
+ readonly method: "GET";
556
+ readonly path: "/v1/me/agreements";
557
+ readonly group: "customer";
558
+ readonly auth: "customer";
559
+ readonly permission: null;
560
+ readonly sensitive: false;
561
+ readonly mountedWhen: null;
562
+ readonly request: null;
563
+ readonly responses: {
564
+ readonly 200: "ListPage<Agreement>";
565
+ };
566
+ readonly errors: readonly ["invalid_cursor"];
567
+ readonly errorSets: readonly ["tier:customer", "request"];
568
+ }, {
569
+ readonly method: "POST";
570
+ readonly path: "/v1/me/agreements/{id}/revoke";
571
+ readonly group: "customer";
572
+ readonly auth: "customer";
573
+ readonly permission: null;
574
+ readonly sensitive: false;
575
+ readonly mountedWhen: null;
576
+ readonly request: null;
577
+ readonly responses: {
578
+ readonly 200: "AgreementRevocation";
579
+ };
580
+ readonly errors: readonly ["cancel_unsupported_on_rail", "customer_action_required", "invalid_param", "payment_in_progress", "provider_cancel_held", "resource_not_found", "service_unavailable", "subscription_not_active"];
581
+ readonly errorSets: readonly ["tier:customer"];
550
582
  }, {
551
583
  readonly method: "GET";
552
584
  readonly path: "/v1/me/notifications";