@takeal/cusfront-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.
Files changed (74) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/LICENSE +21 -0
  3. package/README.md +283 -0
  4. package/dist/customer-CoxPwe5o.d.cts +32 -0
  5. package/dist/customer-CoxPwe5o.d.ts +32 -0
  6. package/dist/http-BkZZZI8K.d.cts +72 -0
  7. package/dist/http-BkZZZI8K.d.ts +72 -0
  8. package/dist/index.cjs +486 -0
  9. package/dist/index.cjs.map +1 -0
  10. package/dist/index.d.cts +149 -0
  11. package/dist/index.d.ts +149 -0
  12. package/dist/index.js +478 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/react/index.cjs +82 -0
  15. package/dist/react/index.cjs.map +1 -0
  16. package/dist/react/index.d.cts +93 -0
  17. package/dist/react/index.d.ts +93 -0
  18. package/dist/react/index.js +75 -0
  19. package/dist/react/index.js.map +1 -0
  20. package/dist/resources/auth.cjs +88 -0
  21. package/dist/resources/auth.cjs.map +1 -0
  22. package/dist/resources/auth.d.cts +132 -0
  23. package/dist/resources/auth.d.ts +132 -0
  24. package/dist/resources/auth.js +86 -0
  25. package/dist/resources/auth.js.map +1 -0
  26. package/dist/resources/balance.cjs +21 -0
  27. package/dist/resources/balance.cjs.map +1 -0
  28. package/dist/resources/balance.d.cts +27 -0
  29. package/dist/resources/balance.d.ts +27 -0
  30. package/dist/resources/balance.js +19 -0
  31. package/dist/resources/balance.js.map +1 -0
  32. package/dist/resources/blog.cjs +39 -0
  33. package/dist/resources/blog.cjs.map +1 -0
  34. package/dist/resources/blog.d.cts +112 -0
  35. package/dist/resources/blog.d.ts +112 -0
  36. package/dist/resources/blog.js +37 -0
  37. package/dist/resources/blog.js.map +1 -0
  38. package/dist/resources/branding.cjs +16 -0
  39. package/dist/resources/branding.cjs.map +1 -0
  40. package/dist/resources/branding.d.cts +35 -0
  41. package/dist/resources/branding.d.ts +35 -0
  42. package/dist/resources/branding.js +14 -0
  43. package/dist/resources/branding.js.map +1 -0
  44. package/dist/resources/cards.cjs +91 -0
  45. package/dist/resources/cards.cjs.map +1 -0
  46. package/dist/resources/cards.d.cts +166 -0
  47. package/dist/resources/cards.d.ts +166 -0
  48. package/dist/resources/cards.js +89 -0
  49. package/dist/resources/cards.js.map +1 -0
  50. package/dist/resources/deposits.cjs +52 -0
  51. package/dist/resources/deposits.cjs.map +1 -0
  52. package/dist/resources/deposits.d.cts +168 -0
  53. package/dist/resources/deposits.d.ts +168 -0
  54. package/dist/resources/deposits.js +50 -0
  55. package/dist/resources/deposits.js.map +1 -0
  56. package/dist/resources/subscriptions.cjs +24 -0
  57. package/dist/resources/subscriptions.cjs.map +1 -0
  58. package/dist/resources/subscriptions.d.cts +36 -0
  59. package/dist/resources/subscriptions.d.ts +36 -0
  60. package/dist/resources/subscriptions.js +22 -0
  61. package/dist/resources/subscriptions.js.map +1 -0
  62. package/dist/telegram.cjs +557 -0
  63. package/dist/telegram.cjs.map +1 -0
  64. package/dist/telegram.d.cts +105 -0
  65. package/dist/telegram.d.ts +105 -0
  66. package/dist/telegram.js +550 -0
  67. package/dist/telegram.js.map +1 -0
  68. package/dist/webhooks.cjs +78 -0
  69. package/dist/webhooks.cjs.map +1 -0
  70. package/dist/webhooks.d.cts +70 -0
  71. package/dist/webhooks.d.ts +70 -0
  72. package/dist/webhooks.js +72 -0
  73. package/dist/webhooks.js.map +1 -0
  74. package/package.json +123 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,66 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@takeal/cusfront-sdk` are documented here. The format
4
+ follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project
5
+ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ### Added
10
+
11
+ - `client.blog.list()` / `client.blog.get(slug)` — public posts returned as
12
+ structured blocks (`body_blocks`) plus raw Markdown, so a site renders them
13
+ in its own components instead of fighting server-side HTML.
14
+ - `client.subscriptions.get()` / `.set(channels)` — the user's announcement
15
+ channels, with `available` reflecting what the deployment can deliver.
16
+ - `client.deposits.quote()` + `quote_id` on `initiate()` — cross-currency
17
+ top-ups now execute against a rate-locked quote (the wallet holds a single
18
+ local currency; the API converts on funding). `Deposit` gains
19
+ `quote_id` / `fx_rate` / `fx_spread_bps` / `credited_currency` /
20
+ `credited_amount`.
21
+ - `client.branding.get()` — fetches the deployment's public runtime brand
22
+ config (`GET /branding`): platform / portal names, logo, favicon and the new
23
+ `wallet_label` (how the end-user's balance is called, e.g. "Acme Wallet").
24
+ - `BrandConfig.walletLabel` — build-time fallback for the same label.
25
+
26
+ ### Changed
27
+
28
+ - Docs: "master balance" wording replaced by "wallet balance" everywhere. No
29
+ API field names changed.
30
+ - Generated types refreshed from the current API spec (card-transaction
31
+ billing fields, issuing metrics, branding). `refresh-types` now scrubs
32
+ internal references from doc comments before generating.
33
+
34
+ ## [0.1.0] — first public release
35
+
36
+ Initial release of the typed client for the Takeal end-user API.
37
+
38
+ ### Added
39
+
40
+ - `createClient({ baseUrl, brand?, tokenStore?, fetch?, validate? })` — the
41
+ entry point; resources hang off the returned client.
42
+ - **Auth** (`client.auth`) — staged login (`login` → optional `verifyTotp` /
43
+ `verifyOtp` / `switchMethod`), `me`, `signOut`. JWT auto-stored on success.
44
+ - **Deposits** (`client.deposits`) — `initiate` / `get` / `list` / `refund`
45
+ (money in via a funder connector), with `Idempotency-Key` support.
46
+ - **Cards** (`client.cards`) — `create` / `get` / `list` / `balance` plus the
47
+ ownership-gated `freeze` / `unfreeze` / `terminate`, and the security-gated
48
+ `reveal` (re-auth with password + optional TOTP).
49
+ - **Balance** (`client.balance`) — per-currency wallet balance lookup.
50
+ - **Telegram Mini App** (`@takeal/cusfront-sdk/telegram`) — `fromTelegramWebApp`
51
+ / `fromInitData` exchange a Telegram-signed `initData` for an authenticated
52
+ client; `parseInitData` / `assertWellFormed` helpers; `client.auth.exchangeTelegram`
53
+ + `client.auth.linkEmail` for the auto-provision-then-attach-email flow.
54
+ - **Webhooks** (`@takeal/cusfront-sdk/webhooks`) — isomorphic, no-network
55
+ HMAC-SHA256 signature verifier (`verifyWebhook`).
56
+ - **React** (`@takeal/cusfront-sdk/react`) — optional `ClientProvider` +
57
+ `useMe` / `useDeposits` / `useCards` / `useBalance` hooks (React is a peer dep,
58
+ never bundled).
59
+ - Pluggable token storage (`defaultBrowserStore` / `inMemoryStore` / custom
60
+ `TokenStore`), typed `ApiError` / `NetworkError`, and runtime `BrandConfig`
61
+ for whitelabel theming.
62
+ - Dual ESM + CJS builds with `.d.ts` types; per-resource sub-entrypoints for
63
+ tree-shaking.
64
+
65
+ [Unreleased]: https://git.wiseless.xyz/takeal/cusfront-sdk/compare/v0.1.0...HEAD
66
+ [0.1.0]: https://git.wiseless.xyz/takeal/cusfront-sdk/releases/tag/v0.1.0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Takeal
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,283 @@
1
+ # @takeal/cusfront-sdk
2
+
3
+ Typed TypeScript client for the **Takeal end-user API** — auth, deposits, cards, balance, and webhook signature verification.
4
+
5
+ The SDK powers any "Cusfront" (consumer front-end) on top of a Takeal deployment: a PWA, a Capacitor-wrapped mobile app, a Telegram Mini App, or anything else that talks to the `/auth/*` + `/me/*` surface. One client, runtime brand swap, zero runtime deps in the core.
6
+
7
+ > **Status:** 0.x — pre-release scaffold. Auth resource lands first; deposits + cards + balance + webhook verifier ship in subsequent releases.
8
+
9
+ ## Install
10
+
11
+ ```sh
12
+ pnpm add @takeal/cusfront-sdk
13
+ # optional peer dep for runtime validation
14
+ pnpm add zod
15
+ ```
16
+
17
+ Native `fetch` is required. Node ≥ 18, modern browsers, Bun, Deno, and React Native ≥ 0.74 (Hermes) all ship it out of the box. For older runtimes inject a polyfill via `createClient({ fetch })`.
18
+
19
+ ## Quick start
20
+
21
+ ```ts
22
+ import { createClient } from "@takeal/cusfront-sdk";
23
+
24
+ const client = createClient({
25
+ baseUrl: "https://api.your-deployment.example.com",
26
+ brand: {
27
+ name: "Your Brand",
28
+ logoUrl: "/logo.svg",
29
+ primaryColor: "#0047AB",
30
+ },
31
+ });
32
+
33
+ const result = await client.auth.login({
34
+ email: "user@example.com",
35
+ password: "secret",
36
+ });
37
+
38
+ if (result.stage === "jwt") {
39
+ // Authenticated — JWT auto-stored.
40
+ const me = await client.auth.me();
41
+ console.log("hello", me.email);
42
+ } else if (result.stage === "totp_required") {
43
+ // Step-up required. Prompt the user for their TOTP code,
44
+ // then call client.auth.verifyTotp({ challenge_token, code }).
45
+ }
46
+ ```
47
+
48
+ ## Telegram Mini App
49
+
50
+ Run inside a Telegram Mini App? Exchange the Telegram-signed `initData` for a
51
+ session in one call — no password:
52
+
53
+ ```ts
54
+ import { fromTelegramWebApp } from "@takeal/cusfront-sdk/telegram";
55
+
56
+ // Reads window.Telegram.WebApp.initData, exchanges it, returns a ready client.
57
+ const client = await fromTelegramWebApp({
58
+ baseUrl: "https://api.your-deployment.example.com",
59
+ });
60
+
61
+ const me = await client.auth.me();
62
+ if (me.email_pending) {
63
+ // First-time Telegram users are auto-provisioned without an email.
64
+ // Collect a real address and attach it:
65
+ await client.auth.linkEmail({ email: "user@example.com" });
66
+ }
67
+ ```
68
+
69
+ Already hold the raw string (e.g. from a custom launch)? Use `fromInitData`:
70
+
71
+ ```ts
72
+ import { fromInitData, parseInitData } from "@takeal/cusfront-sdk/telegram";
73
+
74
+ const client = await fromInitData(initData, {
75
+ baseUrl: "https://api.your-deployment.example.com",
76
+ });
77
+ ```
78
+
79
+ How it works end-to-end:
80
+
81
+ 1. **Telegram** signs `initData` with the bot token when it launches your Mini App.
82
+ 2. The SDK does a fast **structural** check (`hash` + `auth_date` present, not
83
+ stale) and POSTs the raw string to the API's exchange endpoint. The SDK
84
+ **cannot** verify the cryptographic signature — only the server holds the bot
85
+ token, so the API performs the authoritative HMAC check. A forged or stale
86
+ payload is rejected there with a `401 ApiError`.
87
+ 3. On success the JWT is stored in the configured token store; the returned
88
+ client is authenticated for all `client.*` calls.
89
+ 4. First-time Telegram users are **auto-provisioned**. They have no email yet, so
90
+ `me.email_pending === true` — prompt for an address and call
91
+ `client.auth.linkEmail` to clear it.
92
+
93
+ For the lower-level call returning the raw session envelope (including
94
+ `email_pending`), use `client.auth.exchangeTelegram({ initData })` on a client
95
+ you built yourself.
96
+
97
+ ## Brand config
98
+
99
+ Whitelabel-friendly by design — the SDK ships no embedded brand. Pass `brand` at runtime and the consumer Cusfront reads it back via `client.brand`:
100
+
101
+ ```ts
102
+ type BrandConfig = {
103
+ name: string;
104
+ logoUrl?: string;
105
+ primaryColor?: string;
106
+ supportUrl?: string;
107
+ walletLabel?: string; // how the user's balance is called, e.g. "Acme Wallet"
108
+ };
109
+ ```
110
+
111
+ Switching brands does **not** require forking or re-publishing the SDK.
112
+
113
+ The deployment also publishes its live brand config at `GET /branding` (public,
114
+ no login needed) — `client.branding.get()` returns `platform_name`,
115
+ `merchant_portal_name`, `logo_url`, `favicon_url` and `wallet_label`, so a
116
+ Cusfront can re-theme itself at runtime and call the balance whatever the
117
+ operator configured. Server values win over the build-time `brand` when both
118
+ are present.
119
+
120
+ ## Sub-exports
121
+
122
+ Tree-shaking-friendly: import only the resource you need.
123
+
124
+ ```ts
125
+ import { AuthResource } from "@takeal/cusfront-sdk/auth";
126
+ ```
127
+
128
+ Available now:
129
+
130
+ - `@takeal/cusfront-sdk` — `createClient` + types.
131
+ - `@takeal/cusfront-sdk/auth` — auth-only entry.
132
+ - `@takeal/cusfront-sdk/deposits` — `DepositsResource` (money IN via a funder connector).
133
+ - `@takeal/cusfront-sdk/cards` — `CardsResource` (cards backed by the wallet balance).
134
+ - `@takeal/cusfront-sdk/balance` — `BalanceResource` (per-currency wallet balance).
135
+ - `@takeal/cusfront-sdk/blog` — `BlogResource` (public posts as structured blocks).
136
+ - `@takeal/cusfront-sdk/subscriptions` — `SubscriptionsResource` (announcement channels).
137
+ - `@takeal/cusfront-sdk/webhooks` — HMAC-SHA256 signature verifier (no network, isomorphic).
138
+ - `@takeal/cusfront-sdk/react` — optional React hooks layer (`ClientProvider` + `useMe` / `useDeposits` / `useCards` / `useBalance`). React is a `peerDependency`, never bundled.
139
+ - `@takeal/cusfront-sdk/telegram` — Telegram Mini App `initData` → authenticated client bridge.
140
+
141
+ ### Verifying webhooks
142
+
143
+ `verifyWebhook` is pure (no network) and isomorphic (Web Crypto — Node 18+,
144
+ browsers, Bun, Deno, Workers). Verify against the **raw** request body bytes —
145
+ not re-serialised JSON — using the secret your endpoint was provisioned with:
146
+
147
+ ```ts
148
+ import { verifyWebhook, SIGNATURE_HEADER } from "@takeal/cusfront-sdk/webhooks";
149
+
150
+ const ok = await verifyWebhook({
151
+ payload: rawBody, // string or Uint8Array, verbatim
152
+ signatureHeader: req.headers[SIGNATURE_HEADER.toLowerCase()],
153
+ secret: process.env.TAKEAL_WEBHOOK_SECRET!,
154
+ });
155
+ if (!ok) return res.status(401).end();
156
+ ```
157
+
158
+ Algorithm: HMAC-SHA256, header `X-Takeal-Signature: sha256=<hex>`, signed over
159
+ the raw body bytes (no timestamp). Comparison is constant-time.
160
+
161
+ > **Card lifecycle note:** card-data reveal (PAN / CVV) is a separate,
162
+ > security-gated flow with its own re-auth + rate-limit + audit contract, so it
163
+ > is intentionally not part of `client.cards`. The cards resource covers
164
+ > create / get / list / balance plus the ownership-gated freeze / unfreeze /
165
+ > terminate lifecycle actions.
166
+
167
+ ## React hooks (`@takeal/cusfront-sdk/react`)
168
+
169
+ An optional React layer ships from a separate entry point. React is a
170
+ `peerDependency` (`>=18`) and is never bundled, so non-React consumers pay
171
+ nothing for it. Install React in your app, then:
172
+
173
+ ```sh
174
+ pnpm add react # if not already present
175
+ ```
176
+
177
+ Wrap your tree once in a `ClientProvider`, then read data with the hooks:
178
+
179
+ ```tsx
180
+ import { createClient } from "@takeal/cusfront-sdk";
181
+ import { ClientProvider, useBalance } from "@takeal/cusfront-sdk/react";
182
+
183
+ // Build the client once — module scope or a useMemo, not per-render.
184
+ const client = createClient({
185
+ baseUrl: "https://api.your-deployment.example.com",
186
+ });
187
+
188
+ function Root() {
189
+ return (
190
+ <ClientProvider client={client}>
191
+ <Wallet />
192
+ </ClientProvider>
193
+ );
194
+ }
195
+
196
+ function Wallet() {
197
+ const { data, error, loading, refetch } = useBalance("USD");
198
+
199
+ if (loading) return <Spinner />;
200
+ if (error) return <ErrorBanner onRetry={refetch} />;
201
+ return (
202
+ <div>
203
+ {data!.amount} {data!.currency}
204
+ <button onClick={() => void refetch()}>Refresh</button>
205
+ </div>
206
+ );
207
+ }
208
+ ```
209
+
210
+ Every hook returns the same shape — `{ data, error, loading, refetch }`:
211
+
212
+ - `useMe()` — current authenticated user (`client.auth.me()`).
213
+ - `useDeposits()` — the user's deposits (`client.deposits.list()`).
214
+ - `useCards()` — the user's cards (`client.cards.list()`).
215
+ - `useBalance(currency)` — wallet balance for one currency
216
+ (`client.balance.get(currency)`); re-fetches when `currency` changes.
217
+
218
+ `useClient()` exposes the raw client from context for one-off writes
219
+ (e.g. `client.deposits.initiate(...)`) — it throws a clear error if called
220
+ outside a `ClientProvider`.
221
+
222
+ The hooks are SSR-safe (fetches run only inside `useEffect`, never during
223
+ server render) and have no third-party data-fetching dependency. In-flight
224
+ requests are guarded against unmounted-component writes.
225
+
226
+ ## Token storage
227
+
228
+ `createClient` accepts a pluggable `TokenStore`. The default is:
229
+
230
+ - Browser: `localStorage` (key `takeal_jwt`).
231
+ - Node / SSR / Worker: in-memory.
232
+ - Capacitor / React Native: pass your own (Keychain / EncryptedSharedPreferences wrapper).
233
+
234
+ ```ts
235
+ import { createClient, inMemoryStore } from "@takeal/cusfront-sdk";
236
+
237
+ const client = createClient({
238
+ baseUrl: "...",
239
+ tokenStore: inMemoryStore(), // never persist
240
+ });
241
+ ```
242
+
243
+ ## Errors
244
+
245
+ Two narrow error types — branch on the type guard, not `instanceof`:
246
+
247
+ ```ts
248
+ import { isApiError, isNetworkError } from "@takeal/cusfront-sdk";
249
+
250
+ try {
251
+ await client.auth.login({ email, password });
252
+ } catch (e) {
253
+ if (isApiError(e)) {
254
+ // e.status, e.code, e.message, e.body
255
+ if (e.code === "invalid_credentials") showInlineError();
256
+ } else if (isNetworkError(e)) {
257
+ showOfflineBanner();
258
+ } else {
259
+ throw e;
260
+ }
261
+ }
262
+ ```
263
+
264
+ ## Development
265
+
266
+ ```sh
267
+ pnpm install
268
+ pnpm refresh-types # regenerate src/types.gen.ts from openapi-snapshot.json
269
+ pnpm build # tsup → dist/
270
+ pnpm test # vitest
271
+ pnpm typecheck # tsc --noEmit
272
+ ```
273
+
274
+ The OpenAPI snapshot lives at `openapi-snapshot.json` and is committed; refresh it from a running Takeal deployment with:
275
+
276
+ ```sh
277
+ curl https://api.your-deployment.example.com/api/docs/openapi.json > openapi-snapshot.json
278
+ pnpm refresh-types
279
+ ```
280
+
281
+ ## License
282
+
283
+ MIT — see [LICENSE](./LICENSE).
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Structured customer billing details — shared by the deposits and cards
3
+ * resources. Mirrors the Takeal API's `CustomerInfo` / `Address`.
4
+ *
5
+ * Every field is optional at this layer; each provider connector enforces its
6
+ * own required subset and fails the operation if a mandatory field is missing.
7
+ * Real PSPs / card issuers demand a billing address + name + email for
8
+ * compliance, 3DS, or APM redirect flows; crypto / gift rails usually ignore it.
9
+ */
10
+ /** Postal address. ISO 3166 codes recommended where the provider supports them. */
11
+ interface Address {
12
+ line1?: string;
13
+ line2?: string;
14
+ city?: string;
15
+ /** ISO 3166-2 subdivision code, e.g. `"US-CA"`. Free-form fallback accepted. */
16
+ state?: string;
17
+ postal_code?: string;
18
+ /** ISO 3166-1 alpha-2, e.g. `"US"`. */
19
+ country?: string;
20
+ }
21
+ interface CustomerInfo {
22
+ email?: string;
23
+ first_name?: string;
24
+ last_name?: string;
25
+ /** E.164 format, e.g. `"+48..."`. Free-form fallback accepted. */
26
+ phone?: string;
27
+ /** ISO 8601 `YYYY-MM-DD`. KYC-tier issuers may require it. */
28
+ birth_date?: string;
29
+ billing_address?: Address;
30
+ }
31
+
32
+ export type { Address as A, CustomerInfo as C };
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Structured customer billing details — shared by the deposits and cards
3
+ * resources. Mirrors the Takeal API's `CustomerInfo` / `Address`.
4
+ *
5
+ * Every field is optional at this layer; each provider connector enforces its
6
+ * own required subset and fails the operation if a mandatory field is missing.
7
+ * Real PSPs / card issuers demand a billing address + name + email for
8
+ * compliance, 3DS, or APM redirect flows; crypto / gift rails usually ignore it.
9
+ */
10
+ /** Postal address. ISO 3166 codes recommended where the provider supports them. */
11
+ interface Address {
12
+ line1?: string;
13
+ line2?: string;
14
+ city?: string;
15
+ /** ISO 3166-2 subdivision code, e.g. `"US-CA"`. Free-form fallback accepted. */
16
+ state?: string;
17
+ postal_code?: string;
18
+ /** ISO 3166-1 alpha-2, e.g. `"US"`. */
19
+ country?: string;
20
+ }
21
+ interface CustomerInfo {
22
+ email?: string;
23
+ first_name?: string;
24
+ last_name?: string;
25
+ /** E.164 format, e.g. `"+48..."`. Free-form fallback accepted. */
26
+ phone?: string;
27
+ /** ISO 8601 `YYYY-MM-DD`. KYC-tier issuers may require it. */
28
+ birth_date?: string;
29
+ billing_address?: Address;
30
+ }
31
+
32
+ export type { Address as A, CustomerInfo as C };
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Pluggable storage for the JWT.
3
+ *
4
+ * Default behaviour by environment:
5
+ * - browser → `localStorage` (synchronous, persists across reloads)
6
+ * - Node / SSR / worker → in-memory (no global state to leak)
7
+ * - mobile (Capacitor / RN) → consumer must inject Keychain /
8
+ * EncryptedSharedPreferences via `createClient({ tokenStore })`
9
+ *
10
+ * The store is intentionally tiny — three methods, no eviction
11
+ * policy, no encryption. Encryption is the consumer's call (mobile
12
+ * adapters wrap platform-native secure storage). Persistence shape
13
+ * is opaque to the SDK; tokens are passed through verbatim.
14
+ */
15
+ interface TokenStore {
16
+ get(): string | null | Promise<string | null>;
17
+ set(token: string): void | Promise<void>;
18
+ clear(): void | Promise<void>;
19
+ }
20
+ /** Sync `localStorage`-backed store. Falls through to in-memory when
21
+ * `window.localStorage` is unavailable (private mode quota, SSR). */
22
+ declare function defaultBrowserStore(key?: string): TokenStore;
23
+ /** In-memory store. Use for SSR / tests / when persistence isn't
24
+ * desired (e.g. ephemeral kiosk sessions). */
25
+ declare function inMemoryStore(): TokenStore;
26
+
27
+ /**
28
+ * Internal HTTP client. One per SDK instance; injected into each
29
+ * resource. Responsibilities:
30
+ *
31
+ * - Construct absolute URLs from `baseUrl` + path.
32
+ * - Set `Authorization: Bearer <jwt>` from the `TokenStore`.
33
+ * - Set `Content-Type: application/json` on bodies.
34
+ * - Parse JSON responses + map non-2xx to `ApiError`.
35
+ * - Forward `Idempotency-Key` when the caller passes one.
36
+ * - Surface network failures (DNS, abort, broken pipe) as
37
+ * `NetworkError`.
38
+ *
39
+ * Auto-refresh + retry policies are deliberately NOT in here — they
40
+ * belong to the resource layer (auth.refresh) which has the right
41
+ * context for "is this refreshable" decisions.
42
+ */
43
+ type FetchLike = typeof fetch;
44
+ interface HttpOptions {
45
+ /** Request body. Will be JSON-serialised if not already a
46
+ * string / FormData / Blob. */
47
+ body?: unknown;
48
+ /** Idempotency key forwarded as the `Idempotency-Key` header.
49
+ * Required by the Takeal API on most write endpoints. */
50
+ idempotencyKey?: string;
51
+ /** Extra headers merged after the SDK's defaults — caller wins. */
52
+ headers?: Record<string, string>;
53
+ /** Bypass the token store for one call (login, refresh). */
54
+ skipAuth?: boolean;
55
+ /** AbortSignal forwarded to fetch. */
56
+ signal?: AbortSignal;
57
+ }
58
+ declare class HttpClient {
59
+ private readonly baseUrl;
60
+ private readonly tokens;
61
+ private readonly fetchImpl;
62
+ constructor(baseUrl: string, tokens: TokenStore, fetchImpl: FetchLike);
63
+ get<T>(path: string, opts?: HttpOptions): Promise<T>;
64
+ post<T>(path: string, opts?: HttpOptions): Promise<T>;
65
+ put<T>(path: string, opts?: HttpOptions): Promise<T>;
66
+ patch<T>(path: string, opts?: HttpOptions): Promise<T>;
67
+ delete<T>(path: string, opts?: HttpOptions): Promise<T>;
68
+ private request;
69
+ private absolute;
70
+ }
71
+
72
+ export { type FetchLike as F, HttpClient as H, type TokenStore as T, defaultBrowserStore as d, inMemoryStore as i };
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Pluggable storage for the JWT.
3
+ *
4
+ * Default behaviour by environment:
5
+ * - browser → `localStorage` (synchronous, persists across reloads)
6
+ * - Node / SSR / worker → in-memory (no global state to leak)
7
+ * - mobile (Capacitor / RN) → consumer must inject Keychain /
8
+ * EncryptedSharedPreferences via `createClient({ tokenStore })`
9
+ *
10
+ * The store is intentionally tiny — three methods, no eviction
11
+ * policy, no encryption. Encryption is the consumer's call (mobile
12
+ * adapters wrap platform-native secure storage). Persistence shape
13
+ * is opaque to the SDK; tokens are passed through verbatim.
14
+ */
15
+ interface TokenStore {
16
+ get(): string | null | Promise<string | null>;
17
+ set(token: string): void | Promise<void>;
18
+ clear(): void | Promise<void>;
19
+ }
20
+ /** Sync `localStorage`-backed store. Falls through to in-memory when
21
+ * `window.localStorage` is unavailable (private mode quota, SSR). */
22
+ declare function defaultBrowserStore(key?: string): TokenStore;
23
+ /** In-memory store. Use for SSR / tests / when persistence isn't
24
+ * desired (e.g. ephemeral kiosk sessions). */
25
+ declare function inMemoryStore(): TokenStore;
26
+
27
+ /**
28
+ * Internal HTTP client. One per SDK instance; injected into each
29
+ * resource. Responsibilities:
30
+ *
31
+ * - Construct absolute URLs from `baseUrl` + path.
32
+ * - Set `Authorization: Bearer <jwt>` from the `TokenStore`.
33
+ * - Set `Content-Type: application/json` on bodies.
34
+ * - Parse JSON responses + map non-2xx to `ApiError`.
35
+ * - Forward `Idempotency-Key` when the caller passes one.
36
+ * - Surface network failures (DNS, abort, broken pipe) as
37
+ * `NetworkError`.
38
+ *
39
+ * Auto-refresh + retry policies are deliberately NOT in here — they
40
+ * belong to the resource layer (auth.refresh) which has the right
41
+ * context for "is this refreshable" decisions.
42
+ */
43
+ type FetchLike = typeof fetch;
44
+ interface HttpOptions {
45
+ /** Request body. Will be JSON-serialised if not already a
46
+ * string / FormData / Blob. */
47
+ body?: unknown;
48
+ /** Idempotency key forwarded as the `Idempotency-Key` header.
49
+ * Required by the Takeal API on most write endpoints. */
50
+ idempotencyKey?: string;
51
+ /** Extra headers merged after the SDK's defaults — caller wins. */
52
+ headers?: Record<string, string>;
53
+ /** Bypass the token store for one call (login, refresh). */
54
+ skipAuth?: boolean;
55
+ /** AbortSignal forwarded to fetch. */
56
+ signal?: AbortSignal;
57
+ }
58
+ declare class HttpClient {
59
+ private readonly baseUrl;
60
+ private readonly tokens;
61
+ private readonly fetchImpl;
62
+ constructor(baseUrl: string, tokens: TokenStore, fetchImpl: FetchLike);
63
+ get<T>(path: string, opts?: HttpOptions): Promise<T>;
64
+ post<T>(path: string, opts?: HttpOptions): Promise<T>;
65
+ put<T>(path: string, opts?: HttpOptions): Promise<T>;
66
+ patch<T>(path: string, opts?: HttpOptions): Promise<T>;
67
+ delete<T>(path: string, opts?: HttpOptions): Promise<T>;
68
+ private request;
69
+ private absolute;
70
+ }
71
+
72
+ export { type FetchLike as F, HttpClient as H, type TokenStore as T, defaultBrowserStore as d, inMemoryStore as i };