@invonetwork/web-sdk 1.0.0 → 1.2.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/dist/server.cjs CHANGED
@@ -40,6 +40,14 @@ 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
+ }
47
+ /** True if the phone-share (phone, requesting_email) pair was already approved. */
48
+ get isPhoneShareAlreadyApproved() {
49
+ return this.code === "PHONE_SHARE_ALREADY_APPROVED";
50
+ }
43
51
  /** True if an idempotency-keyed request was a duplicate (item purchase → 409). */
44
52
  get isDuplicateRequest() {
45
53
  return this.code === "DUPLICATE_REQUEST" || this.status === 409 && /duplicate/i.test(this.message);
@@ -259,6 +267,49 @@ function retryAfterMs(parsed, headers) {
259
267
  }
260
268
  return void 0;
261
269
  }
270
+
271
+ // src/shared/destinations.ts
272
+ function toDestinationGame(row) {
273
+ const s = (k) => row[k] === void 0 || row[k] === null ? void 0 : String(row[k]);
274
+ return {
275
+ gameId: row["game_id"] ?? "",
276
+ gameName: String(row["game_name"] ?? ""),
277
+ tenantType: s("tenant_type"),
278
+ developerName: s("developer_name"),
279
+ publisherName: s("publisher_name"),
280
+ genre: s("genre"),
281
+ platform: s("platform"),
282
+ gameStatus: s("game_status"),
283
+ gameIcon: s("game_icon"),
284
+ gamePoster: s("game_poster"),
285
+ gameUrl: s("game_url"),
286
+ gameDescription: s("game_description"),
287
+ currencyName: String(row["currency_name"] ?? ""),
288
+ currencySymbol: String(row["currency_symbol"] ?? ""),
289
+ currencySymbolUrl: s("currency_symbol_url"),
290
+ minimumTransfer: String(row["minimum_transfer"] ?? ""),
291
+ maximumTransfer: String(row["maximum_transfer"] ?? ""),
292
+ raw: row
293
+ };
294
+ }
295
+ function toDestinationsResult(raw, direction) {
296
+ const games = Array.isArray(raw["available_games"]) ? raw["available_games"] : [];
297
+ return {
298
+ status: String(raw["status"] ?? ""),
299
+ sourceGameId: raw["source_game_id"] ?? "",
300
+ sourceGameName: String(raw["source_game_name"] ?? ""),
301
+ sourceGameIcon: raw["source_game_icon"],
302
+ sourceCurrencyName: String(raw["source_currency_name"] ?? ""),
303
+ sourceCurrencyIcon: raw["source_currency_icon"],
304
+ universalTransfers: raw["universal_transfers"] === true,
305
+ transferMode: String(raw["transfer_mode"] ?? ""),
306
+ availableGames: games.map(toDestinationGame),
307
+ totalDestinations: Number(raw["total_destinations"] ?? games.length),
308
+ direction: String(raw["direction"] ?? direction),
309
+ linkedGameIds: Array.isArray(raw["linked_game_ids"]) ? raw["linked_game_ids"] : void 0,
310
+ raw
311
+ };
312
+ }
262
313
  var DEFAULT_TOLERANCE_SEC = 300;
263
314
  var ENCODER = new TextEncoder();
264
315
  function toBytes(body) {
@@ -418,7 +469,7 @@ async function hmacHexSubtle(secret, message) {
418
469
  }
419
470
 
420
471
  // src/server.ts
421
- var DEFAULT_UA = "invonetwork-web-sdk/1.0.0 (+https://invo.network)";
472
+ var DEFAULT_UA = "invonetwork-web-sdk/1.2.0 (+https://invo.network)";
422
473
  var MAX_USD_AMOUNT = 999.99;
423
474
  var MAX_ITEM_PRICE = 999999.99;
424
475
  function invalidInput(label, value, why) {
@@ -480,6 +531,75 @@ function toInboundPendingItem(row) {
480
531
  raw: row
481
532
  };
482
533
  }
534
+ function toSmsVerifyResult(raw) {
535
+ return {
536
+ status: String(raw["status"] ?? ""),
537
+ transactionId: String(raw["transaction_id"] ?? ""),
538
+ claimCode: raw["claim_code"],
539
+ newBalance: raw["new_balance"] ?? null,
540
+ orderId: raw["order_id"],
541
+ raw
542
+ };
543
+ }
544
+ function toClaimResult(raw) {
545
+ const status = String(raw["status"] ?? "");
546
+ return {
547
+ status,
548
+ transactionId: raw["transaction_id"],
549
+ newBalance: raw["new_balance"] ?? null,
550
+ currencyName: raw["currency_name"],
551
+ orderId: raw["order_id"],
552
+ needsAccountSelection: status === "needs_account_selection",
553
+ candidates: Array.isArray(raw["candidates"]) ? raw["candidates"] : void 0,
554
+ raw
555
+ };
556
+ }
557
+ function toTransactionStatus(raw) {
558
+ return {
559
+ transactionId: String(raw["transaction_id"] ?? ""),
560
+ transactionType: String(raw["transaction_type"] ?? ""),
561
+ transactionStatus: String(raw["transaction_status"] ?? ""),
562
+ verificationState: String(raw["verification_state"] ?? ""),
563
+ amount: raw["amount"] ?? null,
564
+ netAmount: raw["net_amount"] ?? null,
565
+ currencyId: raw["currency_id"] ?? "",
566
+ orderId: raw["order_id"],
567
+ toPhone: raw["to_phone"],
568
+ toIdentityId: raw["to_identity_id"] ?? null,
569
+ raw
570
+ };
571
+ }
572
+ function toGuardianApprovalStatus(raw) {
573
+ const approval = raw["approval"] ?? {};
574
+ return {
575
+ state: String(approval["state"] ?? ""),
576
+ approvalId: approval["approval_id"],
577
+ transactionId: approval["transaction_id"],
578
+ expiresAt: approval["expires_at"],
579
+ decidedAt: approval["decided_at"],
580
+ decisionSource: approval["decision_source"],
581
+ actionDescription: approval["action_description"],
582
+ raw
583
+ };
584
+ }
585
+ function toPhoneShareInitiateResult(raw) {
586
+ return {
587
+ approvalId: raw["approval_id"],
588
+ codeSent: typeof raw["code_sent"] === "boolean" ? raw["code_sent"] : void 0,
589
+ expiresAt: raw["expires_at"],
590
+ alreadyApproved: typeof raw["already_approved"] === "boolean" ? raw["already_approved"] : void 0,
591
+ raw
592
+ };
593
+ }
594
+ function toPhoneShareApproveResult(raw) {
595
+ return {
596
+ approvalId: raw["approval_id"],
597
+ approved: typeof raw["approved"] === "boolean" ? raw["approved"] : void 0,
598
+ alreadyApproved: typeof raw["already_approved"] === "boolean" ? raw["already_approved"] : void 0,
599
+ expiresAt: raw["expires_at"],
600
+ raw
601
+ };
602
+ }
483
603
  function toCurrencyBalance(row) {
484
604
  return {
485
605
  currencyId: row["currency_id"] ?? "",
@@ -926,6 +1046,227 @@ var InvoServer = class {
926
1046
  raw
927
1047
  };
928
1048
  }
1049
+ /**
1050
+ * Complete a TRANSFER's SMS-PIN step (un-enrolled fallback). NOT idempotent —
1051
+ * each attempt is counted server-side, so this is never auto-retried. A bad PIN
1052
+ * throws a 400 InvoError carrying `attempts_remaining` on `err.body`; a blocked
1053
+ * recipient throws 429 (`sms_verification_blocked`).
1054
+ */
1055
+ async verifySmsTransfer(transactionId, smsPin, opts) {
1056
+ if (typeof transactionId !== "string" || !transactionId.trim()) {
1057
+ throw invalidInput("transactionId", transactionId, "is required");
1058
+ }
1059
+ if (typeof smsPin !== "string" || !smsPin.trim()) {
1060
+ throw invalidInput("smsPin", smsPin, "is required");
1061
+ }
1062
+ const raw = await this.http.post(
1063
+ "/api/transfers/verify-sms",
1064
+ { transaction_id: transactionId, sms_pin: smsPin },
1065
+ this.auth,
1066
+ { signal: opts?.signal }
1067
+ // NOT idempotent — attempts are counted; never auto-retried
1068
+ );
1069
+ return toSmsVerifyResult(raw);
1070
+ }
1071
+ /** Complete a SEND's SMS-PIN step (un-enrolled fallback). See verifySmsTransfer. */
1072
+ async verifySmsSend(transactionId, smsPin, opts) {
1073
+ if (typeof transactionId !== "string" || !transactionId.trim()) {
1074
+ throw invalidInput("transactionId", transactionId, "is required");
1075
+ }
1076
+ if (typeof smsPin !== "string" || !smsPin.trim()) {
1077
+ throw invalidInput("smsPin", smsPin, "is required");
1078
+ }
1079
+ const raw = await this.http.post(
1080
+ "/api/currency-sends/verify-sms",
1081
+ { transaction_id: transactionId, sms_pin: smsPin },
1082
+ this.auth,
1083
+ { signal: opts?.signal }
1084
+ // NOT idempotent — attempts are counted; never auto-retried
1085
+ );
1086
+ return toSmsVerifyResult(raw);
1087
+ }
1088
+ /**
1089
+ * Claim an inbound TRANSFER by its claim code, crediting one of this game's currencies.
1090
+ * NOT idempotent. A 200 may come back as `needsAccountSelection` (the recipient phone
1091
+ * maps to >1 of your players) — inspect `result.candidates` and re-submit with
1092
+ * `targetPlayerId`. Errors surface via InvoError (404 unknown code, 403 phone mismatch,
1093
+ * 400 not-claimable/expired/wrong-value, 409 `err.isPhoneShareApprovalRequired`, …).
1094
+ */
1095
+ async claimTransfer(input, opts) {
1096
+ const body = {
1097
+ claim_code: input.claimCode,
1098
+ target_player_name: input.targetPlayerName,
1099
+ target_player_email: input.targetPlayerEmail,
1100
+ target_player_phone: input.targetPlayerPhone,
1101
+ target_currency_id: input.targetCurrencyId
1102
+ };
1103
+ if (input.targetPlayerId != null) body["target_player_id"] = input.targetPlayerId;
1104
+ const raw = await this.http.post(
1105
+ "/api/transfers/claim-transfer",
1106
+ body,
1107
+ this.auth,
1108
+ { signal: opts?.signal }
1109
+ // NOT idempotent — never auto-retried
1110
+ );
1111
+ return toClaimResult(raw);
1112
+ }
1113
+ /**
1114
+ * Claim an inbound SEND by its claim code. NOT idempotent. May return
1115
+ * `needsAccountSelection` (multi-account phone) — re-submit with `receiverPlayerId`.
1116
+ * See claimTransfer for the error surface.
1117
+ */
1118
+ async claimCurrency(input, opts) {
1119
+ const body = {
1120
+ claim_code: input.claimCode,
1121
+ receiver_player_name: input.receiverPlayerName,
1122
+ receiver_player_email: input.receiverPlayerEmail,
1123
+ receiver_player_phone: input.receiverPlayerPhone
1124
+ };
1125
+ if (input.receiverPlayerId != null) body["receiver_player_id"] = input.receiverPlayerId;
1126
+ const raw = await this.http.post(
1127
+ "/api/currency-sends/claim-currency",
1128
+ body,
1129
+ this.auth,
1130
+ { signal: opts?.signal }
1131
+ // NOT idempotent — never auto-retried
1132
+ );
1133
+ return toClaimResult(raw);
1134
+ }
1135
+ /** Read a TRANSFER's status (verification_state + amounts + destination). */
1136
+ async getTransferStatus(transactionId, opts) {
1137
+ if (typeof transactionId !== "string" || !transactionId.trim()) {
1138
+ throw invalidInput("transactionId", transactionId, "is required");
1139
+ }
1140
+ const raw = await this.http.get(
1141
+ `/api/transfers/${encodeURIComponent(transactionId)}/status`,
1142
+ this.auth,
1143
+ { signal: opts?.signal }
1144
+ );
1145
+ return toTransactionStatus(raw);
1146
+ }
1147
+ /** Read a SEND's status (adds fees, game names, claim_info, … on `raw`). */
1148
+ async getSendStatus(transactionId, opts) {
1149
+ if (typeof transactionId !== "string" || !transactionId.trim()) {
1150
+ throw invalidInput("transactionId", transactionId, "is required");
1151
+ }
1152
+ const raw = await this.http.get(
1153
+ `/api/currency-sends/${encodeURIComponent(transactionId)}/status`,
1154
+ this.auth,
1155
+ { signal: opts?.signal }
1156
+ );
1157
+ return toTransactionStatus(raw);
1158
+ }
1159
+ /**
1160
+ * Poll a transaction's guardian approval status (minor/guardian flow). The retry
1161
+ * layer already retries the 503 (`{status:"unavailable"}`) for this idempotent GET;
1162
+ * a missing approval throws a 404 InvoError (`{status:"not_found"}`).
1163
+ */
1164
+ async getGuardianApprovalStatus(transactionId, opts) {
1165
+ if (typeof transactionId !== "string" || !transactionId.trim()) {
1166
+ throw invalidInput("transactionId", transactionId, "is required");
1167
+ }
1168
+ const raw = await this.http.get(
1169
+ `/api/transactions/${encodeURIComponent(transactionId)}/approval-status`,
1170
+ this.auth,
1171
+ { signal: opts?.signal }
1172
+ );
1173
+ return toGuardianApprovalStatus(raw);
1174
+ }
1175
+ /**
1176
+ * List the games/tenants a player can send/transfer to FROM `sourceGameId`, with
1177
+ * display metadata inline (name, icon, currency, min/max limits) — one call, no
1178
+ * per-game lookup. SERVER variant (game-secret) of the browser `InvoClient.getDestinations`;
1179
+ * the source game is passed explicitly rather than inferred from a player token. Rows
1180
+ * share the browser shape. Idempotent (a POST read — safe to retry).
1181
+ */
1182
+ async getDestinations(query, opts) {
1183
+ if (query.sourceGameId == null || String(query.sourceGameId).trim() === "") {
1184
+ throw invalidInput("sourceGameId", query.sourceGameId, "is required");
1185
+ }
1186
+ const direction = query.direction ?? "transfer";
1187
+ const path = direction === "send" ? "/api/currency-sends/available-destinations" : "/api/transfers/available-destinations";
1188
+ const raw = await this.http.post(
1189
+ path,
1190
+ { source_game_id: query.sourceGameId },
1191
+ this.auth,
1192
+ { idempotent: true, signal: opts?.signal }
1193
+ // a POST read — no side effect, safe to retry
1194
+ );
1195
+ return toDestinationsResult(raw, direction);
1196
+ }
1197
+ /**
1198
+ * Phone-share OTP handshake — resolves a 409 `PHONE_SHARE_APPROVAL_REQUIRED`.
1199
+ *
1200
+ * These three endpoints are UNAUTHENTICATED (phone-owner-driven: no game secret,
1201
+ * no player token) — a partner backend relays the handshake. The triggering 409
1202
+ * carries `approval_id, expires_at, code_sent, existing_account_hints[], next_endpoint`.
1203
+ *
1204
+ * `phoneShareInitiate` is the fallback OTP send (use when the 409 didn't already
1205
+ * dispatch a code). NOT idempotent — never auto-retried. OTP TTL ~10 min.
1206
+ */
1207
+ async phoneShareInitiate(input, opts) {
1208
+ if (typeof input.phone !== "string" || !input.phone.trim()) {
1209
+ throw invalidInput("phone", input.phone, "is required");
1210
+ }
1211
+ if (typeof input.email !== "string" || !input.email.trim()) {
1212
+ throw invalidInput("email", input.email, "is required");
1213
+ }
1214
+ const raw = await this.http.post(
1215
+ "/api/wallet/phone-share/initiate",
1216
+ { phone: input.phone, email: input.email },
1217
+ { kind: "none" },
1218
+ // UNAUTHENTICATED — phone-owner-driven; send no auth header
1219
+ { signal: opts?.signal }
1220
+ // NOT idempotent — sends an OTP; never auto-retried
1221
+ );
1222
+ return toPhoneShareInitiateResult(raw);
1223
+ }
1224
+ /**
1225
+ * Approve a phone-share with the OTP the phone owner received. On success the grant
1226
+ * is stored server-side, keyed by (phone, requesting_email) and auto-consumed — the
1227
+ * caller simply RE-ISSUES the identical original request. UNAUTHENTICATED. NOT
1228
+ * idempotent (attempts are counted) — never auto-retried.
1229
+ */
1230
+ async phoneShareApprove(input, opts) {
1231
+ if (typeof input.approvalId !== "string" || !input.approvalId.trim()) {
1232
+ throw invalidInput("approvalId", input.approvalId, "is required");
1233
+ }
1234
+ if (typeof input.otp !== "string" || !input.otp.trim()) {
1235
+ throw invalidInput("otp", input.otp, "is required");
1236
+ }
1237
+ const raw = await this.http.post(
1238
+ "/api/wallet/phone-share/approve",
1239
+ { approval_id: input.approvalId, otp: input.otp },
1240
+ { kind: "none" },
1241
+ // UNAUTHENTICATED — phone-owner-driven; send no auth header
1242
+ { signal: opts?.signal }
1243
+ // NOT idempotent — OTP attempts are counted; never auto-retried
1244
+ );
1245
+ return toPhoneShareApproveResult(raw);
1246
+ }
1247
+ /**
1248
+ * Poll whether a phone-share (phone, requesting_email) pair is approved yet.
1249
+ * UNAUTHENTICATED, idempotent GET. `approved:true` means the original request can
1250
+ * be re-issued.
1251
+ */
1252
+ async phoneShareStatus(input, opts) {
1253
+ if (typeof input.phone !== "string" || !input.phone.trim()) {
1254
+ throw invalidInput("phone", input.phone, "is required");
1255
+ }
1256
+ if (typeof input.email !== "string" || !input.email.trim()) {
1257
+ throw invalidInput("email", input.email, "is required");
1258
+ }
1259
+ const q = new URLSearchParams();
1260
+ q.set("phone", input.phone);
1261
+ q.set("email", input.email);
1262
+ const raw = await this.http.get(
1263
+ `/api/wallet/phone-share/status?${q.toString()}`,
1264
+ { kind: "none" },
1265
+ // UNAUTHENTICATED — phone-owner-driven; send no auth header
1266
+ { signal: opts?.signal }
1267
+ );
1268
+ return { approved: raw["approved"] === true, raw };
1269
+ }
929
1270
  toInitiateResult(raw) {
930
1271
  const vm = raw["verification_method"];
931
1272
  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, X as ServerDestinationsQuery, c as DestinationsResult, Y as PhoneShareContact, Z as PhoneShareInitiateResult, _ as PhoneShareApproveInput, $ as PhoneShareApproveResult, a0 as PhoneShareStatusResult } from './types-DTAmriOB.cjs';
2
+ export { a1 as CurrencyBalance, f as DestinationGame, a2 as InboundPendingItem, g as InvoErrorInfo, h as InvoHooks, i as InvoRequestInfo, j as InvoResponseInfo, a3 as LinkedIdentityEmail, R as Rail, V as VerificationMethod } from './types-DTAmriOB.cjs';
3
3
 
4
4
  interface VerifyWebhookOptions {
5
5
  /** Max age of the signed timestamp, in seconds. Default 300 (5 min). */
@@ -225,7 +225,72 @@ 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>;
261
+ /**
262
+ * List the games/tenants a player can send/transfer to FROM `sourceGameId`, with
263
+ * display metadata inline (name, icon, currency, min/max limits) — one call, no
264
+ * per-game lookup. SERVER variant (game-secret) of the browser `InvoClient.getDestinations`;
265
+ * the source game is passed explicitly rather than inferred from a player token. Rows
266
+ * share the browser shape. Idempotent (a POST read — safe to retry).
267
+ */
268
+ getDestinations(query: ServerDestinationsQuery, opts?: CallOptions): Promise<DestinationsResult>;
269
+ /**
270
+ * Phone-share OTP handshake — resolves a 409 `PHONE_SHARE_APPROVAL_REQUIRED`.
271
+ *
272
+ * These three endpoints are UNAUTHENTICATED (phone-owner-driven: no game secret,
273
+ * no player token) — a partner backend relays the handshake. The triggering 409
274
+ * carries `approval_id, expires_at, code_sent, existing_account_hints[], next_endpoint`.
275
+ *
276
+ * `phoneShareInitiate` is the fallback OTP send (use when the 409 didn't already
277
+ * dispatch a code). NOT idempotent — never auto-retried. OTP TTL ~10 min.
278
+ */
279
+ phoneShareInitiate(input: PhoneShareContact, opts?: CallOptions): Promise<PhoneShareInitiateResult>;
280
+ /**
281
+ * Approve a phone-share with the OTP the phone owner received. On success the grant
282
+ * is stored server-side, keyed by (phone, requesting_email) and auto-consumed — the
283
+ * caller simply RE-ISSUES the identical original request. UNAUTHENTICATED. NOT
284
+ * idempotent (attempts are counted) — never auto-retried.
285
+ */
286
+ phoneShareApprove(input: PhoneShareApproveInput, opts?: CallOptions): Promise<PhoneShareApproveResult>;
287
+ /**
288
+ * Poll whether a phone-share (phone, requesting_email) pair is approved yet.
289
+ * UNAUTHENTICATED, idempotent GET. `approved:true` means the original request can
290
+ * be re-issued.
291
+ */
292
+ phoneShareStatus(input: PhoneShareContact, opts?: CallOptions): Promise<PhoneShareStatusResult>;
228
293
  private toInitiateResult;
229
294
  }
230
295
 
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 };
296
+ export { CallOptions, ClaimCurrencyInput, ClaimResult, ClaimTransferInput, ConfirmPaymentResult, CreateCheckoutInput, CreateCheckoutResult, DestinationsResult, GuardianApprovalStatusResult, InboundPendingQuery, InboundPendingResult, InitiateResult, InitiateSendInput, InitiateTransferInput, InvoError, InvoServer, type InvoWebhookEvent, ItemHistoryQuery, ItemHistoryResult, ItemOrderQuery, type ItemPurchasedData, LinkedIdentitiesQuery, LinkedIdentitiesResult, OrderDetailsResult, PhoneShareApproveInput, PhoneShareApproveResult, PhoneShareContact, PhoneShareInitiateResult, PhoneShareStatusResult, PlayerBalanceQuery, PlayerBalanceResult, PlayerToken, type PurchaseCompletedData, type PurchaseEventData, PurchaseInput, PurchaseItemInput, PurchaseItemResult, PurchaseResult, ServerConfig, ServerDestinationsQuery, 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, X as ServerDestinationsQuery, c as DestinationsResult, Y as PhoneShareContact, Z as PhoneShareInitiateResult, _ as PhoneShareApproveInput, $ as PhoneShareApproveResult, a0 as PhoneShareStatusResult } from './types-DTAmriOB.js';
2
+ export { a1 as CurrencyBalance, f as DestinationGame, a2 as InboundPendingItem, g as InvoErrorInfo, h as InvoHooks, i as InvoRequestInfo, j as InvoResponseInfo, a3 as LinkedIdentityEmail, R as Rail, V as VerificationMethod } from './types-DTAmriOB.js';
3
3
 
4
4
  interface VerifyWebhookOptions {
5
5
  /** Max age of the signed timestamp, in seconds. Default 300 (5 min). */
@@ -225,7 +225,72 @@ 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>;
261
+ /**
262
+ * List the games/tenants a player can send/transfer to FROM `sourceGameId`, with
263
+ * display metadata inline (name, icon, currency, min/max limits) — one call, no
264
+ * per-game lookup. SERVER variant (game-secret) of the browser `InvoClient.getDestinations`;
265
+ * the source game is passed explicitly rather than inferred from a player token. Rows
266
+ * share the browser shape. Idempotent (a POST read — safe to retry).
267
+ */
268
+ getDestinations(query: ServerDestinationsQuery, opts?: CallOptions): Promise<DestinationsResult>;
269
+ /**
270
+ * Phone-share OTP handshake — resolves a 409 `PHONE_SHARE_APPROVAL_REQUIRED`.
271
+ *
272
+ * These three endpoints are UNAUTHENTICATED (phone-owner-driven: no game secret,
273
+ * no player token) — a partner backend relays the handshake. The triggering 409
274
+ * carries `approval_id, expires_at, code_sent, existing_account_hints[], next_endpoint`.
275
+ *
276
+ * `phoneShareInitiate` is the fallback OTP send (use when the 409 didn't already
277
+ * dispatch a code). NOT idempotent — never auto-retried. OTP TTL ~10 min.
278
+ */
279
+ phoneShareInitiate(input: PhoneShareContact, opts?: CallOptions): Promise<PhoneShareInitiateResult>;
280
+ /**
281
+ * Approve a phone-share with the OTP the phone owner received. On success the grant
282
+ * is stored server-side, keyed by (phone, requesting_email) and auto-consumed — the
283
+ * caller simply RE-ISSUES the identical original request. UNAUTHENTICATED. NOT
284
+ * idempotent (attempts are counted) — never auto-retried.
285
+ */
286
+ phoneShareApprove(input: PhoneShareApproveInput, opts?: CallOptions): Promise<PhoneShareApproveResult>;
287
+ /**
288
+ * Poll whether a phone-share (phone, requesting_email) pair is approved yet.
289
+ * UNAUTHENTICATED, idempotent GET. `approved:true` means the original request can
290
+ * be re-issued.
291
+ */
292
+ phoneShareStatus(input: PhoneShareContact, opts?: CallOptions): Promise<PhoneShareStatusResult>;
228
293
  private toInitiateResult;
229
294
  }
230
295
 
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 };
296
+ export { CallOptions, ClaimCurrencyInput, ClaimResult, ClaimTransferInput, ConfirmPaymentResult, CreateCheckoutInput, CreateCheckoutResult, DestinationsResult, GuardianApprovalStatusResult, InboundPendingQuery, InboundPendingResult, InitiateResult, InitiateSendInput, InitiateTransferInput, InvoError, InvoServer, type InvoWebhookEvent, ItemHistoryQuery, ItemHistoryResult, ItemOrderQuery, type ItemPurchasedData, LinkedIdentitiesQuery, LinkedIdentitiesResult, OrderDetailsResult, PhoneShareApproveInput, PhoneShareApproveResult, PhoneShareContact, PhoneShareInitiateResult, PhoneShareStatusResult, PlayerBalanceQuery, PlayerBalanceResult, PlayerToken, type PurchaseCompletedData, type PurchaseEventData, PurchaseInput, PurchaseItemInput, PurchaseItemResult, PurchaseResult, ServerConfig, ServerDestinationsQuery, SmsVerifyResult, TransactionStatusResult, type TransferEventData, type VerifyWebhookOptions, type WebhookHandlerOptions, createWebhookHandler, verifyWebhook, verifyWebhookAsync };