mpp-test-sdk 1.0.0 → 1.1.2

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/README.md CHANGED
@@ -4,9 +4,9 @@
4
4
  [![Node.js](https://img.shields.io/node/v/mpp-test-sdk)](https://nodejs.org)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-zinc.svg)](LICENSE)
6
6
 
7
- Test pay-per-request APIs on Tempo testnet. Auto-creates wallets, funds from faucet, handles 402 payments zero setup required.
7
+ Test pay-per-request APIs on **Solana** - devnet, testnet, or mainnet. Auto-creates wallets, airdrops SOL, handles HTTP 402 MPP payments. Zero setup required.
8
8
 
9
- **[mpptestkit.com](https://mpptestkit.com)** · [GitHub](https://github.com/mpptestkit/mpp-test-sdk) · [X](https://x.com/mpptestkit)
9
+ **[mpptestkit.com](https://mpptestkit.com)** · [Playground](https://mpptestkit.com/playground) · [Docs](https://mpptestkit.com/docs) · [GitHub](https://github.com/mpptestkit/mpp-test-sdk) · [X](https://x.com/mpptestkit)
10
10
 
11
11
  ---
12
12
 
@@ -16,7 +16,7 @@ Test pay-per-request APIs on Tempo testnet. Auto-creates wallets, funds from fau
16
16
  npm i mpp-test-sdk
17
17
  ```
18
18
 
19
- Requires Node.js 22+.
19
+ Requires Node.js 18+.
20
20
 
21
21
  ---
22
22
 
@@ -25,8 +25,16 @@ Requires Node.js 22+.
25
25
  ```ts
26
26
  import { mppFetch } from "mpp-test-sdk";
27
27
 
28
+ // One line. No wallet setup. No config.
28
29
  const res = await mppFetch("https://your-api.com/api/data");
29
30
  const data = await res.json();
31
+
32
+ // What happened automatically:
33
+ // 1. SDK generated a Solana keypair
34
+ // 2. Airdropped 2 SOL from the devnet faucet
35
+ // 3. Server returned 402 Payment Required
36
+ // 4. SDK sent 0.001 SOL on Solana (~800ms finality)
37
+ // 5. Retried with Payment-Receipt header → 200 OK
30
38
  ```
31
39
 
32
40
  Or with a dedicated client instance:
@@ -35,13 +43,14 @@ Or with a dedicated client instance:
35
43
  import { createTestClient } from "mpp-test-sdk";
36
44
 
37
45
  const client = await createTestClient({
46
+ network: "devnet",
38
47
  onStep: (step) => console.log(step.type, step.message),
39
48
  });
40
49
 
41
50
  const res = await client.fetch("https://your-api.com/api/data");
42
51
  ```
43
52
 
44
- The SDK handles the full flow: wallet generation, testnet faucet funding, 402 detection, on-chain payment, and automatic retry with payment proof.
53
+ Call `mppFetch.reset()` to discard the shared client and generate a fresh wallet on the next request.
45
54
 
46
55
  ---
47
56
 
@@ -52,17 +61,19 @@ import express from "express";
52
61
  import { createTestServer } from "mpp-test-sdk";
53
62
 
54
63
  const app = express();
55
- const mpp = createTestServer({ secretKey: process.env.MPP_SECRET_KEY });
64
+ const mpp = createTestServer(); // auto-generates server wallet
56
65
 
57
- // Free no middleware
66
+ // Free - no middleware
58
67
  app.get("/api/ping", (req, res) => res.json({ ok: true }));
59
68
 
60
- // Paid one line
61
- app.get("/api/data", mpp.charge({ amount: "0.01" }), (req, res) => {
62
- res.json({ data: "premium content" });
63
- });
69
+ // Paid - one line (0.001 SOL)
70
+ app.get("/api/data",
71
+ mpp.charge({ amount: "0.001" }),
72
+ (req, res) => res.json({ data: "premium content" })
73
+ );
64
74
 
65
75
  app.listen(3001);
76
+ console.log("Payments go to:", mpp.recipientAddress);
66
77
  ```
67
78
 
68
79
  ---
@@ -71,47 +82,66 @@ app.listen(3001);
71
82
 
72
83
  ### `mppFetch(url, init?)`
73
84
 
74
- Drop-in replacement for `fetch`. Uses a shared client lazily created on first call.
75
-
76
- Call `mppFetch.reset()` to discard the shared client and generate a new wallet on the next request.
85
+ Drop-in replacement for `fetch`. Lazily creates a shared client on first call and reuses it across requests.
77
86
 
78
87
  ### `createTestClient(config?)`
79
88
 
80
- Creates a client with its own isolated wallet.
89
+ Creates a client with its own isolated Solana wallet.
81
90
 
82
91
  | Option | Type | Default | Description |
83
92
  |---|---|---|---|
84
- | `privateKey` | `` `0x${string}` `` | auto-generated | Reuse a pre-funded wallet |
85
- | `onStep` | `(step: PaymentStep) => void` | | Lifecycle event callback |
93
+ | `network` | `"devnet" \| "testnet" \| "mainnet"` | `"devnet"` | Solana network to use |
94
+ | `secretKey` | `Uint8Array \| number[]` | auto-generated | Reuse a pre-funded keypair (required on mainnet) |
95
+ | `onStep` | `(step: PaymentStep) => void` | - | Lifecycle event callback |
86
96
  | `timeout` | `number` | `30000` | Full flow timeout in ms |
87
- | `maxRetries` | `number` | `1` | Max retry attempts |
88
97
 
89
- Returns `Promise<TestClient>` with `{ address, method, fetch }`.
98
+ Returns `Promise<TestClient>`:
99
+
100
+ ```ts
101
+ interface TestClient {
102
+ address: string; // Solana public key (base58)
103
+ network: SolanaNetwork;
104
+ fetch: (url: string, init?: RequestInit) => Promise<Response>;
105
+ }
106
+ ```
90
107
 
91
- **Throws:** `MppFaucetError` if the testnet faucet is unreachable.
108
+ **Throws:**
109
+ - `MppFaucetError` - devnet/testnet airdrop failed
110
+ - `MppNetworkError` - mainnet used without a `secretKey`
92
111
 
93
- ### `createTestServer(config)`
112
+ ### `createTestServer(config?)`
94
113
 
95
114
  Creates Express middleware that enforces payment on any route.
96
115
 
97
116
  | Option | Type | Default | Description |
98
117
  |---|---|---|---|
99
- | `secretKey` | `string` | **required** | MPP secret key for payment verification |
100
- | `privateKey` | `` `0x${string}` `` | auto-generated | Server wallet private key |
101
- | `currency` | `` `0x${string}` `` | PathUSD | ERC-20 token address to accept |
118
+ | `network` | `"devnet" \| "testnet" \| "mainnet"` | `"devnet"` | Solana network to verify payments on |
119
+ | `secretKey` | `Uint8Array \| number[]` | auto-generated | Server wallet keypair |
102
120
 
103
- Returns `MppServer` with `.charge({ amount })` middleware.
121
+ **Properties:**
104
122
 
105
- **Throws:** `Error` synchronously if `secretKey` is missing.
123
+ | Property | Description |
124
+ |---|---|
125
+ | `mpp.recipientAddress` | Server wallet public key - payments land here |
126
+ | `mpp.network` | Active network |
127
+
128
+ ### `mpp.charge(opts)`
129
+
130
+ Express `RequestHandler`. Place before your route handler.
131
+
132
+ | Option | Type | Description |
133
+ |---|---|---|
134
+ | `amount` | `string` | Amount in SOL, e.g. `"0.001"` |
106
135
 
107
136
  ### `PaymentStep` events
108
137
 
109
138
  | `step.type` | When |
110
139
  |---|---|
111
- | `"wallet-created"` | New ephemeral wallet generated |
112
- | `"funded"` | Faucet funding confirmed |
113
- | `"request"` | Outgoing HTTP request |
114
- | `"payment"` | On-chain payment submitted |
140
+ | `"wallet-created"` | Solana keypair generated |
141
+ | `"funded"` | Faucet airdrop confirmed (devnet/testnet) |
142
+ | `"request"` | Outgoing HTTP request fired |
143
+ | `"payment"` | SOL transaction submitted and confirmed |
144
+ | `"retry"` | Request retried with Payment-Receipt header |
115
145
  | `"success"` | Final 200 response received |
116
146
  | `"error"` | Flow failed |
117
147
 
@@ -120,46 +150,79 @@ Returns `MppServer` with `.charge({ amount })` middleware.
120
150
  ## Error Handling
121
151
 
122
152
  ```ts
123
- import { MppFaucetError, MppPaymentError, MppTimeoutError } from "mpp-test-sdk";
153
+ import {
154
+ MppError,
155
+ MppFaucetError,
156
+ MppPaymentError,
157
+ MppTimeoutError,
158
+ MppNetworkError,
159
+ } from "mpp-test-sdk";
124
160
 
125
161
  try {
126
- const res = await client.fetch("https://api.example.com/data");
162
+ const res = await mppFetch("https://api.example.com/data");
127
163
  } catch (err) {
128
- if (err instanceof MppFaucetError) {
129
- // Testnet faucet unreachable — err.address
164
+ if (err instanceof MppNetworkError) {
165
+ // Mainnet used without secretKey
166
+ } else if (err instanceof MppFaucetError) {
167
+ // Devnet/testnet airdrop failed - err.address
130
168
  } else if (err instanceof MppPaymentError) {
131
- // Payment rejected err.status, err.url
169
+ // Server rejected payment - err.status, err.url
132
170
  } else if (err instanceof MppTimeoutError) {
133
- // Flow timed out err.url, err.timeoutMs
171
+ // Flow timed out - err.url, err.timeoutMs
134
172
  }
135
173
  }
136
174
  ```
137
175
 
138
- **Tip:** Pass a pre-funded `privateKey` to `createTestClient` during development to skip faucet calls.
176
+ **Tip:** Pass a pre-funded `secretKey` to skip faucet calls entirely - useful in CI or when devnet is rate-limiting.
139
177
 
140
178
  ---
141
179
 
142
- ## Network
180
+ ## Protocol
143
181
 
144
- All payments use **PathUSD** test tokens on **Tempo Moderato Testnet** (chain ID 42431). Real on-chain transactions, zero financial risk.
182
+ The SDK implements **MPP (Machine Payments Protocol)** on Solana. Plain HTTP headers, no custom transport:
145
183
 
146
- | | Value |
147
- |---|---|
148
- | Chain ID | `42431` |
149
- | RPC | `https://rpc.testnet.tempo.xyz` |
150
- | Explorer | `https://explorer.testnet.tempo.xyz` |
151
- | PathUSD | `0x20c0000000000000000000000000000000000000` |
152
- | Faucet | Automatic via SDK |
184
+ ```
185
+ # Server → Client (402 Payment Required)
186
+ Payment-Request: solana; amount="0.001"; recipient="9WzDX..."; network="devnet"
187
+
188
+ # Client Server (retry with proof)
189
+ Payment-Receipt: solana; signature="3xKm7..."; network="devnet"
190
+ ```
191
+
192
+ On-chain verification confirms: transaction exists, recipient matches, SOL delta ≥ required amount.
193
+
194
+ ---
195
+
196
+ ## Networks
197
+
198
+ | | devnet | testnet | mainnet |
199
+ |---|---|---|---|
200
+ | Faucet | Auto (2 SOL) | Auto (2 SOL) | Bring your own |
201
+ | Cost | Free | Free | Real SOL |
202
+ | Use for | Local dev, CI | Pre-production | Production |
203
+
204
+ ```ts
205
+ // Devnet (default)
206
+ const client = await createTestClient({ network: "devnet" });
207
+
208
+ // Mainnet - bring your own funded keypair
209
+ const client = await createTestClient({
210
+ network: "mainnet",
211
+ secretKey: Uint8Array.from(JSON.parse(process.env.SOLANA_SECRET_KEY!)),
212
+ });
213
+ ```
153
214
 
154
215
  ---
155
216
 
156
217
  ## Troubleshooting
157
218
 
158
- **`MppFaucetError`** Faucet has rate limits. Wait a few seconds and retry, or pass a pre-funded `privateKey` to skip faucet calls.
219
+ **`MppFaucetError`** - Devnet airdrop is rate-limited. Wait 30–60 seconds and retry, or pass a pre-funded `secretKey` to skip the faucet.
220
+
221
+ **`MppNetworkError`** - You passed `network: "mainnet"` without a `secretKey`. Mainnet has no faucet - provide a funded keypair.
159
222
 
160
- **`MppTimeoutError`** Increase `timeout` in `createTestClient`. On-chain confirmations typically take 25 seconds.
223
+ **`MppTimeoutError`** - Increase `timeout` in `createTestClient`. Solana devnet confirmations typically take 13 seconds.
161
224
 
162
- **402 not handled** Ensure `createTestServer` is called with a valid `secretKey` and the middleware is placed before the route handler.
225
+ **402 not handled** - Ensure `mpp.charge()` middleware is placed before the route handler on the server.
163
226
 
164
227
  ---
165
228
 
package/dist/index.d.mts CHANGED
@@ -1,62 +1,86 @@
1
1
  import { RequestHandler } from 'express';
2
2
 
3
+ /** Solana network to connect to. */
4
+ type SolanaNetwork = "devnet" | "testnet" | "mainnet";
3
5
  interface PaymentStep {
4
- type: "wallet-created" | "funded" | "request" | "payment" | "success" | "error";
6
+ type: "wallet-created" | "funded" | "request" | "payment" | "retry" | "success" | "error";
5
7
  message: string;
6
8
  data?: Record<string, unknown>;
7
9
  }
8
10
  interface TestClientConfig {
9
- /** Use a specific wallet. If omitted, a new wallet is auto-generated. */
10
- privateKey?: `0x${string}`;
11
+ /**
12
+ * Solana network to connect to.
13
+ * - `"devnet"` (default) - Free SOL airdrop, fast confirmation.
14
+ * - `"testnet"` - Solana's testnet. Also has free airdrop.
15
+ * - `"mainnet"` - Real SOL. Requires a pre-funded `secretKey`.
16
+ */
17
+ network?: SolanaNetwork;
18
+ /**
19
+ * Pre-funded Solana keypair secret key (32 or 64 bytes).
20
+ * - On `devnet`/`testnet`: optional - wallet is auto-funded via airdrop.
21
+ * - On `mainnet`: **required** - no airdrop available.
22
+ */
23
+ secretKey?: Uint8Array;
11
24
  /** Lifecycle event callback for observing the payment flow. */
12
25
  onStep?: (step: PaymentStep) => void;
13
- /** Request timeout in milliseconds. Default: 30000 (30s). */
26
+ /** Full flow timeout in ms (wallet + payment + retry). Default: 30000. */
14
27
  timeout?: number;
15
- /** Maximum retry attempts for transient failures. Default: 1 (single 402 retry). */
16
- maxRetries?: number;
28
+ /** Override the Solana RPC endpoint. Takes precedence over `network`. */
29
+ rpcUrl?: string;
17
30
  }
18
31
  interface TestClient {
19
- /** The wallet address. */
32
+ /** Solana wallet address (base58 public key). */
20
33
  address: string;
21
- /** The payment method being used. */
22
- method: "tempo";
23
- /** Fetch a URL with automatic 402 payment handling. */
34
+ /** Network this client is connected to. */
35
+ network: SolanaNetwork;
36
+ /** Payment method. */
37
+ method: "solana";
38
+ /** Fetch a URL with automatic 402 MPP payment handling. */
24
39
  fetch: (url: string, init?: RequestInit) => Promise<Response>;
25
40
  }
26
41
  /**
27
- * Create an MPP test client.
42
+ * Create a Solana MPP test client.
28
43
  *
29
- * Auto-generates a wallet, funds it from the Tempo testnet faucet,
30
- * and handles 402 payments automatically.
44
+ * Automatically creates a Solana wallet, funds it (via airdrop on devnet/testnet),
45
+ * and handles HTTP 402 MPP payments with automatic retry.
31
46
  *
32
47
  * @example
33
48
  * ```ts
34
- * import { createTestClient } from "mpp-test-sdk";
35
- *
49
+ * // devnet (default) - zero config
36
50
  * const client = await createTestClient();
37
- * const res = await client.fetch("http://localhost:3001/api/ping/paid");
38
- * const data = await res.json();
51
+ *
52
+ * // testnet
53
+ * const client = await createTestClient({ network: "testnet" });
54
+ *
55
+ * // mainnet - must provide pre-funded wallet
56
+ * const client = await createTestClient({
57
+ * network: "mainnet",
58
+ * secretKey: loadKeypairFromFile("./wallet.json").secretKey,
59
+ * });
60
+ *
61
+ * const res = await client.fetch("http://localhost:3001/api/data");
39
62
  * ```
40
63
  *
41
- * @throws {MppFaucetError} When the testnet faucet is unreachable or returns an error.
64
+ * @throws {MppNetworkError} When `mainnet` is specified without a `secretKey`.
65
+ * @throws {MppFaucetError} When the devnet/testnet airdrop fails after retries.
42
66
  */
43
67
  declare function createTestClient(config?: TestClientConfig): Promise<TestClient>;
44
68
  /**
45
- * One-shot fetch with automatic wallet creation, funding, and 402 payment.
69
+ * Drop-in replacement for `fetch` with automatic Solana MPP payment.
46
70
  *
47
- * Uses a shared client instance across calls (lazy-initialized on first call).
48
- * Call `mppFetch.reset()` to discard the shared instance.
71
+ * Uses a shared client instance lazily created on first call (devnet by default).
72
+ * Call `mppFetch.reset()` to discard the shared instance and generate a new wallet.
49
73
  *
50
74
  * @example
51
75
  * ```ts
52
76
  * import { mppFetch } from "mpp-test-sdk";
53
77
  *
54
- * const res = await mppFetch("http://localhost:3001/api/ping/paid");
78
+ * const res = await mppFetch("http://localhost:3001/api/data");
55
79
  * const data = await res.json();
56
80
  * ```
57
81
  *
58
- * @throws {MppFaucetError} When the testnet faucet is unreachable.
59
- * @throws {MppTimeoutError} When the request times out.
82
+ * @throws {MppFaucetError} When the devnet airdrop fails after retries.
83
+ * @throws {MppTimeoutError} When the full flow exceeds the timeout.
60
84
  */
61
85
  declare function mppFetch(url: string, init?: RequestInit): Promise<Response>;
62
86
  declare namespace mppFetch {
@@ -64,23 +88,46 @@ declare namespace mppFetch {
64
88
  }
65
89
 
66
90
  interface ChargeOptions {
67
- /** Amount to charge in PathUSD (e.g. "0.01"). */
91
+ /** Amount to charge in SOL (e.g. "0.001"). */
68
92
  amount: string;
69
93
  }
70
94
  interface MppServer {
71
- /** Express middleware that charges the specified amount before passing through. */
95
+ /**
96
+ * Express middleware that requires SOL payment before passing to the route handler.
97
+ *
98
+ * - No receipt → 402 with `Payment-Request` header.
99
+ * - Valid receipt + on-chain confirmation → calls `next()`.
100
+ * - Invalid or insufficient payment → 403.
101
+ */
72
102
  charge: (opts: ChargeOptions) => RequestHandler;
103
+ /** The Solana address where payments are sent. */
104
+ recipientAddress: string;
105
+ /** Network this server is configured for. */
106
+ network: SolanaNetwork;
73
107
  }
74
108
  interface TestServerConfig {
75
- /** Your MPP secret key. Required. */
76
- secretKey: string;
77
- /** Optional private key for the server wallet. Auto-generated if omitted. */
78
- privateKey?: `0x${string}`;
79
- /** Optional currency token address. Defaults to PathUSD on Tempo testnet. */
80
- currency?: `0x${string}`;
109
+ /**
110
+ * Solana network to connect to.
111
+ * - `"devnet"` (default) - Solana devnet.
112
+ * - `"testnet"` - Solana testnet.
113
+ * - `"mainnet"` - Solana mainnet (real SOL).
114
+ */
115
+ network?: SolanaNetwork;
116
+ /** Server wallet keypair secret key. Auto-generated if omitted. */
117
+ secretKey?: Uint8Array;
118
+ /**
119
+ * Override the recipient Solana address (base58).
120
+ * Defaults to the server keypair's public key.
121
+ */
122
+ recipientAddress?: string;
123
+ /** Override the Solana RPC endpoint. Takes precedence over `network`. */
124
+ rpcUrl?: string;
81
125
  }
82
126
  /**
83
- * Create an MPP-enabled Express middleware.
127
+ * Create a Solana MPP-enabled Express server.
128
+ *
129
+ * Handles the HTTP 402 payment flow and verifies SOL transfers on-chain.
130
+ * No config needed - auto-generates a server wallet.
84
131
  *
85
132
  * @example
86
133
  * ```ts
@@ -88,11 +135,15 @@ interface TestServerConfig {
88
135
  * import { createTestServer } from "mpp-test-sdk";
89
136
  *
90
137
  * const app = express();
91
- * const mpp = createTestServer({ secretKey: "sk_test_..." });
92
- * app.get("/api/paid", mpp.charge({ amount: "0.01" }), (req, res) => res.json({ ok: true }));
138
+ * const mpp = createTestServer(); // or createTestServer({ network: "mainnet" })
139
+ *
140
+ * // Charge 0.001 SOL per request
141
+ * app.get("/api/data", mpp.charge({ amount: "0.001" }), (req, res) => {
142
+ * res.json({ data: "premium content" });
143
+ * });
93
144
  * ```
94
145
  */
95
- declare function createTestServer(config: TestServerConfig): MppServer;
146
+ declare function createTestServer(config?: TestServerConfig): MppServer;
96
147
 
97
148
  declare class MppError extends Error {
98
149
  constructor(message: string);
@@ -111,5 +162,9 @@ declare class MppTimeoutError extends MppError {
111
162
  readonly timeoutMs: number;
112
163
  constructor(url: string, timeoutMs: number);
113
164
  }
165
+ declare class MppNetworkError extends MppError {
166
+ readonly network: string;
167
+ constructor(network: string, message?: string);
168
+ }
114
169
 
115
- export { type ChargeOptions, MppError, MppFaucetError, MppPaymentError, type MppServer, MppTimeoutError, type PaymentStep, type TestClient, type TestClientConfig, type TestServerConfig, createTestClient, createTestServer, mppFetch };
170
+ export { type ChargeOptions, MppError, MppFaucetError, MppNetworkError, MppPaymentError, type MppServer, MppTimeoutError, type PaymentStep, type SolanaNetwork, type TestClient, type TestClientConfig, type TestServerConfig, createTestClient, createTestServer, mppFetch };
package/dist/index.d.ts CHANGED
@@ -1,62 +1,86 @@
1
1
  import { RequestHandler } from 'express';
2
2
 
3
+ /** Solana network to connect to. */
4
+ type SolanaNetwork = "devnet" | "testnet" | "mainnet";
3
5
  interface PaymentStep {
4
- type: "wallet-created" | "funded" | "request" | "payment" | "success" | "error";
6
+ type: "wallet-created" | "funded" | "request" | "payment" | "retry" | "success" | "error";
5
7
  message: string;
6
8
  data?: Record<string, unknown>;
7
9
  }
8
10
  interface TestClientConfig {
9
- /** Use a specific wallet. If omitted, a new wallet is auto-generated. */
10
- privateKey?: `0x${string}`;
11
+ /**
12
+ * Solana network to connect to.
13
+ * - `"devnet"` (default) - Free SOL airdrop, fast confirmation.
14
+ * - `"testnet"` - Solana's testnet. Also has free airdrop.
15
+ * - `"mainnet"` - Real SOL. Requires a pre-funded `secretKey`.
16
+ */
17
+ network?: SolanaNetwork;
18
+ /**
19
+ * Pre-funded Solana keypair secret key (32 or 64 bytes).
20
+ * - On `devnet`/`testnet`: optional - wallet is auto-funded via airdrop.
21
+ * - On `mainnet`: **required** - no airdrop available.
22
+ */
23
+ secretKey?: Uint8Array;
11
24
  /** Lifecycle event callback for observing the payment flow. */
12
25
  onStep?: (step: PaymentStep) => void;
13
- /** Request timeout in milliseconds. Default: 30000 (30s). */
26
+ /** Full flow timeout in ms (wallet + payment + retry). Default: 30000. */
14
27
  timeout?: number;
15
- /** Maximum retry attempts for transient failures. Default: 1 (single 402 retry). */
16
- maxRetries?: number;
28
+ /** Override the Solana RPC endpoint. Takes precedence over `network`. */
29
+ rpcUrl?: string;
17
30
  }
18
31
  interface TestClient {
19
- /** The wallet address. */
32
+ /** Solana wallet address (base58 public key). */
20
33
  address: string;
21
- /** The payment method being used. */
22
- method: "tempo";
23
- /** Fetch a URL with automatic 402 payment handling. */
34
+ /** Network this client is connected to. */
35
+ network: SolanaNetwork;
36
+ /** Payment method. */
37
+ method: "solana";
38
+ /** Fetch a URL with automatic 402 MPP payment handling. */
24
39
  fetch: (url: string, init?: RequestInit) => Promise<Response>;
25
40
  }
26
41
  /**
27
- * Create an MPP test client.
42
+ * Create a Solana MPP test client.
28
43
  *
29
- * Auto-generates a wallet, funds it from the Tempo testnet faucet,
30
- * and handles 402 payments automatically.
44
+ * Automatically creates a Solana wallet, funds it (via airdrop on devnet/testnet),
45
+ * and handles HTTP 402 MPP payments with automatic retry.
31
46
  *
32
47
  * @example
33
48
  * ```ts
34
- * import { createTestClient } from "mpp-test-sdk";
35
- *
49
+ * // devnet (default) - zero config
36
50
  * const client = await createTestClient();
37
- * const res = await client.fetch("http://localhost:3001/api/ping/paid");
38
- * const data = await res.json();
51
+ *
52
+ * // testnet
53
+ * const client = await createTestClient({ network: "testnet" });
54
+ *
55
+ * // mainnet - must provide pre-funded wallet
56
+ * const client = await createTestClient({
57
+ * network: "mainnet",
58
+ * secretKey: loadKeypairFromFile("./wallet.json").secretKey,
59
+ * });
60
+ *
61
+ * const res = await client.fetch("http://localhost:3001/api/data");
39
62
  * ```
40
63
  *
41
- * @throws {MppFaucetError} When the testnet faucet is unreachable or returns an error.
64
+ * @throws {MppNetworkError} When `mainnet` is specified without a `secretKey`.
65
+ * @throws {MppFaucetError} When the devnet/testnet airdrop fails after retries.
42
66
  */
43
67
  declare function createTestClient(config?: TestClientConfig): Promise<TestClient>;
44
68
  /**
45
- * One-shot fetch with automatic wallet creation, funding, and 402 payment.
69
+ * Drop-in replacement for `fetch` with automatic Solana MPP payment.
46
70
  *
47
- * Uses a shared client instance across calls (lazy-initialized on first call).
48
- * Call `mppFetch.reset()` to discard the shared instance.
71
+ * Uses a shared client instance lazily created on first call (devnet by default).
72
+ * Call `mppFetch.reset()` to discard the shared instance and generate a new wallet.
49
73
  *
50
74
  * @example
51
75
  * ```ts
52
76
  * import { mppFetch } from "mpp-test-sdk";
53
77
  *
54
- * const res = await mppFetch("http://localhost:3001/api/ping/paid");
78
+ * const res = await mppFetch("http://localhost:3001/api/data");
55
79
  * const data = await res.json();
56
80
  * ```
57
81
  *
58
- * @throws {MppFaucetError} When the testnet faucet is unreachable.
59
- * @throws {MppTimeoutError} When the request times out.
82
+ * @throws {MppFaucetError} When the devnet airdrop fails after retries.
83
+ * @throws {MppTimeoutError} When the full flow exceeds the timeout.
60
84
  */
61
85
  declare function mppFetch(url: string, init?: RequestInit): Promise<Response>;
62
86
  declare namespace mppFetch {
@@ -64,23 +88,46 @@ declare namespace mppFetch {
64
88
  }
65
89
 
66
90
  interface ChargeOptions {
67
- /** Amount to charge in PathUSD (e.g. "0.01"). */
91
+ /** Amount to charge in SOL (e.g. "0.001"). */
68
92
  amount: string;
69
93
  }
70
94
  interface MppServer {
71
- /** Express middleware that charges the specified amount before passing through. */
95
+ /**
96
+ * Express middleware that requires SOL payment before passing to the route handler.
97
+ *
98
+ * - No receipt → 402 with `Payment-Request` header.
99
+ * - Valid receipt + on-chain confirmation → calls `next()`.
100
+ * - Invalid or insufficient payment → 403.
101
+ */
72
102
  charge: (opts: ChargeOptions) => RequestHandler;
103
+ /** The Solana address where payments are sent. */
104
+ recipientAddress: string;
105
+ /** Network this server is configured for. */
106
+ network: SolanaNetwork;
73
107
  }
74
108
  interface TestServerConfig {
75
- /** Your MPP secret key. Required. */
76
- secretKey: string;
77
- /** Optional private key for the server wallet. Auto-generated if omitted. */
78
- privateKey?: `0x${string}`;
79
- /** Optional currency token address. Defaults to PathUSD on Tempo testnet. */
80
- currency?: `0x${string}`;
109
+ /**
110
+ * Solana network to connect to.
111
+ * - `"devnet"` (default) - Solana devnet.
112
+ * - `"testnet"` - Solana testnet.
113
+ * - `"mainnet"` - Solana mainnet (real SOL).
114
+ */
115
+ network?: SolanaNetwork;
116
+ /** Server wallet keypair secret key. Auto-generated if omitted. */
117
+ secretKey?: Uint8Array;
118
+ /**
119
+ * Override the recipient Solana address (base58).
120
+ * Defaults to the server keypair's public key.
121
+ */
122
+ recipientAddress?: string;
123
+ /** Override the Solana RPC endpoint. Takes precedence over `network`. */
124
+ rpcUrl?: string;
81
125
  }
82
126
  /**
83
- * Create an MPP-enabled Express middleware.
127
+ * Create a Solana MPP-enabled Express server.
128
+ *
129
+ * Handles the HTTP 402 payment flow and verifies SOL transfers on-chain.
130
+ * No config needed - auto-generates a server wallet.
84
131
  *
85
132
  * @example
86
133
  * ```ts
@@ -88,11 +135,15 @@ interface TestServerConfig {
88
135
  * import { createTestServer } from "mpp-test-sdk";
89
136
  *
90
137
  * const app = express();
91
- * const mpp = createTestServer({ secretKey: "sk_test_..." });
92
- * app.get("/api/paid", mpp.charge({ amount: "0.01" }), (req, res) => res.json({ ok: true }));
138
+ * const mpp = createTestServer(); // or createTestServer({ network: "mainnet" })
139
+ *
140
+ * // Charge 0.001 SOL per request
141
+ * app.get("/api/data", mpp.charge({ amount: "0.001" }), (req, res) => {
142
+ * res.json({ data: "premium content" });
143
+ * });
93
144
  * ```
94
145
  */
95
- declare function createTestServer(config: TestServerConfig): MppServer;
146
+ declare function createTestServer(config?: TestServerConfig): MppServer;
96
147
 
97
148
  declare class MppError extends Error {
98
149
  constructor(message: string);
@@ -111,5 +162,9 @@ declare class MppTimeoutError extends MppError {
111
162
  readonly timeoutMs: number;
112
163
  constructor(url: string, timeoutMs: number);
113
164
  }
165
+ declare class MppNetworkError extends MppError {
166
+ readonly network: string;
167
+ constructor(network: string, message?: string);
168
+ }
114
169
 
115
- export { type ChargeOptions, MppError, MppFaucetError, MppPaymentError, type MppServer, MppTimeoutError, type PaymentStep, type TestClient, type TestClientConfig, type TestServerConfig, createTestClient, createTestServer, mppFetch };
170
+ export { type ChargeOptions, MppError, MppFaucetError, MppNetworkError, MppPaymentError, type MppServer, MppTimeoutError, type PaymentStep, type SolanaNetwork, type TestClient, type TestClientConfig, type TestServerConfig, createTestClient, createTestServer, mppFetch };