@invonetwork/web-sdk 0.7.0 → 1.0.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 CHANGED
@@ -1,179 +1,208 @@
1
- # Changelog
2
-
3
- All notable changes to `@invonetwork/web-sdk` are documented here. This project follows
4
- [Semantic Versioning](https://semver.org/). Releases are managed with
5
- [changesets](https://github.com/changesets/changesets).
6
-
7
- ## [0.7.0] — 2026-07-01
8
-
9
- Start of the **discovery layer** for send/transfer UIs.
10
-
11
- - **`InvoClient.getDestinations({ direction })`**one player-token call
12
- (`GET /api/sdk/destinations`) returns every game the player can send/transfer to, with
13
- all display metadata **inline** (game name, icon, currency name/symbol, min/max transfer
14
- limits) — no per-game lookup. Includes source game/currency info and `transferMode`
15
- (`universal`/`linked` + `linkedGameIds`).
16
- - _(Coming in 0.7.x: `getBalance` single-game player-token read; `getLinkedIdentities`
17
- server-side.)_
18
-
19
- ## [0.6.0] 2026-07-01
20
-
21
- Three additive browser flows the dashboard needed, built to the live backend contracts.
22
-
23
- - **`InvoClient.getPendingCollect()`** the player's own pending-to-collect list
24
- (player-token `GET /api/sdk/transfers/pending`), PII-free. Each row's `kind`
25
- (`identity_gate` `approve*`, `receiving_confirm` `confirmReceipt*`) tells you the
26
- action; typed fields incl. `held` / `holdReason` / `stepUpRequired`.
27
- - **First-enrollment OTP grant** — `enrollmentBegin()` / `enrollmentVerify(code)` for
28
- `ENROLLMENT_REQUIRES_AUTHORIZATION` (send OTP to phone+email, verify, then retry the
29
- same `enrollPasskey()` the server grant is auto-consumed). New `InvoError` helpers
30
- `isEnrollmentAuthorizationRequired` / `isEnrollmentProofRequired`.
31
- - **Typed approve/confirm holds** `approve*`/`confirmReceipt*` now surface a typed
32
- `holdReason` (from `error_code ?? code`) on HTTP 202 holds, plus `risk` /
33
- `guardianApproval` / `pollEndpoint` on approve. `RECIPIENT_IDENTITY_PENDING` correctly
34
- surfaces on confirm-receipt. Success carries no `holdReason`.
35
-
36
- ## [0.5.0] — 2026-07-01
37
-
38
- Support partner `metadata` end-to-end on the purchase flow (additive, backward-compatible).
39
-
40
- - **`PurchaseCompletedData.metadata?`** the `purchase.completed` webhook now echoes your
41
- metadata back (all rails); `event.data.metadata` is typed.
42
- - **`purchaseCurrency`** now accepts and forwards `metadata` (the direct `/purchase-currency`
43
- endpoint accepts it too, matching `createCheckout`).
44
-
45
- ## [0.4.2] 2026-06-30
46
-
47
- Fixes from an independent line-by-line audit against the live backend.
48
-
49
- - **`linkDevice` now works.** `device/link/webauthn/begin` returns a wrapped
50
- `{ link_id, options }` body (challenge nested under `options`) and binds the
51
- challenge to a server-generated `link_id`. The client now unwraps `options` and
52
- echoes the server's `link_id` to `/complete` (it previously threw on an undefined
53
- challenge and sent the wrong id).
54
- - **`InvoError.code` is now populated for the direct purchase rail + guardian flows.**
55
- `errorFromResponse` reads `error_code` (SecureErrorHandler + `/purchase-currency`)
56
- in addition to `code`, and promotes known no-code tokens (`flow_paused`,
57
- `spending_limit_exceeded`, `receiver_not_enrolled_use_claim_code`) to `.code`. Fixes
58
- `isDuplicateRequest` on the direct rail.
59
- - **Guardian/minor routing.** The 202 guardian body also carries
60
- `verification_method:"sms"`; the SDK now suppresses it (reports
61
- `verificationMethod: undefined`) so a guardian-pending minor isn't routed into the
62
- SMS-PIN UI. README example reordered to branch on `guardianApproval` first.
63
-
64
- ## [0.4.1] — 2026-06-30
65
-
66
- Docs only — replaced the README's internal-leaking "Deployment prerequisites"
67
- (backend flag names, DB columns, gating mechanics) with a partner-facing
68
- "Before you go live"; republished so the npm page README is current.
69
-
70
- ## [0.4.0] 2026-06-30
71
-
72
- Additive release more server reads, edge-ready webhooks, cancellation, and tooling.
73
-
74
- - **`getInboundPending({ playerEmail | playerPhone })`** live, unclaimed inbound
75
- sends/transfers for a player (the source of truth behind the "you have X to collect"
76
- badge; pairs with `transfer.claim_pending`).
77
- - **`verifyWebhookAsync`** — Web Crypto variant of `verifyWebhook` that runs on
78
- Cloudflare Workers / Deno / Vercel+Netlify Edge / Bun / browsers; and
79
- **`createWebhookHandler`** a zero-dep Fetch-API `(Request) => Promise<Response>`
80
- webhook route handler (Next.js App Router, Workers, Deno, Hono, Bun).
81
- - **`iterateItemPurchaseHistory`** async iterator that pages through a player's
82
- full item-purchase history.
83
- - **Per-call `AbortSignal`** every method accepts an optional `{ signal }`; an
84
- aborted call throws `InvoError` code `ABORTED` and is never retried.
85
- - **Tooling**: ESLint (+ lint in CI), changesets release automation, `SECURITY.md`,
86
- and `CODEOWNERS`.
87
-
88
- ## [0.3.0] 2026-06-30
89
-
90
- Additive release new server capabilities plus transport resilience/observability.
91
-
92
- - **Webhook verification** (`/server`): `verifyWebhook(rawBody, signatureHeader, secret | secrets, opts?)`
93
- constant-time HMAC-SHA256 over `${t}.${rawBody}`, 5-minute replay window,
94
- multi-secret rotation; returns a typed `InvoWebhookEvent` discriminated union
95
- (`purchase.*`, `item.purchased`, `transfer.*`, `payout.status_changed`, `webhook.test`).
96
- Throws `InvoError` (`WEBHOOK_SIGNATURE_INVALID` / `WEBHOOK_TIMESTAMP_EXPIRED` /
97
- `WEBHOOK_MALFORMED` / `WEBHOOK_SECRET_MISSING`). Server-only; the browser bundle
98
- stays crypto-free. Independently security-audited.
99
- - **`getPlayerBalance({ playerEmail | playerId })`** (`/server`): typed `player` / `balances` / `summary`.
100
- - **Automatic retries**: network errors/timeouts, `429` (honoring `retry_after`), and
101
- `5xx` are retried with exponential backoff + jitter. New config `maxRetries`
102
- (default 2, `0` disables) and `retryBaseDelayMs` (default 250).
103
- - **Observability hooks**: optional `onRequest` / `onResponse` / `onError` on both
104
- entries (best-effort/non-throwing); `InvoError.requestId` carries the backend
105
- request id for support/tracing.
106
- - **Typed reads**: `confirmPayment` `ConfirmPaymentResult`; `getOrderDetails` /
107
- `getItemOrderDetails` `OrderDetailsResult`; `getItemPurchaseHistory`
108
- `ItemHistoryResult` (previously untyped `Record`). All keep `raw`.
109
- - **Light validation**: `mintPlayerToken` and `createCheckout` require a non-blank
110
- `playerEmail` (throws `INVALID_INPUT` before the network call).
111
- - **License**: `package.json` `license` is now `SEE LICENSE IN LICENSE` (was
112
- `UNLICENSED`); `LICENSE` rewritten as an explicit install-and-use grant for
113
- building INVO integrations.
114
-
115
- ## [0.2.1] — 2026-06-30
116
-
117
- Docs onlyno code change (republished so the npm page README is current).
118
-
119
- - Rewrote the README into a complete integration/deployment guide: capability
120
- overview, architecture, deployment prerequisites, per-flow sections (currency
121
- purchase, item purchase, sends, transfers, passkeys), webhooks, errors, and a
122
- full API reference for both entries.
123
- - Added INVO console onboarding: `https://console.invo.network` (production) and
124
- `https://dev.console.invo.network` (testing/sandbox), mapped to their API base URLs.
125
-
126
- ## [0.2.0] 2026-06-30
127
-
128
- Adds **item purchase** (spend existing game currency on an in-game item, §4.8) — an
129
- additive, server-only surface.
130
-
131
- - `InvoServer` (`/server`): `purchaseItem`, `getItemPurchaseHistory`, `getItemOrderDetails`.
132
- - Server-side, game-secret auth no passkey, no real money, no payment rail (it's a
133
- balance debit). Grant the item off the `item.purchased` webhook.
134
- - Client-side guards: required fields (trim-checked), `itemQuantity` integer `1..1000`,
135
- prices `> 0` and `<= 999999.99` (magnitude-safe 2-decimal check), and
136
- `totalPrice == unitPrice × itemQuantity (±0.01)` compared in integer cents.
137
- - Load-bearing response fields (`transaction_id`, `order_id`) throw `INVALID_RESPONSE`
138
- if missing on a 200.
139
- - `InvoError` helpers: `isInsufficientBalance` (gated to 400; not the `429` throttle),
140
- `isDuplicateRequest` (409), `retryAfter` (numeric or string `retry_after`); all
141
- null-safe against non-JSON error bodies.
142
- - Independently audited (2 agents) against handoff doc §4.8/§6/§8; contract verified,
143
- error-classification edge cases fixed.
144
-
145
- ## [0.1.0] — 2026-06-30
146
-
147
- Initial scaffold.
148
-
149
- - `InvoServer` (`/server`): `mintPlayerToken`, `initiateSend`, `initiateTransfer`,
150
- `createCheckout`, `purchaseCurrency`, `confirmPayment`, `getOrderDetails`.
151
- - Client-side purchase guards (§4.7): USD amount `0 < x ≤ 999.99`, required
152
- `purchaseReference`, and `rail:"steam"` rejected before the network call
153
- (`INVALID_INPUT` / `MISSING_PURCHASE_REFERENCE` / `WRONG_RAIL_ENDPOINT`).
154
- - `InvoClient` (browser): `enrollPasskey`, `approveSend`/`approveTransfer`,
155
- `confirmReceiptSend`/`confirmReceiptTransfer`, and `linkDevice` for the
156
- interchangeable-methods flow (§4.6).
157
- - Optional `refreshToken` hook: transparently re-mints and retries once on
158
- `SDK_TOKEN_EXPIRED` (§11.2).
159
- - Shared: typed `InvoError`, isomorphic HTTP client, WebAuthn JSON⇄binary helpers.
160
- - Tests: HTTP layer, server request mapping + purchase guards, browser client
161
- flows (enroll/approve/link/token-refresh), and WebAuthn serialization.
162
- - Contracts extracted + auditor-verified against the INVO backend.
163
-
164
- ### Hardening (independent red-team pass)
165
-
166
- - **Guardian/minor `202` path no longer mismapped to `verificationMethod:"sms"`** —
167
- `initiateSend`/`initiateTransfer` now return `verificationMethod: undefined` and a
168
- `guardianApproval` block on the guardian path, so callers don't route into the
169
- PIN UI by mistake (§4.3).
170
- - `usdAmount` validation tightened: rejects non-plain-decimal strings
171
- (`"0x10"`, `"1e2"`, whitespace) and >2 decimal places before any network call.
172
- - Load-bearing response fields (`token`, `checkout_url`) now throw `INVALID_RESPONSE`
173
- instead of silently surfacing as empty strings.
174
- - `getOrderDetails` requires at least one of `orderId`/`transactionId`.
175
- - Token refresh now re-runs the **whole** passkey ceremony on `SDK_TOKEN_EXPIRED`
176
- (never replays a single-use assertion) and single-flights concurrent refreshes.
177
- - `baseUrl` must be `https://` (localhost exempt) so the token/secret can't travel
178
- in cleartext.
179
- - Published tarball excludes sourcemaps (no proprietary source shipped).
1
+ # Changelog
2
+
3
+ All notable changes to `@invonetwork/web-sdk` are documented here. This project follows
4
+ [Semantic Versioning](https://semver.org/). Releases are managed with
5
+ [changesets](https://github.com/changesets/changesets).
6
+
7
+ ## [1.0.0] — 2026-07-01
8
+
9
+ **1.0 stable.** The public API is now covered by a stability commitment: it follows
10
+ semver and no breaking change ships without a major bump + migration note. This is a
11
+ milestone release, **not a breaking change** code on `0.8.x` continues to work unchanged.
12
+
13
+ Also in this release (docs + DX):
14
+
15
+ - **Integration modes** documented — **browser-direct** (default) and **behind your proxy**
16
+ (keep the INVO player token server-side via a custom `baseUrl`/`fetch`; no CORS or
17
+ security-posture change). Plus a token-bootstrap example and a CORS + RP-ID/origins
18
+ go-live checklist.
19
+ - `linkDevice` called out as the lowest-risk first adoption (additive, no refactor).
20
+
21
+ ## [0.8.0] 2026-07-01
22
+
23
+ Completes the **discovery layer** for send/transfer UIs.
24
+
25
+ - **`InvoClient.getBalance()`** (browser, player-token, `GET /api/sdk/balance`) the
26
+ player's balances for the token's game: `balances[]` (`currencyId`, `currencyName`,
27
+ `currencySymbol`/`currencySymbolUrl` (nullable), `availableBalance`/`reservedBalance`/
28
+ `totalBalance` as decimal strings) plus `totalValue` / `currencyCount` / `hasFunds`. A
29
+ player who hasn't transacted here yet returns an empty `balances` array (a `200`, not an
30
+ error); `404 GAME_NOT_FOUND` throws.
31
+ - **`InvoServer.getLinkedIdentities({ playerEmail | playerPhone })`** (server-only,
32
+ game-secret) a player's linked wallet identities (game-scoped). Provide one identifier
33
+ (phone wins if both); no in-game match returns `notFound: true` with empty `emails`
34
+ instead of throwing. **Returns first-party PII — never expose to the browser.**
35
+
36
+ ## [0.7.0] — 2026-07-01
37
+
38
+ Start of the **discovery layer** for send/transfer UIs.
39
+
40
+ - **`InvoClient.getDestinations({ direction })`** one player-token call
41
+ (`GET /api/sdk/destinations`) returns every game the player can send/transfer to, with
42
+ all display metadata **inline** (game name, icon, currency name/symbol, min/max transfer
43
+ limits) no per-game lookup. Includes source game/currency info and `transferMode`
44
+ (`universal`/`linked` + `linkedGameIds`).
45
+ - _(Coming in 0.7.x: `getBalance` single-game player-token read; `getLinkedIdentities`
46
+ server-side.)_
47
+
48
+ ## [0.6.0] — 2026-07-01
49
+
50
+ Three additive browser flows the dashboard needed, built to the live backend contracts.
51
+
52
+ - **`InvoClient.getPendingCollect()`** — the player's own pending-to-collect list
53
+ (player-token `GET /api/sdk/transfers/pending`), PII-free. Each row's `kind`
54
+ (`identity_gate` `approve*`, `receiving_confirm` `confirmReceipt*`) tells you the
55
+ action; typed fields incl. `held` / `holdReason` / `stepUpRequired`.
56
+ - **First-enrollment OTP grant** `enrollmentBegin()` / `enrollmentVerify(code)` for
57
+ `ENROLLMENT_REQUIRES_AUTHORIZATION` (send OTP to phone+email, verify, then retry the
58
+ same `enrollPasskey()` the server grant is auto-consumed). New `InvoError` helpers
59
+ `isEnrollmentAuthorizationRequired` / `isEnrollmentProofRequired`.
60
+ - **Typed approve/confirm holds** — `approve*`/`confirmReceipt*` now surface a typed
61
+ `holdReason` (from `error_code ?? code`) on HTTP 202 holds, plus `risk` /
62
+ `guardianApproval` / `pollEndpoint` on approve. `RECIPIENT_IDENTITY_PENDING` correctly
63
+ surfaces on confirm-receipt. Success carries no `holdReason`.
64
+
65
+ ## [0.5.0] — 2026-07-01
66
+
67
+ Support partner `metadata` end-to-end on the purchase flow (additive, backward-compatible).
68
+
69
+ - **`PurchaseCompletedData.metadata?`** — the `purchase.completed` webhook now echoes your
70
+ metadata back (all rails); `event.data.metadata` is typed.
71
+ - **`purchaseCurrency`** now accepts and forwards `metadata` (the direct `/purchase-currency`
72
+ endpoint accepts it too, matching `createCheckout`).
73
+
74
+ ## [0.4.2]2026-06-30
75
+
76
+ Fixes from an independent line-by-line audit against the live backend.
77
+
78
+ - **`linkDevice` now works.** `device/link/webauthn/begin` returns a wrapped
79
+ `{ link_id, options }` body (challenge nested under `options`) and binds the
80
+ challenge to a server-generated `link_id`. The client now unwraps `options` and
81
+ echoes the server's `link_id` to `/complete` (it previously threw on an undefined
82
+ challenge and sent the wrong id).
83
+ - **`InvoError.code` is now populated for the direct purchase rail + guardian flows.**
84
+ `errorFromResponse` reads `error_code` (SecureErrorHandler + `/purchase-currency`)
85
+ in addition to `code`, and promotes known no-code tokens (`flow_paused`,
86
+ `spending_limit_exceeded`, `receiver_not_enrolled_use_claim_code`) to `.code`. Fixes
87
+ `isDuplicateRequest` on the direct rail.
88
+ - **Guardian/minor routing.** The 202 guardian body also carries
89
+ `verification_method:"sms"`; the SDK now suppresses it (reports
90
+ `verificationMethod: undefined`) so a guardian-pending minor isn't routed into the
91
+ SMS-PIN UI. README example reordered to branch on `guardianApproval` first.
92
+
93
+ ## [0.4.1] 2026-06-30
94
+
95
+ Docs only replaced the README's internal-leaking "Deployment prerequisites"
96
+ (backend flag names, DB columns, gating mechanics) with a partner-facing
97
+ "Before you go live"; republished so the npm page README is current.
98
+
99
+ ## [0.4.0] 2026-06-30
100
+
101
+ Additive release more server reads, edge-ready webhooks, cancellation, and tooling.
102
+
103
+ - **`getInboundPending({ playerEmail | playerPhone })`** live, unclaimed inbound
104
+ sends/transfers for a player (the source of truth behind the "you have X to collect"
105
+ badge; pairs with `transfer.claim_pending`).
106
+ - **`verifyWebhookAsync`** Web Crypto variant of `verifyWebhook` that runs on
107
+ Cloudflare Workers / Deno / Vercel+Netlify Edge / Bun / browsers; and
108
+ **`createWebhookHandler`** a zero-dep Fetch-API `(Request) => Promise<Response>`
109
+ webhook route handler (Next.js App Router, Workers, Deno, Hono, Bun).
110
+ - **`iterateItemPurchaseHistory`** async iterator that pages through a player's
111
+ full item-purchase history.
112
+ - **Per-call `AbortSignal`** every method accepts an optional `{ signal }`; an
113
+ aborted call throws `InvoError` code `ABORTED` and is never retried.
114
+ - **Tooling**: ESLint (+ lint in CI), changesets release automation, `SECURITY.md`,
115
+ and `CODEOWNERS`.
116
+
117
+ ## [0.3.0]2026-06-30
118
+
119
+ Additive release new server capabilities plus transport resilience/observability.
120
+
121
+ - **Webhook verification** (`/server`): `verifyWebhook(rawBody, signatureHeader, secret | secrets, opts?)`
122
+ constant-time HMAC-SHA256 over `${t}.${rawBody}`, 5-minute replay window,
123
+ multi-secret rotation; returns a typed `InvoWebhookEvent` discriminated union
124
+ (`purchase.*`, `item.purchased`, `transfer.*`, `payout.status_changed`, `webhook.test`).
125
+ Throws `InvoError` (`WEBHOOK_SIGNATURE_INVALID` / `WEBHOOK_TIMESTAMP_EXPIRED` /
126
+ `WEBHOOK_MALFORMED` / `WEBHOOK_SECRET_MISSING`). Server-only; the browser bundle
127
+ stays crypto-free. Independently security-audited.
128
+ - **`getPlayerBalance({ playerEmail | playerId })`** (`/server`): typed `player` / `balances` / `summary`.
129
+ - **Automatic retries**: network errors/timeouts, `429` (honoring `retry_after`), and
130
+ `5xx` are retried with exponential backoff + jitter. New config `maxRetries`
131
+ (default 2, `0` disables) and `retryBaseDelayMs` (default 250).
132
+ - **Observability hooks**: optional `onRequest` / `onResponse` / `onError` on both
133
+ entries (best-effort/non-throwing); `InvoError.requestId` carries the backend
134
+ request id for support/tracing.
135
+ - **Typed reads**: `confirmPayment` `ConfirmPaymentResult`; `getOrderDetails` /
136
+ `getItemOrderDetails` `OrderDetailsResult`; `getItemPurchaseHistory`
137
+ `ItemHistoryResult` (previously untyped `Record`). All keep `raw`.
138
+ - **Light validation**: `mintPlayerToken` and `createCheckout` require a non-blank
139
+ `playerEmail` (throws `INVALID_INPUT` before the network call).
140
+ - **License**: `package.json` `license` is now `SEE LICENSE IN LICENSE` (was
141
+ `UNLICENSED`); `LICENSE` rewritten as an explicit install-and-use grant for
142
+ building INVO integrations.
143
+
144
+ ## [0.2.1] — 2026-06-30
145
+
146
+ Docs only — no code change (republished so the npm page README is current).
147
+
148
+ - Rewrote the README into a complete integration/deployment guide: capability
149
+ overview, architecture, deployment prerequisites, per-flow sections (currency
150
+ purchase, item purchase, sends, transfers, passkeys), webhooks, errors, and a
151
+ full API reference for both entries.
152
+ - Added INVO console onboarding: `https://console.invo.network` (production) and
153
+ `https://dev.console.invo.network` (testing/sandbox), mapped to their API base URLs.
154
+
155
+ ## [0.2.0] 2026-06-30
156
+
157
+ Adds **item purchase** (spend existing game currency on an in-game item, §4.8) an
158
+ additive, server-only surface.
159
+
160
+ - `InvoServer` (`/server`): `purchaseItem`, `getItemPurchaseHistory`, `getItemOrderDetails`.
161
+ - Server-side, game-secret auth — no passkey, no real money, no payment rail (it's a
162
+ balance debit). Grant the item off the `item.purchased` webhook.
163
+ - Client-side guards: required fields (trim-checked), `itemQuantity` integer `1..1000`,
164
+ prices `> 0` and `<= 999999.99` (magnitude-safe 2-decimal check), and
165
+ `totalPrice == unitPrice × itemQuantity (±0.01)` compared in integer cents.
166
+ - Load-bearing response fields (`transaction_id`, `order_id`) throw `INVALID_RESPONSE`
167
+ if missing on a 200.
168
+ - `InvoError` helpers: `isInsufficientBalance` (gated to 400; not the `429` throttle),
169
+ `isDuplicateRequest` (409), `retryAfter` (numeric or string `retry_after`); all
170
+ null-safe against non-JSON error bodies.
171
+ - Independently audited (2 agents) against handoff doc §4.8/§6/§8; contract verified,
172
+ error-classification edge cases fixed.
173
+
174
+ ## [0.1.0] 2026-06-30
175
+
176
+ Initial scaffold.
177
+
178
+ - `InvoServer` (`/server`): `mintPlayerToken`, `initiateSend`, `initiateTransfer`,
179
+ `createCheckout`, `purchaseCurrency`, `confirmPayment`, `getOrderDetails`.
180
+ - Client-side purchase guards (§4.7): USD amount `0 < x ≤ 999.99`, required
181
+ `purchaseReference`, and `rail:"steam"` rejected before the network call
182
+ (`INVALID_INPUT` / `MISSING_PURCHASE_REFERENCE` / `WRONG_RAIL_ENDPOINT`).
183
+ - `InvoClient` (browser): `enrollPasskey`, `approveSend`/`approveTransfer`,
184
+ `confirmReceiptSend`/`confirmReceiptTransfer`, and `linkDevice` for the
185
+ interchangeable-methods flow (§4.6).
186
+ - Optional `refreshToken` hook: transparently re-mints and retries once on
187
+ `SDK_TOKEN_EXPIRED` (§11.2).
188
+ - Shared: typed `InvoError`, isomorphic HTTP client, WebAuthn JSON⇄binary helpers.
189
+ - Tests: HTTP layer, server request mapping + purchase guards, browser client
190
+ flows (enroll/approve/link/token-refresh), and WebAuthn serialization.
191
+ - Contracts extracted + auditor-verified against the INVO backend.
192
+
193
+ ### Hardening (independent red-team pass)
194
+
195
+ - **Guardian/minor `202` path no longer mismapped to `verificationMethod:"sms"`** —
196
+ `initiateSend`/`initiateTransfer` now return `verificationMethod: undefined` and a
197
+ `guardianApproval` block on the guardian path, so callers don't route into the
198
+ PIN UI by mistake (§4.3).
199
+ - `usdAmount` validation tightened: rejects non-plain-decimal strings
200
+ (`"0x10"`, `"1e2"`, whitespace) and >2 decimal places before any network call.
201
+ - Load-bearing response fields (`token`, `checkout_url`) now throw `INVALID_RESPONSE`
202
+ instead of silently surfacing as empty strings.
203
+ - `getOrderDetails` requires at least one of `orderId`/`transactionId`.
204
+ - Token refresh now re-runs the **whole** passkey ceremony on `SDK_TOKEN_EXPIRED`
205
+ (never replays a single-use assertion) and single-flights concurrent refreshes.
206
+ - `baseUrl` must be `https://` (localhost exempt) so the token/secret can't travel
207
+ in cleartext.
208
+ - Published tarball excludes sourcemaps (no proprietary source shipped).
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  First-party TypeScript SDK for integrating **INVO** into partner **web** platforms (storefronts, web games, dashboards). It wraps INVO's web money flows behind a typed, versioned API — the web analog of the Unity/Unreal plugins.
4
4
 
5
- > **Status:** `v0.7.0`, published on npm. The backend it wraps is **live** on sandbox + production, so you can build and test against sandbox today.
5
+ > **Status:** `v1.0.0` — stable, published on npm. The backend it wraps is **live** on sandbox + production. The API is now covered by a [stability commitment](#stability): no breaking changes without a major bump.
6
6
  > Canonical partner reference: **https://docs.invo.network/docs/currency-purchase** and **https://docs.invo.network/docs/game-developer-integration**.
7
7
 
8
8
  ## What it does
@@ -78,12 +78,54 @@ Build and test against the **dev console + sandbox** first, then switch to the *
78
78
 
79
79
  **Never import `/server` into browser code** — it carries the game secret. The two entries are built separately for exactly this reason.
80
80
 
81
+ ## Integration modes
82
+
83
+ `InvoClient` needs a `token` and a `baseUrl` — which lets you adopt it **two ways**. Same SDK, same code in your components; only the config differs.
84
+
85
+ ### Token bootstrap (both modes)
86
+
87
+ Expose one small backend endpoint that calls `mintPlayerToken` (server-side, with the game secret) and hand the token to the browser:
88
+
89
+ ```ts
90
+ // browser
91
+ const client = new InvoClient({
92
+ baseUrl: "https://api.invo.network",
93
+ token: await fetch("/your/sdk-token").then((r) => r.json()).then((j) => j.token),
94
+ // Called automatically on SDK_TOKEN_EXPIRED (~15 min TTL) — just re-fetch from your backend:
95
+ refreshToken: () => fetch("/your/sdk-token").then((r) => r.json()).then((j) => j.token),
96
+ });
97
+ ```
98
+
99
+ ### Mode A — browser-direct (default)
100
+
101
+ The browser holds the short-lived, game-scoped player token and calls INVO directly (`baseUrl` = the INVO API). Fewer moving parts. Requires INVO to **CORS-allow your web origin** and to have your **RP ID / origins** set up for passkeys (see [Before you go live](#before-you-go-live)).
102
+
103
+ ### Mode B — behind your proxy (keep the token server-side)
104
+
105
+ If your architecture requires the INVO player token to **never reach the browser**, route the SDK through your own backend — **no CORS setup needed, and no security-posture change**:
106
+
107
+ - Set `baseUrl` to your proxy (e.g. `https://yourgame.com/invo`), which forwards to the INVO API and **injects the real `Authorization` header server-side**.
108
+ - Give the browser a short-lived **session/CSRF token** (not the INVO token) as `token`; your proxy validates it and swaps in the real player token. Or use cookie auth with a custom `fetch`:
109
+
110
+ ```ts
111
+ const client = new InvoClient({
112
+ baseUrl: "https://yourgame.com/invo", // your proxy → INVO
113
+ token: sessionToken, // your token; the proxy swaps in the real one
114
+ fetch: (url, init) => fetch(url, { ...init, credentials: "include" }), // send your cookie
115
+ });
116
+ ```
117
+
118
+ The WebAuthn ceremony still runs in the browser (it must — it's `navigator.credentials`), but transport/auth stays behind your proxy. You keep the SDK's ceremony handling, token-refresh, typed holds, and error classifiers either way.
119
+
120
+ > **Lowest-risk first step:** adopt just `linkDevice` (passkey ↔ app device interchange) — it's purely additive and needs no refactor. See [Passkeys](#passkeys-enroll-approve-link).
121
+
81
122
  ## Before you go live
82
123
 
83
124
  INVO enables each flow for your tenant in the [console](#get-your-account--game-secret-invo-console). What you need to do:
84
125
 
85
126
  - **Store the game secret server-side** and expose a small endpoint that calls `mintPlayerToken` so the browser can fetch/refresh its token. Never ship the secret to the browser.
86
- - **For passkeys (sends/transfers):** give INVO the **web origin(s)** you'll serve from. Passkeys only validate on approved origins — if a call returns `WEBAUTHN_NOT_ENABLED_FOR_TENANT`, your origins aren't set up yet; contact INVO. (Until enrolled, the sender falls back to the SMS-PIN path automatically.)
127
+ - **If you use browser-direct ([Mode A](#mode-a--browser-direct-default)):** ask INVO to **CORS-allow your web origin(s)** so the browser can call the API directly. (Not needed for [Mode B / proxy](#mode-b--behind-your-proxy-keep-the-token-server-side) those requests are same-origin to your backend.)
128
+ - **For passkeys (sends/transfers):** give INVO the **RP ID + web origin(s)** you'll serve from. Passkeys only validate on approved origins — if a call returns `WEBAUTHN_NOT_ENABLED_FOR_TENANT`, your origins aren't set up yet; contact INVO. (Until enrolled, the sender falls back to the SMS-PIN path automatically.)
87
129
  - **For currency purchase:** card checkout works out of the box; ask INVO to enable the `game`/`steam` rails if you need them.
88
130
  - **For item purchase:** nothing extra — it's a currency-balance debit.
89
131
 
@@ -258,6 +300,13 @@ const { availableGames, sourceCurrencyName } = await client.getDestinations({ di
258
300
  // each: { gameId, gameName, gameIcon, currencyName, currencySymbol, minimumTransfer, maximumTransfer, … }
259
301
  ```
260
302
 
303
+ **What do I have? (browser, v0.8.0+)** — `client.getBalance()` returns the player's balances for this game (amounts are decimal strings; rows carry `currencySymbol`/`currencySymbolUrl`). A player who hasn't transacted here yet returns an empty `balances` array (show 0.00) — not an error:
304
+
305
+ ```ts
306
+ const { balances, hasFunds, totalValue } = await client.getBalance();
307
+ // each: { currencyId, currencyName, currencySymbol, availableBalance, reservedBalance, totalBalance }
308
+ ```
309
+
261
310
  ```ts
262
311
  // 1. SERVER — initiate, then branch on how the sender must verify
263
312
  const t = await server.initiateTransfer({
@@ -480,6 +529,7 @@ try {
480
529
  | `getItemOrderDetails({ orderId? \| transactionId? \| clientRequestId? })` | `{ order, financialSummary, statusTimeline, raw }` |
481
530
  | `getPlayerBalance({ playerEmail? \| playerId? })` | `{ player, balances, summary, raw }` |
482
531
  | `getInboundPending({ playerEmail? \| playerPhone? })` | `{ inboundPending, raw }` — live unclaimed inbound sends/transfers |
532
+ | `getLinkedIdentities({ playerEmail? \| playerPhone? })` | `{ walletUserId, primaryEmail, primaryPhone, isMinor, emails, notFound, raw }` — **server-only** (returns PII) |
483
533
  | `iterateItemPurchaseHistory({ playerEmail, pageSize? })` | async iterator over all history rows |
484
534
  | `verifyWebhook(rawBody, signatureHeader, secret \| secrets, opts?)` | typed `InvoWebhookEvent` (throws on bad signature) |
485
535
  | `verifyWebhookAsync(...)` | same as `verifyWebhook`, Web Crypto (edge/Workers/Deno/Bun) |
@@ -498,6 +548,7 @@ Every method also accepts an optional final `{ signal }` (`AbortSignal`) for can
498
548
  | `linkDevice(linkId)` | `{ status, raw }` |
499
549
  | `getPendingCollect()` | `{ pending, raw }` — the player's own pending-to-collect list (player-token) |
500
550
  | `getDestinations({ direction? })` | `{ availableGames, sourceGameName, transferMode, … }` — where the player can send/transfer, metadata inline |
551
+ | `getBalance()` | `{ gameId, gameName, balances, totalValue, currencyCount, hasFunds, raw }` — the player's balances for this game |
501
552
 
502
553
  Every method throws `InvoError` on failure and takes an optional final `{ signal }`. Full inline types ship with the package.
503
554
 
@@ -511,7 +562,11 @@ npm run typecheck # tsc --noEmit
511
562
  npm test # vitest
512
563
  ```
513
564
 
514
- The package follows **semver**: patch = fixes, minor = additive surface, major = breaking changes (rare, with a migration note). The server contract is backward-compatible within a major, so an old pinned SDK keeps working. Pin a version and subscribe to release notes for security updates. Full history: [CHANGELOG](https://github.com/Invo-Technologies/invo-web-sdk/blob/main/CHANGELOG.md) · [release notes](https://github.com/Invo-Technologies/invo-web-sdk/blob/main/docs/RELEASES.md) (absolute links to `main`).
565
+ ### Stability
566
+
567
+ As of **`1.0.0`** the public API is **stable**: it follows [semver](https://semver.org/), and **no breaking change ships without a major version bump + a migration note**. Patch = fixes, minor = additive surface (new methods/fields, backward-compatible), major = breaking changes (rare). The wire contract is the same live INVO API you'd call directly, and it's backward-compatible within a major — so a pinned SDK keeps working. Safe to depend on in production; pin a version and watch [releases](https://github.com/Invo-Technologies/invo-web-sdk/releases) for security updates.
568
+
569
+ Full history: [CHANGELOG](https://github.com/Invo-Technologies/invo-web-sdk/blob/main/CHANGELOG.md) · [release notes](https://github.com/Invo-Technologies/invo-web-sdk/blob/main/docs/RELEASES.md) (absolute links to `main`).
515
570
 
516
571
  ## License
517
572
 
package/dist/index.cjs CHANGED
@@ -355,6 +355,18 @@ function toPendingCollectItem(row) {
355
355
  raw: row
356
356
  };
357
357
  }
358
+ function toBalanceRow(row) {
359
+ return {
360
+ currencyId: row["currency_id"] ?? "",
361
+ currencyName: String(row["currency_name"] ?? ""),
362
+ currencySymbol: row["currency_symbol"] ?? null,
363
+ currencySymbolUrl: row["currency_symbol_url"] ?? null,
364
+ availableBalance: String(row["available_balance"] ?? ""),
365
+ reservedBalance: String(row["reserved_balance"] ?? ""),
366
+ totalBalance: String(row["total_balance"] ?? ""),
367
+ raw: row
368
+ };
369
+ }
358
370
  function toDestinationGame(row) {
359
371
  const s = (k) => row[k] === void 0 || row[k] === null ? void 0 : String(row[k]);
360
372
  return {
@@ -516,6 +528,28 @@ var InvoClient = class {
516
528
  };
517
529
  });
518
530
  }
531
+ /**
532
+ * The player's balances for the token's game (browser, player-token). Amounts are
533
+ * decimal strings. A player who hasn't transacted here yet returns `200` with an empty
534
+ * `balances` array (show 0.00) — not an error; a `404 GAME_NOT_FOUND` (bad game/token)
535
+ * throws. Rows include the currency symbol + icon URL (each may be null).
536
+ */
537
+ async getBalance(opts) {
538
+ return this.withTokenRetry(async () => {
539
+ const raw = await this.get("/api/sdk/balance", opts?.signal);
540
+ const rows = Array.isArray(raw["balances"]) ? raw["balances"] : [];
541
+ const summary = raw["summary"] ?? {};
542
+ return {
543
+ gameId: raw["game_id"] ?? "",
544
+ gameName: String(raw["game_name"] ?? ""),
545
+ balances: rows.map(toBalanceRow),
546
+ totalValue: String(summary["total_value"] ?? "0"),
547
+ currencyCount: Number(summary["currency_count"] ?? rows.length),
548
+ hasFunds: summary["has_funds"] === true,
549
+ raw
550
+ };
551
+ });
552
+ }
519
553
  /**
520
554
  * First-enrollment OTP grant (§4.2). When `enrollPasskey()` throws
521
555
  * `ENROLLMENT_REQUIRES_AUTHORIZATION` (`err.isEnrollmentAuthorizationRequired`), call
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { C as ClientConfig, a as CallOptions, A as ApproveResult, b as ConfirmReceiptResult, L as LinkDeviceResult, P as PendingCollectResult, D as DestinationsQuery, c as DestinationsResult, E as EnrollmentBeginResult, d as EnrollmentVerifyResult } from './types-Bi_NMSkN.cjs';
2
- export { e as DestinationGame, I as InvoError, f as InvoErrorInfo, g as InvoHooks, h as InvoRequestInfo, i as InvoResponseInfo, j as PendingCollectItem, R as Rail, V as VerificationMethod } from './types-Bi_NMSkN.cjs';
1
+ import { C as ClientConfig, a as CallOptions, A as ApproveResult, b as ConfirmReceiptResult, L as LinkDeviceResult, P as PendingCollectResult, D as DestinationsQuery, c as DestinationsResult, B as BalanceResult, E as EnrollmentBeginResult, d as EnrollmentVerifyResult } from './types-DLSCxpoT.cjs';
2
+ export { e as BalanceRow, f as DestinationGame, I as InvoError, g as InvoErrorInfo, h as InvoHooks, i as InvoRequestInfo, j as InvoResponseInfo, k as PendingCollectItem, R as Rail, V as VerificationMethod } from './types-DLSCxpoT.cjs';
3
3
 
4
4
  declare class InvoClient {
5
5
  private readonly http;
@@ -45,6 +45,13 @@ declare class InvoClient {
45
45
  * Source game is the token's own game. Player-token (browser).
46
46
  */
47
47
  getDestinations(query?: DestinationsQuery, opts?: CallOptions): Promise<DestinationsResult>;
48
+ /**
49
+ * The player's balances for the token's game (browser, player-token). Amounts are
50
+ * decimal strings. A player who hasn't transacted here yet returns `200` with an empty
51
+ * `balances` array (show 0.00) — not an error; a `404 GAME_NOT_FOUND` (bad game/token)
52
+ * throws. Rows include the currency symbol + icon URL (each may be null).
53
+ */
54
+ getBalance(opts?: CallOptions): Promise<BalanceResult>;
48
55
  /**
49
56
  * First-enrollment OTP grant (§4.2). When `enrollPasskey()` throws
50
57
  * `ENROLLMENT_REQUIRES_AUTHORIZATION` (`err.isEnrollmentAuthorizationRequired`), call
@@ -74,4 +81,4 @@ declare class InvoClient {
74
81
  private confirmReceipt;
75
82
  }
76
83
 
77
- export { ApproveResult, CallOptions, ClientConfig, ConfirmReceiptResult, DestinationsQuery, DestinationsResult, EnrollmentBeginResult, EnrollmentVerifyResult, InvoClient, LinkDeviceResult, PendingCollectResult };
84
+ export { ApproveResult, BalanceResult, CallOptions, ClientConfig, ConfirmReceiptResult, DestinationsQuery, DestinationsResult, EnrollmentBeginResult, EnrollmentVerifyResult, InvoClient, LinkDeviceResult, PendingCollectResult };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { C as ClientConfig, a as CallOptions, A as ApproveResult, b as ConfirmReceiptResult, L as LinkDeviceResult, P as PendingCollectResult, D as DestinationsQuery, c as DestinationsResult, E as EnrollmentBeginResult, d as EnrollmentVerifyResult } from './types-Bi_NMSkN.js';
2
- export { e as DestinationGame, I as InvoError, f as InvoErrorInfo, g as InvoHooks, h as InvoRequestInfo, i as InvoResponseInfo, j as PendingCollectItem, R as Rail, V as VerificationMethod } from './types-Bi_NMSkN.js';
1
+ import { C as ClientConfig, a as CallOptions, A as ApproveResult, b as ConfirmReceiptResult, L as LinkDeviceResult, P as PendingCollectResult, D as DestinationsQuery, c as DestinationsResult, B as BalanceResult, E as EnrollmentBeginResult, d as EnrollmentVerifyResult } from './types-DLSCxpoT.js';
2
+ export { e as BalanceRow, f as DestinationGame, I as InvoError, g as InvoErrorInfo, h as InvoHooks, i as InvoRequestInfo, j as InvoResponseInfo, k as PendingCollectItem, R as Rail, V as VerificationMethod } from './types-DLSCxpoT.js';
3
3
 
4
4
  declare class InvoClient {
5
5
  private readonly http;
@@ -45,6 +45,13 @@ declare class InvoClient {
45
45
  * Source game is the token's own game. Player-token (browser).
46
46
  */
47
47
  getDestinations(query?: DestinationsQuery, opts?: CallOptions): Promise<DestinationsResult>;
48
+ /**
49
+ * The player's balances for the token's game (browser, player-token). Amounts are
50
+ * decimal strings. A player who hasn't transacted here yet returns `200` with an empty
51
+ * `balances` array (show 0.00) — not an error; a `404 GAME_NOT_FOUND` (bad game/token)
52
+ * throws. Rows include the currency symbol + icon URL (each may be null).
53
+ */
54
+ getBalance(opts?: CallOptions): Promise<BalanceResult>;
48
55
  /**
49
56
  * First-enrollment OTP grant (§4.2). When `enrollPasskey()` throws
50
57
  * `ENROLLMENT_REQUIRES_AUTHORIZATION` (`err.isEnrollmentAuthorizationRequired`), call
@@ -74,4 +81,4 @@ declare class InvoClient {
74
81
  private confirmReceipt;
75
82
  }
76
83
 
77
- export { ApproveResult, CallOptions, ClientConfig, ConfirmReceiptResult, DestinationsQuery, DestinationsResult, EnrollmentBeginResult, EnrollmentVerifyResult, InvoClient, LinkDeviceResult, PendingCollectResult };
84
+ export { ApproveResult, BalanceResult, CallOptions, ClientConfig, ConfirmReceiptResult, DestinationsQuery, DestinationsResult, EnrollmentBeginResult, EnrollmentVerifyResult, InvoClient, LinkDeviceResult, PendingCollectResult };
package/dist/index.js CHANGED
@@ -98,6 +98,18 @@ function toPendingCollectItem(row) {
98
98
  raw: row
99
99
  };
100
100
  }
101
+ function toBalanceRow(row) {
102
+ return {
103
+ currencyId: row["currency_id"] ?? "",
104
+ currencyName: String(row["currency_name"] ?? ""),
105
+ currencySymbol: row["currency_symbol"] ?? null,
106
+ currencySymbolUrl: row["currency_symbol_url"] ?? null,
107
+ availableBalance: String(row["available_balance"] ?? ""),
108
+ reservedBalance: String(row["reserved_balance"] ?? ""),
109
+ totalBalance: String(row["total_balance"] ?? ""),
110
+ raw: row
111
+ };
112
+ }
101
113
  function toDestinationGame(row) {
102
114
  const s = (k) => row[k] === void 0 || row[k] === null ? void 0 : String(row[k]);
103
115
  return {
@@ -259,6 +271,28 @@ var InvoClient = class {
259
271
  };
260
272
  });
261
273
  }
274
+ /**
275
+ * The player's balances for the token's game (browser, player-token). Amounts are
276
+ * decimal strings. A player who hasn't transacted here yet returns `200` with an empty
277
+ * `balances` array (show 0.00) — not an error; a `404 GAME_NOT_FOUND` (bad game/token)
278
+ * throws. Rows include the currency symbol + icon URL (each may be null).
279
+ */
280
+ async getBalance(opts) {
281
+ return this.withTokenRetry(async () => {
282
+ const raw = await this.get("/api/sdk/balance", opts?.signal);
283
+ const rows = Array.isArray(raw["balances"]) ? raw["balances"] : [];
284
+ const summary = raw["summary"] ?? {};
285
+ return {
286
+ gameId: raw["game_id"] ?? "",
287
+ gameName: String(raw["game_name"] ?? ""),
288
+ balances: rows.map(toBalanceRow),
289
+ totalValue: String(summary["total_value"] ?? "0"),
290
+ currencyCount: Number(summary["currency_count"] ?? rows.length),
291
+ hasFunds: summary["has_funds"] === true,
292
+ raw
293
+ };
294
+ });
295
+ }
262
296
  /**
263
297
  * First-enrollment OTP grant (§4.2). When `enrollPasskey()` throws
264
298
  * `ENROLLMENT_REQUIRES_AUTHORIZATION` (`err.isEnrollmentAuthorizationRequired`), call
package/dist/server.cjs CHANGED
@@ -418,7 +418,7 @@ async function hmacHexSubtle(secret, message) {
418
418
  }
419
419
 
420
420
  // src/server.ts
421
- var DEFAULT_UA = "invonetwork-web-sdk/0.4.0 (+https://invo.network)";
421
+ var DEFAULT_UA = "invonetwork-web-sdk/1.0.0 (+https://invo.network)";
422
422
  var MAX_USD_AMOUNT = 999.99;
423
423
  var MAX_ITEM_PRICE = 999999.99;
424
424
  function invalidInput(label, value, why) {
@@ -458,6 +458,13 @@ function toOrderDetails(raw) {
458
458
  raw
459
459
  };
460
460
  }
461
+ function toLinkedEmail(row) {
462
+ return {
463
+ email: String(row["email"] ?? ""),
464
+ primary: row["primary"] === true,
465
+ verifiedAt: row["verified_at"] ?? null
466
+ };
467
+ }
461
468
  function toInboundPendingItem(row) {
462
469
  return {
463
470
  transactionId: String(row["transaction_id"] ?? ""),
@@ -877,6 +884,48 @@ var InvoServer = class {
877
884
  const rows = Array.isArray(raw["inbound_pending"]) ? raw["inbound_pending"] : [];
878
885
  return { inboundPending: rows.map(toInboundPendingItem), raw };
879
886
  }
887
+ /**
888
+ * Look up a player's linked wallet identities (SERVER-only, game-secret). Game-scoped:
889
+ * returns wallet members that also have a Player row on this game. Provide a `playerEmail`
890
+ * or `playerPhone` (phone wins if both). No in-game match (backend 404) returns
891
+ * `notFound: true` with empty `emails` — not an error.
892
+ *
893
+ * ⚠️ Returns first-party PII (emails/phones) — never expose this to the browser.
894
+ */
895
+ async getLinkedIdentities(query, opts) {
896
+ const phone = typeof query.playerPhone === "string" && query.playerPhone.trim() ? query.playerPhone.trim() : "";
897
+ const email = typeof query.playerEmail === "string" && query.playerEmail.trim() ? query.playerEmail.trim() : "";
898
+ if (!phone && !email) {
899
+ throw invalidInput("query", query, "requires a playerEmail or playerPhone");
900
+ }
901
+ const q = new URLSearchParams();
902
+ if (phone) q.set("player_phone", phone);
903
+ else q.set("player_email", email);
904
+ let raw;
905
+ try {
906
+ raw = await this.http.get(
907
+ `/api/wallet/identities?${q.toString()}`,
908
+ this.auth,
909
+ { signal: opts?.signal }
910
+ );
911
+ } catch (e) {
912
+ if (e instanceof InvoError && e.status === 404) {
913
+ const raw404 = e.body && typeof e.body === "object" ? e.body : {};
914
+ return { walletUserId: null, primaryEmail: "", primaryPhone: "", isMinor: false, emails: [], notFound: true, raw: raw404 };
915
+ }
916
+ throw e;
917
+ }
918
+ const emails = Array.isArray(raw["emails"]) ? raw["emails"].map(toLinkedEmail) : [];
919
+ return {
920
+ walletUserId: raw["wallet_user_id"] ?? null,
921
+ primaryEmail: String(raw["primary_email"] ?? ""),
922
+ primaryPhone: String(raw["primary_phone"] ?? ""),
923
+ isMinor: raw["is_minor"] === true,
924
+ emails,
925
+ notFound: false,
926
+ raw
927
+ };
928
+ }
880
929
  toInitiateResult(raw) {
881
930
  const vm = raw["verification_method"];
882
931
  const guardian = raw["guardian_approval"];
package/dist/server.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { I as InvoError, S as ServerConfig, a as CallOptions, k as PlayerToken, l as InitiateSendInput, m as InitiateResult, n as InitiateTransferInput, o as CreateCheckoutInput, p as CreateCheckoutResult, q as PurchaseInput, r as PurchaseResult, s as ConfirmPaymentResult, O as OrderDetailsResult, t as PurchaseItemInput, u as PurchaseItemResult, v as ItemHistoryQuery, w as ItemHistoryResult, x as ItemOrderQuery, y as PlayerBalanceQuery, z as PlayerBalanceResult, B as InboundPendingQuery, F as InboundPendingResult } from './types-Bi_NMSkN.cjs';
2
- export { G as CurrencyBalance, H as InboundPendingItem, f as InvoErrorInfo, g as InvoHooks, h as InvoRequestInfo, i as InvoResponseInfo, R as Rail, V as VerificationMethod } from './types-Bi_NMSkN.cjs';
1
+ import { I as InvoError, S as ServerConfig, a as CallOptions, l as PlayerToken, m as InitiateSendInput, n as InitiateResult, o as InitiateTransferInput, p as CreateCheckoutInput, q as CreateCheckoutResult, r as PurchaseInput, s as PurchaseResult, t as ConfirmPaymentResult, O as OrderDetailsResult, u as PurchaseItemInput, v as PurchaseItemResult, w as ItemHistoryQuery, x as ItemHistoryResult, y as ItemOrderQuery, z as PlayerBalanceQuery, F as PlayerBalanceResult, G as InboundPendingQuery, H as InboundPendingResult, J as LinkedIdentitiesQuery, K as LinkedIdentitiesResult } from './types-DLSCxpoT.cjs';
2
+ export { M as CurrencyBalance, N as InboundPendingItem, g as InvoErrorInfo, h as InvoHooks, i as InvoRequestInfo, j as InvoResponseInfo, Q as LinkedIdentityEmail, R as Rail, V as VerificationMethod } from './types-DLSCxpoT.cjs';
3
3
 
4
4
  interface VerifyWebhookOptions {
5
5
  /** Max age of the signed timestamp, in seconds. Default 300 (5 min). */
@@ -216,7 +216,16 @@ declare class InvoServer {
216
216
  * `transfer.claim_pending` webhook (the webhook is the wake-up; this is the list).
217
217
  */
218
218
  getInboundPending(query: InboundPendingQuery, opts?: CallOptions): Promise<InboundPendingResult>;
219
+ /**
220
+ * Look up a player's linked wallet identities (SERVER-only, game-secret). Game-scoped:
221
+ * returns wallet members that also have a Player row on this game. Provide a `playerEmail`
222
+ * or `playerPhone` (phone wins if both). No in-game match (backend 404) returns
223
+ * `notFound: true` with empty `emails` — not an error.
224
+ *
225
+ * ⚠️ Returns first-party PII (emails/phones) — never expose this to the browser.
226
+ */
227
+ getLinkedIdentities(query: LinkedIdentitiesQuery, opts?: CallOptions): Promise<LinkedIdentitiesResult>;
219
228
  private toInitiateResult;
220
229
  }
221
230
 
222
- export { CallOptions, ConfirmPaymentResult, CreateCheckoutInput, CreateCheckoutResult, InboundPendingQuery, InboundPendingResult, InitiateResult, InitiateSendInput, InitiateTransferInput, InvoError, InvoServer, type InvoWebhookEvent, ItemHistoryQuery, ItemHistoryResult, ItemOrderQuery, type ItemPurchasedData, OrderDetailsResult, PlayerBalanceQuery, PlayerBalanceResult, PlayerToken, type PurchaseCompletedData, type PurchaseEventData, PurchaseInput, PurchaseItemInput, PurchaseItemResult, PurchaseResult, ServerConfig, type TransferEventData, type VerifyWebhookOptions, type WebhookHandlerOptions, createWebhookHandler, verifyWebhook, verifyWebhookAsync };
231
+ export { CallOptions, ConfirmPaymentResult, CreateCheckoutInput, CreateCheckoutResult, InboundPendingQuery, InboundPendingResult, InitiateResult, InitiateSendInput, InitiateTransferInput, InvoError, InvoServer, type InvoWebhookEvent, ItemHistoryQuery, ItemHistoryResult, ItemOrderQuery, type ItemPurchasedData, LinkedIdentitiesQuery, LinkedIdentitiesResult, OrderDetailsResult, PlayerBalanceQuery, PlayerBalanceResult, PlayerToken, type PurchaseCompletedData, type PurchaseEventData, PurchaseInput, PurchaseItemInput, PurchaseItemResult, PurchaseResult, ServerConfig, type TransferEventData, type VerifyWebhookOptions, type WebhookHandlerOptions, createWebhookHandler, verifyWebhook, verifyWebhookAsync };
package/dist/server.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { I as InvoError, S as ServerConfig, a as CallOptions, k as PlayerToken, l as InitiateSendInput, m as InitiateResult, n as InitiateTransferInput, o as CreateCheckoutInput, p as CreateCheckoutResult, q as PurchaseInput, r as PurchaseResult, s as ConfirmPaymentResult, O as OrderDetailsResult, t as PurchaseItemInput, u as PurchaseItemResult, v as ItemHistoryQuery, w as ItemHistoryResult, x as ItemOrderQuery, y as PlayerBalanceQuery, z as PlayerBalanceResult, B as InboundPendingQuery, F as InboundPendingResult } from './types-Bi_NMSkN.js';
2
- export { G as CurrencyBalance, H as InboundPendingItem, f as InvoErrorInfo, g as InvoHooks, h as InvoRequestInfo, i as InvoResponseInfo, R as Rail, V as VerificationMethod } from './types-Bi_NMSkN.js';
1
+ import { I as InvoError, S as ServerConfig, a as CallOptions, l as PlayerToken, m as InitiateSendInput, n as InitiateResult, o as InitiateTransferInput, p as CreateCheckoutInput, q as CreateCheckoutResult, r as PurchaseInput, s as PurchaseResult, t as ConfirmPaymentResult, O as OrderDetailsResult, u as PurchaseItemInput, v as PurchaseItemResult, w as ItemHistoryQuery, x as ItemHistoryResult, y as ItemOrderQuery, z as PlayerBalanceQuery, F as PlayerBalanceResult, G as InboundPendingQuery, H as InboundPendingResult, J as LinkedIdentitiesQuery, K as LinkedIdentitiesResult } from './types-DLSCxpoT.js';
2
+ export { M as CurrencyBalance, N as InboundPendingItem, g as InvoErrorInfo, h as InvoHooks, i as InvoRequestInfo, j as InvoResponseInfo, Q as LinkedIdentityEmail, R as Rail, V as VerificationMethod } from './types-DLSCxpoT.js';
3
3
 
4
4
  interface VerifyWebhookOptions {
5
5
  /** Max age of the signed timestamp, in seconds. Default 300 (5 min). */
@@ -216,7 +216,16 @@ declare class InvoServer {
216
216
  * `transfer.claim_pending` webhook (the webhook is the wake-up; this is the list).
217
217
  */
218
218
  getInboundPending(query: InboundPendingQuery, opts?: CallOptions): Promise<InboundPendingResult>;
219
+ /**
220
+ * Look up a player's linked wallet identities (SERVER-only, game-secret). Game-scoped:
221
+ * returns wallet members that also have a Player row on this game. Provide a `playerEmail`
222
+ * or `playerPhone` (phone wins if both). No in-game match (backend 404) returns
223
+ * `notFound: true` with empty `emails` — not an error.
224
+ *
225
+ * ⚠️ Returns first-party PII (emails/phones) — never expose this to the browser.
226
+ */
227
+ getLinkedIdentities(query: LinkedIdentitiesQuery, opts?: CallOptions): Promise<LinkedIdentitiesResult>;
219
228
  private toInitiateResult;
220
229
  }
221
230
 
222
- export { CallOptions, ConfirmPaymentResult, CreateCheckoutInput, CreateCheckoutResult, InboundPendingQuery, InboundPendingResult, InitiateResult, InitiateSendInput, InitiateTransferInput, InvoError, InvoServer, type InvoWebhookEvent, ItemHistoryQuery, ItemHistoryResult, ItemOrderQuery, type ItemPurchasedData, OrderDetailsResult, PlayerBalanceQuery, PlayerBalanceResult, PlayerToken, type PurchaseCompletedData, type PurchaseEventData, PurchaseInput, PurchaseItemInput, PurchaseItemResult, PurchaseResult, ServerConfig, type TransferEventData, type VerifyWebhookOptions, type WebhookHandlerOptions, createWebhookHandler, verifyWebhook, verifyWebhookAsync };
231
+ export { CallOptions, ConfirmPaymentResult, CreateCheckoutInput, CreateCheckoutResult, InboundPendingQuery, InboundPendingResult, InitiateResult, InitiateSendInput, InitiateTransferInput, InvoError, InvoServer, type InvoWebhookEvent, ItemHistoryQuery, ItemHistoryResult, ItemOrderQuery, type ItemPurchasedData, LinkedIdentitiesQuery, LinkedIdentitiesResult, OrderDetailsResult, PlayerBalanceQuery, PlayerBalanceResult, PlayerToken, type PurchaseCompletedData, type PurchaseEventData, PurchaseInput, PurchaseItemInput, PurchaseItemResult, PurchaseResult, ServerConfig, type TransferEventData, type VerifyWebhookOptions, type WebhookHandlerOptions, createWebhookHandler, verifyWebhook, verifyWebhookAsync };
package/dist/server.js CHANGED
@@ -161,7 +161,7 @@ async function hmacHexSubtle(secret, message) {
161
161
  }
162
162
 
163
163
  // src/server.ts
164
- var DEFAULT_UA = "invonetwork-web-sdk/0.4.0 (+https://invo.network)";
164
+ var DEFAULT_UA = "invonetwork-web-sdk/1.0.0 (+https://invo.network)";
165
165
  var MAX_USD_AMOUNT = 999.99;
166
166
  var MAX_ITEM_PRICE = 999999.99;
167
167
  function invalidInput(label, value, why) {
@@ -201,6 +201,13 @@ function toOrderDetails(raw) {
201
201
  raw
202
202
  };
203
203
  }
204
+ function toLinkedEmail(row) {
205
+ return {
206
+ email: String(row["email"] ?? ""),
207
+ primary: row["primary"] === true,
208
+ verifiedAt: row["verified_at"] ?? null
209
+ };
210
+ }
204
211
  function toInboundPendingItem(row) {
205
212
  return {
206
213
  transactionId: String(row["transaction_id"] ?? ""),
@@ -620,6 +627,48 @@ var InvoServer = class {
620
627
  const rows = Array.isArray(raw["inbound_pending"]) ? raw["inbound_pending"] : [];
621
628
  return { inboundPending: rows.map(toInboundPendingItem), raw };
622
629
  }
630
+ /**
631
+ * Look up a player's linked wallet identities (SERVER-only, game-secret). Game-scoped:
632
+ * returns wallet members that also have a Player row on this game. Provide a `playerEmail`
633
+ * or `playerPhone` (phone wins if both). No in-game match (backend 404) returns
634
+ * `notFound: true` with empty `emails` — not an error.
635
+ *
636
+ * ⚠️ Returns first-party PII (emails/phones) — never expose this to the browser.
637
+ */
638
+ async getLinkedIdentities(query, opts) {
639
+ const phone = typeof query.playerPhone === "string" && query.playerPhone.trim() ? query.playerPhone.trim() : "";
640
+ const email = typeof query.playerEmail === "string" && query.playerEmail.trim() ? query.playerEmail.trim() : "";
641
+ if (!phone && !email) {
642
+ throw invalidInput("query", query, "requires a playerEmail or playerPhone");
643
+ }
644
+ const q = new URLSearchParams();
645
+ if (phone) q.set("player_phone", phone);
646
+ else q.set("player_email", email);
647
+ let raw;
648
+ try {
649
+ raw = await this.http.get(
650
+ `/api/wallet/identities?${q.toString()}`,
651
+ this.auth,
652
+ { signal: opts?.signal }
653
+ );
654
+ } catch (e) {
655
+ if (e instanceof InvoError && e.status === 404) {
656
+ const raw404 = e.body && typeof e.body === "object" ? e.body : {};
657
+ return { walletUserId: null, primaryEmail: "", primaryPhone: "", isMinor: false, emails: [], notFound: true, raw: raw404 };
658
+ }
659
+ throw e;
660
+ }
661
+ const emails = Array.isArray(raw["emails"]) ? raw["emails"].map(toLinkedEmail) : [];
662
+ return {
663
+ walletUserId: raw["wallet_user_id"] ?? null,
664
+ primaryEmail: String(raw["primary_email"] ?? ""),
665
+ primaryPhone: String(raw["primary_phone"] ?? ""),
666
+ isMinor: raw["is_minor"] === true,
667
+ emails,
668
+ notFound: false,
669
+ raw
670
+ };
671
+ }
623
672
  toInitiateResult(raw) {
624
673
  const vm = raw["verification_method"];
625
674
  const guardian = raw["guardian_approval"];
@@ -357,6 +357,50 @@ interface PendingCollectResult {
357
357
  pending: PendingCollectItem[];
358
358
  raw: Record<string, unknown>;
359
359
  }
360
+ interface BalanceRow {
361
+ currencyId: string | number;
362
+ currencyName: string;
363
+ /** Short symbol (may be null). */
364
+ currencySymbol: string | null;
365
+ /** Icon URL (may be null). */
366
+ currencySymbolUrl: string | null;
367
+ /** Decimal strings (parseFloat as needed). */
368
+ availableBalance: string;
369
+ reservedBalance: string;
370
+ totalBalance: string;
371
+ raw: Record<string, unknown>;
372
+ }
373
+ interface BalanceResult {
374
+ gameId: string | number;
375
+ gameName: string;
376
+ /** Empty for a player who hasn't transacted on this game yet (a 200, not an error). */
377
+ balances: BalanceRow[];
378
+ /** Sum of total_balance across rows (decimal string). */
379
+ totalValue: string;
380
+ currencyCount: number;
381
+ hasFunds: boolean;
382
+ raw: Record<string, unknown>;
383
+ }
384
+ interface LinkedIdentitiesQuery {
385
+ /** Provide one; phone wins if both are given. */
386
+ playerEmail?: string;
387
+ playerPhone?: string;
388
+ }
389
+ interface LinkedIdentityEmail {
390
+ email: string;
391
+ primary: boolean;
392
+ verifiedAt: string | null;
393
+ }
394
+ interface LinkedIdentitiesResult {
395
+ walletUserId: string | null;
396
+ primaryEmail: string;
397
+ primaryPhone: string;
398
+ isMinor: boolean;
399
+ emails: LinkedIdentityEmail[];
400
+ /** true when there was no in-game match (backend 404) — treat as "no linked identities". */
401
+ notFound: boolean;
402
+ raw: Record<string, unknown>;
403
+ }
360
404
  interface DestinationsQuery {
361
405
  /** "transfer" | "send". Defaults "transfer" (the set is identical for both today). */
362
406
  direction?: "transfer" | "send";
@@ -416,4 +460,4 @@ interface LinkDeviceResult {
416
460
  raw: Record<string, unknown>;
417
461
  }
418
462
 
419
- export { type ApproveResult as A, type InboundPendingQuery as B, type ClientConfig as C, type DestinationsQuery as D, type EnrollmentBeginResult as E, type InboundPendingResult as F, type CurrencyBalance as G, type InboundPendingItem as H, InvoError as I, type LinkDeviceResult as L, type OrderDetailsResult as O, type PendingCollectResult as P, type Rail as R, type ServerConfig as S, type VerificationMethod as V, type CallOptions as a, type ConfirmReceiptResult as b, type DestinationsResult as c, type EnrollmentVerifyResult as d, type DestinationGame as e, type InvoErrorInfo as f, type InvoHooks as g, type InvoRequestInfo as h, type InvoResponseInfo as i, type PendingCollectItem as j, type PlayerToken as k, type InitiateSendInput as l, type InitiateResult as m, type InitiateTransferInput as n, type CreateCheckoutInput as o, type CreateCheckoutResult as p, type PurchaseInput as q, type PurchaseResult as r, type ConfirmPaymentResult as s, type PurchaseItemInput as t, type PurchaseItemResult as u, type ItemHistoryQuery as v, type ItemHistoryResult as w, type ItemOrderQuery as x, type PlayerBalanceQuery as y, type PlayerBalanceResult as z };
463
+ export { type ApproveResult as A, type BalanceResult as B, type ClientConfig as C, type DestinationsQuery as D, type EnrollmentBeginResult as E, type PlayerBalanceResult as F, type InboundPendingQuery as G, type InboundPendingResult as H, InvoError as I, type LinkedIdentitiesQuery as J, type LinkedIdentitiesResult as K, type LinkDeviceResult as L, type CurrencyBalance as M, type InboundPendingItem as N, type OrderDetailsResult as O, type PendingCollectResult as P, type LinkedIdentityEmail as Q, type Rail as R, type ServerConfig as S, type VerificationMethod as V, type CallOptions as a, type ConfirmReceiptResult as b, type DestinationsResult as c, type EnrollmentVerifyResult as d, type BalanceRow as e, type DestinationGame as f, type InvoErrorInfo as g, type InvoHooks as h, type InvoRequestInfo as i, type InvoResponseInfo as j, type PendingCollectItem as k, type PlayerToken as l, type InitiateSendInput as m, type InitiateResult as n, type InitiateTransferInput as o, type CreateCheckoutInput as p, type CreateCheckoutResult as q, type PurchaseInput as r, type PurchaseResult as s, type ConfirmPaymentResult as t, type PurchaseItemInput as u, type PurchaseItemResult as v, type ItemHistoryQuery as w, type ItemHistoryResult as x, type ItemOrderQuery as y, type PlayerBalanceQuery as z };
@@ -357,6 +357,50 @@ interface PendingCollectResult {
357
357
  pending: PendingCollectItem[];
358
358
  raw: Record<string, unknown>;
359
359
  }
360
+ interface BalanceRow {
361
+ currencyId: string | number;
362
+ currencyName: string;
363
+ /** Short symbol (may be null). */
364
+ currencySymbol: string | null;
365
+ /** Icon URL (may be null). */
366
+ currencySymbolUrl: string | null;
367
+ /** Decimal strings (parseFloat as needed). */
368
+ availableBalance: string;
369
+ reservedBalance: string;
370
+ totalBalance: string;
371
+ raw: Record<string, unknown>;
372
+ }
373
+ interface BalanceResult {
374
+ gameId: string | number;
375
+ gameName: string;
376
+ /** Empty for a player who hasn't transacted on this game yet (a 200, not an error). */
377
+ balances: BalanceRow[];
378
+ /** Sum of total_balance across rows (decimal string). */
379
+ totalValue: string;
380
+ currencyCount: number;
381
+ hasFunds: boolean;
382
+ raw: Record<string, unknown>;
383
+ }
384
+ interface LinkedIdentitiesQuery {
385
+ /** Provide one; phone wins if both are given. */
386
+ playerEmail?: string;
387
+ playerPhone?: string;
388
+ }
389
+ interface LinkedIdentityEmail {
390
+ email: string;
391
+ primary: boolean;
392
+ verifiedAt: string | null;
393
+ }
394
+ interface LinkedIdentitiesResult {
395
+ walletUserId: string | null;
396
+ primaryEmail: string;
397
+ primaryPhone: string;
398
+ isMinor: boolean;
399
+ emails: LinkedIdentityEmail[];
400
+ /** true when there was no in-game match (backend 404) — treat as "no linked identities". */
401
+ notFound: boolean;
402
+ raw: Record<string, unknown>;
403
+ }
360
404
  interface DestinationsQuery {
361
405
  /** "transfer" | "send". Defaults "transfer" (the set is identical for both today). */
362
406
  direction?: "transfer" | "send";
@@ -416,4 +460,4 @@ interface LinkDeviceResult {
416
460
  raw: Record<string, unknown>;
417
461
  }
418
462
 
419
- export { type ApproveResult as A, type InboundPendingQuery as B, type ClientConfig as C, type DestinationsQuery as D, type EnrollmentBeginResult as E, type InboundPendingResult as F, type CurrencyBalance as G, type InboundPendingItem as H, InvoError as I, type LinkDeviceResult as L, type OrderDetailsResult as O, type PendingCollectResult as P, type Rail as R, type ServerConfig as S, type VerificationMethod as V, type CallOptions as a, type ConfirmReceiptResult as b, type DestinationsResult as c, type EnrollmentVerifyResult as d, type DestinationGame as e, type InvoErrorInfo as f, type InvoHooks as g, type InvoRequestInfo as h, type InvoResponseInfo as i, type PendingCollectItem as j, type PlayerToken as k, type InitiateSendInput as l, type InitiateResult as m, type InitiateTransferInput as n, type CreateCheckoutInput as o, type CreateCheckoutResult as p, type PurchaseInput as q, type PurchaseResult as r, type ConfirmPaymentResult as s, type PurchaseItemInput as t, type PurchaseItemResult as u, type ItemHistoryQuery as v, type ItemHistoryResult as w, type ItemOrderQuery as x, type PlayerBalanceQuery as y, type PlayerBalanceResult as z };
463
+ export { type ApproveResult as A, type BalanceResult as B, type ClientConfig as C, type DestinationsQuery as D, type EnrollmentBeginResult as E, type PlayerBalanceResult as F, type InboundPendingQuery as G, type InboundPendingResult as H, InvoError as I, type LinkedIdentitiesQuery as J, type LinkedIdentitiesResult as K, type LinkDeviceResult as L, type CurrencyBalance as M, type InboundPendingItem as N, type OrderDetailsResult as O, type PendingCollectResult as P, type LinkedIdentityEmail as Q, type Rail as R, type ServerConfig as S, type VerificationMethod as V, type CallOptions as a, type ConfirmReceiptResult as b, type DestinationsResult as c, type EnrollmentVerifyResult as d, type BalanceRow as e, type DestinationGame as f, type InvoErrorInfo as g, type InvoHooks as h, type InvoRequestInfo as i, type InvoResponseInfo as j, type PendingCollectItem as k, type PlayerToken as l, type InitiateSendInput as m, type InitiateResult as n, type InitiateTransferInput as o, type CreateCheckoutInput as p, type CreateCheckoutResult as q, type PurchaseInput as r, type PurchaseResult as s, type ConfirmPaymentResult as t, type PurchaseItemInput as u, type PurchaseItemResult as v, type ItemHistoryQuery as w, type ItemHistoryResult as x, type ItemOrderQuery as y, type PlayerBalanceQuery as z };
package/package.json CHANGED
@@ -1,76 +1,76 @@
1
- {
2
- "name": "@invonetwork/web-sdk",
3
- "version": "0.7.0",
4
- "description": "INVO Web SDK — currency purchase + passkey (WebAuthn) verification for partner web platforms.",
5
- "license": "SEE LICENSE IN LICENSE",
6
- "private": false,
7
- "repository": {
8
- "type": "git",
9
- "url": "git+https://github.com/Invo-Technologies/invo-web-sdk.git"
10
- },
11
- "homepage": "https://docs.invo.network",
12
- "bugs": {
13
- "url": "https://github.com/Invo-Technologies/invo-web-sdk/issues"
14
- },
15
- "type": "module",
16
- "engines": {
17
- "node": ">=18"
18
- },
19
- "sideEffects": false,
20
- "files": [
21
- "dist",
22
- "!dist/**/*.map",
23
- "README.md",
24
- "LICENSE",
25
- "CHANGELOG.md"
26
- ],
27
- "exports": {
28
- ".": {
29
- "types": "./dist/index.d.ts",
30
- "import": "./dist/index.js",
31
- "require": "./dist/index.cjs"
32
- },
33
- "./server": {
34
- "types": "./dist/server.d.ts",
35
- "import": "./dist/server.js",
36
- "require": "./dist/server.cjs"
37
- }
38
- },
39
- "main": "./dist/index.cjs",
40
- "module": "./dist/index.js",
41
- "types": "./dist/index.d.ts",
42
- "scripts": {
43
- "build": "tsup",
44
- "dev": "tsup --watch",
45
- "typecheck": "tsc --noEmit",
46
- "lint": "eslint .",
47
- "test": "vitest run",
48
- "test:watch": "vitest",
49
- "clean": "rimraf dist",
50
- "changeset": "changeset",
51
- "version-packages": "changeset version",
52
- "release": "npm run build && changeset publish",
53
- "prepublishOnly": "npm run clean && npm run build"
54
- },
55
- "keywords": [
56
- "invo",
57
- "webauthn",
58
- "passkey",
59
- "payments",
60
- "game-currency",
61
- "sdk"
62
- ],
63
- "publishConfig": {
64
- "access": "public"
65
- },
66
- "devDependencies": {
67
- "@changesets/cli": "^2.31.0",
68
- "@eslint/js": "^10.0.1",
69
- "eslint": "^10.6.0",
70
- "rimraf": "^5.0.5",
71
- "tsup": "^8.0.1",
72
- "typescript": "^5.4.5",
73
- "typescript-eslint": "^8.62.1",
74
- "vitest": "^1.5.0"
75
- }
76
- }
1
+ {
2
+ "name": "@invonetwork/web-sdk",
3
+ "version": "1.0.0",
4
+ "description": "INVO Web SDK — currency purchase + passkey (WebAuthn) verification for partner web platforms.",
5
+ "license": "SEE LICENSE IN LICENSE",
6
+ "private": false,
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/Invo-Technologies/invo-web-sdk.git"
10
+ },
11
+ "homepage": "https://docs.invo.network",
12
+ "bugs": {
13
+ "url": "https://github.com/Invo-Technologies/invo-web-sdk/issues"
14
+ },
15
+ "type": "module",
16
+ "engines": {
17
+ "node": ">=18"
18
+ },
19
+ "sideEffects": false,
20
+ "files": [
21
+ "dist",
22
+ "!dist/**/*.map",
23
+ "README.md",
24
+ "LICENSE",
25
+ "CHANGELOG.md"
26
+ ],
27
+ "exports": {
28
+ ".": {
29
+ "types": "./dist/index.d.ts",
30
+ "import": "./dist/index.js",
31
+ "require": "./dist/index.cjs"
32
+ },
33
+ "./server": {
34
+ "types": "./dist/server.d.ts",
35
+ "import": "./dist/server.js",
36
+ "require": "./dist/server.cjs"
37
+ }
38
+ },
39
+ "main": "./dist/index.cjs",
40
+ "module": "./dist/index.js",
41
+ "types": "./dist/index.d.ts",
42
+ "scripts": {
43
+ "build": "tsup",
44
+ "dev": "tsup --watch",
45
+ "typecheck": "tsc --noEmit",
46
+ "lint": "eslint .",
47
+ "test": "vitest run",
48
+ "test:watch": "vitest",
49
+ "clean": "rimraf dist",
50
+ "changeset": "changeset",
51
+ "version-packages": "changeset version",
52
+ "release": "npm run build && changeset publish",
53
+ "prepublishOnly": "npm run clean && npm run build"
54
+ },
55
+ "keywords": [
56
+ "invo",
57
+ "webauthn",
58
+ "passkey",
59
+ "payments",
60
+ "game-currency",
61
+ "sdk"
62
+ ],
63
+ "publishConfig": {
64
+ "access": "public"
65
+ },
66
+ "devDependencies": {
67
+ "@changesets/cli": "^2.31.0",
68
+ "@eslint/js": "^10.0.1",
69
+ "eslint": "^10.6.0",
70
+ "rimraf": "^5.0.5",
71
+ "tsup": "^8.0.1",
72
+ "typescript": "^5.4.5",
73
+ "typescript-eslint": "^8.62.1",
74
+ "vitest": "^1.5.0"
75
+ }
76
+ }