@phala/pay 0.1.2 → 0.3.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 +106 -1
- package/README.md +146 -44
- package/dist/checkout.d.ts +37 -7
- package/dist/checkout.d.ts.map +1 -1
- package/dist/checkout.js +55 -11
- package/dist/checkout.js.map +1 -1
- package/dist/client.d.ts +8 -7
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +14 -8
- package/dist/client.js.map +1 -1
- package/dist/deposit-address.d.ts +47 -0
- package/dist/deposit-address.d.ts.map +1 -0
- package/dist/deposit-address.js +88 -0
- package/dist/deposit-address.js.map +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/payment.d.ts +43 -1
- package/dist/payment.d.ts.map +1 -1
- package/dist/payment.js +32 -10
- package/dist/payment.js.map +1 -1
- package/dist/quote.d.ts +7 -3
- package/dist/quote.d.ts.map +1 -1
- package/dist/quote.js +5 -3
- package/dist/quote.js.map +1 -1
- package/dist/react/Checkout.d.ts +11 -1
- package/dist/react/Checkout.d.ts.map +1 -1
- package/dist/react/Checkout.js +28 -37
- package/dist/react/Checkout.js.map +1 -1
- package/dist/react/DepositAddress.d.ts +31 -0
- package/dist/react/DepositAddress.d.ts.map +1 -0
- package/dist/react/DepositAddress.js +108 -0
- package/dist/react/DepositAddress.js.map +1 -0
- package/dist/react/Field.d.ts +7 -0
- package/dist/react/Field.d.ts.map +1 -0
- package/dist/react/Field.js +21 -0
- package/dist/react/Field.js.map +1 -0
- package/dist/react/appearance.d.ts +1 -1
- package/dist/react/appearance.d.ts.map +1 -1
- package/dist/react/appearance.js +3 -0
- package/dist/react/appearance.js.map +1 -1
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +1 -0
- package/dist/react/index.js.map +1 -1
- package/dist/react/useCheckout.d.ts +3 -1
- package/dist/react/useCheckout.d.ts.map +1 -1
- package/dist/react/useCheckout.js +4 -3
- package/dist/react/useCheckout.js.map +1 -1
- package/dist/server/addresses.d.ts +78 -0
- package/dist/server/addresses.d.ts.map +1 -0
- package/dist/server/addresses.js +118 -0
- package/dist/server/addresses.js.map +1 -0
- package/dist/server/index.d.ts +12 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +12 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/sweeps.d.ts +78 -0
- package/dist/server/sweeps.d.ts.map +1 -0
- package/dist/server/sweeps.js +117 -0
- package/dist/server/sweeps.js.map +1 -0
- package/dist/server/webhook.d.ts +61 -0
- package/dist/server/webhook.d.ts.map +1 -0
- package/dist/server/webhook.js +150 -0
- package/dist/server/webhook.js.map +1 -0
- package/dist/wallet.d.ts +10 -8
- package/dist/wallet.d.ts.map +1 -1
- package/dist/wallet.js +61 -63
- package/dist/wallet.js.map +1 -1
- package/package.json +6 -1
- package/src/checkout.ts +89 -15
- package/src/client.ts +14 -8
- package/src/deposit-address.ts +138 -0
- package/src/index.ts +19 -1
- package/src/payment.ts +72 -10
- package/src/quote.ts +12 -6
- package/src/react/Checkout.tsx +72 -70
- package/src/react/DepositAddress.tsx +213 -0
- package/src/react/Field.tsx +46 -0
- package/src/react/appearance.ts +3 -0
- package/src/react/index.ts +1 -0
- package/src/react/useCheckout.ts +6 -2
- package/src/server/addresses.ts +223 -0
- package/src/server/index.ts +38 -0
- package/src/server/sweeps.ts +197 -0
- package/src/server/webhook.ts +204 -0
- package/src/wallet.ts +74 -68
package/CHANGELOG.md
CHANGED
|
@@ -6,10 +6,101 @@ All notable changes to `@phala/pay` are recorded here. The format follows
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.3.0] - 2026-10-01
|
|
10
|
+
|
|
11
|
+
### Changed (breaking)
|
|
12
|
+
|
|
13
|
+
- `constructEvent` takes webhook public keys only in Standard Webhooks' `whpk_` form (`whpk_` and
|
|
14
|
+
the standard base64 of the key), as `GET /v1/attestation` lists them; a hex or bare base64 key
|
|
15
|
+
is refused.
|
|
16
|
+
- `parseClientQuote` requires `confirmations`, an integer or `null`, as the service always sends
|
|
17
|
+
it.
|
|
18
|
+
- `constructEvent` requires the event's `actor` and `request` (`null` or the causing request), as
|
|
19
|
+
the service always sends them, and refuses an envelope without them; `WebhookEvent.actor` and
|
|
20
|
+
`request` are no longer optional.
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- `constructEvent` returns the event's `request` (the causing API request's `id` and
|
|
25
|
+
`idempotency_key`, or `null`), as `WebhookEvent` declares; it was dropped. A malformed `request`
|
|
26
|
+
is refused like any malformed event.
|
|
27
|
+
|
|
28
|
+
## [0.2.0] - 2026-09-29
|
|
29
|
+
|
|
30
|
+
### Changed (breaking)
|
|
31
|
+
|
|
32
|
+
- `quoteAddress(forwarder, quote, treasury, account)` takes the treasury explicitly, like
|
|
33
|
+
`depositAddress`; it no longer reads the quote's own `treasury`, which a compromised service
|
|
34
|
+
could set to an attacker's with that treasury's valid address.
|
|
35
|
+
|
|
36
|
+
- `quoteSalt(account, clientReferenceId, quoteId)` replaces `lockSalt`: a quote's address salt is
|
|
37
|
+
tagged `"quote"` (design D3; it was `"lock"`), so `quoteAddress` recomputes the service's
|
|
38
|
+
addresses again.
|
|
39
|
+
|
|
40
|
+
- `<Checkout expectedAddress>` is required, as are `expectedAddress` in `createCheckout`,
|
|
41
|
+
`useCheckout`, `PhalaPay.checkout(clientSecret, { expectedAddress })`, and the second argument of
|
|
42
|
+
`retrieveQuote`/`PhalaPay.retrieveQuote`: the address your backend recomputed. A quote naming
|
|
43
|
+
another address fails closed with `CheckoutError` `address_mismatch`; the component shows
|
|
44
|
+
nothing to pay.
|
|
45
|
+
- `ClientQuote` has `livemode` (the checkout shows "Test mode") and `payment_status: "reversed"`;
|
|
46
|
+
`CheckoutStatus` gains `reversed`, with "reversed" copy.
|
|
47
|
+
|
|
48
|
+
### Added
|
|
49
|
+
|
|
50
|
+
- `verifyQuoteAddress(pins, quote)` and `verifyDepositAddress(pins, address)` in
|
|
51
|
+
`@phala/pay/server` recompute an address from `AddressPins` you configure (`account`, `factory`,
|
|
52
|
+
`implementation`, `treasuries` per chain), never from the response's treasury, and throw
|
|
53
|
+
`AddressMismatchError`; in live mode a chain without a pinned treasury fails closed, and test mode
|
|
54
|
+
falls back to the response's treasury with a console warning.
|
|
55
|
+
|
|
56
|
+
- `CheckoutError.requestId` (the failed response's `Request-Id`) and, for `rate_limited`,
|
|
57
|
+
`retryAfter` (its `Retry-After`, in seconds), which `createCheckout` and `<DepositAddress>` wait
|
|
58
|
+
before reading again.
|
|
59
|
+
- `WebhookEvent.request` (the request that caused the event, or `null` for the service's
|
|
60
|
+
workers) and the documented `data.previous_attributes` of `*.updated` events.
|
|
61
|
+
|
|
62
|
+
- `<DepositAddress clientSecret apiBase pollInterval?>` follows the address's payments and shows
|
|
63
|
+
each within about a block of arriving ("1.5 PHA received on Sepolia, 1 confirmation"), then
|
|
64
|
+
credited, rejected, or reversed; `retrieveDepositAddress`, `parseClientDepositAddress`, and
|
|
65
|
+
`depositAddressIdFromClientSecret` read the same public view.
|
|
66
|
+
- `@phala/pay/server`, which takes no secret key: `constructEvent(payload, headers, publicKeys,
|
|
67
|
+
{ expectedAccount, expectedLivemode })` verifies Standard Webhooks `v1a` deliveries with
|
|
68
|
+
WebCrypto (Node 20+, Deno, Bun, edge runtimes), failing closed for another account or mode;
|
|
69
|
+
`forwarderAddress`, `quoteSalt`, `depositAddressSalt`, `quoteAddress`, and `depositAddress`
|
|
70
|
+
recompute addresses; `flushTransaction`, `flushTransactions`, `safeBatch`, and `batchChecksum`
|
|
71
|
+
build sweeps offline, with the Transaction Builder's `BatchFile` type.
|
|
72
|
+
|
|
73
|
+
- `<DepositAddress depositAddress chainId? asset?>` (`@phala/pay/react`): a customer's persistent
|
|
74
|
+
deposit address, one address for all supported tokens and networks. The payer picks a network
|
|
75
|
+
and a token; it shows a QR code of that token's EIP-681 request, the token contract and address
|
|
76
|
+
to copy (each network's own address where they differ), and "send only supported tokens; any
|
|
77
|
+
amount, credited at the market rate when it arrives" copy. `depositAddressTransfer(network,
|
|
78
|
+
asset)` reads and checks one token's amount-less `payment_uri`, and `parseTransferUri(uri)` is
|
|
79
|
+
the EIP-681 ERC-20 transfer parser `quoteTransfer` uses.
|
|
80
|
+
|
|
81
|
+
- `<Checkout walletClient>` and `payWithWallet(walletClient, quote)`: pay with the page's own viem
|
|
82
|
+
`WalletClient`, such as wagmi's `useWalletClient()` (RainbowKit, ConnectKit, AppKit). The wallet
|
|
83
|
+
tab then uses its account and switches its chain, without listing the browser's wallets.
|
|
84
|
+
|
|
85
|
+
### Changed
|
|
86
|
+
|
|
87
|
+
- `<Checkout>` copy for fast credit: a seen payment reads "Payment received, N confirmations.
|
|
88
|
+
Crediting in about 30 seconds"; `confirming` and `credited` are documented for crediting at the
|
|
89
|
+
route's confirmation (two blocks on Ethereum) instead of finality.
|
|
90
|
+
- `watchWallets` discovers EIP-6963 wallets through [mipd](https://github.com/wevm/mipd), wagmi's
|
|
91
|
+
discovery store, now a dependency; the `window.ethereum` fallback stays. Wallet errors are
|
|
92
|
+
classified with viem's `UserRejectedRequestError` and `SwitchChainError`.
|
|
93
|
+
|
|
9
94
|
## [0.1.2] - 2026-09-27
|
|
10
95
|
|
|
11
96
|
### Added
|
|
12
97
|
|
|
98
|
+
- `CheckoutError.requestId` (the failed response's `Request-Id`) and, for `rate_limited`,
|
|
99
|
+
`retryAfter` (its `Retry-After`, in seconds), which `createCheckout` and `<DepositAddress>` wait
|
|
100
|
+
before reading again.
|
|
101
|
+
- `WebhookEvent.request` (the request that caused the event, or `null` for the service's
|
|
102
|
+
workers) and the documented `data.previous_attributes` of `*.updated` events.
|
|
103
|
+
|
|
13
104
|
- `<Checkout onChange>`: called once per status change with `{ status, quote, error }`, like
|
|
14
105
|
Stripe Elements' `onChange`, so the page can react to `seen` or `confirming` without its own
|
|
15
106
|
polling.
|
|
@@ -23,6 +114,12 @@ All notable changes to `@phala/pay` are recorded here. The format follows
|
|
|
23
114
|
|
|
24
115
|
### Added
|
|
25
116
|
|
|
117
|
+
- `CheckoutError.requestId` (the failed response's `Request-Id`) and, for `rate_limited`,
|
|
118
|
+
`retryAfter` (its `Retry-After`, in seconds), which `createCheckout` and `<DepositAddress>` wait
|
|
119
|
+
before reading again.
|
|
120
|
+
- `WebhookEvent.request` (the request that caused the event, or `null` for the service's
|
|
121
|
+
workers) and the documented `data.previous_attributes` of `*.updated` events.
|
|
122
|
+
|
|
26
123
|
- `appearance.variables.accessibleColorOnColorPrimary` (`--pp-accessible-color-on-color-primary`,
|
|
27
124
|
default `#ffffff`): the color of text on a `colorPrimary` background, such as the pay button's
|
|
28
125
|
label, so a light brand primary stays readable. Same name and meaning as Stripe's current
|
|
@@ -38,6 +135,12 @@ All notable changes to `@phala/pay` are recorded here. The format follows
|
|
|
38
135
|
|
|
39
136
|
### Added
|
|
40
137
|
|
|
138
|
+
- `CheckoutError.requestId` (the failed response's `Request-Id`) and, for `rate_limited`,
|
|
139
|
+
`retryAfter` (its `Retry-After`, in seconds), which `createCheckout` and `<DepositAddress>` wait
|
|
140
|
+
before reading again.
|
|
141
|
+
- `WebhookEvent.request` (the request that caused the event, or `null` for the service's
|
|
142
|
+
workers) and the documented `data.previous_attributes` of `*.updated` events.
|
|
143
|
+
|
|
41
144
|
- `PhalaPay({ apiBase })`: `retrieveQuote(clientSecret)` reads a quote's public view
|
|
42
145
|
(`GET /v1/quotes/{id}?client_secret=…`) and `checkout(clientSecret)` follows it, exposing the
|
|
43
146
|
payer-facing status (also `createCheckout` and `retrieveQuote`).
|
|
@@ -49,7 +152,9 @@ All notable changes to `@phala/pay` are recorded here. The format follows
|
|
|
49
152
|
without trailing zeros (`1,273.9185`); `tokenAmount(quote)` is the plain decimal a wallet
|
|
50
153
|
accepts, which `<Checkout>`'s "Exact amount" copies.
|
|
51
154
|
|
|
52
|
-
[
|
|
155
|
+
[Unreleased]: https://github.com/Phala-Network/phala-pay/compare/sdk-js-v0.3.0...HEAD
|
|
156
|
+
[0.3.0]: https://github.com/Phala-Network/phala-pay/compare/sdk-js-v0.2.0...sdk-js-v0.3.0
|
|
157
|
+
[0.2.0]: https://github.com/Phala-Network/phala-pay/compare/sdk-js-v0.1.2...sdk-js-v0.2.0
|
|
53
158
|
[0.1.2]: https://github.com/Phala-Network/phala-pay/compare/sdk-js-v0.1.1...sdk-js-v0.1.2
|
|
54
159
|
[0.1.1]: https://github.com/Phala-Network/phala-pay/compare/sdk-js-v0.1.0...sdk-js-v0.1.1
|
|
55
160
|
[0.1.0]: https://github.com/Phala-Network/phala-pay/releases/tag/sdk-js-v0.1.0
|
package/README.md
CHANGED
|
@@ -11,46 +11,42 @@ Python SDK (`phala-pay`) and fulfils from the signed `deposit.credited` webhook.
|
|
|
11
11
|
npm install @phala/pay viem
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
Peer dependencies: `viem` 2, and `react` 18 or 19 for `@phala/pay/react`.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
prints an `allowBuilds` entry naming the exact commit; add it to `pnpm-workspace.yaml` and run the
|
|
20
|
-
same command again.
|
|
21
|
-
|
|
22
|
-
```sh
|
|
23
|
-
pnpm add "github:Phala-Network/phala-pay#main&path:/sdk/js"
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
```yaml
|
|
27
|
-
# pnpm-workspace.yaml: the entry pnpm printed
|
|
28
|
-
allowBuilds:
|
|
29
|
-
"@phala/pay@https://codeload.github.com/Phala-Network/phala-pay/tar.gz/<commit>#path:/sdk/js": true
|
|
30
|
-
```
|
|
14
|
+
Peer dependencies: `viem` 2, and `react` 18 or 19 for `@phala/pay/react`. Browser wallets are
|
|
15
|
+
found with EIP-6963 through [mipd](https://github.com/wevm/mipd), wagmi's discovery store (with a
|
|
16
|
+
`window.ethereum` fallback for wallets that do not announce themselves). If your page already
|
|
17
|
+
connects a wallet with wagmi, RainbowKit, ConnectKit, or AppKit, pass it in instead; see
|
|
18
|
+
[Your own wallet connection](#your-own-wallet-connection).
|
|
31
19
|
|
|
32
20
|
## Quickstart
|
|
33
21
|
|
|
34
|
-
1. Your backend creates a quote (`POST /v1/quotes
|
|
35
|
-
`
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
|
|
22
|
+
1. Your backend creates a quote (`POST /v1/quotes` with its API key, for example
|
|
23
|
+
`phala-pay`'s `pay.quotes.create`), recomputes its address from the pinned forwarder (the
|
|
24
|
+
Python SDK does it on every call), and returns only its `client_secret` and that address to
|
|
25
|
+
the signed-in user's browser.
|
|
26
|
+
2. Render the checkout with them:
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
"use client";
|
|
30
|
+
import { Checkout } from "@phala/pay/react";
|
|
31
|
+
|
|
32
|
+
export function TopUp(props: {
|
|
33
|
+
clientSecret: string;
|
|
34
|
+
expectedAddress: string;
|
|
35
|
+
onPaid: () => void;
|
|
36
|
+
onRetry: () => void;
|
|
37
|
+
}) {
|
|
38
|
+
return (
|
|
39
|
+
<Checkout
|
|
40
|
+
clientSecret={props.clientSecret}
|
|
41
|
+
expectedAddress={props.expectedAddress}
|
|
42
|
+
apiBase="https://pay.example.com"
|
|
43
|
+
onSuccess={props.onPaid}
|
|
44
|
+
onExpire={props.onRetry}
|
|
45
|
+
buttonText="Pay with crypto"
|
|
46
|
+
/>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
```
|
|
54
50
|
|
|
55
51
|
3. Credit the account when your webhook endpoint receives `deposit.credited`. `onSuccess` is for the
|
|
56
52
|
UI only: the browser is not a trusted source of payment.
|
|
@@ -59,6 +55,10 @@ The component reads `GET /v1/quotes/{id}?client_secret=…` every three seconds.
|
|
|
59
55
|
public (`Access-Control-Allow-Origin: *`) and shows only what the payer needs: the amount, the
|
|
60
56
|
token, the chain, the deposit address, the EIP-681 payment request, the expiry, and the payment
|
|
61
57
|
status. Keep the client secret out of logs and URLs you share; anyone holding it can see that view.
|
|
58
|
+
`expectedAddress` is required and fails closed: when the quote the service returns names another
|
|
59
|
+
address, the checkout shows "This payment address could not be verified" and nothing to pay (a
|
|
60
|
+
compromised service cannot make the page show its own address). A test-mode quote says "Test
|
|
61
|
+
mode".
|
|
62
62
|
|
|
63
63
|
### Statuses
|
|
64
64
|
|
|
@@ -67,9 +67,10 @@ status. Keep the client secret out of logs and URLs you share; anyone holding it
|
|
|
67
67
|
| `loading` | Loading payment details |
|
|
68
68
|
| `waiting` | Payment options, the exact amount, and the time left |
|
|
69
69
|
| `seen` | Payment in a block, with its confirmations (a reorg can still remove it) |
|
|
70
|
-
| `confirming` | Payment
|
|
71
|
-
| `credited` | Credited; `onSuccess` is called once |
|
|
72
|
-
| `rejected` |
|
|
70
|
+
| `confirming` | Payment at the route's confirmation (two blocks on Ethereum), being valued and screened |
|
|
71
|
+
| `credited` | Credited, typically about 30 seconds after paying; `onSuccess` is called once |
|
|
72
|
+
| `rejected` | Will not be credited; the payer contacts support |
|
|
73
|
+
| `reversed` | Credited, then its transaction left the chain before finality: the payment did not happen |
|
|
73
74
|
| `expired`, `canceled` | The address is hidden; `onExpire` is called once |
|
|
74
75
|
| `error` | The client secret is not valid |
|
|
75
76
|
|
|
@@ -77,14 +78,81 @@ status. Keep the client secret out of logs and URLs you share; anyone holding it
|
|
|
77
78
|
Elements' `onChange`, for example to hide your own "new payment" control while a payment is
|
|
78
79
|
`seen` or `confirming`.
|
|
79
80
|
|
|
81
|
+
To resume after a reload, keep the client secret in the browser (for example `localStorage`, per
|
|
82
|
+
signed-in account) until the status is `credited`, `expired`, `canceled`, or `error`. Tell a
|
|
83
|
+
payer who is waiting that the payment is credited in about 30 seconds and the credit arrives on
|
|
84
|
+
its own.
|
|
85
|
+
|
|
86
|
+
A credit is made before Ethereum finality (about 15 minutes). In the rare case that a reorg drops
|
|
87
|
+
the payment's transaction, your backend receives `deposit.reversed` and claws the credit back as
|
|
88
|
+
for `deposit.refunded`; the checkout itself only reflects the quote.
|
|
89
|
+
|
|
80
90
|
The payment options disappear once a payment is seen, and at `expires_at`. A payment of a different
|
|
81
91
|
amount, or after expiry, is still credited, at the market price instead of the quote's.
|
|
82
92
|
|
|
93
|
+
### Your own wallet connection
|
|
94
|
+
|
|
95
|
+
By default the wallet tab lists the browser's wallets. If your page already connects one, pass its
|
|
96
|
+
viem `WalletClient` as `walletClient`: the wallet tab then pays with that client and its account
|
|
97
|
+
(switching its chain to the quote's) and lists nothing else. With wagmi, including RainbowKit,
|
|
98
|
+
ConnectKit, and AppKit, that is `useWalletClient()`:
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
"use client";
|
|
102
|
+
import { Checkout } from "@phala/pay/react";
|
|
103
|
+
import { useWalletClient } from "wagmi";
|
|
104
|
+
|
|
105
|
+
export function TopUp(props: { clientSecret: string; expectedAddress: string }) {
|
|
106
|
+
const { data: walletClient } = useWalletClient();
|
|
107
|
+
return (
|
|
108
|
+
<Checkout
|
|
109
|
+
clientSecret={props.clientSecret}
|
|
110
|
+
expectedAddress={props.expectedAddress}
|
|
111
|
+
apiBase="https://pay.example.com"
|
|
112
|
+
walletClient={walletClient}
|
|
113
|
+
/>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
While no wallet is connected (`walletClient` is `undefined`), the tab lists the browser's wallets
|
|
119
|
+
as usual. The QR code and manual tabs do not change.
|
|
120
|
+
|
|
121
|
+
### A customer's deposit address
|
|
122
|
+
|
|
123
|
+
For top-ups of any amount, your backend creates the customer's persistent deposit address with
|
|
124
|
+
`POST /v1/deposit_addresses` (its API key) and passes its `address` and `networks` to the page:
|
|
125
|
+
one address for all supported tokens and networks. `<DepositAddress>` lets the payer pick a
|
|
126
|
+
network and a token and shows a QR code of that token's transfer request, the token contract, and
|
|
127
|
+
the address to copy, with "send only supported tokens" and "credited at the market rate when it
|
|
128
|
+
arrives" copy; where a network's address differs (another treasury), it shows that network's own.
|
|
129
|
+
Credit the customer from the `deposit.credited` webhook.
|
|
130
|
+
|
|
131
|
+
```tsx
|
|
132
|
+
import { DepositAddress } from "@phala/pay/react";
|
|
133
|
+
|
|
134
|
+
<DepositAddress
|
|
135
|
+
depositAddress={{ address, networks }}
|
|
136
|
+
clientSecret={clientSecret} // the same response's client_secret: shows payments as they arrive
|
|
137
|
+
apiBase="https://pay.example.com"
|
|
138
|
+
chainId={84532}
|
|
139
|
+
asset="usdc"
|
|
140
|
+
/>;
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
With `clientSecret` and `apiBase` it reads the address's public view every three seconds and lists
|
|
144
|
+
its payments of the last 24 hours: "1.5 PHA received on Sepolia, 1 confirmation" within about a
|
|
145
|
+
block of the transfer, then "credited" (or rejected or reversed). Display only.
|
|
146
|
+
|
|
147
|
+
`depositAddressTransfer(network, asset)` reads and checks one token's amount-less EIP-681
|
|
148
|
+
`payment_uri` without React.
|
|
149
|
+
|
|
83
150
|
## Appearance
|
|
84
151
|
|
|
85
152
|
```tsx
|
|
86
153
|
<Checkout
|
|
87
154
|
clientSecret={clientSecret}
|
|
155
|
+
expectedAddress={expectedAddress}
|
|
88
156
|
apiBase={apiBase}
|
|
89
157
|
appearance={{
|
|
90
158
|
theme: "dark",
|
|
@@ -121,8 +189,8 @@ readable.
|
|
|
121
189
|
import { PhalaPay, payWithWallet, watchWallets, type Wallet } from "@phala/pay";
|
|
122
190
|
|
|
123
191
|
const pay = new PhalaPay({ apiBase: "https://pay.example.com" });
|
|
124
|
-
const quote = await pay.retrieveQuote(clientSecret); // one read
|
|
125
|
-
const checkout = pay.checkout(clientSecret); // or follow it until it settles
|
|
192
|
+
const quote = await pay.retrieveQuote(clientSecret, expectedAddress); // one read, checked
|
|
193
|
+
const checkout = pay.checkout(clientSecret, { expectedAddress }); // or follow it until it settles
|
|
126
194
|
const unsubscribe = checkout.subscribe(({ status, quote, error }) => render(status, quote, error));
|
|
127
195
|
|
|
128
196
|
const stop = watchWallets((wallets) => showWalletButtons(wallets));
|
|
@@ -142,10 +210,43 @@ unsubscribe();
|
|
|
142
210
|
checkout.destroy();
|
|
143
211
|
```
|
|
144
212
|
|
|
145
|
-
`payWithWallet`
|
|
213
|
+
`payWithWallet` takes a discovered wallet's EIP-1193 provider or a viem `WalletClient` (such as
|
|
214
|
+
wagmi's `useWalletClient()` or `getWalletClient(config)`; its account is used without asking to
|
|
215
|
+
connect again). It connects, switches the wallet to the quote's chain (adding Ethereum, Sepolia,
|
|
146
216
|
Base, or Base Sepolia when the wallet lacks it), and sends the ERC-20 `transfer` stated by the
|
|
147
217
|
quote's `payment_uri`, after checking that it pays exactly `amount_atomic` to `address`.
|
|
148
218
|
|
|
219
|
+
## Server helpers
|
|
220
|
+
|
|
221
|
+
`@phala/pay/server` is for your backend and takes no API key (call the API itself with a
|
|
222
|
+
restricted key, `ppay_rk_…`, from the backend only, never the browser):
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
import { constructEvent, flushTransactions, safeBatch, verifyQuoteAddress } from "@phala/pay/server";
|
|
226
|
+
|
|
227
|
+
// Your pins, configured on your server and never read from the service. The address is derived
|
|
228
|
+
// from your treasury, not the quote's: a mismatch throws AddressMismatchError, and in live mode a
|
|
229
|
+
// chain without a pinned treasury fails closed.
|
|
230
|
+
const pins = { account: "acct_…", factory: FACTORY, implementation: IMPLEMENTATION, treasuries: { 1: TREASURY } };
|
|
231
|
+
const expectedAddress = verifyQuoteAddress(pins, quote); // pass it to <Checkout expectedAddress>
|
|
232
|
+
|
|
233
|
+
// Standard Webhooks v1a (ed25519, WebCrypto: Node 20+, Deno, Bun, edge runtimes). Fails closed
|
|
234
|
+
// unless the signature verifies with a pinned key and the event is your account's in this mode.
|
|
235
|
+
// WEBHOOK_PUBLIC_KEYS: your account's keys in this mode, `whpk_…`, pinned from GET /v1/attestation.
|
|
236
|
+
const event = await constructEvent(rawBody, request.headers, WEBHOOK_PUBLIC_KEYS, {
|
|
237
|
+
expectedAccount: "acct_…",
|
|
238
|
+
expectedLivemode: false,
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// Sweeping, offline: forwarders from GET /v1/forwarders?sweepable=<token>.
|
|
242
|
+
const calls = flushTransactions(forwarders, PHA);
|
|
243
|
+
const batchFile = safeBatch(1, TREASURY_SAFE, calls); // Safe Transaction Builder JSON
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
`verifyDepositAddress(pins, address)` checks every network of a deposit address the same way.
|
|
247
|
+
`quoteAddress`, `depositAddress`, `forwarderAddress`, `quoteSalt`, and `depositAddressSalt`
|
|
248
|
+
recompute an address offline from a treasury you pass, as the Python SDK does.
|
|
249
|
+
|
|
149
250
|
## Development
|
|
150
251
|
|
|
151
252
|
```sh
|
|
@@ -156,7 +257,8 @@ pnpm run e2e:docker # the same, with Chromium from Playwright's image (as CI ru
|
|
|
156
257
|
```
|
|
157
258
|
|
|
158
259
|
The end-to-end tests start Anvil as Sepolia, deploy a test token, and pay a quote from a mocked
|
|
159
|
-
EIP-6963 wallet backed by the node
|
|
260
|
+
EIP-6963 wallet backed by the node (and again through a viem `WalletClient` passed as
|
|
261
|
+
`walletClient`), then decode the QR code and exercise the manual details.
|
|
160
262
|
|
|
161
263
|
## Releases
|
|
162
264
|
|
package/dist/checkout.d.ts
CHANGED
|
@@ -3,18 +3,39 @@ import { type ClientQuote } from "./quote.js";
|
|
|
3
3
|
* What the payer sees:
|
|
4
4
|
* - `loading` until the quote is first read;
|
|
5
5
|
* - `waiting` for a payment, until `expires_at`;
|
|
6
|
-
* - `seen` once a transfer is in a block, `confirming` once it
|
|
7
|
-
*
|
|
6
|
+
* - `seen` once a transfer is in a block, `confirming` once it reaches the route's confirmation
|
|
7
|
+
* (two blocks on Ethereum) and is being valued;
|
|
8
|
+
* - `credited` (about 30 seconds after paying), or `rejected` for a payment that will not be
|
|
9
|
+
* credited;
|
|
10
|
+
* - `reversed` when a credited payment's transaction left the chain before finality;
|
|
8
11
|
* - `expired` or `canceled` without a payment;
|
|
9
|
-
* - `error` when the client secret is not valid
|
|
12
|
+
* - `error` when the client secret is not valid, or the quote's address is not the one your
|
|
13
|
+
* backend expects (`address_mismatch`): nothing is shown to pay.
|
|
10
14
|
*/
|
|
11
|
-
export type CheckoutStatus = "loading" | "waiting" | "seen" | "confirming" | "credited" | "rejected" | "expired" | "canceled" | "error";
|
|
12
|
-
export type CheckoutErrorCode = "invalid_client_secret" | "rate_limited" | "network_error" | "invalid_response" | "api_error";
|
|
15
|
+
export type CheckoutStatus = "loading" | "waiting" | "seen" | "confirming" | "credited" | "rejected" | "reversed" | "expired" | "canceled" | "error";
|
|
16
|
+
export type CheckoutErrorCode = "invalid_client_secret" | "address_mismatch" | "rate_limited" | "network_error" | "invalid_response" | "api_error";
|
|
17
|
+
export interface CheckoutErrorOptions extends ErrorOptions {
|
|
18
|
+
/** The failed response's `Request-Id`, `req_…`, to quote to support. */
|
|
19
|
+
requestId?: string;
|
|
20
|
+
/** For `rate_limited`: the seconds the service asked to wait (`Retry-After`). */
|
|
21
|
+
retryAfter?: number;
|
|
22
|
+
}
|
|
13
23
|
export declare class CheckoutError extends Error {
|
|
14
24
|
readonly code: CheckoutErrorCode;
|
|
15
25
|
readonly name = "CheckoutError";
|
|
16
|
-
|
|
26
|
+
/** The failed response's `Request-Id`, when the service answered. */
|
|
27
|
+
readonly requestId: string | undefined;
|
|
28
|
+
/** For `rate_limited`: the seconds to wait before the next read. */
|
|
29
|
+
readonly retryAfter: number | undefined;
|
|
30
|
+
constructor(code: CheckoutErrorCode, message: string, options?: CheckoutErrorOptions);
|
|
17
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* The error of a failed read of a public view, with the response's `Request-Id` and, on `429`,
|
|
34
|
+
* its `Retry-After`.
|
|
35
|
+
*/
|
|
36
|
+
export declare function responseError(response: Response, notFound: string): CheckoutError;
|
|
37
|
+
/** The delay before the next read after `failures` failed ones, at least what `error` asks. */
|
|
38
|
+
export declare function pollDelay(interval: number, failures: number, error: CheckoutError | null): number;
|
|
18
39
|
export interface CheckoutState {
|
|
19
40
|
status: CheckoutStatus;
|
|
20
41
|
quote: ClientQuote | null;
|
|
@@ -24,6 +45,12 @@ export interface CheckoutState {
|
|
|
24
45
|
export interface CheckoutOptions {
|
|
25
46
|
/** The quote's `client_secret`, from your backend's `POST /v1/quotes`. */
|
|
26
47
|
clientSecret: string;
|
|
48
|
+
/**
|
|
49
|
+
* The quote's address as your backend's SDK recomputed it from the pinned forwarder. The
|
|
50
|
+
* checkout fails closed, showing nothing to pay, when the quote read from the service names
|
|
51
|
+
* another address.
|
|
52
|
+
*/
|
|
53
|
+
expectedAddress: string;
|
|
27
54
|
/** The service origin, for example `https://topup.example.com`. */
|
|
28
55
|
apiBase: string;
|
|
29
56
|
/** Milliseconds between status reads; default 3000. */
|
|
@@ -43,12 +70,15 @@ export interface CheckoutSession {
|
|
|
43
70
|
}
|
|
44
71
|
export interface RetrieveQuoteOptions {
|
|
45
72
|
clientSecret: string;
|
|
73
|
+
/** The address your backend recomputed; a quote naming another one is refused. */
|
|
74
|
+
expectedAddress: string;
|
|
46
75
|
apiBase: string;
|
|
47
76
|
fetch?: typeof globalThis.fetch;
|
|
48
77
|
}
|
|
49
78
|
/**
|
|
50
79
|
* Reads a quote's public view once, as Stripe.js's `retrievePaymentIntent(clientSecret)` does.
|
|
51
|
-
* Rejects with a `CheckoutError`: `invalid_client_secret` for an unknown quote or secret
|
|
80
|
+
* Rejects with a `CheckoutError`: `invalid_client_secret` for an unknown quote or secret,
|
|
81
|
+
* `address_mismatch` for a quote whose address is not `expectedAddress`.
|
|
52
82
|
*/
|
|
53
83
|
export declare function retrieveQuote(options: RetrieveQuoteOptions): Promise<ClientQuote>;
|
|
54
84
|
/** The payer-facing status of a quote at `nowSeconds`. */
|
package/dist/checkout.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkout.d.ts","sourceRoot":"","sources":["../src/checkout.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"checkout.d.ts","sourceRoot":"","sources":["../src/checkout.ts"],"names":[],"mappings":"AACA,OAAO,EAA6C,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzF;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,cAAc,GACtB,SAAS,GACT,SAAS,GACT,MAAM,GACN,YAAY,GACZ,UAAU,GACV,UAAU,GACV,UAAU,GACV,SAAS,GACT,UAAU,GACV,OAAO,CAAC;AAEZ,MAAM,MAAM,iBAAiB,GACzB,uBAAuB,GACvB,kBAAkB,GAClB,cAAc,GACd,eAAe,GACf,kBAAkB,GAClB,WAAW,CAAC;AAEhB,MAAM,WAAW,oBAAqB,SAAQ,YAAY;IACxD,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iFAAiF;IACjF,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,aAAc,SAAQ,KAAK;IAQpC,QAAQ,CAAC,IAAI,EAAE,iBAAiB;IAPlC,SAAkB,IAAI,mBAAmB;IACzC,qEAAqE;IACrE,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC,oEAAoE;IACpE,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;gBAG7B,IAAI,EAAE,iBAAiB,EAChC,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,oBAAoB;CAMjC;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,GAAG,aAAa,CAkBjF;AAED,+FAA+F;AAC/F,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,IAAI,GAAG,MAAM,CAGjG;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,cAAc,CAAC;IACvB,KAAK,EAAE,WAAW,GAAG,IAAI,CAAC;IAC1B,gGAAgG;IAChG,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,eAAe;IAC9B,0EAA0E;IAC1E,YAAY,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,eAAe,EAAE,MAAM,CAAC;IACxB,mEAAmE;IACnE,OAAO,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAChC,+CAA+C;IAC/C,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,IAAI,aAAa,CAAC;IAC1B,gFAAgF;IAChF,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAChE,uDAAuD;IACvD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,qBAAqB;IACrB,OAAO,IAAI,IAAI,CAAC;CACjB;AAKD,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,kFAAkF;IAClF,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACjC;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,WAAW,CAAC,CA0BvF;AASD,0DAA0D;AAC1D,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,GAAG,cAAc,CAWrF;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,eAAe,GAAG,eAAe,CAqGxE"}
|
package/dist/checkout.js
CHANGED
|
@@ -1,42 +1,80 @@
|
|
|
1
|
+
import { getAddress, isAddress, isAddressEqual } from "viem";
|
|
1
2
|
import { parseClientQuote, quoteIdFromClientSecret } from "./quote.js";
|
|
2
3
|
export class CheckoutError extends Error {
|
|
3
4
|
code;
|
|
4
5
|
name = "CheckoutError";
|
|
6
|
+
/** The failed response's `Request-Id`, when the service answered. */
|
|
7
|
+
requestId;
|
|
8
|
+
/** For `rate_limited`: the seconds to wait before the next read. */
|
|
9
|
+
retryAfter;
|
|
5
10
|
constructor(code, message, options) {
|
|
6
11
|
super(message, options);
|
|
7
12
|
this.code = code;
|
|
13
|
+
this.requestId = options?.requestId;
|
|
14
|
+
this.retryAfter = options?.retryAfter;
|
|
8
15
|
}
|
|
9
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* The error of a failed read of a public view, with the response's `Request-Id` and, on `429`,
|
|
19
|
+
* its `Retry-After`.
|
|
20
|
+
*/
|
|
21
|
+
export function responseError(response, notFound) {
|
|
22
|
+
const header = response.headers.get("request-id");
|
|
23
|
+
const requestId = header === null ? {} : { requestId: header };
|
|
24
|
+
if (response.status === 404) {
|
|
25
|
+
return new CheckoutError("invalid_client_secret", notFound, requestId);
|
|
26
|
+
}
|
|
27
|
+
if (response.status === 429) {
|
|
28
|
+
const seconds = Number(response.headers.get("retry-after"));
|
|
29
|
+
return new CheckoutError("rate_limited", "too many status requests", {
|
|
30
|
+
...requestId,
|
|
31
|
+
...(Number.isFinite(seconds) && seconds > 0 ? { retryAfter: seconds } : {}),
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return new CheckoutError("api_error", `the payment service answered ${response.status}`, requestId);
|
|
35
|
+
}
|
|
36
|
+
/** The delay before the next read after `failures` failed ones, at least what `error` asks. */
|
|
37
|
+
export function pollDelay(interval, failures, error) {
|
|
38
|
+
const backoff = failures === 0 ? interval : Math.min(interval * 2 ** failures, MAX_BACKOFF);
|
|
39
|
+
return Math.max(backoff, (error?.retryAfter ?? 0) * 1000);
|
|
40
|
+
}
|
|
10
41
|
const DEFAULT_POLL_INTERVAL = 3000;
|
|
11
42
|
const MAX_BACKOFF = 30_000;
|
|
12
43
|
/**
|
|
13
44
|
* Reads a quote's public view once, as Stripe.js's `retrievePaymentIntent(clientSecret)` does.
|
|
14
|
-
* Rejects with a `CheckoutError`: `invalid_client_secret` for an unknown quote or secret
|
|
45
|
+
* Rejects with a `CheckoutError`: `invalid_client_secret` for an unknown quote or secret,
|
|
46
|
+
* `address_mismatch` for a quote whose address is not `expectedAddress`.
|
|
15
47
|
*/
|
|
16
48
|
export async function retrieveQuote(options) {
|
|
17
49
|
const quoteId = quoteIdFromClientSecret(options.clientSecret);
|
|
50
|
+
const expected = expectedAddress(options.expectedAddress);
|
|
18
51
|
const base = options.apiBase.replace(/\/+$/, "");
|
|
19
52
|
const url = `${base}/v1/quotes/${quoteId}?client_secret=${encodeURIComponent(options.clientSecret)}`;
|
|
20
53
|
const fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
21
54
|
// A simple GET with no custom headers, so the browser sends no CORS preflight.
|
|
22
55
|
const response = await fetchImpl(url, { cache: "no-store", credentials: "omit" });
|
|
23
|
-
if (response.status === 404) {
|
|
24
|
-
throw new CheckoutError("invalid_client_secret", "the quote or its client secret is unknown");
|
|
25
|
-
}
|
|
26
|
-
if (response.status === 429) {
|
|
27
|
-
throw new CheckoutError("rate_limited", "too many status requests");
|
|
28
|
-
}
|
|
29
56
|
if (!response.ok) {
|
|
30
|
-
throw
|
|
57
|
+
throw responseError(response, "the quote or its client secret is unknown");
|
|
31
58
|
}
|
|
59
|
+
let quote;
|
|
32
60
|
try {
|
|
33
|
-
|
|
61
|
+
quote = parseClientQuote(await response.json());
|
|
34
62
|
}
|
|
35
63
|
catch (cause) {
|
|
36
64
|
throw new CheckoutError("invalid_response", "unexpected response from the payment service", {
|
|
37
65
|
cause,
|
|
38
66
|
});
|
|
39
67
|
}
|
|
68
|
+
if (!isAddressEqual(getAddress(quote.address), expected)) {
|
|
69
|
+
throw new CheckoutError("address_mismatch", "the quote's address is not the one the merchant expects");
|
|
70
|
+
}
|
|
71
|
+
return quote;
|
|
72
|
+
}
|
|
73
|
+
function expectedAddress(value) {
|
|
74
|
+
if (!isAddress(value, { strict: false })) {
|
|
75
|
+
throw new TypeError("expectedAddress is not an address");
|
|
76
|
+
}
|
|
77
|
+
return getAddress(value);
|
|
40
78
|
}
|
|
41
79
|
/** The payer-facing status of a quote at `nowSeconds`. */
|
|
42
80
|
export function checkoutStatus(quote, nowSeconds) {
|
|
@@ -59,6 +97,7 @@ export function checkoutStatus(quote, nowSeconds) {
|
|
|
59
97
|
*/
|
|
60
98
|
export function createCheckout(options) {
|
|
61
99
|
quoteIdFromClientSecret(options.clientSecret);
|
|
100
|
+
expectedAddress(options.expectedAddress);
|
|
62
101
|
const now = options.now ?? Date.now;
|
|
63
102
|
const interval = options.pollInterval ?? DEFAULT_POLL_INTERVAL;
|
|
64
103
|
const listeners = new Set();
|
|
@@ -84,6 +123,7 @@ export function createCheckout(options) {
|
|
|
84
123
|
status === "error" ||
|
|
85
124
|
status === "credited" ||
|
|
86
125
|
status === "rejected" ||
|
|
126
|
+
status === "reversed" ||
|
|
87
127
|
status === "canceled" ||
|
|
88
128
|
(status === "expired" && quote?.status === "expired"));
|
|
89
129
|
}
|
|
@@ -99,7 +139,11 @@ export function createCheckout(options) {
|
|
|
99
139
|
: new CheckoutError("network_error", "could not reach the payment service", { cause });
|
|
100
140
|
failures += 1;
|
|
101
141
|
const quote = state.quote;
|
|
102
|
-
if (error.code === "
|
|
142
|
+
if (error.code === "address_mismatch") {
|
|
143
|
+
// Fail closed: forget the quote, so nothing about its address is shown.
|
|
144
|
+
setState({ status: "error", quote: null, error });
|
|
145
|
+
}
|
|
146
|
+
else if (error.code === "invalid_client_secret") {
|
|
103
147
|
setState({ status: "error", quote, error });
|
|
104
148
|
}
|
|
105
149
|
else {
|
|
@@ -121,7 +165,7 @@ export function createCheckout(options) {
|
|
|
121
165
|
if (finished()) {
|
|
122
166
|
return;
|
|
123
167
|
}
|
|
124
|
-
const delay =
|
|
168
|
+
const delay = pollDelay(interval, failures, state.error);
|
|
125
169
|
timer = setTimeout(() => {
|
|
126
170
|
void refresh().then(schedule);
|
|
127
171
|
}, delay);
|
package/dist/checkout.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkout.js","sourceRoot":"","sources":["../src/checkout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAAoB,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"checkout.js","sourceRoot":"","sources":["../src/checkout.ts"],"names":[],"mappings":"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"}
|