@payai/x402-hono-starter 1.0.0 → 1.0.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/NOTICE CHANGED
@@ -1,2 +1,2 @@
1
1
  This package includes portions derived from coinbase/x402 (examples/typescript/servers/hono), Apache-2.0,
2
- commit f74bda39cc692f97e55fdb0bbfdff4a0f5e7f758. See LICENSE and upstream LICENSE notices.
2
+ commit 7a83a1d57196b27ff9efe30c994f91eb50f60af5. See LICENSE and upstream LICENSE notices.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@payai/x402-hono-starter",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -39,4 +39,4 @@
39
39
  "config": {
40
40
  "x402HonoVersion": "2.2.0"
41
41
  }
42
- }
42
+ }
@@ -1,7 +1,5 @@
1
+ EVM_ADDRESS=
2
+ SVM_ADDRESS=
1
3
  FACILITATOR_URL=https://facilitator.payai.network
2
- NETWORK=solana-devnet
3
- ADDRESS=
4
4
 
5
- # required if using the Base mainnet facilitator
6
- CDP_API_KEY_ID="Coinbase Developer Platform Key"
7
- CDP_API_KEY_SECRET="Coinbase Developer Platform Key Secret"
5
+ NETWORK=solana-devnet
@@ -1,36 +1,64 @@
1
- # x402-hono Example Server
1
+ # @x402/hono Example Server
2
2
 
3
- This is an example Hono server that demonstrates how to use the `x402-hono` middleware to implement paywall functionality in your API endpoints.
3
+ Hono server demonstrating how to protect API endpoints with a paywall using the `@x402/hono` middleware.
4
+
5
+ ```typescript
6
+ import { Hono } from "hono";
7
+ import { paymentMiddleware, x402ResourceServer } from "@x402/hono";
8
+ import { ExactEvmScheme } from "@x402/evm/exact/server";
9
+ import { HTTPFacilitatorClient } from "@x402/core/server";
10
+
11
+ const app = new Hono();
12
+
13
+ app.use(
14
+ paymentMiddleware(
15
+ {
16
+ "GET /weather": {
17
+ accepts: { scheme: "exact", price: "$0.001", network: "eip155:84532", payTo: evmAddress },
18
+ description: "Weather data",
19
+ mimeType: "application/json",
20
+ },
21
+ },
22
+ new x402ResourceServer(new HTTPFacilitatorClient({ url: facilitatorUrl }))
23
+ .register("eip155:84532", new ExactEvmScheme()),
24
+ ),
25
+ );
26
+
27
+ app.get("/weather", c => c.json({ weather: "sunny", temperature: 70 }));
28
+ ```
4
29
 
5
30
  ## Prerequisites
6
31
 
7
32
  - Node.js v20+ (install via [nvm](https://github.com/nvm-sh/nvm))
8
33
  - pnpm v10 (install via [pnpm.io/installation](https://pnpm.io/installation))
9
- - A valid Ethereum address for receiving payments
10
- - Coinbase Developer Platform API Key & Secret (if accepting payments on Base mainnet)
11
- -- get them here [https://portal.cdp.coinbase.com/projects](https://portal.cdp.coinbase.com/projects)
34
+ - Valid EVM and SVM addresses for receiving payments
35
+ - URL of a facilitator supporting the desired payment network, see [facilitator list](https://www.x402.org/ecosystem?category=facilitators)
12
36
 
13
37
  ## Setup
14
38
 
15
- 1. Copy `.env-local` to `.env` and add your Ethereum address to receive payments:
39
+ 1. Copy `.env-local` to `.env`:
16
40
 
17
41
  ```bash
18
42
  cp .env-local .env
19
43
  ```
20
44
 
45
+ and fill required environment variables:
46
+
47
+ - `FACILITATOR_URL` - Facilitator endpoint URL
48
+ - `EVM_ADDRESS` - Ethereum address to receive payments
49
+ - `SVM_ADDRESS` - Solana address to receive payments
50
+
21
51
  2. Install and build all packages from the typescript examples root:
22
52
 
23
53
  ```bash
24
54
  cd ../../
25
- pnpm install
26
- pnpm build
55
+ pnpm install && pnpm build
27
56
  cd servers/hono
28
57
  ```
29
58
 
30
59
  3. Run the server
31
60
 
32
61
  ```bash
33
- pnpm install
34
62
  pnpm dev
35
63
  ```
36
64
 
@@ -43,7 +71,6 @@ You can test the server using one of the example clients:
43
71
  ```bash
44
72
  cd ../clients/fetch
45
73
  # Ensure .env is setup
46
- pnpm install
47
74
  pnpm dev
48
75
  ```
49
76
 
@@ -52,7 +79,6 @@ pnpm dev
52
79
  ```bash
53
80
  cd ../clients/axios
54
81
  # Ensure .env is setup
55
- pnpm install
56
82
  pnpm dev
57
83
  ```
58
84
 
@@ -64,45 +90,94 @@ These clients will demonstrate how to:
64
90
 
65
91
  ## Example Endpoint
66
92
 
67
- The server includes a single example endpoint at `/weather` that requires a payment of $0.001 to access. The endpoint returns a simple weather report.
93
+ The server includes a single example endpoint at `/weather` that requires a payment of 0.001 USDC on Base Sepolia or Solana Devnet to access. The endpoint returns a simple weather report.
68
94
 
69
95
  ## Response Format
70
96
 
71
97
  ### Payment Required (402)
72
98
 
99
+ ```
100
+ HTTP/1.1 402 Payment Required
101
+ Content-Type: application/json; charset=utf-8
102
+ PAYMENT-REQUIRED: <base64-encoded JSON>
103
+
104
+ {}
105
+ ```
106
+
107
+ The `PAYMENT-REQUIRED` header contains base64-encoded JSON with the payment requirements.
108
+ Note: `amount` is in atomic units (e.g., 1000 = 0.001 USDC, since USDC has 6 decimals):
109
+
73
110
  ```json
74
111
  {
75
- "error": "X-PAYMENT header is required",
76
- "paymentRequirements": {
77
- "scheme": "exact",
78
- "network": "base",
79
- "maxAmountRequired": "1000",
80
- "resource": "http://localhost:4021/weather",
81
- "description": "",
82
- "mimeType": "",
83
- "payTo": "0xYourAddress",
84
- "maxTimeoutSeconds": 60,
85
- "asset": "0x...",
86
- "outputSchema": null,
87
- "extra": null
88
- }
112
+ "x402Version": 2,
113
+ "error": "Payment required",
114
+ "resource": {
115
+ "url": "http://localhost:4021/weather",
116
+ "description": "Weather data",
117
+ "mimeType": "application/json"
118
+ },
119
+ "accepts": [
120
+ {
121
+ "scheme": "exact",
122
+ "network": "eip155:84532",
123
+ "amount": "1000",
124
+ "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
125
+ "payTo": "0x1c47E9C085c2B7458F5b6C16cCBD65A65255a9f6",
126
+ "maxTimeoutSeconds": 300,
127
+ "extra": {
128
+ "name": "USDC",
129
+ "version": "2",
130
+ "resourceUrl": "http://localhost:4021/weather"
131
+ }
132
+ },
133
+ {
134
+ "scheme": "exact",
135
+ "network": "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
136
+ "amount": "1000",
137
+ "asset": "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
138
+ "payTo": "FV6JPj6Fy12HG8SYStyHdcecXYmV1oeWERAokrh4GQ1n",
139
+ "maxTimeoutSeconds": 300,
140
+ "extra": {
141
+ "feePayer": "...",
142
+ "resourceUrl": "http://localhost:4021/weather"
143
+ }
144
+ }
145
+ ]
89
146
  }
90
147
  ```
91
148
 
92
149
  ### Successful Response
93
150
 
94
- ```ts
95
- // Body
151
+ ```
152
+ HTTP/1.1 200 OK
153
+ Content-Type: application/json; charset=utf-8
154
+ PAYMENT-RESPONSE: <base64-encoded JSON>
155
+
156
+ {"report":{"weather":"sunny","temperature":70}}
157
+ ```
158
+
159
+ The `PAYMENT-RESPONSE` header contains base64-encoded JSON with the settlement details:
160
+
161
+ ```json
96
162
  {
97
- "report": {
98
- "weather": "sunny",
99
- "temperature": 70
163
+ "success": true,
164
+ "transaction": "0x...",
165
+ "network": "eip155:84532",
166
+ "payer": "0x...",
167
+ "requirements": {
168
+ "scheme": "exact",
169
+ "network": "eip155:84532",
170
+ "amount": "1000",
171
+ "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
172
+ "payTo": "0x...",
173
+ "maxTimeoutSeconds": 300,
174
+ "extra": {
175
+ "name": "USDC",
176
+ "version": "2",
177
+ "resourceUrl": "http://localhost:4021/weather"
178
+ }
100
179
  }
101
180
  }
102
- // Headers
103
- {
104
- "X-PAYMENT-RESPONSE": "..." // Encoded response object
105
- }
106
181
  ```
107
182
 
108
183
  ## Extending the Example
@@ -112,39 +187,68 @@ To add more paid endpoints, follow this pattern:
112
187
  ```typescript
113
188
  // First, configure the payment middleware with your routes
114
189
  app.use(
115
- paymentMiddleware(payTo, {
116
- // Define your routes and their payment requirements
117
- "/your-endpoint": {
118
- price: "$0.10",
119
- network,
120
- },
121
- "/premium/*": {
122
- price: {
123
- amount: "100000",
124
- asset: {
125
- address: "0xabc",
126
- decimals: 18,
127
- eip712: {
128
- name: "WETH",
129
- version: "1",
130
- },
190
+ paymentMiddleware(
191
+ {
192
+ "GET /your-endpoint": {
193
+ accepts: {
194
+ scheme: "exact",
195
+ price: "$0.10",
196
+ network: "eip155:84532",
197
+ payTo: evmAddress,
131
198
  },
199
+ description: "Your endpoint description",
200
+ mimeType: "application/json",
132
201
  },
133
- network,
134
202
  },
135
- }),
203
+ resourceServer,
204
+ ),
136
205
  );
137
206
 
138
207
  // Then define your routes as normal
139
- app.get("/your-endpoint", c => {
208
+ app.get("/your-endpoint", (c) => {
140
209
  return c.json({
141
210
  // Your response data
142
211
  });
143
212
  });
213
+ ```
144
214
 
145
- app.get("/premium/content", c => {
146
- return c.json({
147
- content: "This is premium content",
148
- });
149
- });
215
+ **Network identifiers** use [CAIP-2](https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-2.md) format, for example:
216
+
217
+ - `eip155:84532` Base Sepolia
218
+ - `eip155:8453` — Base Mainnet
219
+ - `solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1` — Solana Devnet
220
+ - `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp` — Solana Mainnet
221
+
222
+ ## x402ResourceServer Config
223
+
224
+ The `x402ResourceServer` uses a builder pattern to register payment schemes that declare how payments for each network should be processed:
225
+
226
+ ```typescript
227
+ const resourceServer = new x402ResourceServer(facilitatorClient)
228
+ .register("eip155:*", new ExactEvmScheme()) // All EVM chains
229
+ .register("solana:*", new ExactSvmScheme()); // All SVM chains
230
+ ```
231
+
232
+ ## Facilitator Config
233
+
234
+ The `HTTPFacilitatorClient` connects to a facilitator service that verifies and settles payments on-chain:
235
+
236
+ ```typescript
237
+ const facilitatorClient = new HTTPFacilitatorClient({ url: facilitatorUrl });
238
+
239
+ // Or use multiple facilitators for redundancy
240
+ const facilitatorClient = [
241
+ new HTTPFacilitatorClient({ url: primaryFacilitatorUrl }),
242
+ new HTTPFacilitatorClient({ url: backupFacilitatorUrl }),
243
+ ];
150
244
  ```
245
+
246
+ ## Next Steps
247
+
248
+ See [Advanced Examples](../advanced/) for:
249
+
250
+ - **Bazaar discovery** — make your API discoverable
251
+ - **Dynamic pricing** — price based on request context
252
+ - **Dynamic payTo** — route payments to different recipients
253
+ - **Lifecycle hooks** — custom logic on verify/settle
254
+ - **Custom tokens** — accept payments in custom tokens
package/template/index.ts CHANGED
@@ -1,35 +1,53 @@
1
1
  import { config } from "dotenv";
2
+ import { paymentMiddleware, x402ResourceServer } from "@x402/hono";
3
+ import { ExactEvmScheme } from "@x402/evm/exact/server";
4
+ import { ExactSvmScheme } from "@x402/svm/exact/server";
5
+ import { HTTPFacilitatorClient } from "@x402/core/server";
2
6
  import { Hono } from "hono";
3
7
  import { serve } from "@hono/node-server";
4
- import { paymentMiddleware, Network, Resource, SolanaAddress } from "x402-hono";
5
-
6
8
  config();
7
9
 
8
- const facilitatorUrl = process.env.FACILITATOR_URL as Resource;
9
- const payTo = process.env.ADDRESS as `0x${string}` | SolanaAddress;
10
- const network = process.env.NETWORK as Network;
11
-
12
- if (!facilitatorUrl || !payTo || !network) {
10
+ const evmAddress = process.env.EVM_ADDRESS as `0x${string}`;
11
+ const svmAddress = process.env.SVM_ADDRESS;
12
+ if (!evmAddress || !svmAddress) {
13
13
  console.error("Missing required environment variables");
14
14
  process.exit(1);
15
15
  }
16
16
 
17
- const app = new Hono();
17
+ const facilitatorUrl = process.env.FACILITATOR_URL;
18
+ if (!facilitatorUrl) {
19
+ console.error("❌ FACILITATOR_URL environment variable is required");
20
+ process.exit(1);
21
+ }
22
+ const facilitatorClient = new HTTPFacilitatorClient({ url: facilitatorUrl });
18
23
 
19
- console.log("Server is running");
24
+ const app = new Hono();
20
25
 
21
26
  app.use(
22
27
  paymentMiddleware(
23
- payTo,
24
28
  {
25
- "/weather": {
26
- price: "$0.001",
27
- network,
29
+ "GET /weather": {
30
+ accepts: [
31
+ {
32
+ scheme: "exact",
33
+ price: "$0.001",
34
+ network: "eip155:84532",
35
+ payTo: evmAddress,
36
+ },
37
+ {
38
+ scheme: "exact",
39
+ price: "$0.001",
40
+ network: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
41
+ payTo: svmAddress,
42
+ },
43
+ ],
44
+ description: "Weather data",
45
+ mimeType: "application/json",
28
46
  },
29
47
  },
30
- {
31
- url: facilitatorUrl,
32
- },
48
+ new x402ResourceServer(facilitatorClient)
49
+ .register("eip155:84532", new ExactEvmScheme())
50
+ .register("solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", new ExactSvmScheme()),
33
51
  ),
34
52
  );
35
53
 
@@ -46,3 +64,5 @@ serve({
46
64
  fetch: app.fetch,
47
65
  port: 4021,
48
66
  });
67
+
68
+ console.log(`Server listening at http://localhost:4021`);
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "hono-server-example",
2
+ "name": "@x402/hono-server-example",
3
3
  "private": true,
4
4
  "type": "module",
5
5
  "scripts": {
@@ -10,22 +10,23 @@
10
10
  "lint:check": "eslint . --ext .ts"
11
11
  },
12
12
  "dependencies": {
13
- "@hono/node-server": "^1.13.8",
14
- "@x402/hono": "^2.2.0",
15
13
  "dotenv": "^16.4.7",
16
- "hono": "^4.7.1"
14
+ "@hono/node-server": "^1.14.0",
15
+ "hono": "^4.7.1",
16
+ "@x402/hono": "^2.2.0"
17
17
  },
18
18
  "devDependencies": {
19
- "tsup": "^7.2.0",
20
- "tsx": "^4.7.0",
21
- "typescript": "^5.3.0",
22
19
  "@eslint/js": "^9.24.0",
23
- "eslint": "^9.24.0",
24
- "eslint-plugin-jsdoc": "^50.6.9",
25
- "eslint-plugin-prettier": "^5.2.6",
20
+ "@types/express": "^5.0.1",
26
21
  "@typescript-eslint/eslint-plugin": "^8.29.1",
27
22
  "@typescript-eslint/parser": "^8.29.1",
23
+ "eslint": "^9.24.0",
28
24
  "eslint-plugin-import": "^2.31.0",
29
- "prettier": "3.5.2"
25
+ "eslint-plugin-jsdoc": "^50.6.9",
26
+ "eslint-plugin-prettier": "^5.2.6",
27
+ "prettier": "3.5.2",
28
+ "tsup": "^7.2.0",
29
+ "tsx": "^4.7.0",
30
+ "typescript": "^5.3.0"
30
31
  }
31
32
  }
@@ -11,5 +11,12 @@
11
11
  "baseUrl": ".",
12
12
  "types": ["node"]
13
13
  },
14
- "include": ["index.ts"]
14
+ "include": [
15
+ "index.ts",
16
+ "bazaar.ts",
17
+ "custom-money-definition.ts",
18
+ "dynamic-pay-to.ts",
19
+ "dynamic-price.ts",
20
+ "hooks.ts"
21
+ ]
15
22
  }