@invonetwork/web-sdk 0.2.1 → 0.4.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 +48 -1
- package/LICENSE +18 -17
- package/README.md +495 -393
- package/dist/chunk-EEWOAUXO.js +249 -0
- package/dist/index.cjs +188 -63
- package/dist/index.d.cts +9 -9
- package/dist/index.d.ts +9 -9
- package/dist/index.js +54 -31
- package/dist/server.cjs +474 -59
- package/dist/server.d.cts +177 -13
- package/dist/server.d.ts +177 -13
- package/dist/server.js +339 -29
- package/dist/{errors-DV5QsftP.d.cts → types-CBMLNwbe.d.cts} +152 -42
- package/dist/{errors-DV5QsftP.d.ts → types-CBMLNwbe.d.ts} +152 -42
- package/package.json +10 -2
- package/dist/chunk-A44O4KC3.js +0 -147
package/dist/server.d.cts
CHANGED
|
@@ -1,5 +1,147 @@
|
|
|
1
|
-
import { S as ServerConfig, P as PlayerToken,
|
|
2
|
-
export {
|
|
1
|
+
import { I as InvoError, S as ServerConfig, a as CallOptions, P as PlayerToken, g as InitiateSendInput, h as InitiateResult, i as InitiateTransferInput, j as CreateCheckoutInput, k as CreateCheckoutResult, l as PurchaseInput, m as PurchaseResult, n as ConfirmPaymentResult, O as OrderDetailsResult, o as PurchaseItemInput, p as PurchaseItemResult, q as ItemHistoryQuery, r as ItemHistoryResult, s as ItemOrderQuery, t as PlayerBalanceQuery, u as PlayerBalanceResult, v as InboundPendingQuery, w as InboundPendingResult } from './types-CBMLNwbe.cjs';
|
|
2
|
+
export { x as CurrencyBalance, y as InboundPendingItem, c as InvoErrorInfo, d as InvoHooks, e as InvoRequestInfo, f as InvoResponseInfo, R as Rail, V as VerificationMethod } from './types-CBMLNwbe.cjs';
|
|
3
|
+
|
|
4
|
+
interface VerifyWebhookOptions {
|
|
5
|
+
/** Max age of the signed timestamp, in seconds. Default 300 (5 min). */
|
|
6
|
+
toleranceSec?: number;
|
|
7
|
+
/** Override the current time (unix seconds) — for tests. Defaults to Date.now(). */
|
|
8
|
+
nowSec?: number;
|
|
9
|
+
}
|
|
10
|
+
interface WebhookBase {
|
|
11
|
+
event_id: string;
|
|
12
|
+
idempotency_key: string;
|
|
13
|
+
schema_version: string;
|
|
14
|
+
created_at: string;
|
|
15
|
+
/** Your game_id. */
|
|
16
|
+
tenant_id: string;
|
|
17
|
+
}
|
|
18
|
+
interface PurchaseCompletedData {
|
|
19
|
+
transaction_id: string;
|
|
20
|
+
order_id: string;
|
|
21
|
+
player_email: string;
|
|
22
|
+
identity_id: string;
|
|
23
|
+
usd_amount: string;
|
|
24
|
+
currency_amount: string;
|
|
25
|
+
currency_name: string;
|
|
26
|
+
new_balance: string;
|
|
27
|
+
rail: string;
|
|
28
|
+
[key: string]: unknown;
|
|
29
|
+
}
|
|
30
|
+
interface PurchaseEventData {
|
|
31
|
+
transaction_id: string;
|
|
32
|
+
order_id?: string;
|
|
33
|
+
player_email?: string;
|
|
34
|
+
identity_id?: string;
|
|
35
|
+
/** Present on purchase.disputed, e.g. "lost". */
|
|
36
|
+
dispute_status?: string;
|
|
37
|
+
[key: string]: unknown;
|
|
38
|
+
}
|
|
39
|
+
interface ItemPurchasedData {
|
|
40
|
+
transaction_id: string;
|
|
41
|
+
order_id: string;
|
|
42
|
+
player_email: string;
|
|
43
|
+
identity_id: string;
|
|
44
|
+
item_id: string;
|
|
45
|
+
item_name: string;
|
|
46
|
+
item_quantity: number;
|
|
47
|
+
unit_price: string;
|
|
48
|
+
total_price: string;
|
|
49
|
+
currency_name: string;
|
|
50
|
+
new_balance: string;
|
|
51
|
+
fee_breakdown?: Record<string, unknown>;
|
|
52
|
+
[key: string]: unknown;
|
|
53
|
+
}
|
|
54
|
+
interface TransferEventData {
|
|
55
|
+
transaction_id: string;
|
|
56
|
+
/** "transfer" | "send". */
|
|
57
|
+
flow?: string;
|
|
58
|
+
amount?: string;
|
|
59
|
+
net_amount?: string;
|
|
60
|
+
/** "inbound" | "outbound" (relative to the receiving tenant). */
|
|
61
|
+
direction?: string;
|
|
62
|
+
/** On inbound claim_pending: the recipient's phone (match to your player). */
|
|
63
|
+
to_phone?: string;
|
|
64
|
+
/** Recipient's opaque identity, present only when the phone maps to a single player. */
|
|
65
|
+
to_identity_id?: string | null;
|
|
66
|
+
[key: string]: unknown;
|
|
67
|
+
}
|
|
68
|
+
type PurchaseEventType = "purchase.failed" | "purchase.refunded" | "purchase.disputed" | "purchase.fraud_warning";
|
|
69
|
+
type TransferEventType = "transfer.sent" | "transfer.received" | "transfer.claim_pending" | "transfer.claim_expired" | "transfer.refunded";
|
|
70
|
+
/**
|
|
71
|
+
* A verified webhook event. Discriminate on `event_type` to narrow `data`.
|
|
72
|
+
* Unknown/future event types fall through to the generic member (data is a record).
|
|
73
|
+
*/
|
|
74
|
+
type InvoWebhookEvent = (WebhookBase & {
|
|
75
|
+
event_type: "purchase.completed";
|
|
76
|
+
data: PurchaseCompletedData;
|
|
77
|
+
}) | (WebhookBase & {
|
|
78
|
+
event_type: "item.purchased";
|
|
79
|
+
data: ItemPurchasedData;
|
|
80
|
+
}) | (WebhookBase & {
|
|
81
|
+
event_type: PurchaseEventType;
|
|
82
|
+
data: PurchaseEventData;
|
|
83
|
+
}) | (WebhookBase & {
|
|
84
|
+
event_type: TransferEventType;
|
|
85
|
+
data: TransferEventData;
|
|
86
|
+
}) | (WebhookBase & {
|
|
87
|
+
event_type: "payout.status_changed";
|
|
88
|
+
data: Record<string, unknown>;
|
|
89
|
+
}) | (WebhookBase & {
|
|
90
|
+
event_type: "webhook.test";
|
|
91
|
+
data: Record<string, unknown>;
|
|
92
|
+
}) | (WebhookBase & {
|
|
93
|
+
event_type: string;
|
|
94
|
+
data: Record<string, unknown>;
|
|
95
|
+
});
|
|
96
|
+
/**
|
|
97
|
+
* Verify an Invo webhook and return the parsed, typed event. **Synchronous; uses
|
|
98
|
+
* `node:crypto`** — for non-Node runtimes (Cloudflare Workers, Deno, Vercel/Netlify
|
|
99
|
+
* Edge, Bun edge) use {@link verifyWebhookAsync}.
|
|
100
|
+
*
|
|
101
|
+
* @param rawBody The exact raw request body (bytes or string) — never a re-serialized object.
|
|
102
|
+
* @param signatureHeader The `X-Invo-Signature` header value.
|
|
103
|
+
* @param secret Your signing secret, or an array (to accept old + new during rotation).
|
|
104
|
+
* @throws InvoError (status 0): WEBHOOK_SIGNATURE_MISSING | WEBHOOK_SECRET_MISSING |
|
|
105
|
+
* WEBHOOK_TIMESTAMP_EXPIRED | WEBHOOK_SIGNATURE_INVALID | WEBHOOK_MALFORMED.
|
|
106
|
+
*/
|
|
107
|
+
declare function verifyWebhook(rawBody: string | Uint8Array, signatureHeader: string | null | undefined, secret: string | string[], opts?: VerifyWebhookOptions): InvoWebhookEvent;
|
|
108
|
+
/**
|
|
109
|
+
* Cross-runtime async variant of {@link verifyWebhook}. Uses the Web Crypto API
|
|
110
|
+
* (`crypto.subtle`) instead of `node:crypto`, so it works on Cloudflare Workers,
|
|
111
|
+
* Deno, Vercel/Netlify Edge, Bun, and modern browsers (as well as Node ≥ 18).
|
|
112
|
+
* Same arguments, errors, and return value — just `await` it.
|
|
113
|
+
*/
|
|
114
|
+
declare function verifyWebhookAsync(rawBody: string | Uint8Array, signatureHeader: string | null | undefined, secret: string | string[], opts?: VerifyWebhookOptions): Promise<InvoWebhookEvent>;
|
|
115
|
+
interface WebhookHandlerOptions {
|
|
116
|
+
/** Signing secret, or an array to accept old + new during rotation. */
|
|
117
|
+
secret: string | string[];
|
|
118
|
+
/** Called with the verified, typed event after the signature passes. De-dupe on
|
|
119
|
+
* `ctx.idempotencyKey`. Throw to return a 500 (Invo will retry). */
|
|
120
|
+
onEvent: (event: InvoWebhookEvent, ctx: {
|
|
121
|
+
idempotencyKey: string | null;
|
|
122
|
+
request: Request;
|
|
123
|
+
}) => void | Promise<void>;
|
|
124
|
+
/** Optional: handle a verification failure. Return a Response to override the default 400. */
|
|
125
|
+
onError?: (error: InvoError, request: Request) => void | Response | Promise<void | Response>;
|
|
126
|
+
/** Replay tolerance (seconds). Default 300. */
|
|
127
|
+
toleranceSec?: number;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Build a webhook route handler from the Fetch API `(Request) => Promise<Response>` —
|
|
131
|
+
* works in Next.js App Router route handlers, Cloudflare Workers, Deno, Hono, and Bun.
|
|
132
|
+
* It reads the raw body, verifies with {@link verifyWebhookAsync}, and on success calls
|
|
133
|
+
* `onEvent` with the typed event and the idempotency key (de-dupe is yours). Verification
|
|
134
|
+
* failures return `400`; a throwing `onEvent` returns `500` so Invo retries.
|
|
135
|
+
*
|
|
136
|
+
* ```ts
|
|
137
|
+
* // app/invo/webhooks/route.ts (Next.js)
|
|
138
|
+
* export const POST = createWebhookHandler({
|
|
139
|
+
* secret: process.env.INVO_WEBHOOK_SECRET!,
|
|
140
|
+
* onEvent: async (event, { idempotencyKey }) => { await grantValue(event); },
|
|
141
|
+
* });
|
|
142
|
+
* ```
|
|
143
|
+
*/
|
|
144
|
+
declare function createWebhookHandler(opts: WebhookHandlerOptions): (request: Request) => Promise<Response>;
|
|
3
145
|
|
|
4
146
|
declare class InvoServer {
|
|
5
147
|
private readonly http;
|
|
@@ -8,16 +150,16 @@ declare class InvoServer {
|
|
|
8
150
|
/** Mint a short-lived, game-scoped player token to hand to the browser InvoClient. */
|
|
9
151
|
mintPlayerToken(input: {
|
|
10
152
|
playerEmail: string;
|
|
11
|
-
}): Promise<PlayerToken>;
|
|
153
|
+
}, opts?: CallOptions): Promise<PlayerToken>;
|
|
12
154
|
/** Initiate a cross-game currency SEND. Inspect result.verificationMethod. */
|
|
13
|
-
initiateSend(input: InitiateSendInput): Promise<InitiateResult>;
|
|
155
|
+
initiateSend(input: InitiateSendInput, opts?: CallOptions): Promise<InitiateResult>;
|
|
14
156
|
/** Initiate a cross-game TRANSFER. Inspect result.verificationMethod. */
|
|
15
|
-
initiateTransfer(input: InitiateTransferInput): Promise<InitiateResult>;
|
|
157
|
+
initiateTransfer(input: InitiateTransferInput, opts?: CallOptions): Promise<InitiateResult>;
|
|
16
158
|
/** Create a hosted checkout session (the recommended purchase path). Open the
|
|
17
159
|
* returned checkoutUrl in a WebView/redirect or an iframe; the INVO-hosted page
|
|
18
160
|
* handles cards, saved cards, and 3-D Secure. Crediting is server-side via the
|
|
19
161
|
* purchase.completed webhook. */
|
|
20
|
-
createCheckout(input: CreateCheckoutInput): Promise<CreateCheckoutResult>;
|
|
162
|
+
createCheckout(input: CreateCheckoutInput, opts?: CallOptions): Promise<CreateCheckoutResult>;
|
|
21
163
|
/**
|
|
22
164
|
* Direct purchase via the rail selector. Use when you need a specific rail.
|
|
23
165
|
* - rail "platform" (default): standard card. May return status "requires_action"
|
|
@@ -25,17 +167,17 @@ declare class InvoServer {
|
|
|
25
167
|
* - rail "game": returns status "pending_payment" + paymentUrl (redirect the browser).
|
|
26
168
|
* - rail "steam": NOT a browser flow — the backend returns WRONG_RAIL_ENDPOINT.
|
|
27
169
|
*/
|
|
28
|
-
purchaseCurrency(input: PurchaseInput): Promise<PurchaseResult>;
|
|
170
|
+
purchaseCurrency(input: PurchaseInput, opts?: CallOptions): Promise<PurchaseResult>;
|
|
29
171
|
/** Complete the Stripe-rail 3-D Secure step after the client finished card action. */
|
|
30
172
|
confirmPayment(input: {
|
|
31
173
|
paymentIntentId: string;
|
|
32
174
|
orderId?: string;
|
|
33
|
-
}): Promise<
|
|
175
|
+
}, opts?: CallOptions): Promise<ConfirmPaymentResult>;
|
|
34
176
|
/** Fetch purchase status (order + financial summary + timeline). */
|
|
35
177
|
getOrderDetails(query: {
|
|
36
178
|
orderId?: string;
|
|
37
179
|
transactionId?: string;
|
|
38
|
-
}): Promise<
|
|
180
|
+
}, opts?: CallOptions): Promise<OrderDetailsResult>;
|
|
39
181
|
/**
|
|
40
182
|
* Buy an in-game item by SPENDING the player's existing game currency (§4.8).
|
|
41
183
|
* No real money, no payment rail, no passkey — it's a balance debit, authenticated
|
|
@@ -44,13 +186,35 @@ declare class InvoServer {
|
|
|
44
186
|
* (err.isInsufficientBalance; required_amount/current_balance on err.body). Grant the
|
|
45
187
|
* item to your inventory off the `item.purchased` webhook, not just this response.
|
|
46
188
|
*/
|
|
47
|
-
purchaseItem(input: PurchaseItemInput): Promise<PurchaseItemResult>;
|
|
189
|
+
purchaseItem(input: PurchaseItemInput, opts?: CallOptions): Promise<PurchaseItemResult>;
|
|
48
190
|
/** Paginated item-purchase history for a player (§4.8 companion read). */
|
|
49
|
-
getItemPurchaseHistory(query: ItemHistoryQuery): Promise<
|
|
191
|
+
getItemPurchaseHistory(query: ItemHistoryQuery, opts?: CallOptions): Promise<ItemHistoryResult>;
|
|
50
192
|
/** Look up one item order by EXACTLY ONE of orderId | transactionId | clientRequestId
|
|
51
193
|
* (§4.8). Use clientRequestId for saga/recovery: "did this purchase actually complete?" */
|
|
52
|
-
getItemOrderDetails(query: ItemOrderQuery): Promise<
|
|
194
|
+
getItemOrderDetails(query: ItemOrderQuery, opts?: CallOptions): Promise<OrderDetailsResult>;
|
|
195
|
+
/**
|
|
196
|
+
* Async iterator over a player's entire item-purchase history — pages through
|
|
197
|
+
* `getItemPurchaseHistory` (limit/offset) until exhausted, yielding one row at a time.
|
|
198
|
+
*
|
|
199
|
+
* ```ts
|
|
200
|
+
* for await (const row of invo.iterateItemPurchaseHistory({ playerEmail })) { … }
|
|
201
|
+
* ```
|
|
202
|
+
*/
|
|
203
|
+
iterateItemPurchaseHistory(query: {
|
|
204
|
+
playerEmail: string;
|
|
205
|
+
pageSize?: number;
|
|
206
|
+
}, opts?: CallOptions): AsyncGenerator<Record<string, unknown>, void, unknown>;
|
|
207
|
+
/** Read a player's currency balances, by email or playerId (game-secret). */
|
|
208
|
+
getPlayerBalance(query: PlayerBalanceQuery, opts?: CallOptions): Promise<PlayerBalanceResult>;
|
|
209
|
+
/**
|
|
210
|
+
* List LIVE, unclaimed inbound sends/transfers addressed to a player at YOUR game —
|
|
211
|
+
* the source of truth for a "you have X to collect" badge. Pass the player's email
|
|
212
|
+
* or phone. Match `toPhone` to the logged-in player (don't require `toIdentityId`,
|
|
213
|
+
* which is null when the phone maps to more than one of your players). Pairs with the
|
|
214
|
+
* `transfer.claim_pending` webhook (the webhook is the wake-up; this is the list).
|
|
215
|
+
*/
|
|
216
|
+
getInboundPending(query: InboundPendingQuery, opts?: CallOptions): Promise<InboundPendingResult>;
|
|
53
217
|
private toInitiateResult;
|
|
54
218
|
}
|
|
55
219
|
|
|
56
|
-
export { CreateCheckoutInput, CreateCheckoutResult, InitiateResult, InitiateSendInput, InitiateTransferInput, InvoServer, ItemHistoryQuery, ItemOrderQuery, PlayerToken, PurchaseInput, PurchaseItemInput, PurchaseItemResult, PurchaseResult, ServerConfig };
|
|
220
|
+
export { CallOptions, ConfirmPaymentResult, CreateCheckoutInput, CreateCheckoutResult, InboundPendingQuery, InboundPendingResult, InitiateResult, InitiateSendInput, InitiateTransferInput, InvoError, InvoServer, type InvoWebhookEvent, ItemHistoryQuery, ItemHistoryResult, ItemOrderQuery, type ItemPurchasedData, OrderDetailsResult, PlayerBalanceQuery, PlayerBalanceResult, PlayerToken, type PurchaseCompletedData, type PurchaseEventData, PurchaseInput, PurchaseItemInput, PurchaseItemResult, PurchaseResult, ServerConfig, type TransferEventData, type VerifyWebhookOptions, type WebhookHandlerOptions, createWebhookHandler, verifyWebhook, verifyWebhookAsync };
|
package/dist/server.d.ts
CHANGED
|
@@ -1,5 +1,147 @@
|
|
|
1
|
-
import { S as ServerConfig, P as PlayerToken,
|
|
2
|
-
export {
|
|
1
|
+
import { I as InvoError, S as ServerConfig, a as CallOptions, P as PlayerToken, g as InitiateSendInput, h as InitiateResult, i as InitiateTransferInput, j as CreateCheckoutInput, k as CreateCheckoutResult, l as PurchaseInput, m as PurchaseResult, n as ConfirmPaymentResult, O as OrderDetailsResult, o as PurchaseItemInput, p as PurchaseItemResult, q as ItemHistoryQuery, r as ItemHistoryResult, s as ItemOrderQuery, t as PlayerBalanceQuery, u as PlayerBalanceResult, v as InboundPendingQuery, w as InboundPendingResult } from './types-CBMLNwbe.js';
|
|
2
|
+
export { x as CurrencyBalance, y as InboundPendingItem, c as InvoErrorInfo, d as InvoHooks, e as InvoRequestInfo, f as InvoResponseInfo, R as Rail, V as VerificationMethod } from './types-CBMLNwbe.js';
|
|
3
|
+
|
|
4
|
+
interface VerifyWebhookOptions {
|
|
5
|
+
/** Max age of the signed timestamp, in seconds. Default 300 (5 min). */
|
|
6
|
+
toleranceSec?: number;
|
|
7
|
+
/** Override the current time (unix seconds) — for tests. Defaults to Date.now(). */
|
|
8
|
+
nowSec?: number;
|
|
9
|
+
}
|
|
10
|
+
interface WebhookBase {
|
|
11
|
+
event_id: string;
|
|
12
|
+
idempotency_key: string;
|
|
13
|
+
schema_version: string;
|
|
14
|
+
created_at: string;
|
|
15
|
+
/** Your game_id. */
|
|
16
|
+
tenant_id: string;
|
|
17
|
+
}
|
|
18
|
+
interface PurchaseCompletedData {
|
|
19
|
+
transaction_id: string;
|
|
20
|
+
order_id: string;
|
|
21
|
+
player_email: string;
|
|
22
|
+
identity_id: string;
|
|
23
|
+
usd_amount: string;
|
|
24
|
+
currency_amount: string;
|
|
25
|
+
currency_name: string;
|
|
26
|
+
new_balance: string;
|
|
27
|
+
rail: string;
|
|
28
|
+
[key: string]: unknown;
|
|
29
|
+
}
|
|
30
|
+
interface PurchaseEventData {
|
|
31
|
+
transaction_id: string;
|
|
32
|
+
order_id?: string;
|
|
33
|
+
player_email?: string;
|
|
34
|
+
identity_id?: string;
|
|
35
|
+
/** Present on purchase.disputed, e.g. "lost". */
|
|
36
|
+
dispute_status?: string;
|
|
37
|
+
[key: string]: unknown;
|
|
38
|
+
}
|
|
39
|
+
interface ItemPurchasedData {
|
|
40
|
+
transaction_id: string;
|
|
41
|
+
order_id: string;
|
|
42
|
+
player_email: string;
|
|
43
|
+
identity_id: string;
|
|
44
|
+
item_id: string;
|
|
45
|
+
item_name: string;
|
|
46
|
+
item_quantity: number;
|
|
47
|
+
unit_price: string;
|
|
48
|
+
total_price: string;
|
|
49
|
+
currency_name: string;
|
|
50
|
+
new_balance: string;
|
|
51
|
+
fee_breakdown?: Record<string, unknown>;
|
|
52
|
+
[key: string]: unknown;
|
|
53
|
+
}
|
|
54
|
+
interface TransferEventData {
|
|
55
|
+
transaction_id: string;
|
|
56
|
+
/** "transfer" | "send". */
|
|
57
|
+
flow?: string;
|
|
58
|
+
amount?: string;
|
|
59
|
+
net_amount?: string;
|
|
60
|
+
/** "inbound" | "outbound" (relative to the receiving tenant). */
|
|
61
|
+
direction?: string;
|
|
62
|
+
/** On inbound claim_pending: the recipient's phone (match to your player). */
|
|
63
|
+
to_phone?: string;
|
|
64
|
+
/** Recipient's opaque identity, present only when the phone maps to a single player. */
|
|
65
|
+
to_identity_id?: string | null;
|
|
66
|
+
[key: string]: unknown;
|
|
67
|
+
}
|
|
68
|
+
type PurchaseEventType = "purchase.failed" | "purchase.refunded" | "purchase.disputed" | "purchase.fraud_warning";
|
|
69
|
+
type TransferEventType = "transfer.sent" | "transfer.received" | "transfer.claim_pending" | "transfer.claim_expired" | "transfer.refunded";
|
|
70
|
+
/**
|
|
71
|
+
* A verified webhook event. Discriminate on `event_type` to narrow `data`.
|
|
72
|
+
* Unknown/future event types fall through to the generic member (data is a record).
|
|
73
|
+
*/
|
|
74
|
+
type InvoWebhookEvent = (WebhookBase & {
|
|
75
|
+
event_type: "purchase.completed";
|
|
76
|
+
data: PurchaseCompletedData;
|
|
77
|
+
}) | (WebhookBase & {
|
|
78
|
+
event_type: "item.purchased";
|
|
79
|
+
data: ItemPurchasedData;
|
|
80
|
+
}) | (WebhookBase & {
|
|
81
|
+
event_type: PurchaseEventType;
|
|
82
|
+
data: PurchaseEventData;
|
|
83
|
+
}) | (WebhookBase & {
|
|
84
|
+
event_type: TransferEventType;
|
|
85
|
+
data: TransferEventData;
|
|
86
|
+
}) | (WebhookBase & {
|
|
87
|
+
event_type: "payout.status_changed";
|
|
88
|
+
data: Record<string, unknown>;
|
|
89
|
+
}) | (WebhookBase & {
|
|
90
|
+
event_type: "webhook.test";
|
|
91
|
+
data: Record<string, unknown>;
|
|
92
|
+
}) | (WebhookBase & {
|
|
93
|
+
event_type: string;
|
|
94
|
+
data: Record<string, unknown>;
|
|
95
|
+
});
|
|
96
|
+
/**
|
|
97
|
+
* Verify an Invo webhook and return the parsed, typed event. **Synchronous; uses
|
|
98
|
+
* `node:crypto`** — for non-Node runtimes (Cloudflare Workers, Deno, Vercel/Netlify
|
|
99
|
+
* Edge, Bun edge) use {@link verifyWebhookAsync}.
|
|
100
|
+
*
|
|
101
|
+
* @param rawBody The exact raw request body (bytes or string) — never a re-serialized object.
|
|
102
|
+
* @param signatureHeader The `X-Invo-Signature` header value.
|
|
103
|
+
* @param secret Your signing secret, or an array (to accept old + new during rotation).
|
|
104
|
+
* @throws InvoError (status 0): WEBHOOK_SIGNATURE_MISSING | WEBHOOK_SECRET_MISSING |
|
|
105
|
+
* WEBHOOK_TIMESTAMP_EXPIRED | WEBHOOK_SIGNATURE_INVALID | WEBHOOK_MALFORMED.
|
|
106
|
+
*/
|
|
107
|
+
declare function verifyWebhook(rawBody: string | Uint8Array, signatureHeader: string | null | undefined, secret: string | string[], opts?: VerifyWebhookOptions): InvoWebhookEvent;
|
|
108
|
+
/**
|
|
109
|
+
* Cross-runtime async variant of {@link verifyWebhook}. Uses the Web Crypto API
|
|
110
|
+
* (`crypto.subtle`) instead of `node:crypto`, so it works on Cloudflare Workers,
|
|
111
|
+
* Deno, Vercel/Netlify Edge, Bun, and modern browsers (as well as Node ≥ 18).
|
|
112
|
+
* Same arguments, errors, and return value — just `await` it.
|
|
113
|
+
*/
|
|
114
|
+
declare function verifyWebhookAsync(rawBody: string | Uint8Array, signatureHeader: string | null | undefined, secret: string | string[], opts?: VerifyWebhookOptions): Promise<InvoWebhookEvent>;
|
|
115
|
+
interface WebhookHandlerOptions {
|
|
116
|
+
/** Signing secret, or an array to accept old + new during rotation. */
|
|
117
|
+
secret: string | string[];
|
|
118
|
+
/** Called with the verified, typed event after the signature passes. De-dupe on
|
|
119
|
+
* `ctx.idempotencyKey`. Throw to return a 500 (Invo will retry). */
|
|
120
|
+
onEvent: (event: InvoWebhookEvent, ctx: {
|
|
121
|
+
idempotencyKey: string | null;
|
|
122
|
+
request: Request;
|
|
123
|
+
}) => void | Promise<void>;
|
|
124
|
+
/** Optional: handle a verification failure. Return a Response to override the default 400. */
|
|
125
|
+
onError?: (error: InvoError, request: Request) => void | Response | Promise<void | Response>;
|
|
126
|
+
/** Replay tolerance (seconds). Default 300. */
|
|
127
|
+
toleranceSec?: number;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Build a webhook route handler from the Fetch API `(Request) => Promise<Response>` —
|
|
131
|
+
* works in Next.js App Router route handlers, Cloudflare Workers, Deno, Hono, and Bun.
|
|
132
|
+
* It reads the raw body, verifies with {@link verifyWebhookAsync}, and on success calls
|
|
133
|
+
* `onEvent` with the typed event and the idempotency key (de-dupe is yours). Verification
|
|
134
|
+
* failures return `400`; a throwing `onEvent` returns `500` so Invo retries.
|
|
135
|
+
*
|
|
136
|
+
* ```ts
|
|
137
|
+
* // app/invo/webhooks/route.ts (Next.js)
|
|
138
|
+
* export const POST = createWebhookHandler({
|
|
139
|
+
* secret: process.env.INVO_WEBHOOK_SECRET!,
|
|
140
|
+
* onEvent: async (event, { idempotencyKey }) => { await grantValue(event); },
|
|
141
|
+
* });
|
|
142
|
+
* ```
|
|
143
|
+
*/
|
|
144
|
+
declare function createWebhookHandler(opts: WebhookHandlerOptions): (request: Request) => Promise<Response>;
|
|
3
145
|
|
|
4
146
|
declare class InvoServer {
|
|
5
147
|
private readonly http;
|
|
@@ -8,16 +150,16 @@ declare class InvoServer {
|
|
|
8
150
|
/** Mint a short-lived, game-scoped player token to hand to the browser InvoClient. */
|
|
9
151
|
mintPlayerToken(input: {
|
|
10
152
|
playerEmail: string;
|
|
11
|
-
}): Promise<PlayerToken>;
|
|
153
|
+
}, opts?: CallOptions): Promise<PlayerToken>;
|
|
12
154
|
/** Initiate a cross-game currency SEND. Inspect result.verificationMethod. */
|
|
13
|
-
initiateSend(input: InitiateSendInput): Promise<InitiateResult>;
|
|
155
|
+
initiateSend(input: InitiateSendInput, opts?: CallOptions): Promise<InitiateResult>;
|
|
14
156
|
/** Initiate a cross-game TRANSFER. Inspect result.verificationMethod. */
|
|
15
|
-
initiateTransfer(input: InitiateTransferInput): Promise<InitiateResult>;
|
|
157
|
+
initiateTransfer(input: InitiateTransferInput, opts?: CallOptions): Promise<InitiateResult>;
|
|
16
158
|
/** Create a hosted checkout session (the recommended purchase path). Open the
|
|
17
159
|
* returned checkoutUrl in a WebView/redirect or an iframe; the INVO-hosted page
|
|
18
160
|
* handles cards, saved cards, and 3-D Secure. Crediting is server-side via the
|
|
19
161
|
* purchase.completed webhook. */
|
|
20
|
-
createCheckout(input: CreateCheckoutInput): Promise<CreateCheckoutResult>;
|
|
162
|
+
createCheckout(input: CreateCheckoutInput, opts?: CallOptions): Promise<CreateCheckoutResult>;
|
|
21
163
|
/**
|
|
22
164
|
* Direct purchase via the rail selector. Use when you need a specific rail.
|
|
23
165
|
* - rail "platform" (default): standard card. May return status "requires_action"
|
|
@@ -25,17 +167,17 @@ declare class InvoServer {
|
|
|
25
167
|
* - rail "game": returns status "pending_payment" + paymentUrl (redirect the browser).
|
|
26
168
|
* - rail "steam": NOT a browser flow — the backend returns WRONG_RAIL_ENDPOINT.
|
|
27
169
|
*/
|
|
28
|
-
purchaseCurrency(input: PurchaseInput): Promise<PurchaseResult>;
|
|
170
|
+
purchaseCurrency(input: PurchaseInput, opts?: CallOptions): Promise<PurchaseResult>;
|
|
29
171
|
/** Complete the Stripe-rail 3-D Secure step after the client finished card action. */
|
|
30
172
|
confirmPayment(input: {
|
|
31
173
|
paymentIntentId: string;
|
|
32
174
|
orderId?: string;
|
|
33
|
-
}): Promise<
|
|
175
|
+
}, opts?: CallOptions): Promise<ConfirmPaymentResult>;
|
|
34
176
|
/** Fetch purchase status (order + financial summary + timeline). */
|
|
35
177
|
getOrderDetails(query: {
|
|
36
178
|
orderId?: string;
|
|
37
179
|
transactionId?: string;
|
|
38
|
-
}): Promise<
|
|
180
|
+
}, opts?: CallOptions): Promise<OrderDetailsResult>;
|
|
39
181
|
/**
|
|
40
182
|
* Buy an in-game item by SPENDING the player's existing game currency (§4.8).
|
|
41
183
|
* No real money, no payment rail, no passkey — it's a balance debit, authenticated
|
|
@@ -44,13 +186,35 @@ declare class InvoServer {
|
|
|
44
186
|
* (err.isInsufficientBalance; required_amount/current_balance on err.body). Grant the
|
|
45
187
|
* item to your inventory off the `item.purchased` webhook, not just this response.
|
|
46
188
|
*/
|
|
47
|
-
purchaseItem(input: PurchaseItemInput): Promise<PurchaseItemResult>;
|
|
189
|
+
purchaseItem(input: PurchaseItemInput, opts?: CallOptions): Promise<PurchaseItemResult>;
|
|
48
190
|
/** Paginated item-purchase history for a player (§4.8 companion read). */
|
|
49
|
-
getItemPurchaseHistory(query: ItemHistoryQuery): Promise<
|
|
191
|
+
getItemPurchaseHistory(query: ItemHistoryQuery, opts?: CallOptions): Promise<ItemHistoryResult>;
|
|
50
192
|
/** Look up one item order by EXACTLY ONE of orderId | transactionId | clientRequestId
|
|
51
193
|
* (§4.8). Use clientRequestId for saga/recovery: "did this purchase actually complete?" */
|
|
52
|
-
getItemOrderDetails(query: ItemOrderQuery): Promise<
|
|
194
|
+
getItemOrderDetails(query: ItemOrderQuery, opts?: CallOptions): Promise<OrderDetailsResult>;
|
|
195
|
+
/**
|
|
196
|
+
* Async iterator over a player's entire item-purchase history — pages through
|
|
197
|
+
* `getItemPurchaseHistory` (limit/offset) until exhausted, yielding one row at a time.
|
|
198
|
+
*
|
|
199
|
+
* ```ts
|
|
200
|
+
* for await (const row of invo.iterateItemPurchaseHistory({ playerEmail })) { … }
|
|
201
|
+
* ```
|
|
202
|
+
*/
|
|
203
|
+
iterateItemPurchaseHistory(query: {
|
|
204
|
+
playerEmail: string;
|
|
205
|
+
pageSize?: number;
|
|
206
|
+
}, opts?: CallOptions): AsyncGenerator<Record<string, unknown>, void, unknown>;
|
|
207
|
+
/** Read a player's currency balances, by email or playerId (game-secret). */
|
|
208
|
+
getPlayerBalance(query: PlayerBalanceQuery, opts?: CallOptions): Promise<PlayerBalanceResult>;
|
|
209
|
+
/**
|
|
210
|
+
* List LIVE, unclaimed inbound sends/transfers addressed to a player at YOUR game —
|
|
211
|
+
* the source of truth for a "you have X to collect" badge. Pass the player's email
|
|
212
|
+
* or phone. Match `toPhone` to the logged-in player (don't require `toIdentityId`,
|
|
213
|
+
* which is null when the phone maps to more than one of your players). Pairs with the
|
|
214
|
+
* `transfer.claim_pending` webhook (the webhook is the wake-up; this is the list).
|
|
215
|
+
*/
|
|
216
|
+
getInboundPending(query: InboundPendingQuery, opts?: CallOptions): Promise<InboundPendingResult>;
|
|
53
217
|
private toInitiateResult;
|
|
54
218
|
}
|
|
55
219
|
|
|
56
|
-
export { CreateCheckoutInput, CreateCheckoutResult, InitiateResult, InitiateSendInput, InitiateTransferInput, InvoServer, ItemHistoryQuery, ItemOrderQuery, PlayerToken, PurchaseInput, PurchaseItemInput, PurchaseItemResult, PurchaseResult, ServerConfig };
|
|
220
|
+
export { CallOptions, ConfirmPaymentResult, CreateCheckoutInput, CreateCheckoutResult, InboundPendingQuery, InboundPendingResult, InitiateResult, InitiateSendInput, InitiateTransferInput, InvoError, InvoServer, type InvoWebhookEvent, ItemHistoryQuery, ItemHistoryResult, ItemOrderQuery, type ItemPurchasedData, OrderDetailsResult, PlayerBalanceQuery, PlayerBalanceResult, PlayerToken, type PurchaseCompletedData, type PurchaseEventData, PurchaseInput, PurchaseItemInput, PurchaseItemResult, PurchaseResult, ServerConfig, type TransferEventData, type VerifyWebhookOptions, type WebhookHandlerOptions, createWebhookHandler, verifyWebhook, verifyWebhookAsync };
|