@playmos/sdk 0.1.5 → 0.1.6
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/chunk-B7SHFZYY.js +62 -0
- package/dist/chunk-B7SHFZYY.js.map +1 -0
- package/dist/errors-B-85VYMv.d.cts +224 -0
- package/dist/errors-B-85VYMv.d.ts +224 -0
- package/dist/index.cjs +11 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -248
- package/dist/index.d.ts +9 -248
- package/dist/index.js +14 -103
- package/dist/index.js.map +1 -1
- package/dist/server.cjs +63 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +26 -0
- package/dist/server.d.ts +26 -0
- package/dist/server.js +47 -0
- package/dist/server.js.map +1 -0
- package/package.json +6 -1
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// src/errors.ts
|
|
2
|
+
var PlaymosError = class extends Error {
|
|
3
|
+
constructor(code, message, detail) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = new.target.name;
|
|
6
|
+
this.code = code;
|
|
7
|
+
this.detail = detail;
|
|
8
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
var InvalidAmountError = class extends PlaymosError {
|
|
12
|
+
constructor(amount) {
|
|
13
|
+
super(
|
|
14
|
+
"invalid_amount",
|
|
15
|
+
`Invalid amount: ${JSON.stringify(amount)}. Provide a positive USD decimal string with at most 2 decimals, e.g. "4.99".`,
|
|
16
|
+
{ amount }
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
var MissingFieldError = class extends PlaymosError {
|
|
21
|
+
constructor(field) {
|
|
22
|
+
super("missing_field", `Missing required field: "${field}".`, { field });
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
var InsufficientGasError = class extends PlaymosError {
|
|
26
|
+
constructor(detail) {
|
|
27
|
+
super(
|
|
28
|
+
"insufficient_gas",
|
|
29
|
+
`The player's wallet has too little ETH to pay gas. Ask them to add a little ETH, or switch to gas.mode: "sponsored".`,
|
|
30
|
+
detail
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
var WalletConnectionError = class extends PlaymosError {
|
|
35
|
+
constructor(message = "Could not connect the player's wallet.", detail) {
|
|
36
|
+
super("wallet_connection", message, detail);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
var PaymentFailedError = class extends PlaymosError {
|
|
40
|
+
constructor(message = "The on-chain payment did not complete.", detail) {
|
|
41
|
+
super("payment_failed", message, detail);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
var AuthError = class extends PlaymosError {
|
|
45
|
+
constructor(message = "Invalid or missing API key.", detail) {
|
|
46
|
+
super("auth", message, detail);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
var ApiError = class extends PlaymosError {
|
|
50
|
+
constructor(message, detail) {
|
|
51
|
+
super("api_error", message, detail);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
var ConfigError = class extends PlaymosError {
|
|
55
|
+
constructor(message, detail) {
|
|
56
|
+
super("config", message, detail);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export { ApiError, AuthError, ConfigError, InsufficientGasError, InvalidAmountError, MissingFieldError, PaymentFailedError, PlaymosError, WalletConnectionError };
|
|
61
|
+
//# sourceMappingURL=chunk-B7SHFZYY.js.map
|
|
62
|
+
//# sourceMappingURL=chunk-B7SHFZYY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts"],"names":[],"mappings":";AAkBO,IAAM,YAAA,GAAN,cAA2B,KAAA,CAAM;AAAA,EAKtC,WAAA,CAAY,IAAA,EAAwB,OAAA,EAAiB,MAAA,EAAkC;AACrF,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,OAAO,GAAA,CAAA,MAAA,CAAW,IAAA;AACvB,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AACZ,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AAEd,IAAA,MAAA,CAAO,cAAA,CAAe,IAAA,EAAM,GAAA,CAAA,MAAA,CAAW,SAAS,CAAA;AAAA,EAClD;AACF;AAGO,IAAM,kBAAA,GAAN,cAAiC,YAAA,CAAa;AAAA,EACnD,YAAY,MAAA,EAAiB;AAC3B,IAAA,KAAA;AAAA,MACE,gBAAA;AAAA,MACA,CAAA,gBAAA,EAAmB,IAAA,CAAK,SAAA,CAAU,MAAM,CAAC,CAAA,6EAAA,CAAA;AAAA,MACzC,EAAE,MAAA;AAAO,KACX;AAAA,EACF;AACF;AAGO,IAAM,iBAAA,GAAN,cAAgC,YAAA,CAAa;AAAA,EAClD,YAAY,KAAA,EAAe;AACzB,IAAA,KAAA,CAAM,iBAAiB,CAAA,yBAAA,EAA4B,KAAK,CAAA,EAAA,CAAA,EAAM,EAAE,OAAO,CAAA;AAAA,EACzE;AACF;AAGO,IAAM,oBAAA,GAAN,cAAmC,YAAA,CAAa;AAAA,EACrD,YAAY,MAAA,EAAkC;AAC5C,IAAA,KAAA;AAAA,MACE,kBAAA;AAAA,MACA,CAAA,oHAAA,CAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF;AACF;AAGO,IAAM,qBAAA,GAAN,cAAoC,YAAA,CAAa;AAAA,EACtD,WAAA,CAAY,OAAA,GAAU,wCAAA,EAA0C,MAAA,EAAkC;AAChG,IAAA,KAAA,CAAM,mBAAA,EAAqB,SAAS,MAAM,CAAA;AAAA,EAC5C;AACF;AAGO,IAAM,kBAAA,GAAN,cAAiC,YAAA,CAAa;AAAA,EACnD,WAAA,CAAY,OAAA,GAAU,wCAAA,EAA0C,MAAA,EAAkC;AAChG,IAAA,KAAA,CAAM,gBAAA,EAAkB,SAAS,MAAM,CAAA;AAAA,EACzC;AACF;AAGO,IAAM,SAAA,GAAN,cAAwB,YAAA,CAAa;AAAA,EAC1C,WAAA,CAAY,OAAA,GAAU,6BAAA,EAA+B,MAAA,EAAkC;AACrF,IAAA,KAAA,CAAM,MAAA,EAAQ,SAAS,MAAM,CAAA;AAAA,EAC/B;AACF;AAGO,IAAM,QAAA,GAAN,cAAuB,YAAA,CAAa;AAAA,EACzC,WAAA,CAAY,SAAiB,MAAA,EAAkC;AAC7D,IAAA,KAAA,CAAM,WAAA,EAAa,SAAS,MAAM,CAAA;AAAA,EACpC;AACF;AAGO,IAAM,WAAA,GAAN,cAA0B,YAAA,CAAa;AAAA,EAC5C,WAAA,CAAY,SAAiB,MAAA,EAAkC;AAC7D,IAAA,KAAA,CAAM,QAAA,EAAU,SAAS,MAAM,CAAA;AAAA,EACjC;AACF","file":"chunk-B7SHFZYY.js","sourcesContent":["/**\n * Typed, actionable errors — the Stripe bar (spec §11).\n *\n * Every error carries a stable machine-readable `code` and is thrown at the\n * EARLIEST possible layer: input errors fire client-side before any network or\n * chain call, so a studio never pays gas to discover a typo.\n */\n\nexport type PlaymosErrorCode =\n | \"invalid_amount\"\n | \"missing_field\"\n | \"insufficient_gas\"\n | \"wallet_connection\"\n | \"payment_failed\"\n | \"auth\"\n | \"api_error\"\n | \"config\";\n\nexport class PlaymosError extends Error {\n readonly code: PlaymosErrorCode;\n /** Optional machine context (e.g. the offending field, the http status). */\n readonly detail?: Record<string, unknown>;\n\n constructor(code: PlaymosErrorCode, message: string, detail?: Record<string, unknown>) {\n super(message);\n this.name = new.target.name;\n this.code = code;\n this.detail = detail;\n // Restore prototype chain for `instanceof` across transpile targets.\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** amount ≤ 0, non-numeric, empty, or more than 2 decimal places. */\nexport class InvalidAmountError extends PlaymosError {\n constructor(amount: unknown) {\n super(\n \"invalid_amount\",\n `Invalid amount: ${JSON.stringify(amount)}. Provide a positive USD decimal string with at most 2 decimals, e.g. \"4.99\".`,\n { amount },\n );\n }\n}\n\n/** A required field (sku, playerId, gameId, roundId, agentId…) was empty. */\nexport class MissingFieldError extends PlaymosError {\n constructor(field: string) {\n super(\"missing_field\", `Missing required field: \"${field}\".`, { field });\n }\n}\n\n/** gas mode \"player\" and the player's ETH is too low to cover gas (pre-check, §7). */\nexport class InsufficientGasError extends PlaymosError {\n constructor(detail?: Record<string, unknown>) {\n super(\n \"insufficient_gas\",\n \"The player's wallet has too little ETH to pay gas. Ask them to add a little ETH, or switch to gas.mode: \\\"sponsored\\\".\",\n detail,\n );\n }\n}\n\n/** The player closed or failed the wallet sheet, or no provider is available. */\nexport class WalletConnectionError extends PlaymosError {\n constructor(message = \"Could not connect the player's wallet.\", detail?: Record<string, unknown>) {\n super(\"wallet_connection\", message, detail);\n }\n}\n\n/** The on-chain settlement reverted, was cancelled, or timed out. */\nexport class PaymentFailedError extends PlaymosError {\n constructor(message = \"The on-chain payment did not complete.\", detail?: Record<string, unknown>) {\n super(\"payment_failed\", message, detail);\n }\n}\n\n/** Bad, missing, or wrong-environment API key (e.g. a pk_test_ key on a live route). */\nexport class AuthError extends PlaymosError {\n constructor(message = \"Invalid or missing API key.\", detail?: Record<string, unknown>) {\n super(\"auth\", message, detail);\n }\n}\n\n/** The Playmos service returned a non-2xx we don't have a more specific error for. */\nexport class ApiError extends PlaymosError {\n constructor(message: string, detail?: Record<string, unknown>) {\n super(\"api_error\", message, detail);\n }\n}\n\n/** SDK misconfiguration (e.g. a missing contract address for on-chain mode). */\nexport class ConfigError extends PlaymosError {\n constructor(message: string, detail?: Record<string, unknown>) {\n super(\"config\", message, detail);\n }\n}\n"]}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public types — pruned to WIRED capability only (fixes #344/I and #335/E).
|
|
3
|
+
*
|
|
4
|
+
* The old vendored surface advertised four wallet connectors that silently
|
|
5
|
+
* returned {connected:false} and config fields the code never read. Here the
|
|
6
|
+
* types expose exactly what V1 does: two connectors, the fields `pay()` /
|
|
7
|
+
* `enterRound()` actually use, and no more.
|
|
8
|
+
*/
|
|
9
|
+
type Network = "base" | "base-sepolia";
|
|
10
|
+
/** Only the connectors that are actually implemented. */
|
|
11
|
+
type WalletConnector = "base-account" | "injected";
|
|
12
|
+
type GasMode = "sponsored" | "player";
|
|
13
|
+
type PaymentStatus = "created" | "pending" | "confirmed" | "failed";
|
|
14
|
+
/** Minimal EIP-1193 provider shape (what the wallet must expose). */
|
|
15
|
+
interface Eip1193Provider {
|
|
16
|
+
request(args: {
|
|
17
|
+
method: string;
|
|
18
|
+
params?: unknown[] | object;
|
|
19
|
+
}): Promise<unknown>;
|
|
20
|
+
}
|
|
21
|
+
interface GasConfig {
|
|
22
|
+
mode?: GasMode;
|
|
23
|
+
/** CDP paymaster URL — required only for `mode: "sponsored"`. */
|
|
24
|
+
paymasterUrl?: string;
|
|
25
|
+
}
|
|
26
|
+
interface WalletConfig {
|
|
27
|
+
connector?: WalletConnector;
|
|
28
|
+
/**
|
|
29
|
+
* Pre-built EIP-1193 provider. If omitted, `injected` uses globalThis.ethereum
|
|
30
|
+
* and `base-account` expects a provider supplied by the Base Account SDK host.
|
|
31
|
+
*/
|
|
32
|
+
provider?: Eip1193Provider;
|
|
33
|
+
}
|
|
34
|
+
/** Per-game contract addresses. On testnet these come from the service intent,
|
|
35
|
+
* but a studio may pin them explicitly (matches the dogfood adapter). */
|
|
36
|
+
interface ContractConfig {
|
|
37
|
+
usdc?: `0x${string}`;
|
|
38
|
+
playmosPay?: `0x${string}`;
|
|
39
|
+
prizePool?: `0x${string}`;
|
|
40
|
+
}
|
|
41
|
+
interface AgentEconomyConfig {
|
|
42
|
+
enabled?: boolean;
|
|
43
|
+
/** Tax skimmed to Playmos on each agent↔agent transfer. 100 = 1% (default). */
|
|
44
|
+
taxBps?: number;
|
|
45
|
+
}
|
|
46
|
+
interface PlaymosConfig {
|
|
47
|
+
/** pk_test_* (sandbox / Base Sepolia) or pk_live_* (production / Base mainnet).
|
|
48
|
+
* The key prefix selects the environment. */
|
|
49
|
+
apiKey: string;
|
|
50
|
+
/** Override the network derived from the key. Explicit value wins. */
|
|
51
|
+
network?: Network;
|
|
52
|
+
wallet?: WalletConfig;
|
|
53
|
+
gas?: GasConfig;
|
|
54
|
+
agentEconomy?: AgentEconomyConfig;
|
|
55
|
+
contracts?: ContractConfig;
|
|
56
|
+
/** Escape hatch; defaults to the right env's base URL. */
|
|
57
|
+
apiBaseUrl?: string;
|
|
58
|
+
/**
|
|
59
|
+
* Explicit, clearly-labeled offline unit-test helper — instant, deterministic,
|
|
60
|
+
* NO network, NO chain. Never the default, never conflated with sandbox. Note:
|
|
61
|
+
* even mock results use the REAL status union (never a synthetic "mocked").
|
|
62
|
+
*/
|
|
63
|
+
mock?: boolean;
|
|
64
|
+
}
|
|
65
|
+
interface PayInput {
|
|
66
|
+
/** USD decimal string ("4.99"). Rejected: ≤ 0, non-numeric, > 2 dp. */
|
|
67
|
+
amount: string;
|
|
68
|
+
/** Your product id, echoed on the receipt + webhook. */
|
|
69
|
+
sku: string;
|
|
70
|
+
/** Your opaque user id. */
|
|
71
|
+
playerId: string;
|
|
72
|
+
/**
|
|
73
|
+
* The game this IAP belongs to. Optional: when your API key maps to exactly
|
|
74
|
+
* one game the service resolves it for you (the quickstart). Supply it
|
|
75
|
+
* explicitly when your key spans multiple games.
|
|
76
|
+
*/
|
|
77
|
+
gameId?: string;
|
|
78
|
+
/** The studio wallet that receives the 99%. Falls back to the service default. */
|
|
79
|
+
studio?: `0x${string}`;
|
|
80
|
+
/** Supply your own to make retries safe; omit and the SDK generates a ULID. */
|
|
81
|
+
idempotencyKey?: string;
|
|
82
|
+
metadata?: Record<string, string>;
|
|
83
|
+
}
|
|
84
|
+
interface EnterRoundInput {
|
|
85
|
+
/** The game — selects its prize-pool contract. */
|
|
86
|
+
gameId: string;
|
|
87
|
+
/** The round being entered. */
|
|
88
|
+
roundId: string;
|
|
89
|
+
/** USD entry — grows this round's pool. */
|
|
90
|
+
amount: string;
|
|
91
|
+
playerId: string;
|
|
92
|
+
idempotencyKey?: string;
|
|
93
|
+
metadata?: Record<string, string>;
|
|
94
|
+
/**
|
|
95
|
+
* Advanced (games that manage their own on-chain rounds, e.g. the Playmos
|
|
96
|
+
* game-hub kit): the EXACT on-chain round key to enter — overrides the default
|
|
97
|
+
* `${gameId}:${roundId}` derivation. Set it to the value your game's server
|
|
98
|
+
* verifies `hasEntered` against (e.g. "bjtest:T1").
|
|
99
|
+
*/
|
|
100
|
+
roundKey?: string;
|
|
101
|
+
/**
|
|
102
|
+
* Advanced: the EXACT on-chain identity for this paid attempt (e.g.
|
|
103
|
+
* "0xWallet#nonce"). Overrides the server-generated identity so the on-chain
|
|
104
|
+
* entry and your server's `hasEntered` check line up. One entry per identity.
|
|
105
|
+
*/
|
|
106
|
+
identity?: string;
|
|
107
|
+
}
|
|
108
|
+
interface Payment {
|
|
109
|
+
/** `pay_…` (IAP) or `entry_…` (prize-pool) — ULID, server-issued, unique. */
|
|
110
|
+
id: string;
|
|
111
|
+
status: PaymentStatus;
|
|
112
|
+
/** "iap" (1% external) or "entry" (60/30/10 prize-pool). */
|
|
113
|
+
kind: "iap" | "entry";
|
|
114
|
+
amount: string;
|
|
115
|
+
fee: string;
|
|
116
|
+
net: string;
|
|
117
|
+
/** Present for prize-pool entries: the 60/30/10 breakdown in USD. */
|
|
118
|
+
split?: {
|
|
119
|
+
pool: string;
|
|
120
|
+
seed: string;
|
|
121
|
+
rake: string;
|
|
122
|
+
};
|
|
123
|
+
sku?: string;
|
|
124
|
+
roundId?: string;
|
|
125
|
+
gameId?: string;
|
|
126
|
+
playerId: string;
|
|
127
|
+
txHash?: `0x${string}`;
|
|
128
|
+
chain: Network;
|
|
129
|
+
createdAt: string;
|
|
130
|
+
metadata?: Record<string, string>;
|
|
131
|
+
/**
|
|
132
|
+
* The on-chain identity used for a prize-pool entry (echo of
|
|
133
|
+
* `EnterRoundInput.identity` or the SDK-derived value) — reconcile against your
|
|
134
|
+
* server's `hasEntered`.
|
|
135
|
+
*/
|
|
136
|
+
identity?: string;
|
|
137
|
+
/** True only when produced by the labeled `mock: true` helper. */
|
|
138
|
+
mock?: boolean;
|
|
139
|
+
}
|
|
140
|
+
/** What `verify()` resolves to (server reads the chain). */
|
|
141
|
+
interface VerifyResult {
|
|
142
|
+
id: string;
|
|
143
|
+
status: PaymentStatus;
|
|
144
|
+
amount: string;
|
|
145
|
+
fee: string;
|
|
146
|
+
net: string;
|
|
147
|
+
txHash?: `0x${string}`;
|
|
148
|
+
playerId: string;
|
|
149
|
+
sku?: string;
|
|
150
|
+
roundId?: string;
|
|
151
|
+
chain: Network;
|
|
152
|
+
/** How the service derived this status: an on-chain read, the honest cache, or
|
|
153
|
+
* degraded (chain reads not configured). Lets the SDK stop polling when the
|
|
154
|
+
* service can never confirm on-chain. */
|
|
155
|
+
verifiedVia?: "chain" | "cache" | "degraded";
|
|
156
|
+
chainReads?: "enabled" | "degraded";
|
|
157
|
+
}
|
|
158
|
+
type WebhookEventType = "payment.confirmed" | "payment.failed" | "payout.settled" | "refund.processed";
|
|
159
|
+
interface WebhookEvent {
|
|
160
|
+
id: string;
|
|
161
|
+
type: WebhookEventType;
|
|
162
|
+
createdAt: string;
|
|
163
|
+
data: {
|
|
164
|
+
id: string;
|
|
165
|
+
status: PaymentStatus;
|
|
166
|
+
amount: string;
|
|
167
|
+
fee: string;
|
|
168
|
+
net: string;
|
|
169
|
+
playerId: string;
|
|
170
|
+
sku?: string;
|
|
171
|
+
txHash?: `0x${string}`;
|
|
172
|
+
chain: Network;
|
|
173
|
+
metadata?: Record<string, string>;
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Typed, actionable errors — the Stripe bar (spec §11).
|
|
179
|
+
*
|
|
180
|
+
* Every error carries a stable machine-readable `code` and is thrown at the
|
|
181
|
+
* EARLIEST possible layer: input errors fire client-side before any network or
|
|
182
|
+
* chain call, so a studio never pays gas to discover a typo.
|
|
183
|
+
*/
|
|
184
|
+
type PlaymosErrorCode = "invalid_amount" | "missing_field" | "insufficient_gas" | "wallet_connection" | "payment_failed" | "auth" | "api_error" | "config";
|
|
185
|
+
declare class PlaymosError extends Error {
|
|
186
|
+
readonly code: PlaymosErrorCode;
|
|
187
|
+
/** Optional machine context (e.g. the offending field, the http status). */
|
|
188
|
+
readonly detail?: Record<string, unknown>;
|
|
189
|
+
constructor(code: PlaymosErrorCode, message: string, detail?: Record<string, unknown>);
|
|
190
|
+
}
|
|
191
|
+
/** amount ≤ 0, non-numeric, empty, or more than 2 decimal places. */
|
|
192
|
+
declare class InvalidAmountError extends PlaymosError {
|
|
193
|
+
constructor(amount: unknown);
|
|
194
|
+
}
|
|
195
|
+
/** A required field (sku, playerId, gameId, roundId, agentId…) was empty. */
|
|
196
|
+
declare class MissingFieldError extends PlaymosError {
|
|
197
|
+
constructor(field: string);
|
|
198
|
+
}
|
|
199
|
+
/** gas mode "player" and the player's ETH is too low to cover gas (pre-check, §7). */
|
|
200
|
+
declare class InsufficientGasError extends PlaymosError {
|
|
201
|
+
constructor(detail?: Record<string, unknown>);
|
|
202
|
+
}
|
|
203
|
+
/** The player closed or failed the wallet sheet, or no provider is available. */
|
|
204
|
+
declare class WalletConnectionError extends PlaymosError {
|
|
205
|
+
constructor(message?: string, detail?: Record<string, unknown>);
|
|
206
|
+
}
|
|
207
|
+
/** The on-chain settlement reverted, was cancelled, or timed out. */
|
|
208
|
+
declare class PaymentFailedError extends PlaymosError {
|
|
209
|
+
constructor(message?: string, detail?: Record<string, unknown>);
|
|
210
|
+
}
|
|
211
|
+
/** Bad, missing, or wrong-environment API key (e.g. a pk_test_ key on a live route). */
|
|
212
|
+
declare class AuthError extends PlaymosError {
|
|
213
|
+
constructor(message?: string, detail?: Record<string, unknown>);
|
|
214
|
+
}
|
|
215
|
+
/** The Playmos service returned a non-2xx we don't have a more specific error for. */
|
|
216
|
+
declare class ApiError extends PlaymosError {
|
|
217
|
+
constructor(message: string, detail?: Record<string, unknown>);
|
|
218
|
+
}
|
|
219
|
+
/** SDK misconfiguration (e.g. a missing contract address for on-chain mode). */
|
|
220
|
+
declare class ConfigError extends PlaymosError {
|
|
221
|
+
constructor(message: string, detail?: Record<string, unknown>);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export { type AgentEconomyConfig as A, ConfigError as C, type EnterRoundInput as E, type GasConfig as G, InsufficientGasError as I, MissingFieldError as M, type Network as N, type PlaymosConfig as P, type VerifyResult as V, type WebhookEvent as W, type PayInput as a, type Payment as b, ApiError as c, AuthError as d, type ContractConfig as e, type Eip1193Provider as f, type GasMode as g, InvalidAmountError as h, PaymentFailedError as i, type PaymentStatus as j, PlaymosError as k, type PlaymosErrorCode as l, type WalletConfig as m, WalletConnectionError as n, type WalletConnector as o, type WebhookEventType as p };
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public types — pruned to WIRED capability only (fixes #344/I and #335/E).
|
|
3
|
+
*
|
|
4
|
+
* The old vendored surface advertised four wallet connectors that silently
|
|
5
|
+
* returned {connected:false} and config fields the code never read. Here the
|
|
6
|
+
* types expose exactly what V1 does: two connectors, the fields `pay()` /
|
|
7
|
+
* `enterRound()` actually use, and no more.
|
|
8
|
+
*/
|
|
9
|
+
type Network = "base" | "base-sepolia";
|
|
10
|
+
/** Only the connectors that are actually implemented. */
|
|
11
|
+
type WalletConnector = "base-account" | "injected";
|
|
12
|
+
type GasMode = "sponsored" | "player";
|
|
13
|
+
type PaymentStatus = "created" | "pending" | "confirmed" | "failed";
|
|
14
|
+
/** Minimal EIP-1193 provider shape (what the wallet must expose). */
|
|
15
|
+
interface Eip1193Provider {
|
|
16
|
+
request(args: {
|
|
17
|
+
method: string;
|
|
18
|
+
params?: unknown[] | object;
|
|
19
|
+
}): Promise<unknown>;
|
|
20
|
+
}
|
|
21
|
+
interface GasConfig {
|
|
22
|
+
mode?: GasMode;
|
|
23
|
+
/** CDP paymaster URL — required only for `mode: "sponsored"`. */
|
|
24
|
+
paymasterUrl?: string;
|
|
25
|
+
}
|
|
26
|
+
interface WalletConfig {
|
|
27
|
+
connector?: WalletConnector;
|
|
28
|
+
/**
|
|
29
|
+
* Pre-built EIP-1193 provider. If omitted, `injected` uses globalThis.ethereum
|
|
30
|
+
* and `base-account` expects a provider supplied by the Base Account SDK host.
|
|
31
|
+
*/
|
|
32
|
+
provider?: Eip1193Provider;
|
|
33
|
+
}
|
|
34
|
+
/** Per-game contract addresses. On testnet these come from the service intent,
|
|
35
|
+
* but a studio may pin them explicitly (matches the dogfood adapter). */
|
|
36
|
+
interface ContractConfig {
|
|
37
|
+
usdc?: `0x${string}`;
|
|
38
|
+
playmosPay?: `0x${string}`;
|
|
39
|
+
prizePool?: `0x${string}`;
|
|
40
|
+
}
|
|
41
|
+
interface AgentEconomyConfig {
|
|
42
|
+
enabled?: boolean;
|
|
43
|
+
/** Tax skimmed to Playmos on each agent↔agent transfer. 100 = 1% (default). */
|
|
44
|
+
taxBps?: number;
|
|
45
|
+
}
|
|
46
|
+
interface PlaymosConfig {
|
|
47
|
+
/** pk_test_* (sandbox / Base Sepolia) or pk_live_* (production / Base mainnet).
|
|
48
|
+
* The key prefix selects the environment. */
|
|
49
|
+
apiKey: string;
|
|
50
|
+
/** Override the network derived from the key. Explicit value wins. */
|
|
51
|
+
network?: Network;
|
|
52
|
+
wallet?: WalletConfig;
|
|
53
|
+
gas?: GasConfig;
|
|
54
|
+
agentEconomy?: AgentEconomyConfig;
|
|
55
|
+
contracts?: ContractConfig;
|
|
56
|
+
/** Escape hatch; defaults to the right env's base URL. */
|
|
57
|
+
apiBaseUrl?: string;
|
|
58
|
+
/**
|
|
59
|
+
* Explicit, clearly-labeled offline unit-test helper — instant, deterministic,
|
|
60
|
+
* NO network, NO chain. Never the default, never conflated with sandbox. Note:
|
|
61
|
+
* even mock results use the REAL status union (never a synthetic "mocked").
|
|
62
|
+
*/
|
|
63
|
+
mock?: boolean;
|
|
64
|
+
}
|
|
65
|
+
interface PayInput {
|
|
66
|
+
/** USD decimal string ("4.99"). Rejected: ≤ 0, non-numeric, > 2 dp. */
|
|
67
|
+
amount: string;
|
|
68
|
+
/** Your product id, echoed on the receipt + webhook. */
|
|
69
|
+
sku: string;
|
|
70
|
+
/** Your opaque user id. */
|
|
71
|
+
playerId: string;
|
|
72
|
+
/**
|
|
73
|
+
* The game this IAP belongs to. Optional: when your API key maps to exactly
|
|
74
|
+
* one game the service resolves it for you (the quickstart). Supply it
|
|
75
|
+
* explicitly when your key spans multiple games.
|
|
76
|
+
*/
|
|
77
|
+
gameId?: string;
|
|
78
|
+
/** The studio wallet that receives the 99%. Falls back to the service default. */
|
|
79
|
+
studio?: `0x${string}`;
|
|
80
|
+
/** Supply your own to make retries safe; omit and the SDK generates a ULID. */
|
|
81
|
+
idempotencyKey?: string;
|
|
82
|
+
metadata?: Record<string, string>;
|
|
83
|
+
}
|
|
84
|
+
interface EnterRoundInput {
|
|
85
|
+
/** The game — selects its prize-pool contract. */
|
|
86
|
+
gameId: string;
|
|
87
|
+
/** The round being entered. */
|
|
88
|
+
roundId: string;
|
|
89
|
+
/** USD entry — grows this round's pool. */
|
|
90
|
+
amount: string;
|
|
91
|
+
playerId: string;
|
|
92
|
+
idempotencyKey?: string;
|
|
93
|
+
metadata?: Record<string, string>;
|
|
94
|
+
/**
|
|
95
|
+
* Advanced (games that manage their own on-chain rounds, e.g. the Playmos
|
|
96
|
+
* game-hub kit): the EXACT on-chain round key to enter — overrides the default
|
|
97
|
+
* `${gameId}:${roundId}` derivation. Set it to the value your game's server
|
|
98
|
+
* verifies `hasEntered` against (e.g. "bjtest:T1").
|
|
99
|
+
*/
|
|
100
|
+
roundKey?: string;
|
|
101
|
+
/**
|
|
102
|
+
* Advanced: the EXACT on-chain identity for this paid attempt (e.g.
|
|
103
|
+
* "0xWallet#nonce"). Overrides the server-generated identity so the on-chain
|
|
104
|
+
* entry and your server's `hasEntered` check line up. One entry per identity.
|
|
105
|
+
*/
|
|
106
|
+
identity?: string;
|
|
107
|
+
}
|
|
108
|
+
interface Payment {
|
|
109
|
+
/** `pay_…` (IAP) or `entry_…` (prize-pool) — ULID, server-issued, unique. */
|
|
110
|
+
id: string;
|
|
111
|
+
status: PaymentStatus;
|
|
112
|
+
/** "iap" (1% external) or "entry" (60/30/10 prize-pool). */
|
|
113
|
+
kind: "iap" | "entry";
|
|
114
|
+
amount: string;
|
|
115
|
+
fee: string;
|
|
116
|
+
net: string;
|
|
117
|
+
/** Present for prize-pool entries: the 60/30/10 breakdown in USD. */
|
|
118
|
+
split?: {
|
|
119
|
+
pool: string;
|
|
120
|
+
seed: string;
|
|
121
|
+
rake: string;
|
|
122
|
+
};
|
|
123
|
+
sku?: string;
|
|
124
|
+
roundId?: string;
|
|
125
|
+
gameId?: string;
|
|
126
|
+
playerId: string;
|
|
127
|
+
txHash?: `0x${string}`;
|
|
128
|
+
chain: Network;
|
|
129
|
+
createdAt: string;
|
|
130
|
+
metadata?: Record<string, string>;
|
|
131
|
+
/**
|
|
132
|
+
* The on-chain identity used for a prize-pool entry (echo of
|
|
133
|
+
* `EnterRoundInput.identity` or the SDK-derived value) — reconcile against your
|
|
134
|
+
* server's `hasEntered`.
|
|
135
|
+
*/
|
|
136
|
+
identity?: string;
|
|
137
|
+
/** True only when produced by the labeled `mock: true` helper. */
|
|
138
|
+
mock?: boolean;
|
|
139
|
+
}
|
|
140
|
+
/** What `verify()` resolves to (server reads the chain). */
|
|
141
|
+
interface VerifyResult {
|
|
142
|
+
id: string;
|
|
143
|
+
status: PaymentStatus;
|
|
144
|
+
amount: string;
|
|
145
|
+
fee: string;
|
|
146
|
+
net: string;
|
|
147
|
+
txHash?: `0x${string}`;
|
|
148
|
+
playerId: string;
|
|
149
|
+
sku?: string;
|
|
150
|
+
roundId?: string;
|
|
151
|
+
chain: Network;
|
|
152
|
+
/** How the service derived this status: an on-chain read, the honest cache, or
|
|
153
|
+
* degraded (chain reads not configured). Lets the SDK stop polling when the
|
|
154
|
+
* service can never confirm on-chain. */
|
|
155
|
+
verifiedVia?: "chain" | "cache" | "degraded";
|
|
156
|
+
chainReads?: "enabled" | "degraded";
|
|
157
|
+
}
|
|
158
|
+
type WebhookEventType = "payment.confirmed" | "payment.failed" | "payout.settled" | "refund.processed";
|
|
159
|
+
interface WebhookEvent {
|
|
160
|
+
id: string;
|
|
161
|
+
type: WebhookEventType;
|
|
162
|
+
createdAt: string;
|
|
163
|
+
data: {
|
|
164
|
+
id: string;
|
|
165
|
+
status: PaymentStatus;
|
|
166
|
+
amount: string;
|
|
167
|
+
fee: string;
|
|
168
|
+
net: string;
|
|
169
|
+
playerId: string;
|
|
170
|
+
sku?: string;
|
|
171
|
+
txHash?: `0x${string}`;
|
|
172
|
+
chain: Network;
|
|
173
|
+
metadata?: Record<string, string>;
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Typed, actionable errors — the Stripe bar (spec §11).
|
|
179
|
+
*
|
|
180
|
+
* Every error carries a stable machine-readable `code` and is thrown at the
|
|
181
|
+
* EARLIEST possible layer: input errors fire client-side before any network or
|
|
182
|
+
* chain call, so a studio never pays gas to discover a typo.
|
|
183
|
+
*/
|
|
184
|
+
type PlaymosErrorCode = "invalid_amount" | "missing_field" | "insufficient_gas" | "wallet_connection" | "payment_failed" | "auth" | "api_error" | "config";
|
|
185
|
+
declare class PlaymosError extends Error {
|
|
186
|
+
readonly code: PlaymosErrorCode;
|
|
187
|
+
/** Optional machine context (e.g. the offending field, the http status). */
|
|
188
|
+
readonly detail?: Record<string, unknown>;
|
|
189
|
+
constructor(code: PlaymosErrorCode, message: string, detail?: Record<string, unknown>);
|
|
190
|
+
}
|
|
191
|
+
/** amount ≤ 0, non-numeric, empty, or more than 2 decimal places. */
|
|
192
|
+
declare class InvalidAmountError extends PlaymosError {
|
|
193
|
+
constructor(amount: unknown);
|
|
194
|
+
}
|
|
195
|
+
/** A required field (sku, playerId, gameId, roundId, agentId…) was empty. */
|
|
196
|
+
declare class MissingFieldError extends PlaymosError {
|
|
197
|
+
constructor(field: string);
|
|
198
|
+
}
|
|
199
|
+
/** gas mode "player" and the player's ETH is too low to cover gas (pre-check, §7). */
|
|
200
|
+
declare class InsufficientGasError extends PlaymosError {
|
|
201
|
+
constructor(detail?: Record<string, unknown>);
|
|
202
|
+
}
|
|
203
|
+
/** The player closed or failed the wallet sheet, or no provider is available. */
|
|
204
|
+
declare class WalletConnectionError extends PlaymosError {
|
|
205
|
+
constructor(message?: string, detail?: Record<string, unknown>);
|
|
206
|
+
}
|
|
207
|
+
/** The on-chain settlement reverted, was cancelled, or timed out. */
|
|
208
|
+
declare class PaymentFailedError extends PlaymosError {
|
|
209
|
+
constructor(message?: string, detail?: Record<string, unknown>);
|
|
210
|
+
}
|
|
211
|
+
/** Bad, missing, or wrong-environment API key (e.g. a pk_test_ key on a live route). */
|
|
212
|
+
declare class AuthError extends PlaymosError {
|
|
213
|
+
constructor(message?: string, detail?: Record<string, unknown>);
|
|
214
|
+
}
|
|
215
|
+
/** The Playmos service returned a non-2xx we don't have a more specific error for. */
|
|
216
|
+
declare class ApiError extends PlaymosError {
|
|
217
|
+
constructor(message: string, detail?: Record<string, unknown>);
|
|
218
|
+
}
|
|
219
|
+
/** SDK misconfiguration (e.g. a missing contract address for on-chain mode). */
|
|
220
|
+
declare class ConfigError extends PlaymosError {
|
|
221
|
+
constructor(message: string, detail?: Record<string, unknown>);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export { type AgentEconomyConfig as A, ConfigError as C, type EnterRoundInput as E, type GasConfig as G, InsufficientGasError as I, MissingFieldError as M, type Network as N, type PlaymosConfig as P, type VerifyResult as V, type WebhookEvent as W, type PayInput as a, type Payment as b, ApiError as c, AuthError as d, type ContractConfig as e, type Eip1193Provider as f, type GasMode as g, InvalidAmountError as h, PaymentFailedError as i, type PaymentStatus as j, PlaymosError as k, type PlaymosErrorCode as l, type WalletConfig as m, WalletConnectionError as n, type WalletConnector as o, type WebhookEventType as p };
|
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var viem = require('viem');
|
|
4
|
-
var crypto = require('crypto');
|
|
5
4
|
|
|
6
5
|
// src/errors.ts
|
|
7
6
|
var PlaymosError = class extends Error {
|
|
@@ -513,46 +512,6 @@ function mockEntryPayment(args) {
|
|
|
513
512
|
mock: true
|
|
514
513
|
};
|
|
515
514
|
}
|
|
516
|
-
var WebhookSignatureError = class extends PlaymosError {
|
|
517
|
-
constructor(message) {
|
|
518
|
-
super("api_error", `Webhook signature verification failed: ${message}`);
|
|
519
|
-
}
|
|
520
|
-
};
|
|
521
|
-
function parseHeader(header) {
|
|
522
|
-
const parts = Object.fromEntries(
|
|
523
|
-
header.split(",").map((kv) => {
|
|
524
|
-
const [k, v] = kv.split("=");
|
|
525
|
-
return [k?.trim(), v?.trim()];
|
|
526
|
-
})
|
|
527
|
-
);
|
|
528
|
-
const t = Number(parts["t"]);
|
|
529
|
-
const v1 = parts["v1"];
|
|
530
|
-
if (!Number.isFinite(t) || !v1) throw new WebhookSignatureError("malformed signature header");
|
|
531
|
-
return { t, v1 };
|
|
532
|
-
}
|
|
533
|
-
function computeSignature(secret, t, rawBody) {
|
|
534
|
-
return crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
|
|
535
|
-
}
|
|
536
|
-
function verifyWebhook(rawBody, signatureHeader, secret, opts = {}) {
|
|
537
|
-
if (!signatureHeader || Array.isArray(signatureHeader)) {
|
|
538
|
-
throw new WebhookSignatureError("missing signature header");
|
|
539
|
-
}
|
|
540
|
-
if (!secret) throw new WebhookSignatureError("missing signing secret");
|
|
541
|
-
const body = typeof rawBody === "string" ? rawBody : rawBody.toString("utf8");
|
|
542
|
-
const { t, v1 } = parseHeader(signatureHeader);
|
|
543
|
-
const tolerance = opts.toleranceSeconds ?? 300;
|
|
544
|
-
const nowSec = Math.floor(Date.now() / 1e3);
|
|
545
|
-
if (Math.abs(nowSec - t) > tolerance) {
|
|
546
|
-
throw new WebhookSignatureError(`timestamp outside tolerance (${tolerance}s)`);
|
|
547
|
-
}
|
|
548
|
-
const expected = computeSignature(secret, t, body);
|
|
549
|
-
const a = Buffer.from(expected, "hex");
|
|
550
|
-
const b = Buffer.from(v1, "hex");
|
|
551
|
-
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
|
|
552
|
-
throw new WebhookSignatureError("signature mismatch");
|
|
553
|
-
}
|
|
554
|
-
return JSON.parse(body);
|
|
555
|
-
}
|
|
556
515
|
|
|
557
516
|
// src/client.ts
|
|
558
517
|
var IAP_FEE_BPS = 100;
|
|
@@ -562,8 +521,17 @@ var RAKE_BPS = 1e3;
|
|
|
562
521
|
var Playmos = class {
|
|
563
522
|
constructor(config) {
|
|
564
523
|
this.webhooks = {
|
|
565
|
-
/**
|
|
566
|
-
|
|
524
|
+
/**
|
|
525
|
+
* Webhook verification is server-only (it uses node:crypto) and no longer
|
|
526
|
+
* ships in the browser entry (issue #9). On a backend, import it directly:
|
|
527
|
+
* import { verifyWebhook } from "@playmos/sdk/server";
|
|
528
|
+
*/
|
|
529
|
+
verify: (_rawBody, _signatureHeader, _secret) => {
|
|
530
|
+
throw new ConfigError(
|
|
531
|
+
'playmos.webhooks.verify is server-only and no longer ships in the browser entry (issue #9). Use: import { verifyWebhook } from "@playmos/sdk/server"',
|
|
532
|
+
{ entry: "@playmos/sdk/server" }
|
|
533
|
+
);
|
|
534
|
+
}
|
|
567
535
|
};
|
|
568
536
|
this.payouts = {
|
|
569
537
|
/** Choose how the studio is paid: "usdc" (default) or "fiat" (Bridge). */
|
|
@@ -840,7 +808,6 @@ exports.PlaymosError = PlaymosError;
|
|
|
840
808
|
exports.USDC_ADDRESS = USDC_ADDRESS;
|
|
841
809
|
exports.USDC_DECIMALS = USDC_DECIMALS;
|
|
842
810
|
exports.WalletConnectionError = WalletConnectionError;
|
|
843
|
-
exports.WebhookSignatureError = WebhookSignatureError;
|
|
844
811
|
exports.computeIapSplit = computeIapSplit;
|
|
845
812
|
exports.computePoolSplit = computePoolSplit;
|
|
846
813
|
exports.formatMicroToUsd = formatMicroToUsd;
|
|
@@ -849,6 +816,5 @@ exports.prefixedId = prefixedId;
|
|
|
849
816
|
exports.previewIapSplit = previewIapSplit;
|
|
850
817
|
exports.previewPoolSplit = previewPoolSplit;
|
|
851
818
|
exports.ulid = ulid;
|
|
852
|
-
exports.verifyWebhook = verifyWebhook;
|
|
853
819
|
//# sourceMappingURL=index.cjs.map
|
|
854
820
|
//# sourceMappingURL=index.cjs.map
|