@strimz/sdk 0.1.0 → 0.2.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/README.md +109 -23
- package/dist/{browser-client-DlZg0Aae.d.cts → browser-client-BzLGc50J.d.cts} +27 -26
- package/dist/{browser-client-DlZg0Aae.d.ts → browser-client-BzLGc50J.d.ts} +27 -26
- package/dist/browser-client.cjs +38 -13
- package/dist/browser-client.cjs.map +1 -1
- package/dist/browser-client.d.cts +1 -1
- package/dist/browser-client.d.ts +1 -1
- package/dist/browser-client.js +39 -14
- package/dist/browser-client.js.map +1 -1
- package/dist/eip712.cjs +70 -0
- package/dist/eip712.cjs.map +1 -1
- package/dist/eip712.d.cts +110 -19
- package/dist/eip712.d.ts +110 -19
- package/dist/eip712.js +67 -1
- package/dist/eip712.js.map +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.cts +4 -3
- package/dist/errors.d.ts +4 -3
- package/dist/errors.js.map +1 -1
- package/dist/index.cjs +39 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -9
- package/dist/index.d.ts +8 -9
- package/dist/index.js +40 -15
- package/dist/index.js.map +1 -1
- package/dist/webhooks.cjs.map +1 -1
- package/dist/webhooks.d.cts +3 -3
- package/dist/webhooks.d.ts +3 -3
- package/dist/webhooks.js.map +1 -1
- package/package.json +4 -4
- package/src/browser-client.ts +11 -11
- package/src/client.ts +4 -4
- package/src/eip712/index.ts +13 -13
- package/src/eip712/pay-intent.ts +54 -0
- package/src/eip712/permit.ts +8 -8
- package/src/eip712/receive-with-authorization.ts +6 -6
- package/src/eip712/subscription-intent.ts +59 -0
- package/src/eip712/types.ts +5 -4
- package/src/errors.ts +4 -3
- package/src/http/fetcher.ts +46 -11
- package/src/http/headers.ts +4 -4
- package/src/idempotency.ts +2 -3
- package/src/resources/checkout.ts +10 -3
- package/src/resources/tokens.ts +10 -11
- package/src/webhooks.ts +3 -3
package/README.md
CHANGED
|
@@ -1,13 +1,41 @@
|
|
|
1
1
|
# @strimz/sdk
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
> The official Strimz Node SDK. Typed resources, automatic idempotency, retries, cursor pagination, EIP-712 typed-data builders, and webhook signature verification.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@strimz/sdk)
|
|
6
|
+
[](https://www.npmjs.com/package/@strimz/sdk)
|
|
7
|
+
[](https://bundlephobia.com/package/@strimz/sdk)
|
|
8
|
+
[](https://www.npmjs.com/package/@strimz/sdk)
|
|
9
|
+
[](./LICENSE)
|
|
10
|
+
|
|
11
|
+
The server-side client for the
|
|
12
|
+
[Strimz API](https://strimz.finance/docs/api/overview). Typed
|
|
13
|
+
resources for every endpoint, end-to-end TypeScript types derived
|
|
14
|
+
from the
|
|
15
|
+
[`@strimz/shared-types`](https://www.npmjs.com/package/@strimz/shared-types)
|
|
16
|
+
Zod schemas, and a webhook verifier you can drop into any handler.
|
|
17
|
+
Built for Node 22, Vercel Edge, and Cloudflare Workers from the same
|
|
18
|
+
code path.
|
|
19
|
+
|
|
20
|
+
## Table of contents
|
|
21
|
+
|
|
22
|
+
- [Install](#install)
|
|
23
|
+
- [Quick start](#quick-start)
|
|
24
|
+
- [Resources](#resources)
|
|
25
|
+
- [Behaviour](#behaviour)
|
|
26
|
+
- [Webhook verification](#webhook-verification)
|
|
27
|
+
- [Browser client](#browser-client)
|
|
28
|
+
- [Error handling](#error-handling)
|
|
29
|
+
- [Runtime support](#runtime-support)
|
|
30
|
+
- [Links](#links)
|
|
31
|
+
- [License](#license)
|
|
32
|
+
|
|
33
|
+
## Install
|
|
8
34
|
|
|
9
35
|
```sh
|
|
10
36
|
pnpm add @strimz/sdk
|
|
37
|
+
# npm install @strimz/sdk
|
|
38
|
+
# yarn add @strimz/sdk
|
|
11
39
|
```
|
|
12
40
|
|
|
13
41
|
## Quick start
|
|
@@ -27,9 +55,18 @@ const session = await strimz.paymentSessions.create({
|
|
|
27
55
|
console.log(session.checkoutUrl)
|
|
28
56
|
```
|
|
29
57
|
|
|
30
|
-
|
|
58
|
+
Redirect the customer to `session.checkoutUrl`. They connect a wallet,
|
|
59
|
+
sign one EIP-3009 authorisation, and the Strimz relayer broadcasts
|
|
60
|
+
the on-chain transaction. Your webhook receives `payment.completed`
|
|
61
|
+
the moment it confirms.
|
|
62
|
+
|
|
63
|
+
## Resources
|
|
31
64
|
|
|
32
|
-
|
|
65
|
+
Every API resource is exposed as a property on the client. Method
|
|
66
|
+
signatures and response types come from
|
|
67
|
+
[`@strimz/shared-types`](https://www.npmjs.com/package/@strimz/shared-types).
|
|
68
|
+
|
|
69
|
+
| Resource | Methods |
|
|
33
70
|
| ------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
34
71
|
| `merchants` | `me`, `update`, `changeTier` |
|
|
35
72
|
| `apiKeys` | `list`, `retrieve`, `create`, `revoke` |
|
|
@@ -47,11 +84,11 @@ console.log(session.checkoutUrl)
|
|
|
47
84
|
|
|
48
85
|
## Behaviour
|
|
49
86
|
|
|
50
|
-
- **Mode
|
|
51
|
-
- **
|
|
52
|
-
- **Retries
|
|
53
|
-
- **Pagination
|
|
54
|
-
- **
|
|
87
|
+
- **Mode auto-detection.** Live versus test mode is derived from the API key prefix (`sk_test_` or `sk_live_`). The server client refuses publishable keys; use the [browser client](#browser-client) for those.
|
|
88
|
+
- **Automatic idempotency.** Every mutating call ships an idempotency key (`strimz_<uuid>`) so a network retry won't double-charge. Override with `{ idempotencyKey }` in the second argument if you want to dedupe across separate calls.
|
|
89
|
+
- **Retries.** Network errors, `5xx` responses on idempotent calls, and `429`s are retried up to 3 times with exponential backoff and jitter. `Retry-After` is respected on 429.
|
|
90
|
+
- **Pagination.** `list(...)` returns one page (`{ data, nextCursor, hasMore }`). `listAuto(...)` (where exposed) returns an async iterator you can `for await` over.
|
|
91
|
+
- **Typed errors.** The SDK throws on failure rather than returning `{ ok: false }`. Catch the base `StrimzError` or narrow with `instanceof` (see [Error handling](#error-handling)).
|
|
55
92
|
|
|
56
93
|
## Webhook verification
|
|
57
94
|
|
|
@@ -80,7 +117,12 @@ export async function POST(req: Request) {
|
|
|
80
117
|
}
|
|
81
118
|
```
|
|
82
119
|
|
|
83
|
-
|
|
120
|
+
`verifyWebhookSignature` does a constant-time comparison and rejects
|
|
121
|
+
any timestamp older than 5 minutes by default. `parseWebhookEvent`
|
|
122
|
+
returns a discriminated union, so a `switch` over `event.type` is
|
|
123
|
+
exhaustively checked by TypeScript.
|
|
124
|
+
|
|
125
|
+
## Browser client
|
|
84
126
|
|
|
85
127
|
```ts
|
|
86
128
|
import { StrimzBrowserClient } from '@strimz/sdk/browser'
|
|
@@ -92,20 +134,64 @@ const strimz = new StrimzBrowserClient({
|
|
|
92
134
|
const session = await strimz.paymentSessions.retrieve(sessionId)
|
|
93
135
|
```
|
|
94
136
|
|
|
95
|
-
The browser client exposes a strict subset of resource
|
|
137
|
+
The browser client exposes a strict subset of read-only resource
|
|
138
|
+
methods. It refuses secret keys at construction time so a
|
|
139
|
+
misconfigured environment fails loud.
|
|
140
|
+
|
|
141
|
+
For a full hosted-checkout UX with components and hooks, use
|
|
142
|
+
[`@strimz/sdk-react`](https://www.npmjs.com/package/@strimz/sdk-react).
|
|
143
|
+
|
|
144
|
+
## Error handling
|
|
145
|
+
|
|
146
|
+
Every error thrown by the SDK is a typed subclass of `StrimzError`:
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
import {
|
|
150
|
+
StrimzError,
|
|
151
|
+
StrimzAuthenticationError,
|
|
152
|
+
StrimzValidationError,
|
|
153
|
+
StrimzRateLimitError,
|
|
154
|
+
StrimzNotFoundError,
|
|
155
|
+
} from '@strimz/sdk/errors'
|
|
156
|
+
|
|
157
|
+
try {
|
|
158
|
+
await strimz.refunds.create({ ... })
|
|
159
|
+
} catch (err) {
|
|
160
|
+
if (err instanceof StrimzValidationError) {
|
|
161
|
+
console.error('validation failed on', err.param, ':', err.message)
|
|
162
|
+
} else if (err instanceof StrimzRateLimitError) {
|
|
163
|
+
await sleep(err.retryAfterMs)
|
|
164
|
+
// … retry
|
|
165
|
+
} else if (err instanceof StrimzError) {
|
|
166
|
+
// unknown Strimz error
|
|
167
|
+
} else {
|
|
168
|
+
// not a Strimz error
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
The full code list is documented at
|
|
174
|
+
[strimz.finance/docs/errors](https://strimz.finance/docs/errors).
|
|
96
175
|
|
|
97
176
|
## Runtime support
|
|
98
177
|
|
|
99
178
|
| Runtime | Supported |
|
|
100
179
|
| ------------------ | ---------------------------------------------------- |
|
|
101
|
-
| Node 22+ |
|
|
102
|
-
| Vercel Edge |
|
|
103
|
-
| Cloudflare Workers |
|
|
104
|
-
| Deno
|
|
105
|
-
|
|
|
180
|
+
| Node 22+ | Yes (primary) |
|
|
181
|
+
| Vercel Edge | Yes (uses Web Crypto + `fetch`) |
|
|
182
|
+
| Cloudflare Workers | Yes |
|
|
183
|
+
| Deno | Should work, not regularly tested |
|
|
184
|
+
| Bun | Should work, not regularly tested |
|
|
185
|
+
| Browsers | Use `StrimzBrowserClient` from `@strimz/sdk/browser` |
|
|
186
|
+
|
|
187
|
+
## Links
|
|
188
|
+
|
|
189
|
+
- [Documentation](https://strimz.finance/docs)
|
|
190
|
+
- [API reference](https://strimz.finance/docs/api/overview)
|
|
191
|
+
- [Repository](https://github.com/StrimzLab/Strimz/tree/main/packages/sdk)
|
|
192
|
+
- [Changelog](./CHANGELOG.md)
|
|
193
|
+
- [Issues](https://github.com/StrimzLab/Strimz/issues)
|
|
106
194
|
|
|
107
|
-
##
|
|
195
|
+
## License
|
|
108
196
|
|
|
109
|
-
|
|
110
|
-
- **No persistent state.** The client is safe to instantiate per-request or reuse across requests.
|
|
111
|
-
- **No secrets logged.** Diagnostics never include the API key. Webhook signatures are verified with constant-time compare via `@strimz/shared-crypto`.
|
|
197
|
+
[MIT](./LICENSE) © Strimz
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ApiKeyMode } from '@strimz/shared-config';
|
|
2
|
-
import { PaymentSession, SubscriptionPlan, TokenMetadata, TokenPermitNonce } from '@strimz/shared-types';
|
|
2
|
+
import { PaymentSession, SubscriptionPlan, MerchantPublicBrand, TokenMetadata, TokenPermitNonce } from '@strimz/shared-types';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -36,7 +36,7 @@ interface FetcherOptions {
|
|
|
36
36
|
ms: number;
|
|
37
37
|
attempt: number;
|
|
38
38
|
}) => void;
|
|
39
|
-
/** Override the global fetch (for tests
|
|
39
|
+
/** Override the global fetch (for tests or Edge polyfills). */
|
|
40
40
|
fetch?: typeof globalThis.fetch;
|
|
41
41
|
}
|
|
42
42
|
interface RequestSpec {
|
|
@@ -121,7 +121,7 @@ type ScalarParam = string | number | boolean | null | undefined;
|
|
|
121
121
|
* Public hosted-checkout reads.
|
|
122
122
|
*
|
|
123
123
|
* Backed by the `/v1/checkout/*` namespace on the Strimz API, which
|
|
124
|
-
* is intentionally unauthenticated
|
|
124
|
+
* is intentionally unauthenticated. Session and plan records are
|
|
125
125
|
* what a merchant offers to payers, so a payer landing on a checkout
|
|
126
126
|
* URL must be able to load the terms without holding any API key.
|
|
127
127
|
*
|
|
@@ -131,16 +131,18 @@ type ScalarParam = string | number | boolean | null | undefined;
|
|
|
131
131
|
* code with a secret key.
|
|
132
132
|
*/
|
|
133
133
|
declare class CheckoutResource extends BaseResource {
|
|
134
|
-
/** Load a payment session by id (public
|
|
134
|
+
/** Load a payment session by id (public, no merchant scoping). */
|
|
135
135
|
session(id: string): Promise<PaymentSession>;
|
|
136
|
-
/** Load a subscription plan by id (public
|
|
136
|
+
/** Load a subscription plan by id (public, no merchant scoping). */
|
|
137
137
|
plan(id: string): Promise<SubscriptionPlan>;
|
|
138
|
+
/** Load the merchant's public brand card by id. */
|
|
139
|
+
merchant(id: string): Promise<MerchantPublicBrand>;
|
|
138
140
|
}
|
|
139
141
|
|
|
140
142
|
/**
|
|
141
|
-
* Token metadata
|
|
143
|
+
* Token metadata and capability lookup.
|
|
142
144
|
*
|
|
143
|
-
* The two endpoints are public on the API (no key required)
|
|
145
|
+
* The two endpoints are public on the API (no key required). Token
|
|
144
146
|
* metadata is purely chain-derived, has no PII, and is what the
|
|
145
147
|
* browser SDK consults to pick which meta-tx path to take for a
|
|
146
148
|
* given token. Bundled here so consumers don't have to write the HTTP
|
|
@@ -149,27 +151,26 @@ declare class CheckoutResource extends BaseResource {
|
|
|
149
151
|
declare class TokensResource extends BaseResource {
|
|
150
152
|
/**
|
|
151
153
|
* Fetch full metadata for a token. Throws via the underlying fetcher
|
|
152
|
-
* (404
|
|
154
|
+
* (404 maps to `StrimzNotFoundError`) if the token isn't whitelisted.
|
|
153
155
|
*/
|
|
154
156
|
retrieve(address: string): Promise<TokenMetadata>;
|
|
155
157
|
/**
|
|
156
158
|
* Read an owner's current EIP-2612 permit nonce. Call this
|
|
157
|
-
* immediately before signing a Permit
|
|
159
|
+
* immediately before signing a Permit. The token contract rejects
|
|
158
160
|
* stale nonces, and the value advances on every successful permit.
|
|
159
161
|
*/
|
|
160
162
|
permitNonce(address: string, owner: string): Promise<TokenPermitNonce>;
|
|
161
163
|
}
|
|
162
164
|
/**
|
|
163
|
-
* Path selector
|
|
165
|
+
* Path selector. Decides which meta-tx flow to take given a token's
|
|
164
166
|
* capabilities and the action a merchant wants to perform.
|
|
165
167
|
*
|
|
166
168
|
* The choice is unambiguous in 99% of cases:
|
|
167
|
-
* - One-shot pay
|
|
168
|
-
*
|
|
169
|
-
* - Subscription enrol
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
* use the classic approve+create flow.
|
|
169
|
+
* - One-shot pay. Prefers EIP-3009. Falls back to approve+pay if the
|
|
170
|
+
* token has neither.
|
|
171
|
+
* - Subscription enrol. Prefers EIP-2612 (the only meta-tx path that
|
|
172
|
+
* grants the recurring allowance subscribers need). If unavailable,
|
|
173
|
+
* the merchant must use the classic approve+create flow.
|
|
173
174
|
*
|
|
174
175
|
* Returned `path` values are strings (not enums) so future schemes
|
|
175
176
|
* can extend the union without breaking existing call sites.
|
|
@@ -179,16 +180,16 @@ declare function selectPaymentPath(meta: TokenMetadata): RelayPath;
|
|
|
179
180
|
declare function selectSubscriptionPath(meta: TokenMetadata): RelayPath;
|
|
180
181
|
|
|
181
182
|
/**
|
|
182
|
-
* StrimzBrowserClient
|
|
183
|
+
* StrimzBrowserClient. The publishable-key, browser-safe SDK entry point.
|
|
183
184
|
*
|
|
184
185
|
* Used by `@strimz/sdk-react` and any other browser code that should
|
|
185
|
-
* never see a secret key. Surfaces a strict subset of resource methods
|
|
186
|
+
* never see a secret key. Surfaces a strict subset of resource methods,
|
|
186
187
|
* only those safe to call from a browser with a `pk_test_...` or
|
|
187
188
|
* `pk_live_...` key.
|
|
188
189
|
*/
|
|
189
190
|
|
|
190
191
|
interface StrimzBrowserClientOptions {
|
|
191
|
-
/** Publishable key
|
|
192
|
+
/** Publishable key. Either `pk_test_...` or `pk_live_...`. */
|
|
192
193
|
publishableKey: string;
|
|
193
194
|
/** Override the API base URL. */
|
|
194
195
|
baseUrl?: string;
|
|
@@ -201,21 +202,21 @@ interface StrimzBrowserClientOptions {
|
|
|
201
202
|
* Designed for hosted-checkout state polling and the React component
|
|
202
203
|
* family. Read-only on most resources. Refuses a secret key.
|
|
203
204
|
*
|
|
204
|
-
* The `tokens` resource
|
|
205
|
-
* selectors
|
|
206
|
-
* token, the SDK asks the
|
|
207
|
-
* supports, and the selectors map that to a `RelayPath` the
|
|
208
|
-
* UI uses to decide which typed-data to build.
|
|
205
|
+
* The `tokens` resource and the `selectPaymentPath` /
|
|
206
|
+
* `selectSubscriptionPath` selectors form the capability-detection
|
|
207
|
+
* layer. Given a token, the SDK asks the API which meta-tx flows the
|
|
208
|
+
* token supports, and the selectors map that to a `RelayPath` the
|
|
209
|
+
* checkout UI uses to decide which typed-data to build.
|
|
209
210
|
*/
|
|
210
211
|
declare class StrimzBrowserClient {
|
|
211
212
|
readonly mode: ApiKeyMode;
|
|
212
213
|
/**
|
|
213
|
-
* Public hosted-checkout reads
|
|
214
|
+
* Public hosted-checkout reads. Payment sessions and subscription
|
|
214
215
|
* plans by id. Backed by the `/v1/checkout/*` namespace on the API,
|
|
215
216
|
* which is intentionally unauthenticated.
|
|
216
217
|
*/
|
|
217
218
|
readonly checkout: CheckoutResource;
|
|
218
|
-
/** Token metadata
|
|
219
|
+
/** Token metadata and EIP-2612 permit nonce lookup. */
|
|
219
220
|
readonly tokens: TokensResource;
|
|
220
221
|
constructor(options: StrimzBrowserClientOptions);
|
|
221
222
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ApiKeyMode } from '@strimz/shared-config';
|
|
2
|
-
import { PaymentSession, SubscriptionPlan, TokenMetadata, TokenPermitNonce } from '@strimz/shared-types';
|
|
2
|
+
import { PaymentSession, SubscriptionPlan, MerchantPublicBrand, TokenMetadata, TokenPermitNonce } from '@strimz/shared-types';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -36,7 +36,7 @@ interface FetcherOptions {
|
|
|
36
36
|
ms: number;
|
|
37
37
|
attempt: number;
|
|
38
38
|
}) => void;
|
|
39
|
-
/** Override the global fetch (for tests
|
|
39
|
+
/** Override the global fetch (for tests or Edge polyfills). */
|
|
40
40
|
fetch?: typeof globalThis.fetch;
|
|
41
41
|
}
|
|
42
42
|
interface RequestSpec {
|
|
@@ -121,7 +121,7 @@ type ScalarParam = string | number | boolean | null | undefined;
|
|
|
121
121
|
* Public hosted-checkout reads.
|
|
122
122
|
*
|
|
123
123
|
* Backed by the `/v1/checkout/*` namespace on the Strimz API, which
|
|
124
|
-
* is intentionally unauthenticated
|
|
124
|
+
* is intentionally unauthenticated. Session and plan records are
|
|
125
125
|
* what a merchant offers to payers, so a payer landing on a checkout
|
|
126
126
|
* URL must be able to load the terms without holding any API key.
|
|
127
127
|
*
|
|
@@ -131,16 +131,18 @@ type ScalarParam = string | number | boolean | null | undefined;
|
|
|
131
131
|
* code with a secret key.
|
|
132
132
|
*/
|
|
133
133
|
declare class CheckoutResource extends BaseResource {
|
|
134
|
-
/** Load a payment session by id (public
|
|
134
|
+
/** Load a payment session by id (public, no merchant scoping). */
|
|
135
135
|
session(id: string): Promise<PaymentSession>;
|
|
136
|
-
/** Load a subscription plan by id (public
|
|
136
|
+
/** Load a subscription plan by id (public, no merchant scoping). */
|
|
137
137
|
plan(id: string): Promise<SubscriptionPlan>;
|
|
138
|
+
/** Load the merchant's public brand card by id. */
|
|
139
|
+
merchant(id: string): Promise<MerchantPublicBrand>;
|
|
138
140
|
}
|
|
139
141
|
|
|
140
142
|
/**
|
|
141
|
-
* Token metadata
|
|
143
|
+
* Token metadata and capability lookup.
|
|
142
144
|
*
|
|
143
|
-
* The two endpoints are public on the API (no key required)
|
|
145
|
+
* The two endpoints are public on the API (no key required). Token
|
|
144
146
|
* metadata is purely chain-derived, has no PII, and is what the
|
|
145
147
|
* browser SDK consults to pick which meta-tx path to take for a
|
|
146
148
|
* given token. Bundled here so consumers don't have to write the HTTP
|
|
@@ -149,27 +151,26 @@ declare class CheckoutResource extends BaseResource {
|
|
|
149
151
|
declare class TokensResource extends BaseResource {
|
|
150
152
|
/**
|
|
151
153
|
* Fetch full metadata for a token. Throws via the underlying fetcher
|
|
152
|
-
* (404
|
|
154
|
+
* (404 maps to `StrimzNotFoundError`) if the token isn't whitelisted.
|
|
153
155
|
*/
|
|
154
156
|
retrieve(address: string): Promise<TokenMetadata>;
|
|
155
157
|
/**
|
|
156
158
|
* Read an owner's current EIP-2612 permit nonce. Call this
|
|
157
|
-
* immediately before signing a Permit
|
|
159
|
+
* immediately before signing a Permit. The token contract rejects
|
|
158
160
|
* stale nonces, and the value advances on every successful permit.
|
|
159
161
|
*/
|
|
160
162
|
permitNonce(address: string, owner: string): Promise<TokenPermitNonce>;
|
|
161
163
|
}
|
|
162
164
|
/**
|
|
163
|
-
* Path selector
|
|
165
|
+
* Path selector. Decides which meta-tx flow to take given a token's
|
|
164
166
|
* capabilities and the action a merchant wants to perform.
|
|
165
167
|
*
|
|
166
168
|
* The choice is unambiguous in 99% of cases:
|
|
167
|
-
* - One-shot pay
|
|
168
|
-
*
|
|
169
|
-
* - Subscription enrol
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
* use the classic approve+create flow.
|
|
169
|
+
* - One-shot pay. Prefers EIP-3009. Falls back to approve+pay if the
|
|
170
|
+
* token has neither.
|
|
171
|
+
* - Subscription enrol. Prefers EIP-2612 (the only meta-tx path that
|
|
172
|
+
* grants the recurring allowance subscribers need). If unavailable,
|
|
173
|
+
* the merchant must use the classic approve+create flow.
|
|
173
174
|
*
|
|
174
175
|
* Returned `path` values are strings (not enums) so future schemes
|
|
175
176
|
* can extend the union without breaking existing call sites.
|
|
@@ -179,16 +180,16 @@ declare function selectPaymentPath(meta: TokenMetadata): RelayPath;
|
|
|
179
180
|
declare function selectSubscriptionPath(meta: TokenMetadata): RelayPath;
|
|
180
181
|
|
|
181
182
|
/**
|
|
182
|
-
* StrimzBrowserClient
|
|
183
|
+
* StrimzBrowserClient. The publishable-key, browser-safe SDK entry point.
|
|
183
184
|
*
|
|
184
185
|
* Used by `@strimz/sdk-react` and any other browser code that should
|
|
185
|
-
* never see a secret key. Surfaces a strict subset of resource methods
|
|
186
|
+
* never see a secret key. Surfaces a strict subset of resource methods,
|
|
186
187
|
* only those safe to call from a browser with a `pk_test_...` or
|
|
187
188
|
* `pk_live_...` key.
|
|
188
189
|
*/
|
|
189
190
|
|
|
190
191
|
interface StrimzBrowserClientOptions {
|
|
191
|
-
/** Publishable key
|
|
192
|
+
/** Publishable key. Either `pk_test_...` or `pk_live_...`. */
|
|
192
193
|
publishableKey: string;
|
|
193
194
|
/** Override the API base URL. */
|
|
194
195
|
baseUrl?: string;
|
|
@@ -201,21 +202,21 @@ interface StrimzBrowserClientOptions {
|
|
|
201
202
|
* Designed for hosted-checkout state polling and the React component
|
|
202
203
|
* family. Read-only on most resources. Refuses a secret key.
|
|
203
204
|
*
|
|
204
|
-
* The `tokens` resource
|
|
205
|
-
* selectors
|
|
206
|
-
* token, the SDK asks the
|
|
207
|
-
* supports, and the selectors map that to a `RelayPath` the
|
|
208
|
-
* UI uses to decide which typed-data to build.
|
|
205
|
+
* The `tokens` resource and the `selectPaymentPath` /
|
|
206
|
+
* `selectSubscriptionPath` selectors form the capability-detection
|
|
207
|
+
* layer. Given a token, the SDK asks the API which meta-tx flows the
|
|
208
|
+
* token supports, and the selectors map that to a `RelayPath` the
|
|
209
|
+
* checkout UI uses to decide which typed-data to build.
|
|
209
210
|
*/
|
|
210
211
|
declare class StrimzBrowserClient {
|
|
211
212
|
readonly mode: ApiKeyMode;
|
|
212
213
|
/**
|
|
213
|
-
* Public hosted-checkout reads
|
|
214
|
+
* Public hosted-checkout reads. Payment sessions and subscription
|
|
214
215
|
* plans by id. Backed by the `/v1/checkout/*` namespace on the API,
|
|
215
216
|
* which is intentionally unauthenticated.
|
|
216
217
|
*/
|
|
217
218
|
readonly checkout: CheckoutResource;
|
|
218
|
-
/** Token metadata
|
|
219
|
+
/** Token metadata and EIP-2612 permit nonce lookup. */
|
|
219
220
|
readonly tokens: TokensResource;
|
|
220
221
|
constructor(options: StrimzBrowserClientOptions);
|
|
221
222
|
}
|
package/dist/browser-client.cjs
CHANGED
|
@@ -210,10 +210,18 @@ var Fetcher = class {
|
|
|
210
210
|
}
|
|
211
211
|
backoffFor(attempt) {
|
|
212
212
|
const expo = this.initialBackoffMs * Math.pow(2, attempt);
|
|
213
|
-
|
|
214
|
-
return Math.min(expo + jitter, 8e3);
|
|
213
|
+
return Math.min(expo + cryptoJitter(this.initialBackoffMs), 8e3);
|
|
215
214
|
}
|
|
216
215
|
};
|
|
216
|
+
function cryptoJitter(maxMs) {
|
|
217
|
+
const g = globalThis;
|
|
218
|
+
if (g.crypto?.getRandomValues) {
|
|
219
|
+
const buf = new Uint32Array(1);
|
|
220
|
+
g.crypto.getRandomValues(buf);
|
|
221
|
+
return (buf[0] ?? 0) / 4294967295 * maxMs;
|
|
222
|
+
}
|
|
223
|
+
return Math.random() * maxMs;
|
|
224
|
+
}
|
|
217
225
|
function isIdempotent(spec) {
|
|
218
226
|
if (spec.method === "GET" || spec.method === "DELETE") return true;
|
|
219
227
|
return spec.idempotencyKey != null;
|
|
@@ -221,16 +229,29 @@ function isIdempotent(spec) {
|
|
|
221
229
|
async function parseErrorBody(res) {
|
|
222
230
|
try {
|
|
223
231
|
const json = await res.json();
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
if (typeof json === "object" && json !== null && "code" in json && "message" in json) {
|
|
228
|
-
return json;
|
|
232
|
+
const raw = typeof json === "object" && json !== null && "error" in json ? json.error : json;
|
|
233
|
+
if (typeof raw === "object" && raw !== null && "code" in raw && "message" in raw) {
|
|
234
|
+
return sanitiseErrorBody(raw);
|
|
229
235
|
}
|
|
230
236
|
} catch {
|
|
231
237
|
}
|
|
232
238
|
return { code: "api_error", message: `HTTP ${res.status}` };
|
|
233
239
|
}
|
|
240
|
+
var ALLOWED_DETAIL_KEYS = /* @__PURE__ */ new Set(["issues", "field", "reason", "chargeAttemptId"]);
|
|
241
|
+
function sanitiseErrorBody(raw) {
|
|
242
|
+
const details = typeof raw.details === "object" && raw.details !== null ? Object.fromEntries(
|
|
243
|
+
Object.entries(raw.details).filter(
|
|
244
|
+
([k]) => ALLOWED_DETAIL_KEYS.has(k)
|
|
245
|
+
)
|
|
246
|
+
) : void 0;
|
|
247
|
+
return {
|
|
248
|
+
code: typeof raw.code === "string" ? raw.code : "api_error",
|
|
249
|
+
message: typeof raw.message === "string" ? raw.message : "Request failed",
|
|
250
|
+
param: typeof raw.param === "string" ? raw.param : void 0,
|
|
251
|
+
requestId: typeof raw.requestId === "string" ? raw.requestId : void 0,
|
|
252
|
+
details: details && Object.keys(details).length > 0 ? details : void 0
|
|
253
|
+
};
|
|
254
|
+
}
|
|
234
255
|
function parseRetryAfter(res) {
|
|
235
256
|
const v = res.headers.get("Retry-After");
|
|
236
257
|
if (!v) return void 0;
|
|
@@ -351,26 +372,30 @@ function parsePage(raw, itemSchema) {
|
|
|
351
372
|
|
|
352
373
|
// src/resources/checkout.ts
|
|
353
374
|
var CheckoutResource = class extends BaseResource {
|
|
354
|
-
/** Load a payment session by id (public
|
|
375
|
+
/** Load a payment session by id (public, no merchant scoping). */
|
|
355
376
|
session(id) {
|
|
356
377
|
return this.get(`/v1/checkout/sessions/${encodeURIComponent(id)}`, sharedTypes.paymentSessionSchema);
|
|
357
378
|
}
|
|
358
|
-
/** Load a subscription plan by id (public
|
|
379
|
+
/** Load a subscription plan by id (public, no merchant scoping). */
|
|
359
380
|
plan(id) {
|
|
360
381
|
return this.get(`/v1/checkout/plans/${encodeURIComponent(id)}`, sharedTypes.subscriptionPlanSchema);
|
|
361
382
|
}
|
|
383
|
+
/** Load the merchant's public brand card by id. */
|
|
384
|
+
merchant(id) {
|
|
385
|
+
return this.get(`/v1/checkout/merchants/${encodeURIComponent(id)}`, sharedTypes.merchantPublicBrandSchema);
|
|
386
|
+
}
|
|
362
387
|
};
|
|
363
388
|
var TokensResource = class extends BaseResource {
|
|
364
389
|
/**
|
|
365
390
|
* Fetch full metadata for a token. Throws via the underlying fetcher
|
|
366
|
-
* (404
|
|
391
|
+
* (404 maps to `StrimzNotFoundError`) if the token isn't whitelisted.
|
|
367
392
|
*/
|
|
368
393
|
retrieve(address) {
|
|
369
394
|
return this.get(`/v1/tokens/${encodeURIComponent(address.toLowerCase())}`, sharedTypes.tokenMetadataSchema);
|
|
370
395
|
}
|
|
371
396
|
/**
|
|
372
397
|
* Read an owner's current EIP-2612 permit nonce. Call this
|
|
373
|
-
* immediately before signing a Permit
|
|
398
|
+
* immediately before signing a Permit. The token contract rejects
|
|
374
399
|
* stale nonces, and the value advances on every successful permit.
|
|
375
400
|
*/
|
|
376
401
|
permitNonce(address, owner) {
|
|
@@ -393,12 +418,12 @@ var DEFAULT_BASE_URL = "https://api.strimz.io";
|
|
|
393
418
|
var StrimzBrowserClient = class {
|
|
394
419
|
mode;
|
|
395
420
|
/**
|
|
396
|
-
* Public hosted-checkout reads
|
|
421
|
+
* Public hosted-checkout reads. Payment sessions and subscription
|
|
397
422
|
* plans by id. Backed by the `/v1/checkout/*` namespace on the API,
|
|
398
423
|
* which is intentionally unauthenticated.
|
|
399
424
|
*/
|
|
400
425
|
checkout;
|
|
401
|
-
/** Token metadata
|
|
426
|
+
/** Token metadata and EIP-2612 permit nonce lookup. */
|
|
402
427
|
tokens;
|
|
403
428
|
constructor(options) {
|
|
404
429
|
const key = options.publishableKey;
|