mpp-test-sdk 1.0.0 → 1.1.1
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 +46 -32
- package/dist/index.d.mts +92 -37
- package/dist/index.d.ts +92 -37
- package/dist/index.js +273 -85
- package/dist/index.mjs +285 -85
- package/package.json +15 -7
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
[](https://nodejs.org)
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
|
|
7
|
-
Test pay-per-request APIs on
|
|
7
|
+
Test pay-per-request APIs on **Solana devnet**. Auto-creates wallets, airdrops SOL, handles HTTP 402 MPP payments — zero setup required.
|
|
8
8
|
|
|
9
9
|
**[mpptestkit.com](https://mpptestkit.com)** · [GitHub](https://github.com/mpptestkit/mpp-test-sdk) · [X](https://x.com/mpptestkit)
|
|
10
10
|
|
|
@@ -25,6 +25,7 @@ Requires Node.js 22+.
|
|
|
25
25
|
```ts
|
|
26
26
|
import { mppFetch } from "mpp-test-sdk";
|
|
27
27
|
|
|
28
|
+
// Auto-creates Solana wallet, airdrops SOL, handles 402
|
|
28
29
|
const res = await mppFetch("https://your-api.com/api/data");
|
|
29
30
|
const data = await res.json();
|
|
30
31
|
```
|
|
@@ -41,7 +42,7 @@ const client = await createTestClient({
|
|
|
41
42
|
const res = await client.fetch("https://your-api.com/api/data");
|
|
42
43
|
```
|
|
43
44
|
|
|
44
|
-
The SDK handles the full flow:
|
|
45
|
+
The SDK handles the full flow: Solana keypair generation, devnet SOL airdrop, 402 detection, on-chain SOL transfer, and automatic retry with payment proof.
|
|
45
46
|
|
|
46
47
|
---
|
|
47
48
|
|
|
@@ -52,13 +53,15 @@ import express from "express";
|
|
|
52
53
|
import { createTestServer } from "mpp-test-sdk";
|
|
53
54
|
|
|
54
55
|
const app = express();
|
|
55
|
-
|
|
56
|
+
|
|
57
|
+
// No config needed — auto-generates a server wallet
|
|
58
|
+
const mpp = createTestServer();
|
|
56
59
|
|
|
57
60
|
// Free — no middleware
|
|
58
61
|
app.get("/api/ping", (req, res) => res.json({ ok: true }));
|
|
59
62
|
|
|
60
|
-
// Paid — one line
|
|
61
|
-
app.get("/api/data", mpp.charge({ amount: "0.
|
|
63
|
+
// Paid — one line (0.001 SOL)
|
|
64
|
+
app.get("/api/data", mpp.charge({ amount: "0.001" }), (req, res) => {
|
|
62
65
|
res.json({ data: "premium content" });
|
|
63
66
|
});
|
|
64
67
|
|
|
@@ -77,41 +80,40 @@ Call `mppFetch.reset()` to discard the shared client and generate a new wallet o
|
|
|
77
80
|
|
|
78
81
|
### `createTestClient(config?)`
|
|
79
82
|
|
|
80
|
-
Creates a client with its own isolated wallet.
|
|
83
|
+
Creates a client with its own isolated Solana wallet.
|
|
81
84
|
|
|
82
85
|
| Option | Type | Default | Description |
|
|
83
86
|
|---|---|---|---|
|
|
84
|
-
| `
|
|
87
|
+
| `secretKey` | `Uint8Array` | auto-generated | Reuse a pre-funded Solana keypair |
|
|
85
88
|
| `onStep` | `(step: PaymentStep) => void` | — | Lifecycle event callback |
|
|
86
89
|
| `timeout` | `number` | `30000` | Full flow timeout in ms |
|
|
87
|
-
| `
|
|
90
|
+
| `rpcUrl` | `string` | devnet RPC | Solana RPC endpoint |
|
|
88
91
|
|
|
89
92
|
Returns `Promise<TestClient>` with `{ address, method, fetch }`.
|
|
90
93
|
|
|
91
|
-
**Throws:** `MppFaucetError` if the
|
|
94
|
+
**Throws:** `MppFaucetError` if the Solana devnet airdrop fails.
|
|
92
95
|
|
|
93
|
-
### `createTestServer(config)`
|
|
96
|
+
### `createTestServer(config?)`
|
|
94
97
|
|
|
95
|
-
Creates Express middleware that enforces payment on any route.
|
|
98
|
+
Creates Express middleware that enforces MPP payment on any route.
|
|
96
99
|
|
|
97
100
|
| Option | Type | Default | Description |
|
|
98
101
|
|---|---|---|---|
|
|
99
|
-
| `secretKey` | `
|
|
100
|
-
| `
|
|
101
|
-
| `
|
|
102
|
+
| `secretKey` | `Uint8Array` | auto-generated | Server wallet keypair |
|
|
103
|
+
| `recipientAddress` | `string` | derived from keypair | Override recipient base58 address |
|
|
104
|
+
| `rpcUrl` | `string` | devnet RPC | Solana RPC for verification |
|
|
105
|
+
| `network` | `"devnet" \| "mainnet-beta"` | `"devnet"` | Network label in headers |
|
|
102
106
|
|
|
103
107
|
Returns `MppServer` with `.charge({ amount })` middleware.
|
|
104
108
|
|
|
105
|
-
**Throws:** `Error` synchronously if `secretKey` is missing.
|
|
106
|
-
|
|
107
109
|
### `PaymentStep` events
|
|
108
110
|
|
|
109
111
|
| `step.type` | When |
|
|
110
112
|
|---|---|
|
|
111
|
-
| `"wallet-created"` |
|
|
112
|
-
| `"funded"` |
|
|
113
|
+
| `"wallet-created"` | Solana keypair generated |
|
|
114
|
+
| `"funded"` | Devnet airdrop confirmed |
|
|
113
115
|
| `"request"` | Outgoing HTTP request |
|
|
114
|
-
| `"payment"` |
|
|
116
|
+
| `"payment"` | SOL transfer submitted or confirmed |
|
|
115
117
|
| `"success"` | Final 200 response received |
|
|
116
118
|
| `"error"` | Flow failed |
|
|
117
119
|
|
|
@@ -126,40 +128,52 @@ try {
|
|
|
126
128
|
const res = await client.fetch("https://api.example.com/data");
|
|
127
129
|
} catch (err) {
|
|
128
130
|
if (err instanceof MppFaucetError) {
|
|
129
|
-
//
|
|
131
|
+
// Devnet airdrop failed (rate limited) — err.address
|
|
130
132
|
} else if (err instanceof MppPaymentError) {
|
|
131
|
-
//
|
|
133
|
+
// Server rejected payment — err.status, err.url
|
|
132
134
|
} else if (err instanceof MppTimeoutError) {
|
|
133
135
|
// Flow timed out — err.url, err.timeoutMs
|
|
134
136
|
}
|
|
135
137
|
}
|
|
136
138
|
```
|
|
137
139
|
|
|
138
|
-
**Tip:** Pass a pre-funded `
|
|
140
|
+
**Tip:** Pass a pre-funded `secretKey` to `createTestClient` to skip the airdrop step.
|
|
139
141
|
|
|
140
142
|
---
|
|
141
143
|
|
|
142
|
-
##
|
|
144
|
+
## Protocol
|
|
145
|
+
|
|
146
|
+
The SDK implements **MPP (Machine Payments Protocol)** on Solana. The wire format is plain HTTP headers:
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
# Server → Client (402)
|
|
150
|
+
Payment-Request: solana; amount="0.001"; recipient="<base58>"; network="devnet"
|
|
143
151
|
|
|
144
|
-
|
|
152
|
+
# Client → Server (retry)
|
|
153
|
+
Payment-Receipt: solana; signature="<base58_sig>"; network="devnet"; amount="0.001"
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Network
|
|
145
159
|
|
|
146
160
|
| | Value |
|
|
147
161
|
|---|---|
|
|
148
|
-
|
|
|
149
|
-
|
|
|
150
|
-
|
|
|
151
|
-
|
|
|
152
|
-
| Faucet | Automatic via
|
|
162
|
+
| Network | Solana Devnet |
|
|
163
|
+
| Currency | SOL (test) |
|
|
164
|
+
| RPC | `https://api.devnet.solana.com` |
|
|
165
|
+
| Explorer | `https://explorer.solana.com/?cluster=devnet` |
|
|
166
|
+
| Faucet | Automatic via `connection.requestAirdrop()` |
|
|
153
167
|
|
|
154
168
|
---
|
|
155
169
|
|
|
156
170
|
## Troubleshooting
|
|
157
171
|
|
|
158
|
-
**`MppFaucetError`** —
|
|
172
|
+
**`MppFaucetError`** — Devnet airdrop is rate limited. Wait 30–60 seconds and retry, or pass a pre-funded `secretKey` to skip the faucet.
|
|
159
173
|
|
|
160
|
-
**`MppTimeoutError`** — Increase `timeout` in `createTestClient`.
|
|
174
|
+
**`MppTimeoutError`** — Increase `timeout` in `createTestClient`. Solana devnet confirmations typically take 1–5 seconds.
|
|
161
175
|
|
|
162
|
-
**402 not handled** — Ensure
|
|
176
|
+
**402 not handled** — Ensure the server has `createTestServer()` middleware in place before the route handler.
|
|
163
177
|
|
|
164
178
|
---
|
|
165
179
|
|
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
|
-
/**
|
|
10
|
-
|
|
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
|
-
/**
|
|
26
|
+
/** Full flow timeout in ms (wallet + payment + retry). Default: 30000. */
|
|
14
27
|
timeout?: number;
|
|
15
|
-
/**
|
|
16
|
-
|
|
28
|
+
/** Override the Solana RPC endpoint. Takes precedence over `network`. */
|
|
29
|
+
rpcUrl?: string;
|
|
17
30
|
}
|
|
18
31
|
interface TestClient {
|
|
19
|
-
/**
|
|
32
|
+
/** Solana wallet address (base58 public key). */
|
|
20
33
|
address: string;
|
|
21
|
-
/**
|
|
22
|
-
|
|
23
|
-
/**
|
|
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
|
|
42
|
+
* Create a Solana MPP test client.
|
|
28
43
|
*
|
|
29
|
-
*
|
|
30
|
-
* and handles 402 payments
|
|
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
|
-
*
|
|
35
|
-
*
|
|
49
|
+
* // devnet (default) — zero config
|
|
36
50
|
* const client = await createTestClient();
|
|
37
|
-
*
|
|
38
|
-
*
|
|
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 {
|
|
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
|
-
*
|
|
69
|
+
* Drop-in replacement for `fetch` with automatic Solana MPP payment.
|
|
46
70
|
*
|
|
47
|
-
* Uses a shared client instance
|
|
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/
|
|
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
|
|
59
|
-
* @throws {MppTimeoutError} When the
|
|
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
|
|
91
|
+
/** Amount to charge in SOL (e.g. "0.001"). */
|
|
68
92
|
amount: string;
|
|
69
93
|
}
|
|
70
94
|
interface MppServer {
|
|
71
|
-
/**
|
|
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
|
-
/**
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
|
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({
|
|
92
|
-
*
|
|
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
|
|
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
|
-
/**
|
|
10
|
-
|
|
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
|
-
/**
|
|
26
|
+
/** Full flow timeout in ms (wallet + payment + retry). Default: 30000. */
|
|
14
27
|
timeout?: number;
|
|
15
|
-
/**
|
|
16
|
-
|
|
28
|
+
/** Override the Solana RPC endpoint. Takes precedence over `network`. */
|
|
29
|
+
rpcUrl?: string;
|
|
17
30
|
}
|
|
18
31
|
interface TestClient {
|
|
19
|
-
/**
|
|
32
|
+
/** Solana wallet address (base58 public key). */
|
|
20
33
|
address: string;
|
|
21
|
-
/**
|
|
22
|
-
|
|
23
|
-
/**
|
|
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
|
|
42
|
+
* Create a Solana MPP test client.
|
|
28
43
|
*
|
|
29
|
-
*
|
|
30
|
-
* and handles 402 payments
|
|
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
|
-
*
|
|
35
|
-
*
|
|
49
|
+
* // devnet (default) — zero config
|
|
36
50
|
* const client = await createTestClient();
|
|
37
|
-
*
|
|
38
|
-
*
|
|
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 {
|
|
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
|
-
*
|
|
69
|
+
* Drop-in replacement for `fetch` with automatic Solana MPP payment.
|
|
46
70
|
*
|
|
47
|
-
* Uses a shared client instance
|
|
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/
|
|
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
|
|
59
|
-
* @throws {MppTimeoutError} When the
|
|
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
|
|
91
|
+
/** Amount to charge in SOL (e.g. "0.001"). */
|
|
68
92
|
amount: string;
|
|
69
93
|
}
|
|
70
94
|
interface MppServer {
|
|
71
|
-
/**
|
|
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
|
-
/**
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
|
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({
|
|
92
|
-
*
|
|
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
|
|
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 };
|