@invonetwork/web-sdk 0.4.0 → 0.4.2
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 +141 -116
- package/README.md +12 -20
- package/dist/{chunk-EEWOAUXO.js → chunk-JOVATUDY.js} +6 -2
- package/dist/index.cjs +12 -2
- package/dist/index.js +10 -4
- package/dist/server.cjs +5 -1
- package/dist/server.js +3 -3
- package/package.json +76 -76
package/CHANGELOG.md
CHANGED
|
@@ -1,116 +1,141 @@
|
|
|
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.4.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
- **`
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
- **`
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
- `
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
-
|
|
95
|
-
`
|
|
96
|
-
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
- `
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
-
|
|
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.4.2] — 2026-06-30
|
|
8
|
+
|
|
9
|
+
Fixes from an independent line-by-line audit against the live backend.
|
|
10
|
+
|
|
11
|
+
- **`linkDevice` now works.** `device/link/webauthn/begin` returns a wrapped
|
|
12
|
+
`{ link_id, options }` body (challenge nested under `options`) and binds the
|
|
13
|
+
challenge to a server-generated `link_id`. The client now unwraps `options` and
|
|
14
|
+
echoes the server's `link_id` to `/complete` (it previously threw on an undefined
|
|
15
|
+
challenge and sent the wrong id).
|
|
16
|
+
- **`InvoError.code` is now populated for the direct purchase rail + guardian flows.**
|
|
17
|
+
`errorFromResponse` reads `error_code` (SecureErrorHandler + `/purchase-currency`)
|
|
18
|
+
in addition to `code`, and promotes known no-code tokens (`flow_paused`,
|
|
19
|
+
`spending_limit_exceeded`, `receiver_not_enrolled_use_claim_code`) to `.code`. Fixes
|
|
20
|
+
`isDuplicateRequest` on the direct rail.
|
|
21
|
+
- **Guardian/minor routing.** The 202 guardian body also carries
|
|
22
|
+
`verification_method:"sms"`; the SDK now suppresses it (reports
|
|
23
|
+
`verificationMethod: undefined`) so a guardian-pending minor isn't routed into the
|
|
24
|
+
SMS-PIN UI. README example reordered to branch on `guardianApproval` first.
|
|
25
|
+
|
|
26
|
+
## [0.4.1] — 2026-06-30
|
|
27
|
+
|
|
28
|
+
Docs only — replaced the README's internal-leaking "Deployment prerequisites"
|
|
29
|
+
(backend flag names, DB columns, gating mechanics) with a partner-facing
|
|
30
|
+
"Before you go live"; republished so the npm page README is current.
|
|
31
|
+
|
|
32
|
+
## [0.4.0] — 2026-06-30
|
|
33
|
+
|
|
34
|
+
Additive release — more server reads, edge-ready webhooks, cancellation, and tooling.
|
|
35
|
+
|
|
36
|
+
- **`getInboundPending({ playerEmail | playerPhone })`** — live, unclaimed inbound
|
|
37
|
+
sends/transfers for a player (the source of truth behind the "you have X to collect"
|
|
38
|
+
badge; pairs with `transfer.claim_pending`).
|
|
39
|
+
- **`verifyWebhookAsync`** — Web Crypto variant of `verifyWebhook` that runs on
|
|
40
|
+
Cloudflare Workers / Deno / Vercel+Netlify Edge / Bun / browsers; and
|
|
41
|
+
**`createWebhookHandler`** — a zero-dep Fetch-API `(Request) => Promise<Response>`
|
|
42
|
+
webhook route handler (Next.js App Router, Workers, Deno, Hono, Bun).
|
|
43
|
+
- **`iterateItemPurchaseHistory`** — async iterator that pages through a player's
|
|
44
|
+
full item-purchase history.
|
|
45
|
+
- **Per-call `AbortSignal`** — every method accepts an optional `{ signal }`; an
|
|
46
|
+
aborted call throws `InvoError` code `ABORTED` and is never retried.
|
|
47
|
+
- **Tooling**: ESLint (+ lint in CI), changesets release automation, `SECURITY.md`,
|
|
48
|
+
and `CODEOWNERS`.
|
|
49
|
+
|
|
50
|
+
## [0.3.0] — 2026-06-30
|
|
51
|
+
|
|
52
|
+
Additive release — new server capabilities plus transport resilience/observability.
|
|
53
|
+
|
|
54
|
+
- **Webhook verification** (`/server`): `verifyWebhook(rawBody, signatureHeader, secret | secrets, opts?)`
|
|
55
|
+
— constant-time HMAC-SHA256 over `${t}.${rawBody}`, 5-minute replay window,
|
|
56
|
+
multi-secret rotation; returns a typed `InvoWebhookEvent` discriminated union
|
|
57
|
+
(`purchase.*`, `item.purchased`, `transfer.*`, `payout.status_changed`, `webhook.test`).
|
|
58
|
+
Throws `InvoError` (`WEBHOOK_SIGNATURE_INVALID` / `WEBHOOK_TIMESTAMP_EXPIRED` /
|
|
59
|
+
`WEBHOOK_MALFORMED` / `WEBHOOK_SECRET_MISSING`). Server-only; the browser bundle
|
|
60
|
+
stays crypto-free. Independently security-audited.
|
|
61
|
+
- **`getPlayerBalance({ playerEmail | playerId })`** (`/server`): typed `player` / `balances` / `summary`.
|
|
62
|
+
- **Automatic retries**: network errors/timeouts, `429` (honoring `retry_after`), and
|
|
63
|
+
`5xx` are retried with exponential backoff + jitter. New config `maxRetries`
|
|
64
|
+
(default 2, `0` disables) and `retryBaseDelayMs` (default 250).
|
|
65
|
+
- **Observability hooks**: optional `onRequest` / `onResponse` / `onError` on both
|
|
66
|
+
entries (best-effort/non-throwing); `InvoError.requestId` carries the backend
|
|
67
|
+
request id for support/tracing.
|
|
68
|
+
- **Typed reads**: `confirmPayment` → `ConfirmPaymentResult`; `getOrderDetails` /
|
|
69
|
+
`getItemOrderDetails` → `OrderDetailsResult`; `getItemPurchaseHistory` →
|
|
70
|
+
`ItemHistoryResult` (previously untyped `Record`). All keep `raw`.
|
|
71
|
+
- **Light validation**: `mintPlayerToken` and `createCheckout` require a non-blank
|
|
72
|
+
`playerEmail` (throws `INVALID_INPUT` before the network call).
|
|
73
|
+
- **License**: `package.json` `license` is now `SEE LICENSE IN LICENSE` (was
|
|
74
|
+
`UNLICENSED`); `LICENSE` rewritten as an explicit install-and-use grant for
|
|
75
|
+
building INVO integrations.
|
|
76
|
+
|
|
77
|
+
## [0.2.1] — 2026-06-30
|
|
78
|
+
|
|
79
|
+
Docs only — no code change (republished so the npm page README is current).
|
|
80
|
+
|
|
81
|
+
- Rewrote the README into a complete integration/deployment guide: capability
|
|
82
|
+
overview, architecture, deployment prerequisites, per-flow sections (currency
|
|
83
|
+
purchase, item purchase, sends, transfers, passkeys), webhooks, errors, and a
|
|
84
|
+
full API reference for both entries.
|
|
85
|
+
- Added INVO console onboarding: `https://console.invo.network` (production) and
|
|
86
|
+
`https://dev.console.invo.network` (testing/sandbox), mapped to their API base URLs.
|
|
87
|
+
|
|
88
|
+
## [0.2.0] — 2026-06-30
|
|
89
|
+
|
|
90
|
+
Adds **item purchase** (spend existing game currency on an in-game item, §4.8) — an
|
|
91
|
+
additive, server-only surface.
|
|
92
|
+
|
|
93
|
+
- `InvoServer` (`/server`): `purchaseItem`, `getItemPurchaseHistory`, `getItemOrderDetails`.
|
|
94
|
+
- Server-side, game-secret auth — no passkey, no real money, no payment rail (it's a
|
|
95
|
+
balance debit). Grant the item off the `item.purchased` webhook.
|
|
96
|
+
- Client-side guards: required fields (trim-checked), `itemQuantity` integer `1..1000`,
|
|
97
|
+
prices `> 0` and `<= 999999.99` (magnitude-safe 2-decimal check), and
|
|
98
|
+
`totalPrice == unitPrice × itemQuantity (±0.01)` compared in integer cents.
|
|
99
|
+
- Load-bearing response fields (`transaction_id`, `order_id`) throw `INVALID_RESPONSE`
|
|
100
|
+
if missing on a 200.
|
|
101
|
+
- `InvoError` helpers: `isInsufficientBalance` (gated to 400; not the `429` throttle),
|
|
102
|
+
`isDuplicateRequest` (409), `retryAfter` (numeric or string `retry_after`); all
|
|
103
|
+
null-safe against non-JSON error bodies.
|
|
104
|
+
- Independently audited (2 agents) against handoff doc §4.8/§6/§8; contract verified,
|
|
105
|
+
error-classification edge cases fixed.
|
|
106
|
+
|
|
107
|
+
## [0.1.0] — 2026-06-30
|
|
108
|
+
|
|
109
|
+
Initial scaffold.
|
|
110
|
+
|
|
111
|
+
- `InvoServer` (`/server`): `mintPlayerToken`, `initiateSend`, `initiateTransfer`,
|
|
112
|
+
`createCheckout`, `purchaseCurrency`, `confirmPayment`, `getOrderDetails`.
|
|
113
|
+
- Client-side purchase guards (§4.7): USD amount `0 < x ≤ 999.99`, required
|
|
114
|
+
`purchaseReference`, and `rail:"steam"` rejected before the network call
|
|
115
|
+
(`INVALID_INPUT` / `MISSING_PURCHASE_REFERENCE` / `WRONG_RAIL_ENDPOINT`).
|
|
116
|
+
- `InvoClient` (browser): `enrollPasskey`, `approveSend`/`approveTransfer`,
|
|
117
|
+
`confirmReceiptSend`/`confirmReceiptTransfer`, and `linkDevice` for the
|
|
118
|
+
interchangeable-methods flow (§4.6).
|
|
119
|
+
- Optional `refreshToken` hook: transparently re-mints and retries once on
|
|
120
|
+
`SDK_TOKEN_EXPIRED` (§11.2).
|
|
121
|
+
- Shared: typed `InvoError`, isomorphic HTTP client, WebAuthn JSON⇄binary helpers.
|
|
122
|
+
- Tests: HTTP layer, server request mapping + purchase guards, browser client
|
|
123
|
+
flows (enroll/approve/link/token-refresh), and WebAuthn serialization.
|
|
124
|
+
- Contracts extracted + auditor-verified against the INVO backend.
|
|
125
|
+
|
|
126
|
+
### Hardening (independent red-team pass)
|
|
127
|
+
|
|
128
|
+
- **Guardian/minor `202` path no longer mismapped to `verificationMethod:"sms"`** —
|
|
129
|
+
`initiateSend`/`initiateTransfer` now return `verificationMethod: undefined` and a
|
|
130
|
+
`guardianApproval` block on the guardian path, so callers don't route into the
|
|
131
|
+
PIN UI by mistake (§4.3).
|
|
132
|
+
- `usdAmount` validation tightened: rejects non-plain-decimal strings
|
|
133
|
+
(`"0x10"`, `"1e2"`, whitespace) and >2 decimal places before any network call.
|
|
134
|
+
- Load-bearing response fields (`token`, `checkout_url`) now throw `INVALID_RESPONSE`
|
|
135
|
+
instead of silently surfacing as empty strings.
|
|
136
|
+
- `getOrderDetails` requires at least one of `orderId`/`transactionId`.
|
|
137
|
+
- Token refresh now re-runs the **whole** passkey ceremony on `SDK_TOKEN_EXPIRED`
|
|
138
|
+
(never replays a single-use assertion) and single-flights concurrent refreshes.
|
|
139
|
+
- `baseUrl` must be `https://` (localhost exempt) so the token/secret can't travel
|
|
140
|
+
in cleartext.
|
|
141
|
+
- Published tarball excludes sourcemaps (no proprietary source shipped).
|
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ The **game secret stays on your server**; the browser only ever holds a short-li
|
|
|
22
22
|
|
|
23
23
|
- [Install](#install)
|
|
24
24
|
- [Architecture & the two entry points](#architecture--the-two-entry-points)
|
|
25
|
-
- [
|
|
25
|
+
- [Before you go live](#before-you-go-live)
|
|
26
26
|
- [Configuration](#configuration)
|
|
27
27
|
- [Currency purchase (real money in)](#currency-purchase-real-money-in)
|
|
28
28
|
- [Item purchase (spend game currency)](#item-purchase-spend-game-currency)
|
|
@@ -78,25 +78,16 @@ 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
|
-
##
|
|
81
|
+
## Before you go live
|
|
82
82
|
|
|
83
|
-
INVO
|
|
83
|
+
INVO enables each flow for your tenant in the [console](#get-your-account--game-secret-invo-console). What you need to do:
|
|
84
84
|
|
|
85
|
-
**
|
|
86
|
-
-
|
|
87
|
-
- `
|
|
88
|
-
-
|
|
89
|
-
- Tenant migrated (`games.sdk_verification_enabled`).
|
|
90
|
-
- **Per-tenant RP ID + origins** (`webauthn_rp_id`, `webauthn_origins`). There's no separate "webauthn on" flag — *the presence of a valid RP ID is the gate*. Until it's set, WebAuthn endpoints return `403 WEBAUTHN_NOT_ENABLED_FOR_TENANT`. **You must serve your integration from an origin listed in `webauthn_origins`,** or passkeys won't validate.
|
|
85
|
+
- **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.)
|
|
87
|
+
- **For currency purchase:** card checkout works out of the box; ask INVO to enable the `game`/`steam` rails if you need them.
|
|
88
|
+
- **For item purchase:** nothing extra — it's a currency-balance debit.
|
|
91
89
|
|
|
92
|
-
|
|
93
|
-
- `platform` (card) rail is always on; `game`/`steam` rails are off by default and each gated by their own flag + per-game config.
|
|
94
|
-
- Honors the platform `purchases` kill switch (`503 flow_paused` when paused).
|
|
95
|
-
|
|
96
|
-
**For item purchase**
|
|
97
|
-
- The game must be in `live` or `testing` state (else `403`). No passkey/payment flags involved — it's a balance debit.
|
|
98
|
-
|
|
99
|
-
**On your side:** store the game secret in server-side config/secrets (never ship it to the browser), and expose a small endpoint that calls `mintPlayerToken` so the browser can fetch/refresh its token.
|
|
90
|
+
If a flow isn't enabled for your tenant yet, calls return a clear `InvoError` (e.g. `TENANT_NOT_MIGRATED`, `WEBAUTHN_NOT_ENABLED_FOR_TENANT`, or `flow_paused`) — coordinate with your INVO contact to turn it on.
|
|
100
91
|
|
|
101
92
|
## Configuration
|
|
102
93
|
|
|
@@ -269,10 +260,11 @@ const t = await server.initiateTransfer({
|
|
|
269
260
|
});
|
|
270
261
|
// (initiateSend uses sender*/receiver* + receivingGameId instead)
|
|
271
262
|
|
|
263
|
+
// Check guardianApproval FIRST — the guardian path takes precedence.
|
|
272
264
|
switch (true) {
|
|
265
|
+
case !!t.guardianApproval: // minor/guardian path (HTTP 202) → pending approval, no PIN UI
|
|
273
266
|
case t.verificationMethod === "in_app": // sender is passkey-enrolled → approve in the browser
|
|
274
267
|
case t.verificationMethod === "sms": // not enrolled, a PIN was sent → show a PIN-entry fallback
|
|
275
|
-
case !!t.guardianApproval: // minor/guardian path (HTTP 202) → do NOT show the PIN UI
|
|
276
268
|
}
|
|
277
269
|
|
|
278
270
|
// 2. BROWSER — the sender approves with their passkey (give them t.transactionId + the player token)
|
|
@@ -289,10 +281,10 @@ try {
|
|
|
289
281
|
}
|
|
290
282
|
```
|
|
291
283
|
|
|
292
|
-
- **`verificationMethod`** (from initiate): `"in_app"` → passkey approve; `"sms"` → un-enrolled, PIN sent; `undefined` + `guardianApproval` → minor/guardian (HTTP 202), do **not** show the PIN UI.
|
|
284
|
+
- **`verificationMethod`** (from initiate): `"in_app"` → passkey approve; `"sms"` → un-enrolled, PIN sent; `undefined` + `guardianApproval` → minor/guardian (HTTP 202), do **not** show the PIN UI. On the guardian path the SDK reports `verificationMethod: undefined` (even though the raw 202 body also carries `"sms"`) so `guardianApproval` wins — but branch on it first to be safe.
|
|
293
285
|
- **Claim codes** are returned only by `approveTransfer`; they're the out-of-band fallback when the recipient isn't enrolled.
|
|
294
286
|
- **`err.isReceiverNotEnrolled`** on `confirmReceipt*` is the explicit signal to switch to claim-code entry.
|
|
295
|
-
- Transfer self-claim
|
|
287
|
+
- Transfer self-claim may be disabled for your tenant; if it is, surface the claim-code path instead.
|
|
296
288
|
|
|
297
289
|
**"You have X to collect"** — to render a collect badge, list a player's live, unclaimed inbound sends/transfers (the source of truth behind the `transfer.claim_pending` webhook):
|
|
298
290
|
|
|
@@ -50,8 +50,12 @@ function errorFromResponse(status, body, requestId) {
|
|
|
50
50
|
if (body && typeof body === "object") {
|
|
51
51
|
const b = body;
|
|
52
52
|
if (typeof b["code"] === "string") code = b["code"];
|
|
53
|
+
else if (typeof b["error_code"] === "string") code = b["error_code"];
|
|
53
54
|
if (typeof b["error"] === "string") message = b["error"];
|
|
54
55
|
else if (typeof b["message"] === "string") message = b["message"];
|
|
56
|
+
if (!code && typeof b["error"] === "string" && /^(flow_paused|spending_limit_exceeded|receiver_not_enrolled_use_claim_code)$/.test(b["error"])) {
|
|
57
|
+
code = b["error"];
|
|
58
|
+
}
|
|
55
59
|
} else if (typeof body === "string" && body.trim()) {
|
|
56
60
|
message = body;
|
|
57
61
|
}
|
|
@@ -245,5 +249,5 @@ function retryAfterMs(parsed, headers) {
|
|
|
245
249
|
}
|
|
246
250
|
|
|
247
251
|
export { Http, InvoError, assertSecureBaseUrl };
|
|
248
|
-
//# sourceMappingURL=chunk-
|
|
249
|
-
//# sourceMappingURL=chunk-
|
|
252
|
+
//# sourceMappingURL=chunk-JOVATUDY.js.map
|
|
253
|
+
//# sourceMappingURL=chunk-JOVATUDY.js.map
|
package/dist/index.cjs
CHANGED
|
@@ -52,8 +52,12 @@ function errorFromResponse(status, body, requestId) {
|
|
|
52
52
|
if (body && typeof body === "object") {
|
|
53
53
|
const b = body;
|
|
54
54
|
if (typeof b["code"] === "string") code = b["code"];
|
|
55
|
+
else if (typeof b["error_code"] === "string") code = b["error_code"];
|
|
55
56
|
if (typeof b["error"] === "string") message = b["error"];
|
|
56
57
|
else if (typeof b["message"] === "string") message = b["message"];
|
|
58
|
+
if (!code && typeof b["error"] === "string" && /^(flow_paused|spending_limit_exceeded|receiver_not_enrolled_use_claim_code)$/.test(b["error"])) {
|
|
59
|
+
code = b["error"];
|
|
60
|
+
}
|
|
57
61
|
} else if (typeof body === "string" && body.trim()) {
|
|
58
62
|
message = body;
|
|
59
63
|
}
|
|
@@ -399,16 +403,22 @@ var InvoClient = class {
|
|
|
399
403
|
*/
|
|
400
404
|
async linkDevice(linkId, opts) {
|
|
401
405
|
if (!linkId) throw new Error("linkDevice requires a `linkId`.");
|
|
406
|
+
this.assertWebAuthn();
|
|
402
407
|
const signal = opts?.signal;
|
|
403
408
|
return this.withTokenRetry(async () => {
|
|
404
|
-
const
|
|
409
|
+
const begin = await this.post(
|
|
405
410
|
"/api/sdk/device/link/webauthn/begin",
|
|
406
411
|
{ link_id: linkId },
|
|
407
412
|
signal
|
|
408
413
|
);
|
|
414
|
+
const cred = await navigator.credentials.get({
|
|
415
|
+
publicKey: toRequestOptions(begin.options),
|
|
416
|
+
signal
|
|
417
|
+
});
|
|
418
|
+
if (!cred) throw new Error("Passkey assertion was cancelled or returned no credential.");
|
|
409
419
|
const raw = await this.post(
|
|
410
420
|
"/api/sdk/device/link/webauthn/complete",
|
|
411
|
-
{ link_id:
|
|
421
|
+
{ link_id: begin.link_id, webauthn_assertion: assertionToJSON(cred) },
|
|
412
422
|
signal
|
|
413
423
|
);
|
|
414
424
|
return { status: String(raw["status"] ?? ""), raw };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { assertSecureBaseUrl, Http, InvoError } from './chunk-
|
|
2
|
-
export { InvoError } from './chunk-
|
|
1
|
+
import { assertSecureBaseUrl, Http, InvoError } from './chunk-JOVATUDY.js';
|
|
2
|
+
export { InvoError } from './chunk-JOVATUDY.js';
|
|
3
3
|
|
|
4
4
|
// src/shared/webauthn.ts
|
|
5
5
|
function b64urlToBuffer(value) {
|
|
@@ -154,16 +154,22 @@ var InvoClient = class {
|
|
|
154
154
|
*/
|
|
155
155
|
async linkDevice(linkId, opts) {
|
|
156
156
|
if (!linkId) throw new Error("linkDevice requires a `linkId`.");
|
|
157
|
+
this.assertWebAuthn();
|
|
157
158
|
const signal = opts?.signal;
|
|
158
159
|
return this.withTokenRetry(async () => {
|
|
159
|
-
const
|
|
160
|
+
const begin = await this.post(
|
|
160
161
|
"/api/sdk/device/link/webauthn/begin",
|
|
161
162
|
{ link_id: linkId },
|
|
162
163
|
signal
|
|
163
164
|
);
|
|
165
|
+
const cred = await navigator.credentials.get({
|
|
166
|
+
publicKey: toRequestOptions(begin.options),
|
|
167
|
+
signal
|
|
168
|
+
});
|
|
169
|
+
if (!cred) throw new Error("Passkey assertion was cancelled or returned no credential.");
|
|
164
170
|
const raw = await this.post(
|
|
165
171
|
"/api/sdk/device/link/webauthn/complete",
|
|
166
|
-
{ link_id:
|
|
172
|
+
{ link_id: begin.link_id, webauthn_assertion: assertionToJSON(cred) },
|
|
167
173
|
signal
|
|
168
174
|
);
|
|
169
175
|
return { status: String(raw["status"] ?? ""), raw };
|
package/dist/server.cjs
CHANGED
|
@@ -54,8 +54,12 @@ function errorFromResponse(status, body, requestId) {
|
|
|
54
54
|
if (body && typeof body === "object") {
|
|
55
55
|
const b = body;
|
|
56
56
|
if (typeof b["code"] === "string") code = b["code"];
|
|
57
|
+
else if (typeof b["error_code"] === "string") code = b["error_code"];
|
|
57
58
|
if (typeof b["error"] === "string") message = b["error"];
|
|
58
59
|
else if (typeof b["message"] === "string") message = b["message"];
|
|
60
|
+
if (!code && typeof b["error"] === "string" && /^(flow_paused|spending_limit_exceeded|receiver_not_enrolled_use_claim_code)$/.test(b["error"])) {
|
|
61
|
+
code = b["error"];
|
|
62
|
+
}
|
|
59
63
|
} else if (typeof body === "string" && body.trim()) {
|
|
60
64
|
message = body;
|
|
61
65
|
}
|
|
@@ -869,7 +873,7 @@ var InvoServer = class {
|
|
|
869
873
|
const guardian = raw["guardian_approval"];
|
|
870
874
|
return {
|
|
871
875
|
transactionId: String(raw["transaction_id"] ?? ""),
|
|
872
|
-
verificationMethod: vm === "in_app" || vm === "sms" ? vm : void 0,
|
|
876
|
+
verificationMethod: guardian ? void 0 : vm === "in_app" || vm === "sms" ? vm : void 0,
|
|
873
877
|
guardianApproval: guardian ?? void 0,
|
|
874
878
|
raw
|
|
875
879
|
};
|
package/dist/server.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { InvoError, assertSecureBaseUrl, Http } from './chunk-
|
|
2
|
-
export { InvoError } from './chunk-
|
|
1
|
+
import { InvoError, assertSecureBaseUrl, Http } from './chunk-JOVATUDY.js';
|
|
2
|
+
export { InvoError } from './chunk-JOVATUDY.js';
|
|
3
3
|
import { createHmac } from 'crypto';
|
|
4
4
|
|
|
5
5
|
var DEFAULT_TOLERANCE_SEC = 300;
|
|
@@ -624,7 +624,7 @@ var InvoServer = class {
|
|
|
624
624
|
const guardian = raw["guardian_approval"];
|
|
625
625
|
return {
|
|
626
626
|
transactionId: String(raw["transaction_id"] ?? ""),
|
|
627
|
-
verificationMethod: vm === "in_app" || vm === "sms" ? vm : void 0,
|
|
627
|
+
verificationMethod: guardian ? void 0 : vm === "in_app" || vm === "sms" ? vm : void 0,
|
|
628
628
|
guardianApproval: guardian ?? void 0,
|
|
629
629
|
raw
|
|
630
630
|
};
|
package/package.json
CHANGED
|
@@ -1,76 +1,76 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@invonetwork/web-sdk",
|
|
3
|
-
"version": "0.4.
|
|
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": "0.4.2",
|
|
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
|
+
}
|