@openrails/billing-ui 0.235.0 → 0.236.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 +103 -0
- package/README.md +103 -73
- package/dist/account/account-billing.d.ts +1 -3
- package/dist/account/payment-methods-panel.d.ts +1 -3
- package/dist/buy-button.d.ts +35 -0
- package/dist/cart.d.ts +18 -0
- package/dist/checkout.d.ts +1 -3
- package/dist/client/checkout.d.ts +28 -0
- package/dist/client/client.d.ts +71 -47
- package/dist/client/errors.d.ts +4 -0
- package/dist/client/generated/error-codes.d.ts +1 -1
- package/dist/client/generated/routes.d.ts +117 -88
- package/dist/client/generated/wire.d.ts +88 -66
- package/dist/client/index.d.ts +2 -2
- package/dist/client/types.d.ts +230 -17
- package/dist/{client-BpzCZes3.js → client-BuvolBPT.js} +477 -316
- package/dist/client-BuvolBPT.js.map +1 -0
- package/dist/client.js +2 -2
- package/dist/components/stripe-card.d.ts +8 -2
- package/dist/components/stripe-wallets.d.ts +12 -0
- package/dist/components/ui/badge.d.ts +1 -1
- package/dist/components/ui/button.d.ts +1 -1
- package/dist/frame.d.ts +5 -5
- package/dist/hooks-2d-XjpuI.js +526 -0
- package/dist/hooks-2d-XjpuI.js.map +1 -0
- package/dist/i18n/context.d.ts +1 -1
- package/dist/index.d.ts +7 -5
- package/dist/index.js +1966 -1655
- package/dist/index.js.map +1 -1
- package/dist/lib/money.d.ts +1 -0
- package/dist/lib/offer-label.d.ts +4 -0
- package/dist/locales/de.js +20 -0
- package/dist/locales/de.js.map +1 -1
- package/dist/locales/en.d.ts +20 -0
- package/dist/locales/en.js +20 -0
- package/dist/locales/en.js.map +1 -1
- package/dist/locales/es.js +20 -0
- package/dist/locales/es.js.map +1 -1
- package/dist/locales/ja.js +11 -0
- package/dist/locales/ja.js.map +1 -1
- package/dist/locales/ko.js +11 -0
- package/dist/locales/ko.js.map +1 -1
- package/dist/locales/zh.js +11 -0
- package/dist/locales/zh.js.map +1 -1
- package/dist/offers.d.ts +33 -0
- package/dist/order-checkout.d.ts +30 -0
- package/dist/react/hooks.d.ts +8 -7
- package/dist/react/index.d.ts +1 -1
- package/dist/react/provider.d.ts +24 -2
- package/dist/react.js +2 -33
- package/dist/save-payment-method.d.ts +0 -6
- package/dist/styles.css +1 -1
- package/dist/types.d.ts +16 -16
- package/docs/payment-form-contract.md +6 -6
- package/package.json +1 -1
- package/dist/client-BpzCZes3.js.map +0 -1
- package/dist/config-qLcHvK0Q.js +0 -341
- package/dist/config-qLcHvK0Q.js.map +0 -1
- package/dist/provider.d.ts +0 -18
- package/dist/react.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,108 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Apple Pay and Google Pay
|
|
4
|
+
|
|
5
|
+
- `<OrderCheckout wallets>` offers Apple Pay and Google Pay through a Stripe
|
|
6
|
+
PSP's Express Checkout Element, above the saved and new cards. Off by
|
|
7
|
+
default: the page's host must be a payment-method domain registered on that
|
|
8
|
+
Stripe account.
|
|
9
|
+
|
|
10
|
+
## Renewals are orders
|
|
11
|
+
|
|
12
|
+
- `Order` gains `renewal` (a subscription renewal's period and `due_at`) and
|
|
13
|
+
`voided_at`; `expires_at` is null on a renewal; `status` may be `void`.
|
|
14
|
+
- `PaymentRecovery` gains `order_id`: an engine-owned subscription's unpaid
|
|
15
|
+
renewal, paid through `<OrderCheckout>`.
|
|
16
|
+
- `<OrderCheckout>` offers new card entry only where the order takes one (a
|
|
17
|
+
renewal takes saved cards), and lets the customer authenticate a payment
|
|
18
|
+
their bank challenged while they were away.
|
|
19
|
+
|
|
20
|
+
## Hosted checkout
|
|
21
|
+
|
|
22
|
+
- `getConfig()` returns `merchant` (`PublicMerchant`: `display_name`, and
|
|
23
|
+
`logo_url` and `support_url` when they are https), null when the request
|
|
24
|
+
resolves no merchant.
|
|
25
|
+
- `Order` carries `checkout` (its hosted checkout: `expires_at`,
|
|
26
|
+
`success_url`, `cancel_url`, `saved_payment_methods`; `url` only on the
|
|
27
|
+
answer that made it), null without one.
|
|
28
|
+
|
|
29
|
+
## Orders take Solana and hosted pages
|
|
30
|
+
|
|
31
|
+
- `<OrderCheckout>` offers an order's Solana option: one button per token,
|
|
32
|
+
then the Solana Pay QR of the order's `solana_pay` next action, confirmed
|
|
33
|
+
until the transfer lands.
|
|
34
|
+
- A PSP with no fields in the page (Stripe without a publishable key) takes
|
|
35
|
+
the order on its hosted page: `<OrderCheckout>` sends the customer there and
|
|
36
|
+
back to `returnUrl` (default: the current page).
|
|
37
|
+
- `OrderPaymentInput` gains `{ token_symbol, psp_id? }` and
|
|
38
|
+
`{ return_url, psp_id? }`.
|
|
39
|
+
|
|
40
|
+
## OrderCheckout pays an order
|
|
41
|
+
|
|
42
|
+
- `<OrderCheckout orderId>` shows an order and pays it with a saved card or a
|
|
43
|
+
new card in the PSP's fields, running any `next_action` and `confirm`. It
|
|
44
|
+
takes an optional `client` and offers only cards kept for one-click buys
|
|
45
|
+
(`PaymentMethod.reusable`).
|
|
46
|
+
- The client gains `getOrder`, and `payOrder` and `confirmOrder`, which answer
|
|
47
|
+
`{ order, error? }`: a declined payment (402) is the order with
|
|
48
|
+
`payment.last_payment_error`, not a throw. `BillingError` carries `order`.
|
|
49
|
+
- `StripeCardEntry`'s handle gains `tokenize()` and `handleNextAction()`.
|
|
50
|
+
|
|
51
|
+
## A card is saved in one call
|
|
52
|
+
|
|
53
|
+
- `addPaymentMethod` takes `token` (was `payment_token`). A Stripe card is a
|
|
54
|
+
`pm_` from Stripe's fields, saved by the same call; a card the bank wants to
|
|
55
|
+
authenticate answers `status: "requires_action"` with `next_action`, and
|
|
56
|
+
`confirmPaymentMethod(id)` finishes it after Stripe.js `handleNextAction`.
|
|
57
|
+
`StripeCardEntry` and `SavePaymentMethod` do this themselves.
|
|
58
|
+
- `createCardSetup`, `getCardSetup`, `confirmCardSetup`, the `CardSetup` type
|
|
59
|
+
and the `returnURL` / `cardSetupReturnURL` props are gone: 3-D Secure runs in
|
|
60
|
+
the page.
|
|
61
|
+
|
|
62
|
+
## Offers sell products by key
|
|
63
|
+
|
|
64
|
+
- The public catalog is a lookup by product key: `listProducts({ keys })` and
|
|
65
|
+
`useProducts({ keys })` take the keys alone (1 to 100) and read one page;
|
|
66
|
+
`entitlements`, `limit` and `cursor` are gone, and so is
|
|
67
|
+
`ProductsState.nextCursor`. No keys fetch nothing.
|
|
68
|
+
- `<Offers keys>` replaces `<Offers entitlements>`: your server names the
|
|
69
|
+
products, with the Go `Client.ListOffers` when it finds them by
|
|
70
|
+
entitlement.
|
|
71
|
+
|
|
72
|
+
## Apps buy through BuyButton and Offers
|
|
73
|
+
|
|
74
|
+
- `Checkout` and `CheckoutModal` (and their props), the fixture sources and
|
|
75
|
+
the `CheckoutSource` type are no longer exported; the client no longer has
|
|
76
|
+
`createCheckoutSession` or `checkoutSource`. Buy with `<BuyButton>` (it takes
|
|
77
|
+
`amount`, `autoRenew` and `successUrl`) or `<Offers>`. `CheckoutPage` and
|
|
78
|
+
`CheckoutFrame` stay, for the shared payment page.
|
|
79
|
+
- The main entry also exports `createBillingClient` (with `BillingClient`,
|
|
80
|
+
`BillingClientOptions` and `ProductListOptions`) beside `formatAmount`, so a
|
|
81
|
+
React app imports from `@openrails/billing-ui` alone; `./client` and
|
|
82
|
+
`./react` are unchanged.
|
|
83
|
+
|
|
84
|
+
## One provider
|
|
85
|
+
|
|
86
|
+
- `BillingProvider` takes what `BillingUiProvider` took (`appearance`,
|
|
87
|
+
`messages`, `t`, `locale`, `navigate`) beside `client` and `onChange`, and
|
|
88
|
+
`BillingUiProvider` is gone. The main entry exports it; `./react` exports the
|
|
89
|
+
same component for hook-only apps, without the stylesheet. `client` is
|
|
90
|
+
optional: leave it out where no customer signs in, such as `CheckoutPage`.
|
|
91
|
+
- Inside a provider with a client, the checkout saves a new card to the
|
|
92
|
+
customer first and pays on the customer surface.
|
|
93
|
+
|
|
94
|
+
## Offers sell an entitlement
|
|
95
|
+
|
|
96
|
+
- `<BuyButton product price onPaid onSignInRequired>` buys one price, labelled
|
|
97
|
+
from the catalog unless given `label`. The app never touches a checkout
|
|
98
|
+
session: when orders take a new card (#1168) it creates an order instead.
|
|
99
|
+
- `<Offers entitlements onPaid onSignInRequired>` lists every product on sale
|
|
100
|
+
granting any of the entitlements, a `BuyButton` per price; `products` feeds
|
|
101
|
+
it from the host's server instead.
|
|
102
|
+
- `listProducts({ entitlements, keys })` and `useProducts({ entitlements, keys })`
|
|
103
|
+
filter the public catalog. One of them is required: OpenRails no longer
|
|
104
|
+
lists the whole catalog publicly.
|
|
105
|
+
|
|
3
106
|
## Checkout saves a new card under its PSP
|
|
4
107
|
|
|
5
108
|
- Inside a `BillingProvider`, `Checkout` saved a new card with the session
|
package/README.md
CHANGED
|
@@ -6,13 +6,13 @@ billing (subscriptions, saved cards, payment history).
|
|
|
6
6
|
| Import | Contents |
|
|
7
7
|
| --------------------------------- | ---------------------------------------------------------------------------------- |
|
|
8
8
|
| `@openrails/billing-ui/client` | Framework-free typed client for `/billing/v1/me/*` and the catalog, `BillingError` |
|
|
9
|
-
| `@openrails/billing-ui/react` | `BillingProvider` and headless hooks
|
|
10
|
-
| `@openrails/billing-ui` |
|
|
9
|
+
| `@openrails/billing-ui/react` | `BillingProvider` and headless hooks, without styles |
|
|
10
|
+
| `@openrails/billing-ui` | `BillingProvider`, styled checkout and account components, i18n |
|
|
11
11
|
| `@openrails/billing-ui/locales/*` | `en de es ja ko zh` message bundles |
|
|
12
12
|
|
|
13
|
-
The checkout owns the browser payment flow
|
|
14
|
-
session: the signed-in customer mints
|
|
15
|
-
reads and pays it. Card data is tokenized in NMI-hosted Collect.js iframes and
|
|
13
|
+
The checkout owns the browser payment flow, behind `<BuyButton>` and
|
|
14
|
+
`<Offers>`. OpenRails holds the checkout session: the signed-in customer mints
|
|
15
|
+
one for a price, and its `ocs_` id alone reads and pays it. Card data is tokenized in NMI-hosted Collect.js iframes and
|
|
16
16
|
never enters the host application, unless the PSP takes cards on OpenRails
|
|
17
17
|
itself (`card_entry: server`, driver `card`).
|
|
18
18
|
|
|
@@ -28,35 +28,58 @@ provenance.
|
|
|
28
28
|
pnpm add @openrails/billing-ui@X.Y.Z
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
##
|
|
31
|
+
## Buying
|
|
32
|
+
|
|
33
|
+
A React app imports from one place:
|
|
32
34
|
|
|
33
35
|
```tsx
|
|
34
|
-
import { createBillingClient } from "@openrails/billing-ui
|
|
35
|
-
import { Checkout, CheckoutFrame } from "@openrails/billing-ui"
|
|
36
|
-
import "@openrails/billing-ui/styles.css"
|
|
37
|
-
|
|
38
|
-
const billing = createBillingClient({ fetch: auth.authFetch })
|
|
39
|
-
const session = await billing.createCheckoutSession({
|
|
40
|
-
productKey: "pro",
|
|
41
|
-
priceKey: "monthly",
|
|
42
|
-
})
|
|
36
|
+
import { BillingProvider, BuyButton, Offers, createBillingClient, formatAmount } from "@openrails/billing-ui"
|
|
43
37
|
|
|
44
|
-
//
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
38
|
+
// auth-ui's authFetch attaches the bearer and retries once after a refresh;
|
|
39
|
+
// `getToken: () => token` works for any other auth.
|
|
40
|
+
const billing = createBillingClient({ baseUrl: "/billing/v1", fetch: auth.authFetch })
|
|
41
|
+
|
|
42
|
+
<BillingProvider client={billing} appearance={{ theme: "auto" }}>
|
|
43
|
+
<App />
|
|
44
|
+
</BillingProvider>
|
|
48
45
|
```
|
|
49
46
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
47
|
+
`<BuyButton>` buys one price, `<Offers>` the products your server names by
|
|
48
|
+
key (a course, its bundle and the membership that also unlocks it; the Go
|
|
49
|
+
`Client.ListOffers` finds the products granting an entitlement): a
|
|
50
|
+
`BuyButton` per price ("$4.99", "Rent for 3 days, $1.99",
|
|
51
|
+
"$10.00 every 30 days"). The purchase is theirs from start to payment, so the
|
|
52
|
+
app never handles a checkout session: today a button opens one in a checkout
|
|
53
|
+
dialog; when orders take a new card it will create an order instead,
|
|
54
|
+
and apps won't change. Signing in is the host's: with `signedIn={false}`, or
|
|
55
|
+
when OpenRails answers 401, they call `onSignInRequired` instead. `onPaid`
|
|
56
|
+
follows a successful payment only; it is a hint, so confirm access from your
|
|
57
|
+
own API (your gate) before granting anything.
|
|
55
58
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
```tsx
|
|
60
|
+
<BuyButton
|
|
61
|
+
product="course-101"
|
|
62
|
+
price="rent" // labelled from the catalog unless you pass label
|
|
63
|
+
signedIn={signedIn}
|
|
64
|
+
onSignInRequired={() => openSignIn()}
|
|
65
|
+
onPaid={() => navigate("/courses/css-101")} // your gate now admits them
|
|
66
|
+
/>
|
|
67
|
+
|
|
68
|
+
<Offers
|
|
69
|
+
keys={["course-101", "course-bundle", "channel-membership"]} // from your server, or products={...} from its Client.ListOffers
|
|
70
|
+
signedIn={signedIn}
|
|
71
|
+
onSignInRequired={() => openSignIn()}
|
|
72
|
+
onPaid={() => navigate("/courses/css-101")}
|
|
73
|
+
/>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`successUrl` returns the buyer from a redirect step (Stripe's hosted page); it
|
|
77
|
+
must be on one of your app's return origins. `autoRenew={false}` buys a
|
|
78
|
+
recurring price's first term only.
|
|
79
|
+
|
|
80
|
+
For prepaid credits, a fixed pack is another product/price. A
|
|
81
|
+
`customer_amount` price takes the buyer's `amount`: collect it on your product
|
|
82
|
+
page, and the checkout charges exactly that:
|
|
60
83
|
|
|
61
84
|
```tsx
|
|
62
85
|
import { decimalToAmount } from "@openrails/billing-ui/client"
|
|
@@ -65,42 +88,46 @@ import { decimalToAmount } from "@openrails/billing-ui/client"
|
|
|
65
88
|
// `enteredAmount` is the text from an <input inputMode="decimal">, e.g. "100".
|
|
66
89
|
const amount = decimalToAmount(enteredAmount, currency.decimals)
|
|
67
90
|
const bounds = depositPrice.customer_amount
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
BigInt(amount) < BigInt(bounds.min_amount) ||
|
|
72
|
-
BigInt(amount) > BigInt(bounds.max_amount)
|
|
73
|
-
) {
|
|
74
|
-
throw new Error("Choose an amount within the deposit limits")
|
|
75
|
-
}
|
|
76
|
-
const session = await billing.createCheckoutSession({
|
|
77
|
-
priceId: depositPrice.id,
|
|
78
|
-
amount, // USD 100 becomes "100000000"; no floating-point conversion
|
|
79
|
-
})
|
|
91
|
+
const valid = amount && bounds && BigInt(amount) >= BigInt(bounds.min_amount) && BigInt(amount) <= BigInt(bounds.max_amount)
|
|
92
|
+
|
|
93
|
+
<BuyButton product="api-credits" price="deposit" amount={valid ? amount : undefined} label="Add credits" {...handlers} />
|
|
80
94
|
```
|
|
81
95
|
|
|
82
96
|
OpenRails validates the bounds and whole currency minor units before accepting
|
|
83
97
|
payment. Fixed prices reject `amount`; their pack benefit can be greater than
|
|
84
98
|
the purchase price for a bulk discount. Customer-selected deposits currently
|
|
85
|
-
use NMI or Stripe.
|
|
99
|
+
use NMI or Stripe.
|
|
86
100
|
|
|
87
101
|
A rail's own step is the pay result's `next_action`: `redirect_to_url` (an
|
|
88
102
|
https page, opened in the top window) or `solana_pay` (a `solana:` link shown
|
|
89
103
|
as a QR code). A card challenge is its `operation`, authenticated in the page.
|
|
90
|
-
|
|
91
|
-
|
|
104
|
+
|
|
105
|
+
### A shared payment page
|
|
106
|
+
|
|
107
|
+
Several sites selling for one merchant can share one payment page
|
|
108
|
+
(`Config.Checkout.PageURL`). A server mints the session with the Go
|
|
109
|
+
`Client.CreateCheckoutSession` and hands the browser its `url`, which the site
|
|
110
|
+
frames:
|
|
111
|
+
|
|
112
|
+
```tsx
|
|
113
|
+
<CheckoutFrame url={session.url} theme="dark" onComplete={() => refetchAccess()} />
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Stripe Elements needs the buyer's own client, so the shared page offers token
|
|
117
|
+
rails only.
|
|
92
118
|
|
|
93
119
|
The payment host serves `<CheckoutPage>` from one HTML entry at `PageURL`,
|
|
94
120
|
behind its adapter's `CheckoutFramePolicy` (only the sites in
|
|
95
|
-
`Config.Checkout.EmbedOrigins` may frame it)
|
|
121
|
+
`Config.Checkout.EmbedOrigins` may frame it). No customer signs in there, so
|
|
122
|
+
its `BillingProvider` has no client:
|
|
96
123
|
|
|
97
124
|
```tsx
|
|
98
|
-
import {
|
|
125
|
+
import { BillingProvider, CheckoutPage } from "@openrails/billing-ui"
|
|
99
126
|
|
|
100
127
|
createRoot(root).render(
|
|
101
|
-
<
|
|
128
|
+
<BillingProvider>
|
|
102
129
|
<CheckoutPage appearance={{ variables: brand }} />
|
|
103
|
-
</
|
|
130
|
+
</BillingProvider>
|
|
104
131
|
)
|
|
105
132
|
```
|
|
106
133
|
|
|
@@ -112,25 +139,30 @@ cannot navigate the top window, so the app does); app to page
|
|
|
112
139
|
|
|
113
140
|
## Account billing
|
|
114
141
|
|
|
142
|
+
One `BillingProvider`, usually at the app's root, gives every component the
|
|
143
|
+
client, the appearance and the words. `@openrails/billing-ui/react` exports the
|
|
144
|
+
same provider for an app that uses only the hooks; it loads no stylesheet.
|
|
145
|
+
|
|
115
146
|
```tsx
|
|
116
|
-
import { createBillingClient } from "@openrails/billing-ui
|
|
117
|
-
import { BillingProvider } from "@openrails/billing-ui/react"
|
|
118
|
-
import { AccountBilling, BillingUiProvider } from "@openrails/billing-ui"
|
|
147
|
+
import { AccountBilling, BillingProvider, createBillingClient } from "@openrails/billing-ui"
|
|
119
148
|
import { de } from "@openrails/billing-ui/locales/de"
|
|
120
149
|
|
|
121
|
-
// auth-ui's authFetch attaches the bearer and retries once after a refresh;
|
|
122
|
-
// `getToken: () => token` works for any other auth.
|
|
123
150
|
const billing = createBillingClient({ baseUrl: "/billing/v1", fetch: auth.authFetch })
|
|
124
151
|
|
|
125
|
-
<
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
152
|
+
<BillingProvider
|
|
153
|
+
client={billing}
|
|
154
|
+
appearance={{ theme: "auto" }}
|
|
155
|
+
messages={de}
|
|
156
|
+
locale="de"
|
|
157
|
+
navigate={navigate}
|
|
158
|
+
onChange={() => queryClient.invalidateQueries({ queryKey: ["billing"] })}
|
|
159
|
+
>
|
|
160
|
+
<AccountBilling
|
|
161
|
+
plansHref="/plans"
|
|
162
|
+
defaultCurrency="USD" // offers "Make default"
|
|
163
|
+
sendSolanaTransaction={(tx) => wallet.signAndSend(tx)} // signs a Solana cancel
|
|
164
|
+
/>
|
|
165
|
+
</BillingProvider>
|
|
134
166
|
```
|
|
135
167
|
|
|
136
168
|
The panels read OpenRails' public configuration (`GET /config`) themselves,
|
|
@@ -143,13 +175,12 @@ for host components.
|
|
|
143
175
|
## Catalog and plan changes
|
|
144
176
|
|
|
145
177
|
The client also reads the public catalog and changes a subscription's plan.
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
routes; `GET /config` is always served.
|
|
178
|
+
The catalog and Solana reads are public routes, served only when the host
|
|
179
|
+
mounts OpenRails' checkout routes; `GET /config` is always served.
|
|
149
180
|
|
|
150
181
|
| Call | Route |
|
|
151
182
|
| --------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- |
|
|
152
|
-
| `listProducts()` (
|
|
183
|
+
| `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` |
|
|
153
184
|
| `getConfig()` (capabilities, `currencies`, `payment` with PSPs, `solana.network` and tokens; `client.currencies` is the pinned registry) | `GET /config` |
|
|
154
185
|
| `previewSubscriptionChange(id, { priceId?, quantity? })` | `POST /me/subscriptions/{id}/change/preview` |
|
|
155
186
|
| `changeSubscription(id, { priceId?, quantity?, idempotencyKey, signature? })` | `POST /me/subscriptions/{id}/change` |
|
|
@@ -195,8 +226,9 @@ provider:
|
|
|
195
226
|
Card rails (`collect_js`, `card`, `stripe_elements`) render one panel: saved cards,
|
|
196
227
|
an inline new card and one Pay/Subscribe button, which is the customer's
|
|
197
228
|
confirmation of the displayed terms. No provider chooser with one rail.
|
|
198
|
-
- Inside a `BillingProvider
|
|
199
|
-
and the
|
|
229
|
+
- Inside a `BillingProvider` with a client, a new card is always saved to the
|
|
230
|
+
account first and the session is paid with `payment_method_id` on the
|
|
231
|
+
customer surface; `requires_action` results
|
|
200
232
|
carrying an `operation` run 3-D Secure in the page. A `failed` result stays
|
|
201
233
|
on the panel with `failure.message` next to its `field`, so the buyer can
|
|
202
234
|
pick another card; the host gives each new attempt a new idempotency key.
|
|
@@ -208,10 +240,8 @@ provider:
|
|
|
208
240
|
`returnURL(setupId)`; confirm with `client.confirmCardSetup(id)`.
|
|
209
241
|
- `authenticatePayment(client, operationId, psp)` completes a pending
|
|
210
242
|
payment's 3-D Secure challenge when `canAuthenticatePayment(psp)`.
|
|
211
|
-
- `defaultCountry` (
|
|
212
|
-
the billing country, else the browser locale's region.
|
|
213
|
-
- `CheckoutModal gate={<SignIn />}` renders host content (e.g. sign-in) in
|
|
214
|
-
the modal instead of the checkout until cleared.
|
|
243
|
+
- `defaultCountry` (CheckoutPage, SavePaymentMethod, AccountBilling)
|
|
244
|
+
preselects the billing country, else the browser locale's region.
|
|
215
245
|
|
|
216
246
|
- Panels: `SubscriptionsPanel` (cancel, resume, change card),
|
|
217
247
|
`PaymentMethodsPanel`, `PaymentHistory`, `BillingStatusBadge`,
|
|
@@ -220,8 +250,8 @@ provider:
|
|
|
220
250
|
subscription row; `useBillingRefresh()` refetches after host-side changes.
|
|
221
251
|
- Hooks: `useSubscriptions` (`cancel`, `resume`, `setPaymentMethod`,
|
|
222
252
|
`changeSubscription`, per-row `pending`, `nextCursor`), `usePaymentMethods` (`add`,
|
|
223
|
-
`remove`, `setDefault`), `usePayments` (cursor pages), `useProducts` (
|
|
224
|
-
catalog).
|
|
253
|
+
`remove`, `setDefault`), `usePayments` (cursor pages), `useProducts` (a
|
|
254
|
+
catalog lookup by product key).
|
|
225
255
|
Actions resolve to `null` or a `BillingError`; they never throw.
|
|
226
256
|
`changeSubscription` resolves to the `SubscriptionChange` instead of `null`. Cancel, resume
|
|
227
257
|
and the card change answer the subscription, which replaces the row.
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { CheckoutAppearance } from '../appearance';
|
|
2
2
|
import { SendSolanaTransaction } from '../client/client';
|
|
3
|
-
import { PaymentMethodsPanelProps } from './payment-methods-panel';
|
|
4
3
|
import { SubscriptionsPanelProps } from './subscriptions-panel';
|
|
5
4
|
export interface AccountBillingProps {
|
|
6
|
-
cardSetupReturnURL?: PaymentMethodsPanelProps["cardSetupReturnURL"];
|
|
7
5
|
/**
|
|
8
6
|
* Offers to make a card the default for this currency: it pays the
|
|
9
7
|
* currency's invoices and every subscription in it without its own card.
|
|
@@ -19,4 +17,4 @@ export interface AccountBillingProps {
|
|
|
19
17
|
className?: string;
|
|
20
18
|
}
|
|
21
19
|
/** Subscriptions, payment methods and history in one column. */
|
|
22
|
-
export declare function AccountBilling({
|
|
20
|
+
export declare function AccountBilling({ defaultCurrency, defaultCountry, sendSolanaTransaction, plansHref, renderSubscriptionFooter, historyPageSize, appearance, className, }: AccountBillingProps): import("react").JSX.Element;
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { CheckoutAppearance } from '../appearance';
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
export interface PaymentMethodsPanelProps {
|
|
4
|
-
/** Return target after off-page card verification; see `SavePaymentMethod`. */
|
|
5
|
-
cardSetupReturnURL?: (setupId: string) => string;
|
|
6
4
|
/**
|
|
7
5
|
* Offers to make a card the default for this currency: it pays the
|
|
8
6
|
* currency's invoices and every subscription in it without its own card.
|
|
@@ -17,4 +15,4 @@ export interface PaymentMethodsPanelProps {
|
|
|
17
15
|
* The customer's saved cards. "Add card" offers each PSP of the merchant's
|
|
18
16
|
* payment setup (`GET /config`) that saves a card in the page.
|
|
19
17
|
*/
|
|
20
|
-
export declare function PaymentMethodsPanel({
|
|
18
|
+
export declare function PaymentMethodsPanel({ defaultCurrency, defaultCountry, appearance, className, }: PaymentMethodsPanelProps): React.JSX.Element;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { CheckoutAppearance } from './appearance';
|
|
2
|
+
import { PayResult } from './types';
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
export interface BuyButtonProps {
|
|
5
|
+
/** The product's catalog key. */
|
|
6
|
+
product: string;
|
|
7
|
+
/** The price's key within the product. */
|
|
8
|
+
price: string;
|
|
9
|
+
/** The button's text; by default the price's amount and terms, from the catalog. */
|
|
10
|
+
label?: string;
|
|
11
|
+
/**
|
|
12
|
+
* A customer-chosen deposit, in the price currency's native units, for a
|
|
13
|
+
* price with `customer_amount`; the checkout charges exactly this.
|
|
14
|
+
*/
|
|
15
|
+
amount?: string;
|
|
16
|
+
/** false buys a recurring price's first term only; default: renews. */
|
|
17
|
+
autoRenew?: boolean;
|
|
18
|
+
/** Where a redirect rail (Stripe's hosted page) returns the buyer: one of the app's return origins. */
|
|
19
|
+
successUrl?: string;
|
|
20
|
+
/**
|
|
21
|
+
* The purchase succeeded. A hint: the host's own gate decides access, so
|
|
22
|
+
* send the buyer to the content it guards.
|
|
23
|
+
*/
|
|
24
|
+
onPaid: (result: PayResult) => void;
|
|
25
|
+
/**
|
|
26
|
+
* Buying needs a signed-in customer: called instead of the checkout when
|
|
27
|
+
* `signedIn` is false or OpenRails answers 401.
|
|
28
|
+
*/
|
|
29
|
+
onSignInRequired: () => void;
|
|
30
|
+
/** false when the host knows the visitor is signed out. */
|
|
31
|
+
signedIn?: boolean;
|
|
32
|
+
appearance?: CheckoutAppearance;
|
|
33
|
+
className?: string;
|
|
34
|
+
}
|
|
35
|
+
export declare function BuyButton(props: BuyButtonProps): React.JSX.Element;
|
package/dist/cart.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { CheckoutAppearance } from './appearance';
|
|
2
|
+
import { BillingClient } from './client/client';
|
|
3
|
+
import { Order } from './client/types';
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
export interface CartProps {
|
|
6
|
+
/** The client to read and buy through; default: the provider's. */
|
|
7
|
+
client?: BillingClient;
|
|
8
|
+
/**
|
|
9
|
+
* The order made from the cart. Default: pay it here with OrderCheckout.
|
|
10
|
+
*/
|
|
11
|
+
onOrder?: (order: Order) => void;
|
|
12
|
+
/** Passed to OrderCheckout: the order is complete, or processing. */
|
|
13
|
+
onSettled?: (order: Order) => void;
|
|
14
|
+
appearance?: CheckoutAppearance;
|
|
15
|
+
className?: string;
|
|
16
|
+
}
|
|
17
|
+
/** The customer's cart. Requires `BillingProvider`. */
|
|
18
|
+
export declare function Cart({ client: given, onOrder, onSettled, appearance, className, }: CartProps): React.JSX.Element;
|
package/dist/checkout.d.ts
CHANGED
|
@@ -9,8 +9,6 @@ export interface CheckoutProps {
|
|
|
9
9
|
layout?: CheckoutLayout;
|
|
10
10
|
/** Billing country to preselect; default: the browser locale's region. */
|
|
11
11
|
defaultCountry?: string;
|
|
12
|
-
/** Return target after an off-page card verification (Stripe setup). */
|
|
13
|
-
cardSetupReturnURL?: (setupId: string) => string;
|
|
14
12
|
onComplete?: (result: PayResult) => void;
|
|
15
13
|
/**
|
|
16
14
|
* Leaves for a redirect rail's page. Default: navigate the top window,
|
|
@@ -21,4 +19,4 @@ export interface CheckoutProps {
|
|
|
21
19
|
completionMode?: "embedded" | "redirect";
|
|
22
20
|
className?: string;
|
|
23
21
|
}
|
|
24
|
-
export declare function Checkout({ source, appearance, layout, defaultCountry,
|
|
22
|
+
export declare function Checkout({ source, appearance, layout, defaultCountry, onComplete, onRedirect, onPhaseChange, completionMode, className, }: CheckoutProps): React.JSX.Element;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { CheckoutSource } from '../source';
|
|
2
|
+
import { CheckoutSessionLink } from './types';
|
|
3
|
+
export interface CheckoutSourceOptions {
|
|
4
|
+
/**
|
|
5
|
+
* A customer surface's base: its `/me` prefix, such as `/billing/v1/me` or
|
|
6
|
+
* a host's own `/api/v1/merchants/acme/billing/me`. The session is read and
|
|
7
|
+
* paid there with this client's credential, which proves the session's
|
|
8
|
+
* customer, so its saved cards pay. Default: the session id alone, at
|
|
9
|
+
* `{baseUrl}/checkout-sessions`.
|
|
10
|
+
*/
|
|
11
|
+
customerBase?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface CheckoutCalls {
|
|
14
|
+
createCheckoutSession(input: {
|
|
15
|
+
priceKey?: string;
|
|
16
|
+
productKey?: string;
|
|
17
|
+
priceId?: string;
|
|
18
|
+
/** Customer-selected deposit in the price currency's native units. */
|
|
19
|
+
amount?: string;
|
|
20
|
+
/** Defaults to true for recurring prices; false buys the initial term only. */
|
|
21
|
+
autoRenew?: boolean;
|
|
22
|
+
successUrl?: string;
|
|
23
|
+
}): Promise<CheckoutSessionLink>;
|
|
24
|
+
checkoutSource(sessionId: string, opts?: CheckoutSourceOptions): CheckoutSource;
|
|
25
|
+
}
|
|
26
|
+
export declare function registerCheckout(client: object, checkout: CheckoutCalls): void;
|
|
27
|
+
/** The checkout calls of a client from createBillingClient. */
|
|
28
|
+
export declare function checkoutOf(client: object): CheckoutCalls;
|