@invonetwork/web-sdk 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,19 @@ All notable changes to `@invonetwork/web-sdk` are documented here. This project
4
4
  [Semantic Versioning](https://semver.org/). Releases are managed with
5
5
  [changesets](https://github.com/changesets/changesets).
6
6
 
7
+ ## [1.1.0] — 2026-07-01
8
+
9
+ Four additive server-side flows on `InvoServer` (the SMS-PIN / claim-code / status /
10
+ guardian paths partners still need alongside the passkey path).
11
+
12
+ - **SMS-PIN completion** — `verifySmsTransfer` / `verifySmsSend` (for `verificationMethod: "sms"`).
13
+ - **Claim-by-code** — `claimTransfer` / `claimCurrency`, incl. the `needsAccountSelection`
14
+ multi-account disambiguation (a `200` with `candidates`, not an error).
15
+ - **Status reads** — `getTransferStatus` / `getSendStatus` (surface `verificationState`).
16
+ - **Guardian approval-status poll** — `getGuardianApprovalStatus` (poll a hold to `state`).
17
+ - New typed results (`SmsVerifyResult`, `ClaimResult`, `TransactionStatusResult`,
18
+ `GuardianApprovalStatusResult`) + `InvoError.isPhoneShareApprovalRequired`.
19
+
7
20
  ## [1.0.0] — 2026-07-01
8
21
 
9
22
  **1.0 — stable.** The public API is now covered by a stability commitment: it follows
package/README.md CHANGED
@@ -179,7 +179,7 @@ const { checkoutUrl, sessionId, expiresAt } = await server.createCheckout({
179
179
  rail: "platform", // optional: "platform" (default) | "game" | "steam"
180
180
  successUrl: "https://you/buy/ok",
181
181
  cancelUrl: "https://you/buy/cancel",
182
- metadata: { yourOrderId: "ord_42" }, // echoed on the purchase.completed webhook
182
+ metadata: { yourOrderId: "ord_42" }, // echoed on the purchase.completed webhook (all rails); order_id also reconciles
183
183
  });
184
184
  // → send the browser to checkoutUrl (single-use, ~15 min)
185
185
  ```
@@ -225,7 +225,7 @@ const purchase = await server.purchaseCurrency({
225
225
  purchaseReference: crypto.randomUUID(), // idempotency key, required
226
226
  rail: "platform",
227
227
  paymentMethodId: "pm_...", // a tokenized payment method
228
- metadata: { yourOrderId: "ord_42" }, // echoed back on the purchase.completed webhook
228
+ metadata: { yourOrderId: "ord_42" }, // echoed on the purchase.completed webhook (all rails); order_id also reconciles
229
229
  });
230
230
  // purchase.status:
231
231
  // "success" → captured, purchase.newBalance updated
@@ -444,7 +444,7 @@ export const POST = createWebhookHandler({
444
444
 
445
445
  | Event | Fires for | Use it to |
446
446
  |---|---|---|
447
- | `purchase.completed` | every currency-purchase rail | grant currency (payload: `transaction_id, order_id, player_email, identity_id, usd_amount, currency_amount, currency_name, new_balance, rail, metadata`) — `metadata` echoes what you passed to `createCheckout`/`purchaseCurrency` |
447
+ | `purchase.completed` | every currency-purchase rail | grant currency (payload: `transaction_id, order_id, player_email, identity_id, usd_amount, currency_amount, currency_name, new_balance, rail`, `metadata`) — `metadata` echoes what you passed to `createCheckout`/`purchaseCurrency` (all rails); `order_id` is also on every webhook as a secondary reconciliation key (`getOrderDetails`). |
448
448
  | `purchase.failed` / `purchase.disputed` | `platform` rail only | handle failures/disputes |
449
449
  | `purchase.refunded` | `game` / `steam` rails | handle refunds |
450
450
  | `item.purchased` | every item purchase | **grant the in-game item** (payload includes `transaction_id, order_id, player_email, identity_id, item_id, item_name, item_quantity, unit_price, total_price, currency_name, new_balance, fee_breakdown`) |
@@ -530,6 +530,10 @@ try {
530
530
  | `getPlayerBalance({ playerEmail? \| playerId? })` | `{ player, balances, summary, raw }` |
531
531
  | `getInboundPending({ playerEmail? \| playerPhone? })` | `{ inboundPending, raw }` — live unclaimed inbound sends/transfers |
532
532
  | `getLinkedIdentities({ playerEmail? \| playerPhone? })` | `{ walletUserId, primaryEmail, primaryPhone, isMinor, emails, notFound, raw }` — **server-only** (returns PII) |
533
+ | `verifySmsTransfer(txnId, smsPin)` / `verifySmsSend(txnId, smsPin)` | `SmsVerifyResult` — complete the SMS-PIN path when `verificationMethod: "sms"` |
534
+ | `claimTransfer(input)` / `claimCurrency(input)` | `ClaimResult` — redeem a claim code (`needsAccountSelection` + `candidates` on a multi-account phone) |
535
+ | `getTransferStatus(txnId)` / `getSendStatus(txnId)` | `TransactionStatusResult` — poll outbound state (`verificationState`) |
536
+ | `getGuardianApprovalStatus(txnId)` | `GuardianApprovalStatusResult` — poll a guardian hold to resolution (`state`) |
533
537
  | `iterateItemPurchaseHistory({ playerEmail, pageSize? })` | async iterator over all history rows |
534
538
  | `verifyWebhook(rawBody, signatureHeader, secret \| secrets, opts?)` | typed `InvoWebhookEvent` (throws on bad signature) |
535
539
  | `verifyWebhookAsync(...)` | same as `verifyWebhook`, Web Crypto (edge/Workers/Deno/Bun) |
@@ -36,6 +36,10 @@ var InvoError = class _InvoError extends Error {
36
36
  const b = this.bodyObject();
37
37
  return this.code === "INSUFFICIENT_BALANCE" || "required_amount" in b || /insufficient[_ ]balance/i.test(this.message);
38
38
  }
39
+ /** True if a claim needs phone-share approval before it can complete (claim → 409). */
40
+ get isPhoneShareApprovalRequired() {
41
+ return this.code === "PHONE_SHARE_APPROVAL_REQUIRED";
42
+ }
39
43
  /** True if an idempotency-keyed request was a duplicate (item purchase → 409). */
40
44
  get isDuplicateRequest() {
41
45
  return this.code === "DUPLICATE_REQUEST" || this.status === 409 && /duplicate/i.test(this.message);
@@ -257,5 +261,5 @@ function retryAfterMs(parsed, headers) {
257
261
  }
258
262
 
259
263
  export { Http, InvoError, assertSecureBaseUrl };
260
- //# sourceMappingURL=chunk-D3XBTH4C.js.map
261
- //# sourceMappingURL=chunk-D3XBTH4C.js.map
264
+ //# sourceMappingURL=chunk-ZVKWDXSL.js.map
265
+ //# sourceMappingURL=chunk-ZVKWDXSL.js.map
package/dist/index.cjs CHANGED
@@ -38,6 +38,10 @@ var InvoError = class _InvoError extends Error {
38
38
  const b = this.bodyObject();
39
39
  return this.code === "INSUFFICIENT_BALANCE" || "required_amount" in b || /insufficient[_ ]balance/i.test(this.message);
40
40
  }
41
+ /** True if a claim needs phone-share approval before it can complete (claim → 409). */
42
+ get isPhoneShareApprovalRequired() {
43
+ return this.code === "PHONE_SHARE_APPROVAL_REQUIRED";
44
+ }
41
45
  /** True if an idempotency-keyed request was a duplicate (item purchase → 409). */
42
46
  get isDuplicateRequest() {
43
47
  return this.code === "DUPLICATE_REQUEST" || this.status === 409 && /duplicate/i.test(this.message);
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, 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';
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-CPt_5_Aw.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-CPt_5_Aw.cjs';
3
3
 
4
4
  declare class InvoClient {
5
5
  private readonly http;
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, 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';
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-CPt_5_Aw.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-CPt_5_Aw.js';
3
3
 
4
4
  declare class InvoClient {
5
5
  private readonly http;
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { assertSecureBaseUrl, Http, InvoError } from './chunk-D3XBTH4C.js';
2
- export { InvoError } from './chunk-D3XBTH4C.js';
1
+ import { assertSecureBaseUrl, Http, InvoError } from './chunk-ZVKWDXSL.js';
2
+ export { InvoError } from './chunk-ZVKWDXSL.js';
3
3
 
4
4
  // src/shared/webauthn.ts
5
5
  function b64urlToBuffer(value) {
package/dist/server.cjs CHANGED
@@ -40,6 +40,10 @@ var InvoError = class _InvoError extends Error {
40
40
  const b = this.bodyObject();
41
41
  return this.code === "INSUFFICIENT_BALANCE" || "required_amount" in b || /insufficient[_ ]balance/i.test(this.message);
42
42
  }
43
+ /** True if a claim needs phone-share approval before it can complete (claim → 409). */
44
+ get isPhoneShareApprovalRequired() {
45
+ return this.code === "PHONE_SHARE_APPROVAL_REQUIRED";
46
+ }
43
47
  /** True if an idempotency-keyed request was a duplicate (item purchase → 409). */
44
48
  get isDuplicateRequest() {
45
49
  return this.code === "DUPLICATE_REQUEST" || this.status === 409 && /duplicate/i.test(this.message);
@@ -418,7 +422,7 @@ async function hmacHexSubtle(secret, message) {
418
422
  }
419
423
 
420
424
  // src/server.ts
421
- var DEFAULT_UA = "invonetwork-web-sdk/1.0.0 (+https://invo.network)";
425
+ var DEFAULT_UA = "invonetwork-web-sdk/1.1.0 (+https://invo.network)";
422
426
  var MAX_USD_AMOUNT = 999.99;
423
427
  var MAX_ITEM_PRICE = 999999.99;
424
428
  function invalidInput(label, value, why) {
@@ -480,6 +484,57 @@ function toInboundPendingItem(row) {
480
484
  raw: row
481
485
  };
482
486
  }
487
+ function toSmsVerifyResult(raw) {
488
+ return {
489
+ status: String(raw["status"] ?? ""),
490
+ transactionId: String(raw["transaction_id"] ?? ""),
491
+ claimCode: raw["claim_code"],
492
+ newBalance: raw["new_balance"] ?? null,
493
+ orderId: raw["order_id"],
494
+ raw
495
+ };
496
+ }
497
+ function toClaimResult(raw) {
498
+ const status = String(raw["status"] ?? "");
499
+ return {
500
+ status,
501
+ transactionId: raw["transaction_id"],
502
+ newBalance: raw["new_balance"] ?? null,
503
+ currencyName: raw["currency_name"],
504
+ orderId: raw["order_id"],
505
+ needsAccountSelection: status === "needs_account_selection",
506
+ candidates: Array.isArray(raw["candidates"]) ? raw["candidates"] : void 0,
507
+ raw
508
+ };
509
+ }
510
+ function toTransactionStatus(raw) {
511
+ return {
512
+ transactionId: String(raw["transaction_id"] ?? ""),
513
+ transactionType: String(raw["transaction_type"] ?? ""),
514
+ transactionStatus: String(raw["transaction_status"] ?? ""),
515
+ verificationState: String(raw["verification_state"] ?? ""),
516
+ amount: raw["amount"] ?? null,
517
+ netAmount: raw["net_amount"] ?? null,
518
+ currencyId: raw["currency_id"] ?? "",
519
+ orderId: raw["order_id"],
520
+ toPhone: raw["to_phone"],
521
+ toIdentityId: raw["to_identity_id"] ?? null,
522
+ raw
523
+ };
524
+ }
525
+ function toGuardianApprovalStatus(raw) {
526
+ const approval = raw["approval"] ?? {};
527
+ return {
528
+ state: String(approval["state"] ?? ""),
529
+ approvalId: approval["approval_id"],
530
+ transactionId: approval["transaction_id"],
531
+ expiresAt: approval["expires_at"],
532
+ decidedAt: approval["decided_at"],
533
+ decisionSource: approval["decision_source"],
534
+ actionDescription: approval["action_description"],
535
+ raw
536
+ };
537
+ }
483
538
  function toCurrencyBalance(row) {
484
539
  return {
485
540
  currencyId: row["currency_id"] ?? "",
@@ -926,6 +981,132 @@ var InvoServer = class {
926
981
  raw
927
982
  };
928
983
  }
984
+ /**
985
+ * Complete a TRANSFER's SMS-PIN step (un-enrolled fallback). NOT idempotent —
986
+ * each attempt is counted server-side, so this is never auto-retried. A bad PIN
987
+ * throws a 400 InvoError carrying `attempts_remaining` on `err.body`; a blocked
988
+ * recipient throws 429 (`sms_verification_blocked`).
989
+ */
990
+ async verifySmsTransfer(transactionId, smsPin, opts) {
991
+ if (typeof transactionId !== "string" || !transactionId.trim()) {
992
+ throw invalidInput("transactionId", transactionId, "is required");
993
+ }
994
+ if (typeof smsPin !== "string" || !smsPin.trim()) {
995
+ throw invalidInput("smsPin", smsPin, "is required");
996
+ }
997
+ const raw = await this.http.post(
998
+ "/api/transfers/verify-sms",
999
+ { transaction_id: transactionId, sms_pin: smsPin },
1000
+ this.auth,
1001
+ { signal: opts?.signal }
1002
+ // NOT idempotent — attempts are counted; never auto-retried
1003
+ );
1004
+ return toSmsVerifyResult(raw);
1005
+ }
1006
+ /** Complete a SEND's SMS-PIN step (un-enrolled fallback). See verifySmsTransfer. */
1007
+ async verifySmsSend(transactionId, smsPin, opts) {
1008
+ if (typeof transactionId !== "string" || !transactionId.trim()) {
1009
+ throw invalidInput("transactionId", transactionId, "is required");
1010
+ }
1011
+ if (typeof smsPin !== "string" || !smsPin.trim()) {
1012
+ throw invalidInput("smsPin", smsPin, "is required");
1013
+ }
1014
+ const raw = await this.http.post(
1015
+ "/api/currency-sends/verify-sms",
1016
+ { transaction_id: transactionId, sms_pin: smsPin },
1017
+ this.auth,
1018
+ { signal: opts?.signal }
1019
+ // NOT idempotent — attempts are counted; never auto-retried
1020
+ );
1021
+ return toSmsVerifyResult(raw);
1022
+ }
1023
+ /**
1024
+ * Claim an inbound TRANSFER by its claim code, crediting one of this game's currencies.
1025
+ * NOT idempotent. A 200 may come back as `needsAccountSelection` (the recipient phone
1026
+ * maps to >1 of your players) — inspect `result.candidates` and re-submit with
1027
+ * `targetPlayerId`. Errors surface via InvoError (404 unknown code, 403 phone mismatch,
1028
+ * 400 not-claimable/expired/wrong-value, 409 `err.isPhoneShareApprovalRequired`, …).
1029
+ */
1030
+ async claimTransfer(input, opts) {
1031
+ const body = {
1032
+ claim_code: input.claimCode,
1033
+ target_player_name: input.targetPlayerName,
1034
+ target_player_email: input.targetPlayerEmail,
1035
+ target_player_phone: input.targetPlayerPhone,
1036
+ target_currency_id: input.targetCurrencyId
1037
+ };
1038
+ if (input.targetPlayerId != null) body["target_player_id"] = input.targetPlayerId;
1039
+ const raw = await this.http.post(
1040
+ "/api/transfers/claim-transfer",
1041
+ body,
1042
+ this.auth,
1043
+ { signal: opts?.signal }
1044
+ // NOT idempotent — never auto-retried
1045
+ );
1046
+ return toClaimResult(raw);
1047
+ }
1048
+ /**
1049
+ * Claim an inbound SEND by its claim code. NOT idempotent. May return
1050
+ * `needsAccountSelection` (multi-account phone) — re-submit with `receiverPlayerId`.
1051
+ * See claimTransfer for the error surface.
1052
+ */
1053
+ async claimCurrency(input, opts) {
1054
+ const body = {
1055
+ claim_code: input.claimCode,
1056
+ receiver_player_name: input.receiverPlayerName,
1057
+ receiver_player_email: input.receiverPlayerEmail,
1058
+ receiver_player_phone: input.receiverPlayerPhone
1059
+ };
1060
+ if (input.receiverPlayerId != null) body["receiver_player_id"] = input.receiverPlayerId;
1061
+ const raw = await this.http.post(
1062
+ "/api/currency-sends/claim-currency",
1063
+ body,
1064
+ this.auth,
1065
+ { signal: opts?.signal }
1066
+ // NOT idempotent — never auto-retried
1067
+ );
1068
+ return toClaimResult(raw);
1069
+ }
1070
+ /** Read a TRANSFER's status (verification_state + amounts + destination). */
1071
+ async getTransferStatus(transactionId, opts) {
1072
+ if (typeof transactionId !== "string" || !transactionId.trim()) {
1073
+ throw invalidInput("transactionId", transactionId, "is required");
1074
+ }
1075
+ const raw = await this.http.get(
1076
+ `/api/transfers/${encodeURIComponent(transactionId)}/status`,
1077
+ this.auth,
1078
+ { signal: opts?.signal }
1079
+ );
1080
+ return toTransactionStatus(raw);
1081
+ }
1082
+ /** Read a SEND's status (adds fees, game names, claim_info, … on `raw`). */
1083
+ async getSendStatus(transactionId, opts) {
1084
+ if (typeof transactionId !== "string" || !transactionId.trim()) {
1085
+ throw invalidInput("transactionId", transactionId, "is required");
1086
+ }
1087
+ const raw = await this.http.get(
1088
+ `/api/currency-sends/${encodeURIComponent(transactionId)}/status`,
1089
+ this.auth,
1090
+ { signal: opts?.signal }
1091
+ );
1092
+ return toTransactionStatus(raw);
1093
+ }
1094
+ /**
1095
+ * Poll a transaction's guardian approval status (minor/guardian flow). The retry
1096
+ * layer already retries the 503 (`{status:"unavailable"}`) for this idempotent GET;
1097
+ * a missing approval throws a 404 InvoError (`{status:"not_found"}`).
1098
+ */
1099
+ async getGuardianApprovalStatus(transactionId, opts) {
1100
+ if (typeof transactionId !== "string" || !transactionId.trim()) {
1101
+ throw invalidInput("transactionId", transactionId, "is required");
1102
+ }
1103
+ const raw = await this.http.get(
1104
+ `/api/transactions/${encodeURIComponent(transactionId)}/approval-status`,
1105
+ this.auth,
1106
+ { signal: opts?.signal }
1107
+ );
1108
+ return toGuardianApprovalStatus(raw);
1109
+ }
929
1110
  toInitiateResult(raw) {
930
1111
  const vm = raw["verification_method"];
931
1112
  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, 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';
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, M as SmsVerifyResult, N as ClaimTransferInput, Q as ClaimResult, T as ClaimCurrencyInput, U as TransactionStatusResult, W as GuardianApprovalStatusResult } from './types-CPt_5_Aw.cjs';
2
+ export { X as CurrencyBalance, Y as InboundPendingItem, g as InvoErrorInfo, h as InvoHooks, i as InvoRequestInfo, j as InvoResponseInfo, Z as LinkedIdentityEmail, R as Rail, V as VerificationMethod } from './types-CPt_5_Aw.cjs';
3
3
 
4
4
  interface VerifyWebhookOptions {
5
5
  /** Max age of the signed timestamp, in seconds. Default 300 (5 min). */
@@ -225,7 +225,40 @@ declare class InvoServer {
225
225
  * ⚠️ Returns first-party PII (emails/phones) — never expose this to the browser.
226
226
  */
227
227
  getLinkedIdentities(query: LinkedIdentitiesQuery, opts?: CallOptions): Promise<LinkedIdentitiesResult>;
228
+ /**
229
+ * Complete a TRANSFER's SMS-PIN step (un-enrolled fallback). NOT idempotent —
230
+ * each attempt is counted server-side, so this is never auto-retried. A bad PIN
231
+ * throws a 400 InvoError carrying `attempts_remaining` on `err.body`; a blocked
232
+ * recipient throws 429 (`sms_verification_blocked`).
233
+ */
234
+ verifySmsTransfer(transactionId: string, smsPin: string, opts?: CallOptions): Promise<SmsVerifyResult>;
235
+ /** Complete a SEND's SMS-PIN step (un-enrolled fallback). See verifySmsTransfer. */
236
+ verifySmsSend(transactionId: string, smsPin: string, opts?: CallOptions): Promise<SmsVerifyResult>;
237
+ /**
238
+ * Claim an inbound TRANSFER by its claim code, crediting one of this game's currencies.
239
+ * NOT idempotent. A 200 may come back as `needsAccountSelection` (the recipient phone
240
+ * maps to >1 of your players) — inspect `result.candidates` and re-submit with
241
+ * `targetPlayerId`. Errors surface via InvoError (404 unknown code, 403 phone mismatch,
242
+ * 400 not-claimable/expired/wrong-value, 409 `err.isPhoneShareApprovalRequired`, …).
243
+ */
244
+ claimTransfer(input: ClaimTransferInput, opts?: CallOptions): Promise<ClaimResult>;
245
+ /**
246
+ * Claim an inbound SEND by its claim code. NOT idempotent. May return
247
+ * `needsAccountSelection` (multi-account phone) — re-submit with `receiverPlayerId`.
248
+ * See claimTransfer for the error surface.
249
+ */
250
+ claimCurrency(input: ClaimCurrencyInput, opts?: CallOptions): Promise<ClaimResult>;
251
+ /** Read a TRANSFER's status (verification_state + amounts + destination). */
252
+ getTransferStatus(transactionId: string, opts?: CallOptions): Promise<TransactionStatusResult>;
253
+ /** Read a SEND's status (adds fees, game names, claim_info, … on `raw`). */
254
+ getSendStatus(transactionId: string, opts?: CallOptions): Promise<TransactionStatusResult>;
255
+ /**
256
+ * Poll a transaction's guardian approval status (minor/guardian flow). The retry
257
+ * layer already retries the 503 (`{status:"unavailable"}`) for this idempotent GET;
258
+ * a missing approval throws a 404 InvoError (`{status:"not_found"}`).
259
+ */
260
+ getGuardianApprovalStatus(transactionId: string, opts?: CallOptions): Promise<GuardianApprovalStatusResult>;
228
261
  private toInitiateResult;
229
262
  }
230
263
 
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 };
264
+ export { CallOptions, ClaimCurrencyInput, ClaimResult, ClaimTransferInput, ConfirmPaymentResult, CreateCheckoutInput, CreateCheckoutResult, GuardianApprovalStatusResult, 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, SmsVerifyResult, TransactionStatusResult, 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, 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';
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, M as SmsVerifyResult, N as ClaimTransferInput, Q as ClaimResult, T as ClaimCurrencyInput, U as TransactionStatusResult, W as GuardianApprovalStatusResult } from './types-CPt_5_Aw.js';
2
+ export { X as CurrencyBalance, Y as InboundPendingItem, g as InvoErrorInfo, h as InvoHooks, i as InvoRequestInfo, j as InvoResponseInfo, Z as LinkedIdentityEmail, R as Rail, V as VerificationMethod } from './types-CPt_5_Aw.js';
3
3
 
4
4
  interface VerifyWebhookOptions {
5
5
  /** Max age of the signed timestamp, in seconds. Default 300 (5 min). */
@@ -225,7 +225,40 @@ declare class InvoServer {
225
225
  * ⚠️ Returns first-party PII (emails/phones) — never expose this to the browser.
226
226
  */
227
227
  getLinkedIdentities(query: LinkedIdentitiesQuery, opts?: CallOptions): Promise<LinkedIdentitiesResult>;
228
+ /**
229
+ * Complete a TRANSFER's SMS-PIN step (un-enrolled fallback). NOT idempotent —
230
+ * each attempt is counted server-side, so this is never auto-retried. A bad PIN
231
+ * throws a 400 InvoError carrying `attempts_remaining` on `err.body`; a blocked
232
+ * recipient throws 429 (`sms_verification_blocked`).
233
+ */
234
+ verifySmsTransfer(transactionId: string, smsPin: string, opts?: CallOptions): Promise<SmsVerifyResult>;
235
+ /** Complete a SEND's SMS-PIN step (un-enrolled fallback). See verifySmsTransfer. */
236
+ verifySmsSend(transactionId: string, smsPin: string, opts?: CallOptions): Promise<SmsVerifyResult>;
237
+ /**
238
+ * Claim an inbound TRANSFER by its claim code, crediting one of this game's currencies.
239
+ * NOT idempotent. A 200 may come back as `needsAccountSelection` (the recipient phone
240
+ * maps to >1 of your players) — inspect `result.candidates` and re-submit with
241
+ * `targetPlayerId`. Errors surface via InvoError (404 unknown code, 403 phone mismatch,
242
+ * 400 not-claimable/expired/wrong-value, 409 `err.isPhoneShareApprovalRequired`, …).
243
+ */
244
+ claimTransfer(input: ClaimTransferInput, opts?: CallOptions): Promise<ClaimResult>;
245
+ /**
246
+ * Claim an inbound SEND by its claim code. NOT idempotent. May return
247
+ * `needsAccountSelection` (multi-account phone) — re-submit with `receiverPlayerId`.
248
+ * See claimTransfer for the error surface.
249
+ */
250
+ claimCurrency(input: ClaimCurrencyInput, opts?: CallOptions): Promise<ClaimResult>;
251
+ /** Read a TRANSFER's status (verification_state + amounts + destination). */
252
+ getTransferStatus(transactionId: string, opts?: CallOptions): Promise<TransactionStatusResult>;
253
+ /** Read a SEND's status (adds fees, game names, claim_info, … on `raw`). */
254
+ getSendStatus(transactionId: string, opts?: CallOptions): Promise<TransactionStatusResult>;
255
+ /**
256
+ * Poll a transaction's guardian approval status (minor/guardian flow). The retry
257
+ * layer already retries the 503 (`{status:"unavailable"}`) for this idempotent GET;
258
+ * a missing approval throws a 404 InvoError (`{status:"not_found"}`).
259
+ */
260
+ getGuardianApprovalStatus(transactionId: string, opts?: CallOptions): Promise<GuardianApprovalStatusResult>;
228
261
  private toInitiateResult;
229
262
  }
230
263
 
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 };
264
+ export { CallOptions, ClaimCurrencyInput, ClaimResult, ClaimTransferInput, ConfirmPaymentResult, CreateCheckoutInput, CreateCheckoutResult, GuardianApprovalStatusResult, 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, SmsVerifyResult, TransactionStatusResult, type TransferEventData, type VerifyWebhookOptions, type WebhookHandlerOptions, createWebhookHandler, verifyWebhook, verifyWebhookAsync };
package/dist/server.js CHANGED
@@ -1,5 +1,5 @@
1
- import { InvoError, assertSecureBaseUrl, Http } from './chunk-D3XBTH4C.js';
2
- export { InvoError } from './chunk-D3XBTH4C.js';
1
+ import { InvoError, assertSecureBaseUrl, Http } from './chunk-ZVKWDXSL.js';
2
+ export { InvoError } from './chunk-ZVKWDXSL.js';
3
3
  import { createHmac } from 'crypto';
4
4
 
5
5
  var DEFAULT_TOLERANCE_SEC = 300;
@@ -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/1.0.0 (+https://invo.network)";
164
+ var DEFAULT_UA = "invonetwork-web-sdk/1.1.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) {
@@ -223,6 +223,57 @@ function toInboundPendingItem(row) {
223
223
  raw: row
224
224
  };
225
225
  }
226
+ function toSmsVerifyResult(raw) {
227
+ return {
228
+ status: String(raw["status"] ?? ""),
229
+ transactionId: String(raw["transaction_id"] ?? ""),
230
+ claimCode: raw["claim_code"],
231
+ newBalance: raw["new_balance"] ?? null,
232
+ orderId: raw["order_id"],
233
+ raw
234
+ };
235
+ }
236
+ function toClaimResult(raw) {
237
+ const status = String(raw["status"] ?? "");
238
+ return {
239
+ status,
240
+ transactionId: raw["transaction_id"],
241
+ newBalance: raw["new_balance"] ?? null,
242
+ currencyName: raw["currency_name"],
243
+ orderId: raw["order_id"],
244
+ needsAccountSelection: status === "needs_account_selection",
245
+ candidates: Array.isArray(raw["candidates"]) ? raw["candidates"] : void 0,
246
+ raw
247
+ };
248
+ }
249
+ function toTransactionStatus(raw) {
250
+ return {
251
+ transactionId: String(raw["transaction_id"] ?? ""),
252
+ transactionType: String(raw["transaction_type"] ?? ""),
253
+ transactionStatus: String(raw["transaction_status"] ?? ""),
254
+ verificationState: String(raw["verification_state"] ?? ""),
255
+ amount: raw["amount"] ?? null,
256
+ netAmount: raw["net_amount"] ?? null,
257
+ currencyId: raw["currency_id"] ?? "",
258
+ orderId: raw["order_id"],
259
+ toPhone: raw["to_phone"],
260
+ toIdentityId: raw["to_identity_id"] ?? null,
261
+ raw
262
+ };
263
+ }
264
+ function toGuardianApprovalStatus(raw) {
265
+ const approval = raw["approval"] ?? {};
266
+ return {
267
+ state: String(approval["state"] ?? ""),
268
+ approvalId: approval["approval_id"],
269
+ transactionId: approval["transaction_id"],
270
+ expiresAt: approval["expires_at"],
271
+ decidedAt: approval["decided_at"],
272
+ decisionSource: approval["decision_source"],
273
+ actionDescription: approval["action_description"],
274
+ raw
275
+ };
276
+ }
226
277
  function toCurrencyBalance(row) {
227
278
  return {
228
279
  currencyId: row["currency_id"] ?? "",
@@ -669,6 +720,132 @@ var InvoServer = class {
669
720
  raw
670
721
  };
671
722
  }
723
+ /**
724
+ * Complete a TRANSFER's SMS-PIN step (un-enrolled fallback). NOT idempotent —
725
+ * each attempt is counted server-side, so this is never auto-retried. A bad PIN
726
+ * throws a 400 InvoError carrying `attempts_remaining` on `err.body`; a blocked
727
+ * recipient throws 429 (`sms_verification_blocked`).
728
+ */
729
+ async verifySmsTransfer(transactionId, smsPin, opts) {
730
+ if (typeof transactionId !== "string" || !transactionId.trim()) {
731
+ throw invalidInput("transactionId", transactionId, "is required");
732
+ }
733
+ if (typeof smsPin !== "string" || !smsPin.trim()) {
734
+ throw invalidInput("smsPin", smsPin, "is required");
735
+ }
736
+ const raw = await this.http.post(
737
+ "/api/transfers/verify-sms",
738
+ { transaction_id: transactionId, sms_pin: smsPin },
739
+ this.auth,
740
+ { signal: opts?.signal }
741
+ // NOT idempotent — attempts are counted; never auto-retried
742
+ );
743
+ return toSmsVerifyResult(raw);
744
+ }
745
+ /** Complete a SEND's SMS-PIN step (un-enrolled fallback). See verifySmsTransfer. */
746
+ async verifySmsSend(transactionId, smsPin, opts) {
747
+ if (typeof transactionId !== "string" || !transactionId.trim()) {
748
+ throw invalidInput("transactionId", transactionId, "is required");
749
+ }
750
+ if (typeof smsPin !== "string" || !smsPin.trim()) {
751
+ throw invalidInput("smsPin", smsPin, "is required");
752
+ }
753
+ const raw = await this.http.post(
754
+ "/api/currency-sends/verify-sms",
755
+ { transaction_id: transactionId, sms_pin: smsPin },
756
+ this.auth,
757
+ { signal: opts?.signal }
758
+ // NOT idempotent — attempts are counted; never auto-retried
759
+ );
760
+ return toSmsVerifyResult(raw);
761
+ }
762
+ /**
763
+ * Claim an inbound TRANSFER by its claim code, crediting one of this game's currencies.
764
+ * NOT idempotent. A 200 may come back as `needsAccountSelection` (the recipient phone
765
+ * maps to >1 of your players) — inspect `result.candidates` and re-submit with
766
+ * `targetPlayerId`. Errors surface via InvoError (404 unknown code, 403 phone mismatch,
767
+ * 400 not-claimable/expired/wrong-value, 409 `err.isPhoneShareApprovalRequired`, …).
768
+ */
769
+ async claimTransfer(input, opts) {
770
+ const body = {
771
+ claim_code: input.claimCode,
772
+ target_player_name: input.targetPlayerName,
773
+ target_player_email: input.targetPlayerEmail,
774
+ target_player_phone: input.targetPlayerPhone,
775
+ target_currency_id: input.targetCurrencyId
776
+ };
777
+ if (input.targetPlayerId != null) body["target_player_id"] = input.targetPlayerId;
778
+ const raw = await this.http.post(
779
+ "/api/transfers/claim-transfer",
780
+ body,
781
+ this.auth,
782
+ { signal: opts?.signal }
783
+ // NOT idempotent — never auto-retried
784
+ );
785
+ return toClaimResult(raw);
786
+ }
787
+ /**
788
+ * Claim an inbound SEND by its claim code. NOT idempotent. May return
789
+ * `needsAccountSelection` (multi-account phone) — re-submit with `receiverPlayerId`.
790
+ * See claimTransfer for the error surface.
791
+ */
792
+ async claimCurrency(input, opts) {
793
+ const body = {
794
+ claim_code: input.claimCode,
795
+ receiver_player_name: input.receiverPlayerName,
796
+ receiver_player_email: input.receiverPlayerEmail,
797
+ receiver_player_phone: input.receiverPlayerPhone
798
+ };
799
+ if (input.receiverPlayerId != null) body["receiver_player_id"] = input.receiverPlayerId;
800
+ const raw = await this.http.post(
801
+ "/api/currency-sends/claim-currency",
802
+ body,
803
+ this.auth,
804
+ { signal: opts?.signal }
805
+ // NOT idempotent — never auto-retried
806
+ );
807
+ return toClaimResult(raw);
808
+ }
809
+ /** Read a TRANSFER's status (verification_state + amounts + destination). */
810
+ async getTransferStatus(transactionId, opts) {
811
+ if (typeof transactionId !== "string" || !transactionId.trim()) {
812
+ throw invalidInput("transactionId", transactionId, "is required");
813
+ }
814
+ const raw = await this.http.get(
815
+ `/api/transfers/${encodeURIComponent(transactionId)}/status`,
816
+ this.auth,
817
+ { signal: opts?.signal }
818
+ );
819
+ return toTransactionStatus(raw);
820
+ }
821
+ /** Read a SEND's status (adds fees, game names, claim_info, … on `raw`). */
822
+ async getSendStatus(transactionId, opts) {
823
+ if (typeof transactionId !== "string" || !transactionId.trim()) {
824
+ throw invalidInput("transactionId", transactionId, "is required");
825
+ }
826
+ const raw = await this.http.get(
827
+ `/api/currency-sends/${encodeURIComponent(transactionId)}/status`,
828
+ this.auth,
829
+ { signal: opts?.signal }
830
+ );
831
+ return toTransactionStatus(raw);
832
+ }
833
+ /**
834
+ * Poll a transaction's guardian approval status (minor/guardian flow). The retry
835
+ * layer already retries the 503 (`{status:"unavailable"}`) for this idempotent GET;
836
+ * a missing approval throws a 404 InvoError (`{status:"not_found"}`).
837
+ */
838
+ async getGuardianApprovalStatus(transactionId, opts) {
839
+ if (typeof transactionId !== "string" || !transactionId.trim()) {
840
+ throw invalidInput("transactionId", transactionId, "is required");
841
+ }
842
+ const raw = await this.http.get(
843
+ `/api/transactions/${encodeURIComponent(transactionId)}/approval-status`,
844
+ this.auth,
845
+ { signal: opts?.signal }
846
+ );
847
+ return toGuardianApprovalStatus(raw);
848
+ }
672
849
  toInitiateResult(raw) {
673
850
  const vm = raw["verification_method"];
674
851
  const guardian = raw["guardian_approval"];
@@ -37,6 +37,8 @@ declare class InvoError extends Error {
37
37
  * is a rate-limit, not a top-up condition) is NOT misclassified as this.
38
38
  */
39
39
  get isInsufficientBalance(): boolean;
40
+ /** True if a claim needs phone-share approval before it can complete (claim → 409). */
41
+ get isPhoneShareApprovalRequired(): boolean;
40
42
  /** True if an idempotency-keyed request was a duplicate (item purchase → 409). */
41
43
  get isDuplicateRequest(): boolean;
42
44
  /** Seconds to wait before retrying, when the backend throttled the call (429 `retry_after`). */
@@ -459,5 +461,79 @@ interface LinkDeviceResult {
459
461
  status: string;
460
462
  raw: Record<string, unknown>;
461
463
  }
464
+ interface SmsVerifyResult {
465
+ /** "success" on a completed verification. */
466
+ status: string;
467
+ transactionId: string;
468
+ /** The recipient claim code, when the completion issued one. */
469
+ claimCode?: string;
470
+ /** Canonical balance AFTER completion, when the backend returned it. */
471
+ newBalance?: string | number | null;
472
+ orderId?: string;
473
+ raw: Record<string, unknown>;
474
+ }
475
+ interface ClaimTransferInput {
476
+ claimCode: string;
477
+ targetPlayerName: string;
478
+ targetPlayerEmail: string;
479
+ targetPlayerPhone: string;
480
+ /** Which of this game's currencies to credit. */
481
+ targetCurrencyId: string | number;
482
+ /** Re-submit with this after a `needsAccountSelection` result to disambiguate a
483
+ * phone that maps to more than one of your players. */
484
+ targetPlayerId?: string | number;
485
+ }
486
+ interface ClaimCurrencyInput {
487
+ claimCode: string;
488
+ receiverPlayerName: string;
489
+ receiverPlayerEmail: string;
490
+ receiverPlayerPhone: string;
491
+ /** Re-submit with this after a `needsAccountSelection` result (multi-account phone). */
492
+ receiverPlayerId?: string | number;
493
+ }
494
+ interface ClaimResult {
495
+ /** "success" on completion, or "needs_account_selection" (see `needsAccountSelection`). */
496
+ status: string;
497
+ transactionId?: string;
498
+ newBalance?: string | number | null;
499
+ currencyName?: string;
500
+ orderId?: string;
501
+ /**
502
+ * true when a 200 came back as `status:"needs_account_selection"` (the recipient
503
+ * phone maps to more than one of your players). NOT an error — re-submit the claim
504
+ * with `targetPlayerId` (transfer) / `receiverPlayerId` (send) from `candidates`.
505
+ */
506
+ needsAccountSelection: boolean;
507
+ /** Candidate accounts to disambiguate, present on a `needsAccountSelection` result. */
508
+ candidates?: Record<string, unknown>[];
509
+ raw: Record<string, unknown>;
510
+ }
511
+ /** `verification_state` ∈ awaiting | approved | completed | expired | failed | unknown. */
512
+ interface TransactionStatusResult {
513
+ transactionId: string;
514
+ /** "transfer" | "send". */
515
+ transactionType: string;
516
+ transactionStatus: string;
517
+ verificationState: string;
518
+ amount: string | number | null;
519
+ netAmount: string | number | null;
520
+ currencyId: string | number;
521
+ orderId?: string;
522
+ toPhone?: string;
523
+ toIdentityId?: string | null;
524
+ /** Everything else (send fees, game names, claim_info, failure_reason, sender claim_code, …). */
525
+ raw: Record<string, unknown>;
526
+ }
527
+ /** `state` ∈ pending | approved | rejected | expired. */
528
+ interface GuardianApprovalStatusResult {
529
+ state: string;
530
+ approvalId?: string;
531
+ transactionId?: string;
532
+ expiresAt?: string;
533
+ decidedAt?: string;
534
+ decisionSource?: string;
535
+ actionDescription?: string;
536
+ raw: Record<string, unknown>;
537
+ }
462
538
 
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 };
539
+ 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 SmsVerifyResult as M, type ClaimTransferInput as N, type OrderDetailsResult as O, type PendingCollectResult as P, type ClaimResult as Q, type Rail as R, type ServerConfig as S, type ClaimCurrencyInput as T, type TransactionStatusResult as U, type VerificationMethod as V, type GuardianApprovalStatusResult as W, type CurrencyBalance as X, type InboundPendingItem as Y, type LinkedIdentityEmail as Z, 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 };
@@ -37,6 +37,8 @@ declare class InvoError extends Error {
37
37
  * is a rate-limit, not a top-up condition) is NOT misclassified as this.
38
38
  */
39
39
  get isInsufficientBalance(): boolean;
40
+ /** True if a claim needs phone-share approval before it can complete (claim → 409). */
41
+ get isPhoneShareApprovalRequired(): boolean;
40
42
  /** True if an idempotency-keyed request was a duplicate (item purchase → 409). */
41
43
  get isDuplicateRequest(): boolean;
42
44
  /** Seconds to wait before retrying, when the backend throttled the call (429 `retry_after`). */
@@ -459,5 +461,79 @@ interface LinkDeviceResult {
459
461
  status: string;
460
462
  raw: Record<string, unknown>;
461
463
  }
464
+ interface SmsVerifyResult {
465
+ /** "success" on a completed verification. */
466
+ status: string;
467
+ transactionId: string;
468
+ /** The recipient claim code, when the completion issued one. */
469
+ claimCode?: string;
470
+ /** Canonical balance AFTER completion, when the backend returned it. */
471
+ newBalance?: string | number | null;
472
+ orderId?: string;
473
+ raw: Record<string, unknown>;
474
+ }
475
+ interface ClaimTransferInput {
476
+ claimCode: string;
477
+ targetPlayerName: string;
478
+ targetPlayerEmail: string;
479
+ targetPlayerPhone: string;
480
+ /** Which of this game's currencies to credit. */
481
+ targetCurrencyId: string | number;
482
+ /** Re-submit with this after a `needsAccountSelection` result to disambiguate a
483
+ * phone that maps to more than one of your players. */
484
+ targetPlayerId?: string | number;
485
+ }
486
+ interface ClaimCurrencyInput {
487
+ claimCode: string;
488
+ receiverPlayerName: string;
489
+ receiverPlayerEmail: string;
490
+ receiverPlayerPhone: string;
491
+ /** Re-submit with this after a `needsAccountSelection` result (multi-account phone). */
492
+ receiverPlayerId?: string | number;
493
+ }
494
+ interface ClaimResult {
495
+ /** "success" on completion, or "needs_account_selection" (see `needsAccountSelection`). */
496
+ status: string;
497
+ transactionId?: string;
498
+ newBalance?: string | number | null;
499
+ currencyName?: string;
500
+ orderId?: string;
501
+ /**
502
+ * true when a 200 came back as `status:"needs_account_selection"` (the recipient
503
+ * phone maps to more than one of your players). NOT an error — re-submit the claim
504
+ * with `targetPlayerId` (transfer) / `receiverPlayerId` (send) from `candidates`.
505
+ */
506
+ needsAccountSelection: boolean;
507
+ /** Candidate accounts to disambiguate, present on a `needsAccountSelection` result. */
508
+ candidates?: Record<string, unknown>[];
509
+ raw: Record<string, unknown>;
510
+ }
511
+ /** `verification_state` ∈ awaiting | approved | completed | expired | failed | unknown. */
512
+ interface TransactionStatusResult {
513
+ transactionId: string;
514
+ /** "transfer" | "send". */
515
+ transactionType: string;
516
+ transactionStatus: string;
517
+ verificationState: string;
518
+ amount: string | number | null;
519
+ netAmount: string | number | null;
520
+ currencyId: string | number;
521
+ orderId?: string;
522
+ toPhone?: string;
523
+ toIdentityId?: string | null;
524
+ /** Everything else (send fees, game names, claim_info, failure_reason, sender claim_code, …). */
525
+ raw: Record<string, unknown>;
526
+ }
527
+ /** `state` ∈ pending | approved | rejected | expired. */
528
+ interface GuardianApprovalStatusResult {
529
+ state: string;
530
+ approvalId?: string;
531
+ transactionId?: string;
532
+ expiresAt?: string;
533
+ decidedAt?: string;
534
+ decisionSource?: string;
535
+ actionDescription?: string;
536
+ raw: Record<string, unknown>;
537
+ }
462
538
 
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 };
539
+ 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 SmsVerifyResult as M, type ClaimTransferInput as N, type OrderDetailsResult as O, type PendingCollectResult as P, type ClaimResult as Q, type Rail as R, type ServerConfig as S, type ClaimCurrencyInput as T, type TransactionStatusResult as U, type VerificationMethod as V, type GuardianApprovalStatusResult as W, type CurrencyBalance as X, type InboundPendingItem as Y, type LinkedIdentityEmail as Z, 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,6 +1,6 @@
1
1
  {
2
2
  "name": "@invonetwork/web-sdk",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "INVO Web SDK — currency purchase + passkey (WebAuthn) verification for partner web platforms.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "private": false,