@armory-sh/middleware-next 0.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.
Files changed (3) hide show
  1. package/README.md +110 -0
  2. package/dist/index.js +127 -0
  3. package/package.json +43 -0
package/README.md ADDED
@@ -0,0 +1,110 @@
1
+ # @armory-sh/middleware-next
2
+
3
+ x402 payment middleware for Next.js App Router.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ bun add @armory-sh/middleware-next
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### Basic Setup
14
+
15
+ ```typescript
16
+ // middleware.ts
17
+ import { paymentProxy, x402ResourceServer } from "@armory-sh/middleware-next";
18
+
19
+ const facilitatorClient = {
20
+ async verify(headers: Headers) {
21
+ // Implement verification logic
22
+ return { success: true, payerAddress: "0x..." };
23
+ }
24
+ };
25
+
26
+ const resourceServer = new x402ResourceServer(facilitatorClient);
27
+
28
+ export const proxy = paymentProxy(
29
+ {
30
+ "/api/protected": {
31
+ accepts: {
32
+ scheme: "exact",
33
+ price: "1000000",
34
+ network: "eip155:8453",
35
+ payTo: "0x...",
36
+ },
37
+ description: "Access to protected API",
38
+ },
39
+ },
40
+ resourceServer
41
+ );
42
+
43
+ export const config = { matcher: ["/api/protected/:path*"] };
44
+ ```
45
+
46
+ ### Per-Route Configuration
47
+
48
+ ```typescript
49
+ export const proxy = paymentProxy(
50
+ {
51
+ "/api/basic": {
52
+ accepts: { scheme: "exact", price: "1000000", network: "eip155:8453", payTo: "0x..." },
53
+ description: "Basic tier",
54
+ },
55
+ "/api/premium": {
56
+ accepts: { scheme: "exact", price: "50000000", network: "eip155:8453", payTo: "0x..." },
57
+ description: "Premium tier",
58
+ },
59
+ },
60
+ resourceServer
61
+ );
62
+ ```
63
+
64
+ ### With Facilitator Settlement
65
+
66
+ ```typescript
67
+ const facilitatorClient = {
68
+ async verify(headers: Headers) {
69
+ const response = await fetch("https://facilitator.com/verify", {
70
+ headers: Object.fromEntries(headers.entries()),
71
+ });
72
+ return response.json();
73
+ },
74
+ async settle(headers: Headers) {
75
+ const response = await fetch("https://facilitator.com/settle", {
76
+ method: "POST",
77
+ headers: Object.fromEntries(headers.entries()),
78
+ });
79
+ return response.json();
80
+ },
81
+ };
82
+ ```
83
+
84
+ ## Route Patterns
85
+
86
+ - Exact match: `/api/users`
87
+ - Wildcard: `/api/*` (matches `/api/users`, `/api/posts/123`)
88
+ - Next.js style: `/protected/:path*` (matches `/protected/foo/bar`)
89
+
90
+ ## API
91
+
92
+ ### `paymentProxy(routes, resourceServer)`
93
+
94
+ Creates a payment proxy handler for Next.js middleware.
95
+
96
+ **Parameters:**
97
+ - `routes`: Record mapping route patterns to payment config
98
+ - `resourceServer`: x402ResourceServer instance
99
+
100
+ **Returns:** Next.js middleware function
101
+
102
+ ### `x402ResourceServer`
103
+
104
+ Registers and manages payment schemes.
105
+
106
+ **Methods:**
107
+ - `register(chainId, scheme)`: Register a payment scheme
108
+ - `getRequirements(chainId)`: Get requirements for a chain
109
+ - `getAllRequirements()`: Get all registered requirements
110
+ - `getFacilitator()`: Get the facilitator client
package/dist/index.js ADDED
@@ -0,0 +1,127 @@
1
+ // src/resource-server.ts
2
+ var x402ResourceServer = class {
3
+ facilitator;
4
+ schemes;
5
+ constructor(facilitatorClient) {
6
+ this.facilitator = facilitatorClient;
7
+ this.schemes = /* @__PURE__ */ new Map();
8
+ }
9
+ register(chainId, scheme) {
10
+ this.schemes.set(chainId, scheme);
11
+ return this;
12
+ }
13
+ getRequirements(chainId) {
14
+ const scheme = this.schemes.get(chainId);
15
+ if (!scheme) {
16
+ throw new Error(`No scheme registered for chain: ${chainId}`);
17
+ }
18
+ return scheme.getRequirements();
19
+ }
20
+ getAllRequirements() {
21
+ return Array.from(this.schemes.values()).map((s) => s.getRequirements());
22
+ }
23
+ getFacilitator() {
24
+ return this.facilitator;
25
+ }
26
+ };
27
+
28
+ // src/proxy.ts
29
+ import {
30
+ safeBase64Encode,
31
+ V2_HEADERS,
32
+ findMatchingRoute
33
+ } from "@armory-sh/base";
34
+ function paymentProxy(routes, resourceServer) {
35
+ const routeConfigs = Object.entries(routes).map(
36
+ ([pattern, config]) => ({ pattern, config })
37
+ );
38
+ return async (request) => {
39
+ const path = new URL(request.url).pathname;
40
+ const matchedRoute = findMatchingRoute(routeConfigs, path);
41
+ if (!matchedRoute) {
42
+ return new Response(
43
+ JSON.stringify({ error: "Route not found" }),
44
+ { status: 404, headers: { "Content-Type": "application/json" } }
45
+ );
46
+ }
47
+ const paymentHeader = request.headers.get(V2_HEADERS.PAYMENT_SIGNATURE);
48
+ if (!paymentHeader) {
49
+ const config = matchedRoute.config;
50
+ const resource = {
51
+ url: request.url,
52
+ description: config.description || "API Access",
53
+ mimeType: "application/json"
54
+ };
55
+ const paymentRequired = {
56
+ x402Version: 2,
57
+ error: "Payment required",
58
+ resource,
59
+ accepts: [
60
+ {
61
+ scheme: config.accepts.scheme,
62
+ network: config.accepts.network,
63
+ amount: config.accepts.price,
64
+ asset: config.accepts.payTo,
65
+ payTo: config.accepts.payTo,
66
+ maxTimeoutSeconds: 300,
67
+ extra: {}
68
+ }
69
+ ]
70
+ };
71
+ return new Response(
72
+ JSON.stringify({
73
+ error: "Payment required",
74
+ accepts: paymentRequired.accepts
75
+ }),
76
+ {
77
+ status: 402,
78
+ headers: {
79
+ "Content-Type": "application/json",
80
+ [V2_HEADERS.PAYMENT_REQUIRED]: safeBase64Encode(
81
+ JSON.stringify(paymentRequired)
82
+ )
83
+ }
84
+ }
85
+ );
86
+ }
87
+ const facilitator = resourceServer.getFacilitator();
88
+ const verifyResult = await facilitator.verify(request.headers);
89
+ if (!verifyResult.success) {
90
+ return new Response(
91
+ JSON.stringify({
92
+ error: "Payment verification failed",
93
+ details: verifyResult.error
94
+ }),
95
+ { status: 402, headers: { "Content-Type": "application/json" } }
96
+ );
97
+ }
98
+ return new Response(
99
+ JSON.stringify({
100
+ verified: true,
101
+ payerAddress: verifyResult.payerAddress
102
+ }),
103
+ {
104
+ status: 200,
105
+ headers: {
106
+ "Content-Type": "application/json",
107
+ "X-Payment-Verified": "true",
108
+ "X-Payer-Address": verifyResult.payerAddress || ""
109
+ }
110
+ }
111
+ );
112
+ };
113
+ }
114
+
115
+ // src/middleware.ts
116
+ function createMiddleware(routes, resourceServer) {
117
+ const proxy = paymentProxy(routes, resourceServer);
118
+ return async (request) => {
119
+ const response = await proxy(request);
120
+ return NextResponse.json(await response.json(), { status: response.status });
121
+ };
122
+ }
123
+ export {
124
+ createMiddleware,
125
+ paymentProxy,
126
+ x402ResourceServer
127
+ };
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@armory-sh/middleware-next",
3
+ "version": "0.1.1",
4
+ "license": "MIT",
5
+ "author": "Sawyer Cutler <sawyer@dirtroad.dev>",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "bun": "./src/index.ts",
13
+ "default": "./dist/index.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/thegreataxios/armory.git",
25
+ "directory": "packages/middleware-next"
26
+ },
27
+ "peerDependencies": {
28
+ "next": ">=13"
29
+ },
30
+ "dependencies": {
31
+ "@armory-sh/base": "0.2.20"
32
+ },
33
+ "devDependencies": {
34
+ "bun-types": "latest",
35
+ "next": "latest",
36
+ "typescript": "5.9.3"
37
+ },
38
+ "scripts": {
39
+ "build": "rm -rf dist && tsup",
40
+ "build:dts": "rm -rf dist && tsc --emitDeclarationOnly",
41
+ "test": "bun test"
42
+ }
43
+ }