@strimz/sdk 0.1.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/LICENSE +21 -0
- package/README.md +111 -0
- package/dist/browser-client-DlZg0Aae.d.cts +223 -0
- package/dist/browser-client-DlZg0Aae.d.ts +223 -0
- package/dist/browser-client.cjs +443 -0
- package/dist/browser-client.cjs.map +1 -0
- package/dist/browser-client.d.cts +4 -0
- package/dist/browser-client.d.ts +4 -0
- package/dist/browser-client.js +439 -0
- package/dist/browser-client.js.map +1 -0
- package/dist/eip712.cjs +70 -0
- package/dist/eip712.cjs.map +1 -0
- package/dist/eip712.d.cts +181 -0
- package/dist/eip712.d.ts +181 -0
- package/dist/eip712.js +65 -0
- package/dist/eip712.js.map +1 -0
- package/dist/errors.cjs +102 -0
- package/dist/errors.cjs.map +1 -0
- package/dist/errors.d.cts +56 -0
- package/dist/errors.d.ts +56 -0
- package/dist/errors.js +90 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.cjs +831 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +193 -0
- package/dist/index.d.ts +193 -0
- package/dist/index.js +806 -0
- package/dist/index.js.map +1 -0
- package/dist/webhooks.cjs +54 -0
- package/dist/webhooks.cjs.map +1 -0
- package/dist/webhooks.d.cts +19 -0
- package/dist/webhooks.d.ts +19 -0
- package/dist/webhooks.js +13 -0
- package/dist/webhooks.js.map +1 -0
- package/package.json +94 -0
- package/src/browser-client.ts +97 -0
- package/src/client.ts +122 -0
- package/src/eip712/index.ts +25 -0
- package/src/eip712/permit.ts +74 -0
- package/src/eip712/receive-with-authorization.ts +88 -0
- package/src/eip712/types.ts +46 -0
- package/src/errors.ts +138 -0
- package/src/http/fetcher.ts +226 -0
- package/src/http/headers.ts +32 -0
- package/src/idempotency.ts +20 -0
- package/src/index.ts +77 -0
- package/src/pagination.ts +48 -0
- package/src/resources/agents.ts +64 -0
- package/src/resources/api-keys.ts +29 -0
- package/src/resources/base-resource.ts +116 -0
- package/src/resources/checkout.ts +32 -0
- package/src/resources/customers.ts +25 -0
- package/src/resources/invoices.ts +31 -0
- package/src/resources/merchants.ts +26 -0
- package/src/resources/payment-sessions.ts +41 -0
- package/src/resources/refunds.ts +36 -0
- package/src/resources/storefronts.ts +62 -0
- package/src/resources/subscription-plans.ts +32 -0
- package/src/resources/subscriptions.ts +37 -0
- package/src/resources/tokens.ts +70 -0
- package/src/resources/transactions.ts +20 -0
- package/src/resources/webhook-deliveries.ts +29 -0
- package/src/resources/webhook-endpoints.ts +56 -0
- package/src/webhooks.ts +64 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Strimz
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# @strimz/sdk
|
|
2
|
+
|
|
3
|
+
Server-side SDK for the Strimz API. Stripe-shaped: typed resources,
|
|
4
|
+
automatic idempotency, retries, cursor pagination, and webhook
|
|
5
|
+
signature verification.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
pnpm add @strimz/sdk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick start
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { StrimzClient } from '@strimz/sdk'
|
|
17
|
+
|
|
18
|
+
const strimz = new StrimzClient({ apiKey: process.env.STRIMZ_SECRET_KEY! })
|
|
19
|
+
|
|
20
|
+
const session = await strimz.paymentSessions.create({
|
|
21
|
+
amount: '1000000', // 1 USDC in smallest units
|
|
22
|
+
currency: 'USDC',
|
|
23
|
+
description: 'Premium plan, April',
|
|
24
|
+
successUrl: 'https://app.example.com/success',
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
console.log(session.checkoutUrl)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Modules
|
|
31
|
+
|
|
32
|
+
| Resource | Path |
|
|
33
|
+
| ------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
34
|
+
| `merchants` | `me`, `update`, `changeTier` |
|
|
35
|
+
| `apiKeys` | `list`, `retrieve`, `create`, `revoke` |
|
|
36
|
+
| `customers` | `retrieve`, `list`, `upsert` |
|
|
37
|
+
| `paymentSessions` | `create`, `retrieve`, `list`, `cancel`, `expire` |
|
|
38
|
+
| `transactions` | `retrieve`, `list` |
|
|
39
|
+
| `subscriptionPlans` | `create`, `retrieve`, `list`, `archive` |
|
|
40
|
+
| `subscriptions` | `create`, `retrieve`, `list`, `cancel` |
|
|
41
|
+
| `refunds` | `create`, `retrieve`, `list`, `submitSignature` |
|
|
42
|
+
| `webhookEndpoints` | `create`, `retrieve`, `list`, `enable`, `disable`, `rotateSecret` |
|
|
43
|
+
| `webhookDeliveries` | `retrieve`, `list`, `replay` |
|
|
44
|
+
| `invoices` | `create`, `retrieve`, `list`, `send`, `void` |
|
|
45
|
+
| `storefronts` | `retrieve`, `upsert`, `publish`, `archive`, `listProducts`, `createProduct`, `retrieveProduct`, `archiveProduct` |
|
|
46
|
+
| `agents` | `retrieveConfig`, `updateConfig`, `listActivity`, `listJobs`, `retrieveJob`, `createJob`, `approveJob` |
|
|
47
|
+
|
|
48
|
+
## Behaviour
|
|
49
|
+
|
|
50
|
+
- **Mode is auto-detected** from the API key prefix (`sk_test_` ↔ `sk_live_`). The client refuses publishable keys; use `StrimzBrowserClient` from `@strimz/sdk/browser` for those.
|
|
51
|
+
- **Idempotency** is automatic. Every mutating call generates an idempotency key (`strimz_<uuid>`) so a network retry doesn't double-charge a merchant. Override with `{ idempotencyKey }` in the second arg.
|
|
52
|
+
- **Retries**: network failures and 5xx (idempotent calls only) and 429 are retried up to 3 times with exponential backoff and jitter. `Retry-After` is respected on 429.
|
|
53
|
+
- **Pagination**: `list(...)` returns a single page; `listAuto(...)` (where exposed) returns an `AutoPagingIterator` that you can `for await` over.
|
|
54
|
+
- **Errors are typed**. The SDK throws on failure rather than resolving to `{ ok: false }`. Catch `StrimzAuthenticationError`, `StrimzNotFoundError`, `StrimzValidationError`, `StrimzRateLimitError`, etc., or the base `StrimzError`.
|
|
55
|
+
|
|
56
|
+
## Webhook verification
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
import { verifyWebhookSignature, parseWebhookEvent } from '@strimz/sdk/webhooks'
|
|
60
|
+
|
|
61
|
+
export async function POST(req: Request) {
|
|
62
|
+
const body = await req.text()
|
|
63
|
+
const result = await verifyWebhookSignature(
|
|
64
|
+
body,
|
|
65
|
+
req.headers.get('strimz-signature') ?? '',
|
|
66
|
+
process.env.STRIMZ_WEBHOOK_SECRET!,
|
|
67
|
+
)
|
|
68
|
+
if (!result.valid) return new Response('invalid signature', { status: 400 })
|
|
69
|
+
|
|
70
|
+
const event = parseWebhookEvent(body)
|
|
71
|
+
switch (event.type) {
|
|
72
|
+
case 'payment.completed':
|
|
73
|
+
// event.data is typed as { session, transaction }
|
|
74
|
+
break
|
|
75
|
+
case 'subscription.charge_failed':
|
|
76
|
+
// event.data is typed as { subscription, charge }
|
|
77
|
+
break
|
|
78
|
+
}
|
|
79
|
+
return new Response('ok')
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Browser
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
import { StrimzBrowserClient } from '@strimz/sdk/browser'
|
|
87
|
+
|
|
88
|
+
const strimz = new StrimzBrowserClient({
|
|
89
|
+
publishableKey: process.env.NEXT_PUBLIC_STRIMZ_PUBLISHABLE_KEY!,
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
const session = await strimz.paymentSessions.retrieve(sessionId)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
The browser client exposes a strict subset of resource methods. It will refuse a secret key.
|
|
96
|
+
|
|
97
|
+
## Runtime support
|
|
98
|
+
|
|
99
|
+
| Runtime | Supported |
|
|
100
|
+
| ------------------ | ---------------------------------------------------- |
|
|
101
|
+
| Node 22+ | ✓ (primary) |
|
|
102
|
+
| Vercel Edge | ✓ (uses Web Crypto + fetch) |
|
|
103
|
+
| Cloudflare Workers | ✓ |
|
|
104
|
+
| Deno / Bun | should work; not regularly tested |
|
|
105
|
+
| Browsers | use `StrimzBrowserClient` from `@strimz/sdk/browser` |
|
|
106
|
+
|
|
107
|
+
## Boundaries
|
|
108
|
+
|
|
109
|
+
- **No business logic.** The SDK validates inputs against `@strimz/shared-types` and forwards to the API.
|
|
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`.
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { ApiKeyMode } from '@strimz/shared-config';
|
|
2
|
+
import { PaymentSession, SubscriptionPlan, TokenMetadata, TokenPermitNonce } from '@strimz/shared-types';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Fetch wrapper with retry, idempotency, and Strimz error parsing.
|
|
7
|
+
*
|
|
8
|
+
* Retries on:
|
|
9
|
+
* - Network failures (TypeError on fetch).
|
|
10
|
+
* - 5xx responses for idempotent methods (GET, POST with idempotency key).
|
|
11
|
+
* - 429 with Retry-After.
|
|
12
|
+
*
|
|
13
|
+
* Does NOT retry on:
|
|
14
|
+
* - 4xx other than 429 (caller's bug).
|
|
15
|
+
* - 5xx for non-idempotent calls without an idempotency key.
|
|
16
|
+
*/
|
|
17
|
+
interface FetcherOptions {
|
|
18
|
+
baseUrl: string;
|
|
19
|
+
baseHeaders: Record<string, string>;
|
|
20
|
+
/** Default timeout per attempt, milliseconds. */
|
|
21
|
+
timeoutMs?: number;
|
|
22
|
+
/** How many retries before giving up. */
|
|
23
|
+
maxRetries?: number;
|
|
24
|
+
/** Initial backoff in ms; doubled with jitter on each retry. */
|
|
25
|
+
initialBackoffMs?: number;
|
|
26
|
+
/** Optional logger. */
|
|
27
|
+
onRequest?: (info: {
|
|
28
|
+
method: string;
|
|
29
|
+
url: string;
|
|
30
|
+
attempt: number;
|
|
31
|
+
}) => void;
|
|
32
|
+
onResponse?: (info: {
|
|
33
|
+
method: string;
|
|
34
|
+
url: string;
|
|
35
|
+
status: number;
|
|
36
|
+
ms: number;
|
|
37
|
+
attempt: number;
|
|
38
|
+
}) => void;
|
|
39
|
+
/** Override the global fetch (for tests / Edge polyfills). */
|
|
40
|
+
fetch?: typeof globalThis.fetch;
|
|
41
|
+
}
|
|
42
|
+
interface RequestSpec {
|
|
43
|
+
method: 'GET' | 'POST' | 'PATCH' | 'DELETE';
|
|
44
|
+
path: string;
|
|
45
|
+
query?: Record<string, string | number | boolean | null | undefined>;
|
|
46
|
+
body?: unknown;
|
|
47
|
+
idempotencyKey?: string;
|
|
48
|
+
/** Per-call timeout override. */
|
|
49
|
+
timeoutMs?: number;
|
|
50
|
+
}
|
|
51
|
+
declare class Fetcher {
|
|
52
|
+
private readonly baseUrl;
|
|
53
|
+
private readonly baseHeaders;
|
|
54
|
+
private readonly timeoutMs;
|
|
55
|
+
private readonly maxRetries;
|
|
56
|
+
private readonly initialBackoffMs;
|
|
57
|
+
private readonly onRequest;
|
|
58
|
+
private readonly onResponse;
|
|
59
|
+
private readonly fetchImpl;
|
|
60
|
+
constructor(opts: FetcherOptions);
|
|
61
|
+
request<T>(spec: RequestSpec): Promise<T>;
|
|
62
|
+
private fetchWithTimeout;
|
|
63
|
+
private buildUrl;
|
|
64
|
+
private backoffFor;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Cursor-based pagination, identical shape to the API's list responses.
|
|
69
|
+
*/
|
|
70
|
+
interface Page<T> {
|
|
71
|
+
data: T[];
|
|
72
|
+
nextCursor: string | null;
|
|
73
|
+
hasMore: boolean;
|
|
74
|
+
}
|
|
75
|
+
interface PaginationParams {
|
|
76
|
+
limit?: number;
|
|
77
|
+
cursor?: string | null;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Async-iterable wrapper that auto-paginates through every page of a list.
|
|
81
|
+
*
|
|
82
|
+
* for await (const tx of strimz.transactions.listAll()) { ... }
|
|
83
|
+
*/
|
|
84
|
+
declare class AutoPagingIterator<T> implements AsyncIterable<T> {
|
|
85
|
+
private readonly fetchPage;
|
|
86
|
+
constructor(fetchPage: (cursor: string | null) => Promise<Page<T>>);
|
|
87
|
+
[Symbol.asyncIterator](): AsyncIterator<T>;
|
|
88
|
+
forEach(handler: (item: T) => void | Promise<void>): Promise<void>;
|
|
89
|
+
toArray(): Promise<T[]>;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Base class every resource extends. Centralises endpoint plumbing so
|
|
94
|
+
* resource classes are tiny declarative wrappers.
|
|
95
|
+
*/
|
|
96
|
+
|
|
97
|
+
type AnySchema = z.ZodTypeAny;
|
|
98
|
+
type Inferred<S extends AnySchema> = z.output<S>;
|
|
99
|
+
interface ResourceContext {
|
|
100
|
+
fetcher: Fetcher;
|
|
101
|
+
}
|
|
102
|
+
interface RequestOptions {
|
|
103
|
+
/** Override or supply the idempotency key for a mutating call. */
|
|
104
|
+
idempotencyKey?: string;
|
|
105
|
+
/** Per-request timeout in ms. */
|
|
106
|
+
timeoutMs?: number;
|
|
107
|
+
}
|
|
108
|
+
declare abstract class BaseResource {
|
|
109
|
+
protected readonly ctx: ResourceContext;
|
|
110
|
+
constructor(ctx: ResourceContext);
|
|
111
|
+
protected get<S extends AnySchema>(path: string, schema?: S, query?: RequestSpec['query']): Promise<Inferred<S>>;
|
|
112
|
+
protected post<S extends AnySchema>(path: string, body: unknown, schema?: S, options?: RequestOptions): Promise<Inferred<S>>;
|
|
113
|
+
protected patch<S extends AnySchema>(path: string, body: unknown, schema?: S, options?: RequestOptions): Promise<Inferred<S>>;
|
|
114
|
+
protected delete<S extends AnySchema>(path: string, schema?: S): Promise<Inferred<S>>;
|
|
115
|
+
protected listPage<S extends AnySchema>(path: string, itemSchema: S, params?: PaginationParams | Record<string, ScalarParam>): Promise<Page<Inferred<S>>>;
|
|
116
|
+
protected listAuto<S extends AnySchema>(path: string, itemSchema: S, params?: Record<string, ScalarParam>): AutoPagingIterator<Inferred<S>>;
|
|
117
|
+
}
|
|
118
|
+
type ScalarParam = string | number | boolean | null | undefined;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Public hosted-checkout reads.
|
|
122
|
+
*
|
|
123
|
+
* Backed by the `/v1/checkout/*` namespace on the Strimz API, which
|
|
124
|
+
* is intentionally unauthenticated — session and plan records are
|
|
125
|
+
* what a merchant offers to payers, so a payer landing on a checkout
|
|
126
|
+
* URL must be able to load the terms without holding any API key.
|
|
127
|
+
*
|
|
128
|
+
* Use this resource (via `StrimzBrowserClient.checkout`) from the
|
|
129
|
+
* browser. The merchant-scoped `paymentSessions` / `subscriptionPlans`
|
|
130
|
+
* resources on `StrimzClient` remain the right call from server-side
|
|
131
|
+
* code with a secret key.
|
|
132
|
+
*/
|
|
133
|
+
declare class CheckoutResource extends BaseResource {
|
|
134
|
+
/** Load a payment session by id (public — no merchant scoping). */
|
|
135
|
+
session(id: string): Promise<PaymentSession>;
|
|
136
|
+
/** Load a subscription plan by id (public — no merchant scoping). */
|
|
137
|
+
plan(id: string): Promise<SubscriptionPlan>;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Token metadata + capability lookup.
|
|
142
|
+
*
|
|
143
|
+
* The two endpoints are public on the API (no key required) — token
|
|
144
|
+
* metadata is purely chain-derived, has no PII, and is what the
|
|
145
|
+
* browser SDK consults to pick which meta-tx path to take for a
|
|
146
|
+
* given token. Bundled here so consumers don't have to write the HTTP
|
|
147
|
+
* fetch themselves.
|
|
148
|
+
*/
|
|
149
|
+
declare class TokensResource extends BaseResource {
|
|
150
|
+
/**
|
|
151
|
+
* Fetch full metadata for a token. Throws via the underlying fetcher
|
|
152
|
+
* (404 → `StrimzNotFoundError`) if the token isn't whitelisted.
|
|
153
|
+
*/
|
|
154
|
+
retrieve(address: string): Promise<TokenMetadata>;
|
|
155
|
+
/**
|
|
156
|
+
* Read an owner's current EIP-2612 permit nonce. Call this
|
|
157
|
+
* immediately before signing a Permit — the token contract rejects
|
|
158
|
+
* stale nonces, and the value advances on every successful permit.
|
|
159
|
+
*/
|
|
160
|
+
permitNonce(address: string, owner: string): Promise<TokenPermitNonce>;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Path selector — decides which meta-tx flow to take given a token's
|
|
164
|
+
* capabilities and the action a merchant wants to perform.
|
|
165
|
+
*
|
|
166
|
+
* The choice is unambiguous in 99% of cases:
|
|
167
|
+
* - One-shot pay → EIP-3009 (preferred). Falls back to
|
|
168
|
+
* approve+pay if the token has neither.
|
|
169
|
+
* - Subscription enrol → EIP-2612 (the only meta-tx path that
|
|
170
|
+
* grants the recurring allowance subscribers
|
|
171
|
+
* need). If unavailable, the merchant must
|
|
172
|
+
* use the classic approve+create flow.
|
|
173
|
+
*
|
|
174
|
+
* Returned `path` values are strings (not enums) so future schemes
|
|
175
|
+
* can extend the union without breaking existing call sites.
|
|
176
|
+
*/
|
|
177
|
+
type RelayPath = 'pay_with_authorization' | 'permit_and_create_subscription' | 'approve_then_pay' | 'approve_then_create_subscription';
|
|
178
|
+
declare function selectPaymentPath(meta: TokenMetadata): RelayPath;
|
|
179
|
+
declare function selectSubscriptionPath(meta: TokenMetadata): RelayPath;
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* StrimzBrowserClient — the publishable-key, browser-safe SDK entry point.
|
|
183
|
+
*
|
|
184
|
+
* 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
|
+
* only those safe to call from a browser with a `pk_test_...` or
|
|
187
|
+
* `pk_live_...` key.
|
|
188
|
+
*/
|
|
189
|
+
|
|
190
|
+
interface StrimzBrowserClientOptions {
|
|
191
|
+
/** Publishable key — `pk_test_...` or `pk_live_...`. */
|
|
192
|
+
publishableKey: string;
|
|
193
|
+
/** Override the API base URL. */
|
|
194
|
+
baseUrl?: string;
|
|
195
|
+
/** Per-request timeout in milliseconds. Default 30s. */
|
|
196
|
+
timeoutMs?: number;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Browser-safe Strimz client.
|
|
200
|
+
*
|
|
201
|
+
* Designed for hosted-checkout state polling and the React component
|
|
202
|
+
* family. Read-only on most resources. Refuses a secret key.
|
|
203
|
+
*
|
|
204
|
+
* The `tokens` resource + `selectPaymentPath` / `selectSubscriptionPath`
|
|
205
|
+
* selectors are the core of the capability-detection layer: given a
|
|
206
|
+
* token, the SDK asks the backend which meta-tx flows the token
|
|
207
|
+
* supports, and the selectors map that to a `RelayPath` the checkout
|
|
208
|
+
* UI uses to decide which typed-data to build.
|
|
209
|
+
*/
|
|
210
|
+
declare class StrimzBrowserClient {
|
|
211
|
+
readonly mode: ApiKeyMode;
|
|
212
|
+
/**
|
|
213
|
+
* Public hosted-checkout reads — payment sessions and subscription
|
|
214
|
+
* plans by id. Backed by the `/v1/checkout/*` namespace on the API,
|
|
215
|
+
* which is intentionally unauthenticated.
|
|
216
|
+
*/
|
|
217
|
+
readonly checkout: CheckoutResource;
|
|
218
|
+
/** Token metadata + EIP-2612 permit nonce lookup. */
|
|
219
|
+
readonly tokens: TokensResource;
|
|
220
|
+
constructor(options: StrimzBrowserClientOptions);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export { AutoPagingIterator as A, BaseResource as B, Fetcher as F, type PaginationParams as P, type RequestOptions as R, StrimzBrowserClient as S, type Page as a, type StrimzBrowserClientOptions as b, type RelayPath as c, selectSubscriptionPath as d, selectPaymentPath as s };
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { ApiKeyMode } from '@strimz/shared-config';
|
|
2
|
+
import { PaymentSession, SubscriptionPlan, TokenMetadata, TokenPermitNonce } from '@strimz/shared-types';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Fetch wrapper with retry, idempotency, and Strimz error parsing.
|
|
7
|
+
*
|
|
8
|
+
* Retries on:
|
|
9
|
+
* - Network failures (TypeError on fetch).
|
|
10
|
+
* - 5xx responses for idempotent methods (GET, POST with idempotency key).
|
|
11
|
+
* - 429 with Retry-After.
|
|
12
|
+
*
|
|
13
|
+
* Does NOT retry on:
|
|
14
|
+
* - 4xx other than 429 (caller's bug).
|
|
15
|
+
* - 5xx for non-idempotent calls without an idempotency key.
|
|
16
|
+
*/
|
|
17
|
+
interface FetcherOptions {
|
|
18
|
+
baseUrl: string;
|
|
19
|
+
baseHeaders: Record<string, string>;
|
|
20
|
+
/** Default timeout per attempt, milliseconds. */
|
|
21
|
+
timeoutMs?: number;
|
|
22
|
+
/** How many retries before giving up. */
|
|
23
|
+
maxRetries?: number;
|
|
24
|
+
/** Initial backoff in ms; doubled with jitter on each retry. */
|
|
25
|
+
initialBackoffMs?: number;
|
|
26
|
+
/** Optional logger. */
|
|
27
|
+
onRequest?: (info: {
|
|
28
|
+
method: string;
|
|
29
|
+
url: string;
|
|
30
|
+
attempt: number;
|
|
31
|
+
}) => void;
|
|
32
|
+
onResponse?: (info: {
|
|
33
|
+
method: string;
|
|
34
|
+
url: string;
|
|
35
|
+
status: number;
|
|
36
|
+
ms: number;
|
|
37
|
+
attempt: number;
|
|
38
|
+
}) => void;
|
|
39
|
+
/** Override the global fetch (for tests / Edge polyfills). */
|
|
40
|
+
fetch?: typeof globalThis.fetch;
|
|
41
|
+
}
|
|
42
|
+
interface RequestSpec {
|
|
43
|
+
method: 'GET' | 'POST' | 'PATCH' | 'DELETE';
|
|
44
|
+
path: string;
|
|
45
|
+
query?: Record<string, string | number | boolean | null | undefined>;
|
|
46
|
+
body?: unknown;
|
|
47
|
+
idempotencyKey?: string;
|
|
48
|
+
/** Per-call timeout override. */
|
|
49
|
+
timeoutMs?: number;
|
|
50
|
+
}
|
|
51
|
+
declare class Fetcher {
|
|
52
|
+
private readonly baseUrl;
|
|
53
|
+
private readonly baseHeaders;
|
|
54
|
+
private readonly timeoutMs;
|
|
55
|
+
private readonly maxRetries;
|
|
56
|
+
private readonly initialBackoffMs;
|
|
57
|
+
private readonly onRequest;
|
|
58
|
+
private readonly onResponse;
|
|
59
|
+
private readonly fetchImpl;
|
|
60
|
+
constructor(opts: FetcherOptions);
|
|
61
|
+
request<T>(spec: RequestSpec): Promise<T>;
|
|
62
|
+
private fetchWithTimeout;
|
|
63
|
+
private buildUrl;
|
|
64
|
+
private backoffFor;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Cursor-based pagination, identical shape to the API's list responses.
|
|
69
|
+
*/
|
|
70
|
+
interface Page<T> {
|
|
71
|
+
data: T[];
|
|
72
|
+
nextCursor: string | null;
|
|
73
|
+
hasMore: boolean;
|
|
74
|
+
}
|
|
75
|
+
interface PaginationParams {
|
|
76
|
+
limit?: number;
|
|
77
|
+
cursor?: string | null;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Async-iterable wrapper that auto-paginates through every page of a list.
|
|
81
|
+
*
|
|
82
|
+
* for await (const tx of strimz.transactions.listAll()) { ... }
|
|
83
|
+
*/
|
|
84
|
+
declare class AutoPagingIterator<T> implements AsyncIterable<T> {
|
|
85
|
+
private readonly fetchPage;
|
|
86
|
+
constructor(fetchPage: (cursor: string | null) => Promise<Page<T>>);
|
|
87
|
+
[Symbol.asyncIterator](): AsyncIterator<T>;
|
|
88
|
+
forEach(handler: (item: T) => void | Promise<void>): Promise<void>;
|
|
89
|
+
toArray(): Promise<T[]>;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Base class every resource extends. Centralises endpoint plumbing so
|
|
94
|
+
* resource classes are tiny declarative wrappers.
|
|
95
|
+
*/
|
|
96
|
+
|
|
97
|
+
type AnySchema = z.ZodTypeAny;
|
|
98
|
+
type Inferred<S extends AnySchema> = z.output<S>;
|
|
99
|
+
interface ResourceContext {
|
|
100
|
+
fetcher: Fetcher;
|
|
101
|
+
}
|
|
102
|
+
interface RequestOptions {
|
|
103
|
+
/** Override or supply the idempotency key for a mutating call. */
|
|
104
|
+
idempotencyKey?: string;
|
|
105
|
+
/** Per-request timeout in ms. */
|
|
106
|
+
timeoutMs?: number;
|
|
107
|
+
}
|
|
108
|
+
declare abstract class BaseResource {
|
|
109
|
+
protected readonly ctx: ResourceContext;
|
|
110
|
+
constructor(ctx: ResourceContext);
|
|
111
|
+
protected get<S extends AnySchema>(path: string, schema?: S, query?: RequestSpec['query']): Promise<Inferred<S>>;
|
|
112
|
+
protected post<S extends AnySchema>(path: string, body: unknown, schema?: S, options?: RequestOptions): Promise<Inferred<S>>;
|
|
113
|
+
protected patch<S extends AnySchema>(path: string, body: unknown, schema?: S, options?: RequestOptions): Promise<Inferred<S>>;
|
|
114
|
+
protected delete<S extends AnySchema>(path: string, schema?: S): Promise<Inferred<S>>;
|
|
115
|
+
protected listPage<S extends AnySchema>(path: string, itemSchema: S, params?: PaginationParams | Record<string, ScalarParam>): Promise<Page<Inferred<S>>>;
|
|
116
|
+
protected listAuto<S extends AnySchema>(path: string, itemSchema: S, params?: Record<string, ScalarParam>): AutoPagingIterator<Inferred<S>>;
|
|
117
|
+
}
|
|
118
|
+
type ScalarParam = string | number | boolean | null | undefined;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Public hosted-checkout reads.
|
|
122
|
+
*
|
|
123
|
+
* Backed by the `/v1/checkout/*` namespace on the Strimz API, which
|
|
124
|
+
* is intentionally unauthenticated — session and plan records are
|
|
125
|
+
* what a merchant offers to payers, so a payer landing on a checkout
|
|
126
|
+
* URL must be able to load the terms without holding any API key.
|
|
127
|
+
*
|
|
128
|
+
* Use this resource (via `StrimzBrowserClient.checkout`) from the
|
|
129
|
+
* browser. The merchant-scoped `paymentSessions` / `subscriptionPlans`
|
|
130
|
+
* resources on `StrimzClient` remain the right call from server-side
|
|
131
|
+
* code with a secret key.
|
|
132
|
+
*/
|
|
133
|
+
declare class CheckoutResource extends BaseResource {
|
|
134
|
+
/** Load a payment session by id (public — no merchant scoping). */
|
|
135
|
+
session(id: string): Promise<PaymentSession>;
|
|
136
|
+
/** Load a subscription plan by id (public — no merchant scoping). */
|
|
137
|
+
plan(id: string): Promise<SubscriptionPlan>;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Token metadata + capability lookup.
|
|
142
|
+
*
|
|
143
|
+
* The two endpoints are public on the API (no key required) — token
|
|
144
|
+
* metadata is purely chain-derived, has no PII, and is what the
|
|
145
|
+
* browser SDK consults to pick which meta-tx path to take for a
|
|
146
|
+
* given token. Bundled here so consumers don't have to write the HTTP
|
|
147
|
+
* fetch themselves.
|
|
148
|
+
*/
|
|
149
|
+
declare class TokensResource extends BaseResource {
|
|
150
|
+
/**
|
|
151
|
+
* Fetch full metadata for a token. Throws via the underlying fetcher
|
|
152
|
+
* (404 → `StrimzNotFoundError`) if the token isn't whitelisted.
|
|
153
|
+
*/
|
|
154
|
+
retrieve(address: string): Promise<TokenMetadata>;
|
|
155
|
+
/**
|
|
156
|
+
* Read an owner's current EIP-2612 permit nonce. Call this
|
|
157
|
+
* immediately before signing a Permit — the token contract rejects
|
|
158
|
+
* stale nonces, and the value advances on every successful permit.
|
|
159
|
+
*/
|
|
160
|
+
permitNonce(address: string, owner: string): Promise<TokenPermitNonce>;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Path selector — decides which meta-tx flow to take given a token's
|
|
164
|
+
* capabilities and the action a merchant wants to perform.
|
|
165
|
+
*
|
|
166
|
+
* The choice is unambiguous in 99% of cases:
|
|
167
|
+
* - One-shot pay → EIP-3009 (preferred). Falls back to
|
|
168
|
+
* approve+pay if the token has neither.
|
|
169
|
+
* - Subscription enrol → EIP-2612 (the only meta-tx path that
|
|
170
|
+
* grants the recurring allowance subscribers
|
|
171
|
+
* need). If unavailable, the merchant must
|
|
172
|
+
* use the classic approve+create flow.
|
|
173
|
+
*
|
|
174
|
+
* Returned `path` values are strings (not enums) so future schemes
|
|
175
|
+
* can extend the union without breaking existing call sites.
|
|
176
|
+
*/
|
|
177
|
+
type RelayPath = 'pay_with_authorization' | 'permit_and_create_subscription' | 'approve_then_pay' | 'approve_then_create_subscription';
|
|
178
|
+
declare function selectPaymentPath(meta: TokenMetadata): RelayPath;
|
|
179
|
+
declare function selectSubscriptionPath(meta: TokenMetadata): RelayPath;
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* StrimzBrowserClient — the publishable-key, browser-safe SDK entry point.
|
|
183
|
+
*
|
|
184
|
+
* 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
|
+
* only those safe to call from a browser with a `pk_test_...` or
|
|
187
|
+
* `pk_live_...` key.
|
|
188
|
+
*/
|
|
189
|
+
|
|
190
|
+
interface StrimzBrowserClientOptions {
|
|
191
|
+
/** Publishable key — `pk_test_...` or `pk_live_...`. */
|
|
192
|
+
publishableKey: string;
|
|
193
|
+
/** Override the API base URL. */
|
|
194
|
+
baseUrl?: string;
|
|
195
|
+
/** Per-request timeout in milliseconds. Default 30s. */
|
|
196
|
+
timeoutMs?: number;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Browser-safe Strimz client.
|
|
200
|
+
*
|
|
201
|
+
* Designed for hosted-checkout state polling and the React component
|
|
202
|
+
* family. Read-only on most resources. Refuses a secret key.
|
|
203
|
+
*
|
|
204
|
+
* The `tokens` resource + `selectPaymentPath` / `selectSubscriptionPath`
|
|
205
|
+
* selectors are the core of the capability-detection layer: given a
|
|
206
|
+
* token, the SDK asks the backend which meta-tx flows the token
|
|
207
|
+
* supports, and the selectors map that to a `RelayPath` the checkout
|
|
208
|
+
* UI uses to decide which typed-data to build.
|
|
209
|
+
*/
|
|
210
|
+
declare class StrimzBrowserClient {
|
|
211
|
+
readonly mode: ApiKeyMode;
|
|
212
|
+
/**
|
|
213
|
+
* Public hosted-checkout reads — payment sessions and subscription
|
|
214
|
+
* plans by id. Backed by the `/v1/checkout/*` namespace on the API,
|
|
215
|
+
* which is intentionally unauthenticated.
|
|
216
|
+
*/
|
|
217
|
+
readonly checkout: CheckoutResource;
|
|
218
|
+
/** Token metadata + EIP-2612 permit nonce lookup. */
|
|
219
|
+
readonly tokens: TokensResource;
|
|
220
|
+
constructor(options: StrimzBrowserClientOptions);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export { AutoPagingIterator as A, BaseResource as B, Fetcher as F, type PaginationParams as P, type RequestOptions as R, StrimzBrowserClient as S, type Page as a, type StrimzBrowserClientOptions as b, type RelayPath as c, selectSubscriptionPath as d, selectPaymentPath as s };
|