@invonetwork/web-sdk 0.5.0 → 0.7.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 +179 -150
- package/README.md +41 -11
- package/dist/{chunk-JOVATUDY.js → chunk-D3XBTH4C.js} +10 -2
- package/dist/index.cjs +140 -1
- package/dist/index.d.cts +28 -3
- package/dist/index.d.ts +28 -3
- package/dist/index.js +134 -3
- package/dist/server.cjs +8 -0
- package/dist/server.d.cts +2 -2
- package/dist/server.d.ts +2 -2
- package/dist/server.js +2 -2
- package/dist/{types-CZdmipNK.d.cts → types-Bi_NMSkN.d.cts} +97 -1
- package/dist/{types-CZdmipNK.d.ts → types-Bi_NMSkN.d.ts} +97 -1
- package/package.json +76 -76
package/dist/index.cjs
CHANGED
|
@@ -19,6 +19,14 @@ var InvoError = class _InvoError extends Error {
|
|
|
19
19
|
get isTokenExpired() {
|
|
20
20
|
return this.code === "SDK_TOKEN_EXPIRED";
|
|
21
21
|
}
|
|
22
|
+
/** True if `enrollPasskey()` needs the OTP-grant flow (`enrollmentBegin`/`enrollmentVerify`). */
|
|
23
|
+
get isEnrollmentAuthorizationRequired() {
|
|
24
|
+
return this.code === "ENROLLMENT_REQUIRES_AUTHORIZATION";
|
|
25
|
+
}
|
|
26
|
+
/** True if enrolling is blocked because another method exists — use `linkDevice`. */
|
|
27
|
+
get isEnrollmentProofRequired() {
|
|
28
|
+
return this.code === "ENROLLMENT_REQUIRES_PROOF";
|
|
29
|
+
}
|
|
22
30
|
/**
|
|
23
31
|
* True if an item purchase failed because the player's balance was too low (§4.8 → 400).
|
|
24
32
|
* The backend carries `required_amount` + `current_balance` on the body for the UI.
|
|
@@ -332,6 +340,44 @@ function assertionToJSON(cred) {
|
|
|
332
340
|
}
|
|
333
341
|
|
|
334
342
|
// src/index.ts
|
|
343
|
+
function toPendingCollectItem(row) {
|
|
344
|
+
return {
|
|
345
|
+
transferId: String(row["transfer_id"] ?? ""),
|
|
346
|
+
kind: String(row["kind"] ?? ""),
|
|
347
|
+
flow: String(row["flow"] ?? ""),
|
|
348
|
+
amount: row["amount"] ?? null,
|
|
349
|
+
currency: String(row["currency"] ?? ""),
|
|
350
|
+
counterpartyGame: String(row["counterparty_game"] ?? ""),
|
|
351
|
+
expiresAt: row["expires_at"] ?? null,
|
|
352
|
+
stepUpRequired: row["step_up_required"] === true,
|
|
353
|
+
held: row["held"] === true,
|
|
354
|
+
holdReason: row["hold_reason"] ?? null,
|
|
355
|
+
raw: row
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
function toDestinationGame(row) {
|
|
359
|
+
const s = (k) => row[k] === void 0 || row[k] === null ? void 0 : String(row[k]);
|
|
360
|
+
return {
|
|
361
|
+
gameId: row["game_id"] ?? "",
|
|
362
|
+
gameName: String(row["game_name"] ?? ""),
|
|
363
|
+
tenantType: s("tenant_type"),
|
|
364
|
+
developerName: s("developer_name"),
|
|
365
|
+
publisherName: s("publisher_name"),
|
|
366
|
+
genre: s("genre"),
|
|
367
|
+
platform: s("platform"),
|
|
368
|
+
gameStatus: s("game_status"),
|
|
369
|
+
gameIcon: s("game_icon"),
|
|
370
|
+
gamePoster: s("game_poster"),
|
|
371
|
+
gameUrl: s("game_url"),
|
|
372
|
+
gameDescription: s("game_description"),
|
|
373
|
+
currencyName: String(row["currency_name"] ?? ""),
|
|
374
|
+
currencySymbol: String(row["currency_symbol"] ?? ""),
|
|
375
|
+
currencySymbolUrl: s("currency_symbol_url"),
|
|
376
|
+
minimumTransfer: String(row["minimum_transfer"] ?? ""),
|
|
377
|
+
maximumTransfer: String(row["maximum_transfer"] ?? ""),
|
|
378
|
+
raw: row
|
|
379
|
+
};
|
|
380
|
+
}
|
|
335
381
|
var InvoClient = class {
|
|
336
382
|
constructor(config) {
|
|
337
383
|
if (!config.token) throw new Error("InvoClient requires a player `token`.");
|
|
@@ -424,12 +470,95 @@ var InvoClient = class {
|
|
|
424
470
|
return { status: String(raw["status"] ?? ""), raw };
|
|
425
471
|
});
|
|
426
472
|
}
|
|
473
|
+
/**
|
|
474
|
+
* List the player's own pending items to collect (browser, player-token). Each row's
|
|
475
|
+
* `kind` tells you which action to call: `"identity_gate"` → `approve*` (you initiated);
|
|
476
|
+
* `"receiving_confirm"` → `confirmReceipt*` (a peer sent to you). PII-free — no claim
|
|
477
|
+
* code or phone (those are only on the server-side `getInboundPending`).
|
|
478
|
+
*/
|
|
479
|
+
async getPendingCollect(opts) {
|
|
480
|
+
return this.withTokenRetry(async () => {
|
|
481
|
+
const raw = await this.get(
|
|
482
|
+
"/api/sdk/transfers/pending",
|
|
483
|
+
opts?.signal
|
|
484
|
+
);
|
|
485
|
+
const rows = Array.isArray(raw["pending"]) ? raw["pending"] : [];
|
|
486
|
+
return { pending: rows.map(toPendingCollectItem), raw };
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* List the games/tenants this player can send/transfer to, with display metadata
|
|
491
|
+
* inline (name, icon, currency, min/max limits) — one call, no per-game lookup.
|
|
492
|
+
* Source game is the token's own game. Player-token (browser).
|
|
493
|
+
*/
|
|
494
|
+
async getDestinations(query, opts) {
|
|
495
|
+
const direction = query?.direction ?? "transfer";
|
|
496
|
+
return this.withTokenRetry(async () => {
|
|
497
|
+
const raw = await this.get(
|
|
498
|
+
`/api/sdk/destinations?direction=${encodeURIComponent(direction)}`,
|
|
499
|
+
opts?.signal
|
|
500
|
+
);
|
|
501
|
+
const games = Array.isArray(raw["available_games"]) ? raw["available_games"] : [];
|
|
502
|
+
return {
|
|
503
|
+
status: String(raw["status"] ?? ""),
|
|
504
|
+
sourceGameId: raw["source_game_id"] ?? "",
|
|
505
|
+
sourceGameName: String(raw["source_game_name"] ?? ""),
|
|
506
|
+
sourceGameIcon: raw["source_game_icon"],
|
|
507
|
+
sourceCurrencyName: String(raw["source_currency_name"] ?? ""),
|
|
508
|
+
sourceCurrencyIcon: raw["source_currency_icon"],
|
|
509
|
+
universalTransfers: raw["universal_transfers"] === true,
|
|
510
|
+
transferMode: String(raw["transfer_mode"] ?? ""),
|
|
511
|
+
availableGames: games.map(toDestinationGame),
|
|
512
|
+
totalDestinations: Number(raw["total_destinations"] ?? games.length),
|
|
513
|
+
direction: String(raw["direction"] ?? direction),
|
|
514
|
+
linkedGameIds: Array.isArray(raw["linked_game_ids"]) ? raw["linked_game_ids"] : void 0,
|
|
515
|
+
raw
|
|
516
|
+
};
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* First-enrollment OTP grant (§4.2). When `enrollPasskey()` throws
|
|
521
|
+
* `ENROLLMENT_REQUIRES_AUTHORIZATION` (`err.isEnrollmentAuthorizationRequired`), call
|
|
522
|
+
* this to send a 6-digit code to the player's phone + email on file, collect the code,
|
|
523
|
+
* call `enrollmentVerify(code)`, then RETRY the SAME `enrollPasskey()` —
|
|
524
|
+
* `register/complete` auto-consumes the server-side grant (30-min TTL). For
|
|
525
|
+
* `ENROLLMENT_REQUIRES_PROOF` (`err.isEnrollmentProofRequired`) use `linkDevice` instead.
|
|
526
|
+
*/
|
|
527
|
+
async enrollmentBegin(opts) {
|
|
528
|
+
return this.withTokenRetry(async () => {
|
|
529
|
+
const raw = await this.post(
|
|
530
|
+
"/api/sdk/device/enrollment/begin",
|
|
531
|
+
void 0,
|
|
532
|
+
opts?.signal
|
|
533
|
+
);
|
|
534
|
+
return {
|
|
535
|
+
status: String(raw["status"] ?? ""),
|
|
536
|
+
channels: Array.isArray(raw["channels"]) ? raw["channels"] : [],
|
|
537
|
+
raw
|
|
538
|
+
};
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
/** Verify the enrollment OTP (see {@link enrollmentBegin}); on success, retry `enrollPasskey()`. */
|
|
542
|
+
async enrollmentVerify(code, opts) {
|
|
543
|
+
if (!code) throw new Error("enrollmentVerify requires the OTP `code`.");
|
|
544
|
+
return this.withTokenRetry(async () => {
|
|
545
|
+
const raw = await this.post(
|
|
546
|
+
"/api/sdk/device/enrollment/verify",
|
|
547
|
+
{ code },
|
|
548
|
+
opts?.signal
|
|
549
|
+
);
|
|
550
|
+
return { status: String(raw["status"] ?? ""), raw };
|
|
551
|
+
});
|
|
552
|
+
}
|
|
427
553
|
// --- internals ---
|
|
428
554
|
/** POST with the current player token. Token-expiry retry is handled one level
|
|
429
555
|
* up by withTokenRetry (which re-runs the whole ceremony, not a single call). */
|
|
430
556
|
async post(path, body, signal) {
|
|
431
557
|
return this.http.post(path, body, this.auth, { signal });
|
|
432
558
|
}
|
|
559
|
+
async get(path, signal) {
|
|
560
|
+
return this.http.get(path, this.auth, { signal });
|
|
561
|
+
}
|
|
433
562
|
/**
|
|
434
563
|
* Run a whole flow, retrying it ONCE if any call fails with SDK_TOKEN_EXPIRED
|
|
435
564
|
* and a `refreshToken` hook is configured. We re-run the entire begin→get→
|
|
@@ -483,12 +612,17 @@ var InvoClient = class {
|
|
|
483
612
|
{ webauthn_assertion: assertion },
|
|
484
613
|
signal
|
|
485
614
|
);
|
|
615
|
+
const holdReason = raw["error_code"] ?? raw["code"];
|
|
486
616
|
return {
|
|
487
617
|
status: String(raw["status"] ?? ""),
|
|
488
618
|
next: String(raw["next"] ?? ""),
|
|
489
619
|
transactionId: String(raw["transaction_id"] ?? transactionId),
|
|
490
620
|
claimCode: raw["claim_code"],
|
|
491
621
|
claimCodeExpiresAt: raw["claim_code_expires_at"],
|
|
622
|
+
holdReason: typeof holdReason === "string" ? holdReason : void 0,
|
|
623
|
+
risk: raw["risk"],
|
|
624
|
+
guardianApproval: raw["guardian_approval"] ?? void 0,
|
|
625
|
+
pollEndpoint: raw["poll_endpoint"],
|
|
492
626
|
raw
|
|
493
627
|
};
|
|
494
628
|
});
|
|
@@ -507,7 +641,12 @@ var InvoClient = class {
|
|
|
507
641
|
{ webauthn_assertion: assertion },
|
|
508
642
|
signal
|
|
509
643
|
);
|
|
510
|
-
|
|
644
|
+
const holdReason = raw["error_code"] ?? raw["code"];
|
|
645
|
+
return {
|
|
646
|
+
status: String(raw["status"] ?? ""),
|
|
647
|
+
holdReason: typeof holdReason === "string" ? holdReason : void 0,
|
|
648
|
+
raw
|
|
649
|
+
};
|
|
511
650
|
});
|
|
512
651
|
}
|
|
513
652
|
};
|
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 } from './types-
|
|
2
|
-
export { I as InvoError,
|
|
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';
|
|
3
3
|
|
|
4
4
|
declare class InvoClient {
|
|
5
5
|
private readonly http;
|
|
@@ -32,9 +32,34 @@ declare class InvoClient {
|
|
|
32
32
|
* begin -> navigator.credentials.get() -> complete with { link_id, webauthn_assertion }.
|
|
33
33
|
*/
|
|
34
34
|
linkDevice(linkId: string, opts?: CallOptions): Promise<LinkDeviceResult>;
|
|
35
|
+
/**
|
|
36
|
+
* List the player's own pending items to collect (browser, player-token). Each row's
|
|
37
|
+
* `kind` tells you which action to call: `"identity_gate"` → `approve*` (you initiated);
|
|
38
|
+
* `"receiving_confirm"` → `confirmReceipt*` (a peer sent to you). PII-free — no claim
|
|
39
|
+
* code or phone (those are only on the server-side `getInboundPending`).
|
|
40
|
+
*/
|
|
41
|
+
getPendingCollect(opts?: CallOptions): Promise<PendingCollectResult>;
|
|
42
|
+
/**
|
|
43
|
+
* List the games/tenants this player can send/transfer to, with display metadata
|
|
44
|
+
* inline (name, icon, currency, min/max limits) — one call, no per-game lookup.
|
|
45
|
+
* Source game is the token's own game. Player-token (browser).
|
|
46
|
+
*/
|
|
47
|
+
getDestinations(query?: DestinationsQuery, opts?: CallOptions): Promise<DestinationsResult>;
|
|
48
|
+
/**
|
|
49
|
+
* First-enrollment OTP grant (§4.2). When `enrollPasskey()` throws
|
|
50
|
+
* `ENROLLMENT_REQUIRES_AUTHORIZATION` (`err.isEnrollmentAuthorizationRequired`), call
|
|
51
|
+
* this to send a 6-digit code to the player's phone + email on file, collect the code,
|
|
52
|
+
* call `enrollmentVerify(code)`, then RETRY the SAME `enrollPasskey()` —
|
|
53
|
+
* `register/complete` auto-consumes the server-side grant (30-min TTL). For
|
|
54
|
+
* `ENROLLMENT_REQUIRES_PROOF` (`err.isEnrollmentProofRequired`) use `linkDevice` instead.
|
|
55
|
+
*/
|
|
56
|
+
enrollmentBegin(opts?: CallOptions): Promise<EnrollmentBeginResult>;
|
|
57
|
+
/** Verify the enrollment OTP (see {@link enrollmentBegin}); on success, retry `enrollPasskey()`. */
|
|
58
|
+
enrollmentVerify(code: string, opts?: CallOptions): Promise<EnrollmentVerifyResult>;
|
|
35
59
|
/** POST with the current player token. Token-expiry retry is handled one level
|
|
36
60
|
* up by withTokenRetry (which re-runs the whole ceremony, not a single call). */
|
|
37
61
|
private post;
|
|
62
|
+
private get;
|
|
38
63
|
/**
|
|
39
64
|
* Run a whole flow, retrying it ONCE if any call fails with SDK_TOKEN_EXPIRED
|
|
40
65
|
* and a `refreshToken` hook is configured. We re-run the entire begin→get→
|
|
@@ -49,4 +74,4 @@ declare class InvoClient {
|
|
|
49
74
|
private confirmReceipt;
|
|
50
75
|
}
|
|
51
76
|
|
|
52
|
-
export { ApproveResult, CallOptions, ClientConfig, ConfirmReceiptResult, InvoClient, LinkDeviceResult };
|
|
77
|
+
export { ApproveResult, 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 } from './types-
|
|
2
|
-
export { I as InvoError,
|
|
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';
|
|
3
3
|
|
|
4
4
|
declare class InvoClient {
|
|
5
5
|
private readonly http;
|
|
@@ -32,9 +32,34 @@ declare class InvoClient {
|
|
|
32
32
|
* begin -> navigator.credentials.get() -> complete with { link_id, webauthn_assertion }.
|
|
33
33
|
*/
|
|
34
34
|
linkDevice(linkId: string, opts?: CallOptions): Promise<LinkDeviceResult>;
|
|
35
|
+
/**
|
|
36
|
+
* List the player's own pending items to collect (browser, player-token). Each row's
|
|
37
|
+
* `kind` tells you which action to call: `"identity_gate"` → `approve*` (you initiated);
|
|
38
|
+
* `"receiving_confirm"` → `confirmReceipt*` (a peer sent to you). PII-free — no claim
|
|
39
|
+
* code or phone (those are only on the server-side `getInboundPending`).
|
|
40
|
+
*/
|
|
41
|
+
getPendingCollect(opts?: CallOptions): Promise<PendingCollectResult>;
|
|
42
|
+
/**
|
|
43
|
+
* List the games/tenants this player can send/transfer to, with display metadata
|
|
44
|
+
* inline (name, icon, currency, min/max limits) — one call, no per-game lookup.
|
|
45
|
+
* Source game is the token's own game. Player-token (browser).
|
|
46
|
+
*/
|
|
47
|
+
getDestinations(query?: DestinationsQuery, opts?: CallOptions): Promise<DestinationsResult>;
|
|
48
|
+
/**
|
|
49
|
+
* First-enrollment OTP grant (§4.2). When `enrollPasskey()` throws
|
|
50
|
+
* `ENROLLMENT_REQUIRES_AUTHORIZATION` (`err.isEnrollmentAuthorizationRequired`), call
|
|
51
|
+
* this to send a 6-digit code to the player's phone + email on file, collect the code,
|
|
52
|
+
* call `enrollmentVerify(code)`, then RETRY the SAME `enrollPasskey()` —
|
|
53
|
+
* `register/complete` auto-consumes the server-side grant (30-min TTL). For
|
|
54
|
+
* `ENROLLMENT_REQUIRES_PROOF` (`err.isEnrollmentProofRequired`) use `linkDevice` instead.
|
|
55
|
+
*/
|
|
56
|
+
enrollmentBegin(opts?: CallOptions): Promise<EnrollmentBeginResult>;
|
|
57
|
+
/** Verify the enrollment OTP (see {@link enrollmentBegin}); on success, retry `enrollPasskey()`. */
|
|
58
|
+
enrollmentVerify(code: string, opts?: CallOptions): Promise<EnrollmentVerifyResult>;
|
|
35
59
|
/** POST with the current player token. Token-expiry retry is handled one level
|
|
36
60
|
* up by withTokenRetry (which re-runs the whole ceremony, not a single call). */
|
|
37
61
|
private post;
|
|
62
|
+
private get;
|
|
38
63
|
/**
|
|
39
64
|
* Run a whole flow, retrying it ONCE if any call fails with SDK_TOKEN_EXPIRED
|
|
40
65
|
* and a `refreshToken` hook is configured. We re-run the entire begin→get→
|
|
@@ -49,4 +74,4 @@ declare class InvoClient {
|
|
|
49
74
|
private confirmReceipt;
|
|
50
75
|
}
|
|
51
76
|
|
|
52
|
-
export { ApproveResult, CallOptions, ClientConfig, ConfirmReceiptResult, InvoClient, LinkDeviceResult };
|
|
77
|
+
export { ApproveResult, CallOptions, ClientConfig, ConfirmReceiptResult, DestinationsQuery, DestinationsResult, EnrollmentBeginResult, EnrollmentVerifyResult, InvoClient, LinkDeviceResult, PendingCollectResult };
|
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-D3XBTH4C.js';
|
|
2
|
+
export { InvoError } from './chunk-D3XBTH4C.js';
|
|
3
3
|
|
|
4
4
|
// src/shared/webauthn.ts
|
|
5
5
|
function b64urlToBuffer(value) {
|
|
@@ -83,6 +83,44 @@ function assertionToJSON(cred) {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
// src/index.ts
|
|
86
|
+
function toPendingCollectItem(row) {
|
|
87
|
+
return {
|
|
88
|
+
transferId: String(row["transfer_id"] ?? ""),
|
|
89
|
+
kind: String(row["kind"] ?? ""),
|
|
90
|
+
flow: String(row["flow"] ?? ""),
|
|
91
|
+
amount: row["amount"] ?? null,
|
|
92
|
+
currency: String(row["currency"] ?? ""),
|
|
93
|
+
counterpartyGame: String(row["counterparty_game"] ?? ""),
|
|
94
|
+
expiresAt: row["expires_at"] ?? null,
|
|
95
|
+
stepUpRequired: row["step_up_required"] === true,
|
|
96
|
+
held: row["held"] === true,
|
|
97
|
+
holdReason: row["hold_reason"] ?? null,
|
|
98
|
+
raw: row
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function toDestinationGame(row) {
|
|
102
|
+
const s = (k) => row[k] === void 0 || row[k] === null ? void 0 : String(row[k]);
|
|
103
|
+
return {
|
|
104
|
+
gameId: row["game_id"] ?? "",
|
|
105
|
+
gameName: String(row["game_name"] ?? ""),
|
|
106
|
+
tenantType: s("tenant_type"),
|
|
107
|
+
developerName: s("developer_name"),
|
|
108
|
+
publisherName: s("publisher_name"),
|
|
109
|
+
genre: s("genre"),
|
|
110
|
+
platform: s("platform"),
|
|
111
|
+
gameStatus: s("game_status"),
|
|
112
|
+
gameIcon: s("game_icon"),
|
|
113
|
+
gamePoster: s("game_poster"),
|
|
114
|
+
gameUrl: s("game_url"),
|
|
115
|
+
gameDescription: s("game_description"),
|
|
116
|
+
currencyName: String(row["currency_name"] ?? ""),
|
|
117
|
+
currencySymbol: String(row["currency_symbol"] ?? ""),
|
|
118
|
+
currencySymbolUrl: s("currency_symbol_url"),
|
|
119
|
+
minimumTransfer: String(row["minimum_transfer"] ?? ""),
|
|
120
|
+
maximumTransfer: String(row["maximum_transfer"] ?? ""),
|
|
121
|
+
raw: row
|
|
122
|
+
};
|
|
123
|
+
}
|
|
86
124
|
var InvoClient = class {
|
|
87
125
|
constructor(config) {
|
|
88
126
|
if (!config.token) throw new Error("InvoClient requires a player `token`.");
|
|
@@ -175,12 +213,95 @@ var InvoClient = class {
|
|
|
175
213
|
return { status: String(raw["status"] ?? ""), raw };
|
|
176
214
|
});
|
|
177
215
|
}
|
|
216
|
+
/**
|
|
217
|
+
* List the player's own pending items to collect (browser, player-token). Each row's
|
|
218
|
+
* `kind` tells you which action to call: `"identity_gate"` → `approve*` (you initiated);
|
|
219
|
+
* `"receiving_confirm"` → `confirmReceipt*` (a peer sent to you). PII-free — no claim
|
|
220
|
+
* code or phone (those are only on the server-side `getInboundPending`).
|
|
221
|
+
*/
|
|
222
|
+
async getPendingCollect(opts) {
|
|
223
|
+
return this.withTokenRetry(async () => {
|
|
224
|
+
const raw = await this.get(
|
|
225
|
+
"/api/sdk/transfers/pending",
|
|
226
|
+
opts?.signal
|
|
227
|
+
);
|
|
228
|
+
const rows = Array.isArray(raw["pending"]) ? raw["pending"] : [];
|
|
229
|
+
return { pending: rows.map(toPendingCollectItem), raw };
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* List the games/tenants this player can send/transfer to, with display metadata
|
|
234
|
+
* inline (name, icon, currency, min/max limits) — one call, no per-game lookup.
|
|
235
|
+
* Source game is the token's own game. Player-token (browser).
|
|
236
|
+
*/
|
|
237
|
+
async getDestinations(query, opts) {
|
|
238
|
+
const direction = query?.direction ?? "transfer";
|
|
239
|
+
return this.withTokenRetry(async () => {
|
|
240
|
+
const raw = await this.get(
|
|
241
|
+
`/api/sdk/destinations?direction=${encodeURIComponent(direction)}`,
|
|
242
|
+
opts?.signal
|
|
243
|
+
);
|
|
244
|
+
const games = Array.isArray(raw["available_games"]) ? raw["available_games"] : [];
|
|
245
|
+
return {
|
|
246
|
+
status: String(raw["status"] ?? ""),
|
|
247
|
+
sourceGameId: raw["source_game_id"] ?? "",
|
|
248
|
+
sourceGameName: String(raw["source_game_name"] ?? ""),
|
|
249
|
+
sourceGameIcon: raw["source_game_icon"],
|
|
250
|
+
sourceCurrencyName: String(raw["source_currency_name"] ?? ""),
|
|
251
|
+
sourceCurrencyIcon: raw["source_currency_icon"],
|
|
252
|
+
universalTransfers: raw["universal_transfers"] === true,
|
|
253
|
+
transferMode: String(raw["transfer_mode"] ?? ""),
|
|
254
|
+
availableGames: games.map(toDestinationGame),
|
|
255
|
+
totalDestinations: Number(raw["total_destinations"] ?? games.length),
|
|
256
|
+
direction: String(raw["direction"] ?? direction),
|
|
257
|
+
linkedGameIds: Array.isArray(raw["linked_game_ids"]) ? raw["linked_game_ids"] : void 0,
|
|
258
|
+
raw
|
|
259
|
+
};
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* First-enrollment OTP grant (§4.2). When `enrollPasskey()` throws
|
|
264
|
+
* `ENROLLMENT_REQUIRES_AUTHORIZATION` (`err.isEnrollmentAuthorizationRequired`), call
|
|
265
|
+
* this to send a 6-digit code to the player's phone + email on file, collect the code,
|
|
266
|
+
* call `enrollmentVerify(code)`, then RETRY the SAME `enrollPasskey()` —
|
|
267
|
+
* `register/complete` auto-consumes the server-side grant (30-min TTL). For
|
|
268
|
+
* `ENROLLMENT_REQUIRES_PROOF` (`err.isEnrollmentProofRequired`) use `linkDevice` instead.
|
|
269
|
+
*/
|
|
270
|
+
async enrollmentBegin(opts) {
|
|
271
|
+
return this.withTokenRetry(async () => {
|
|
272
|
+
const raw = await this.post(
|
|
273
|
+
"/api/sdk/device/enrollment/begin",
|
|
274
|
+
void 0,
|
|
275
|
+
opts?.signal
|
|
276
|
+
);
|
|
277
|
+
return {
|
|
278
|
+
status: String(raw["status"] ?? ""),
|
|
279
|
+
channels: Array.isArray(raw["channels"]) ? raw["channels"] : [],
|
|
280
|
+
raw
|
|
281
|
+
};
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
/** Verify the enrollment OTP (see {@link enrollmentBegin}); on success, retry `enrollPasskey()`. */
|
|
285
|
+
async enrollmentVerify(code, opts) {
|
|
286
|
+
if (!code) throw new Error("enrollmentVerify requires the OTP `code`.");
|
|
287
|
+
return this.withTokenRetry(async () => {
|
|
288
|
+
const raw = await this.post(
|
|
289
|
+
"/api/sdk/device/enrollment/verify",
|
|
290
|
+
{ code },
|
|
291
|
+
opts?.signal
|
|
292
|
+
);
|
|
293
|
+
return { status: String(raw["status"] ?? ""), raw };
|
|
294
|
+
});
|
|
295
|
+
}
|
|
178
296
|
// --- internals ---
|
|
179
297
|
/** POST with the current player token. Token-expiry retry is handled one level
|
|
180
298
|
* up by withTokenRetry (which re-runs the whole ceremony, not a single call). */
|
|
181
299
|
async post(path, body, signal) {
|
|
182
300
|
return this.http.post(path, body, this.auth, { signal });
|
|
183
301
|
}
|
|
302
|
+
async get(path, signal) {
|
|
303
|
+
return this.http.get(path, this.auth, { signal });
|
|
304
|
+
}
|
|
184
305
|
/**
|
|
185
306
|
* Run a whole flow, retrying it ONCE if any call fails with SDK_TOKEN_EXPIRED
|
|
186
307
|
* and a `refreshToken` hook is configured. We re-run the entire begin→get→
|
|
@@ -234,12 +355,17 @@ var InvoClient = class {
|
|
|
234
355
|
{ webauthn_assertion: assertion },
|
|
235
356
|
signal
|
|
236
357
|
);
|
|
358
|
+
const holdReason = raw["error_code"] ?? raw["code"];
|
|
237
359
|
return {
|
|
238
360
|
status: String(raw["status"] ?? ""),
|
|
239
361
|
next: String(raw["next"] ?? ""),
|
|
240
362
|
transactionId: String(raw["transaction_id"] ?? transactionId),
|
|
241
363
|
claimCode: raw["claim_code"],
|
|
242
364
|
claimCodeExpiresAt: raw["claim_code_expires_at"],
|
|
365
|
+
holdReason: typeof holdReason === "string" ? holdReason : void 0,
|
|
366
|
+
risk: raw["risk"],
|
|
367
|
+
guardianApproval: raw["guardian_approval"] ?? void 0,
|
|
368
|
+
pollEndpoint: raw["poll_endpoint"],
|
|
243
369
|
raw
|
|
244
370
|
};
|
|
245
371
|
});
|
|
@@ -258,7 +384,12 @@ var InvoClient = class {
|
|
|
258
384
|
{ webauthn_assertion: assertion },
|
|
259
385
|
signal
|
|
260
386
|
);
|
|
261
|
-
|
|
387
|
+
const holdReason = raw["error_code"] ?? raw["code"];
|
|
388
|
+
return {
|
|
389
|
+
status: String(raw["status"] ?? ""),
|
|
390
|
+
holdReason: typeof holdReason === "string" ? holdReason : void 0,
|
|
391
|
+
raw
|
|
392
|
+
};
|
|
262
393
|
});
|
|
263
394
|
}
|
|
264
395
|
};
|
package/dist/server.cjs
CHANGED
|
@@ -21,6 +21,14 @@ var InvoError = class _InvoError extends Error {
|
|
|
21
21
|
get isTokenExpired() {
|
|
22
22
|
return this.code === "SDK_TOKEN_EXPIRED";
|
|
23
23
|
}
|
|
24
|
+
/** True if `enrollPasskey()` needs the OTP-grant flow (`enrollmentBegin`/`enrollmentVerify`). */
|
|
25
|
+
get isEnrollmentAuthorizationRequired() {
|
|
26
|
+
return this.code === "ENROLLMENT_REQUIRES_AUTHORIZATION";
|
|
27
|
+
}
|
|
28
|
+
/** True if enrolling is blocked because another method exists — use `linkDevice`. */
|
|
29
|
+
get isEnrollmentProofRequired() {
|
|
30
|
+
return this.code === "ENROLLMENT_REQUIRES_PROOF";
|
|
31
|
+
}
|
|
24
32
|
/**
|
|
25
33
|
* True if an item purchase failed because the player's balance was too low (§4.8 → 400).
|
|
26
34
|
* The backend carries `required_amount` + `current_balance` on the body for the UI.
|
package/dist/server.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { I as InvoError, S as ServerConfig, a as CallOptions,
|
|
2
|
-
export {
|
|
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';
|
|
3
3
|
|
|
4
4
|
interface VerifyWebhookOptions {
|
|
5
5
|
/** Max age of the signed timestamp, in seconds. Default 300 (5 min). */
|
package/dist/server.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { I as InvoError, S as ServerConfig, a as CallOptions,
|
|
2
|
-
export {
|
|
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';
|
|
3
3
|
|
|
4
4
|
interface VerifyWebhookOptions {
|
|
5
5
|
/** Max age of the signed timestamp, in seconds. Default 300 (5 min). */
|
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-D3XBTH4C.js';
|
|
2
|
+
export { InvoError } from './chunk-D3XBTH4C.js';
|
|
3
3
|
import { createHmac } from 'crypto';
|
|
4
4
|
|
|
5
5
|
var DEFAULT_TOLERANCE_SEC = 300;
|
|
@@ -26,6 +26,10 @@ declare class InvoError extends Error {
|
|
|
26
26
|
get isReceiverNotEnrolled(): boolean;
|
|
27
27
|
/** True if the session/SDK token has expired and the caller should re-mint + retry. */
|
|
28
28
|
get isTokenExpired(): boolean;
|
|
29
|
+
/** True if `enrollPasskey()` needs the OTP-grant flow (`enrollmentBegin`/`enrollmentVerify`). */
|
|
30
|
+
get isEnrollmentAuthorizationRequired(): boolean;
|
|
31
|
+
/** True if enrolling is blocked because another method exists — use `linkDevice`. */
|
|
32
|
+
get isEnrollmentProofRequired(): boolean;
|
|
29
33
|
/**
|
|
30
34
|
* True if an item purchase failed because the player's balance was too low (§4.8 → 400).
|
|
31
35
|
* The backend carries `required_amount` + `current_balance` on the body for the UI.
|
|
@@ -308,9 +312,101 @@ interface ApproveResult {
|
|
|
308
312
|
/** transfer-approve returns the sender's claim code; send-approve does not. */
|
|
309
313
|
claimCode?: string;
|
|
310
314
|
claimCodeExpiresAt?: string;
|
|
315
|
+
/**
|
|
316
|
+
* Present when the approve came back as an HTTP 202 **hold** rather than success —
|
|
317
|
+
* e.g. `"RISK_HOLD"`, `"GUARDIAN_APPROVAL_PENDING"`, `"STEP_UP_REQUIRED"` (the last
|
|
318
|
+
* only on non-passkey approvals). Success carries no holdReason. Terminal guardian
|
|
319
|
+
* outcomes (`GUARDIAN_APPROVAL_REJECTED`/`_EXPIRED` 410, `..._CHECK_UNAVAILABLE` 503)
|
|
320
|
+
* are thrown as `InvoError` instead.
|
|
321
|
+
*/
|
|
322
|
+
holdReason?: string;
|
|
323
|
+
/** Risk-engine detail on a RISK_HOLD: `{ decision, reasons[] }`. */
|
|
324
|
+
risk?: Record<string, unknown>;
|
|
325
|
+
/** The guardian_approval audit object on GUARDIAN_APPROVAL_PENDING. */
|
|
326
|
+
guardianApproval?: unknown;
|
|
327
|
+
/** Poll endpoint for guardian status (transfer holds only). */
|
|
328
|
+
pollEndpoint?: string;
|
|
311
329
|
raw: Record<string, unknown>;
|
|
312
330
|
}
|
|
313
331
|
interface ConfirmReceiptResult {
|
|
332
|
+
status: string;
|
|
333
|
+
/** Present on a 202 hold — e.g. `"RECIPIENT_IDENTITY_PENDING"`. */
|
|
334
|
+
holdReason?: string;
|
|
335
|
+
raw: Record<string, unknown>;
|
|
336
|
+
}
|
|
337
|
+
interface PendingCollectItem {
|
|
338
|
+
/** The transaction id — note the backend key is `transfer_id`. */
|
|
339
|
+
transferId: string;
|
|
340
|
+
/** "identity_gate" (you initiated → call approve*) | "receiving_confirm" (peer send → call confirmReceipt*). */
|
|
341
|
+
kind: string;
|
|
342
|
+
/** "transfer" | "send". */
|
|
343
|
+
flow: string;
|
|
344
|
+
amount: string | null;
|
|
345
|
+
/** Currency name (not id). */
|
|
346
|
+
currency: string;
|
|
347
|
+
/** Tenant name — destination (identity_gate) or source (receiving_confirm). */
|
|
348
|
+
counterpartyGame: string;
|
|
349
|
+
expiresAt: string | null;
|
|
350
|
+
stepUpRequired: boolean;
|
|
351
|
+
held: boolean;
|
|
352
|
+
/** "guardian_pending" | "device_requires_elevation" | null. */
|
|
353
|
+
holdReason: string | null;
|
|
354
|
+
raw: Record<string, unknown>;
|
|
355
|
+
}
|
|
356
|
+
interface PendingCollectResult {
|
|
357
|
+
pending: PendingCollectItem[];
|
|
358
|
+
raw: Record<string, unknown>;
|
|
359
|
+
}
|
|
360
|
+
interface DestinationsQuery {
|
|
361
|
+
/** "transfer" | "send". Defaults "transfer" (the set is identical for both today). */
|
|
362
|
+
direction?: "transfer" | "send";
|
|
363
|
+
}
|
|
364
|
+
/** A game/tenant this player can send/transfer to, with display metadata inline. */
|
|
365
|
+
interface DestinationGame {
|
|
366
|
+
gameId: string | number;
|
|
367
|
+
gameName: string;
|
|
368
|
+
tenantType?: string;
|
|
369
|
+
developerName?: string;
|
|
370
|
+
publisherName?: string;
|
|
371
|
+
genre?: string;
|
|
372
|
+
platform?: string;
|
|
373
|
+
gameStatus?: string;
|
|
374
|
+
gameIcon?: string;
|
|
375
|
+
gamePoster?: string;
|
|
376
|
+
gameUrl?: string;
|
|
377
|
+
gameDescription?: string;
|
|
378
|
+
currencyName: string;
|
|
379
|
+
currencySymbol: string;
|
|
380
|
+
currencySymbolUrl?: string;
|
|
381
|
+
/** Limits are decimal strings. */
|
|
382
|
+
minimumTransfer: string;
|
|
383
|
+
maximumTransfer: string;
|
|
384
|
+
raw: Record<string, unknown>;
|
|
385
|
+
}
|
|
386
|
+
interface DestinationsResult {
|
|
387
|
+
status: string;
|
|
388
|
+
sourceGameId: string | number;
|
|
389
|
+
sourceGameName: string;
|
|
390
|
+
sourceGameIcon?: string;
|
|
391
|
+
sourceCurrencyName: string;
|
|
392
|
+
sourceCurrencyIcon?: string;
|
|
393
|
+
universalTransfers: boolean;
|
|
394
|
+
/** "universal" (every live tenant) | "linked" (intersect linkedGameIds). */
|
|
395
|
+
transferMode: string;
|
|
396
|
+
availableGames: DestinationGame[];
|
|
397
|
+
totalDestinations: number;
|
|
398
|
+
direction: string;
|
|
399
|
+
/** Present in "linked" mode. */
|
|
400
|
+
linkedGameIds?: (string | number)[];
|
|
401
|
+
raw: Record<string, unknown>;
|
|
402
|
+
}
|
|
403
|
+
interface EnrollmentBeginResult {
|
|
404
|
+
status: string;
|
|
405
|
+
/** Channels the OTP was sent to, e.g. ["sms","email"]. */
|
|
406
|
+
channels: string[];
|
|
407
|
+
raw: Record<string, unknown>;
|
|
408
|
+
}
|
|
409
|
+
interface EnrollmentVerifyResult {
|
|
314
410
|
status: string;
|
|
315
411
|
raw: Record<string, unknown>;
|
|
316
412
|
}
|
|
@@ -320,4 +416,4 @@ interface LinkDeviceResult {
|
|
|
320
416
|
raw: Record<string, unknown>;
|
|
321
417
|
}
|
|
322
418
|
|
|
323
|
-
export { type ApproveResult as A, type ClientConfig as C, InvoError as I, type LinkDeviceResult as L, type OrderDetailsResult as O, type
|
|
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 };
|