@faremeter/middleware 0.12.0 → 0.14.0
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 +41 -0
- package/dist/src/common.d.ts +6 -0
- package/dist/src/common.d.ts.map +1 -1
- package/dist/src/common.js +62 -27
- package/dist/src/express.d.ts +1 -1
- package/dist/src/express.d.ts.map +1 -1
- package/dist/src/express.js +8 -5
- package/dist/src/hono.d.ts +3 -1
- package/dist/src/hono.d.ts.map +1 -1
- package/dist/src/hono.js +34 -5
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +11 -2
package/README.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# @faremeter/middleware
|
|
2
|
+
|
|
3
|
+
Server middleware for adding payment walls to API endpoints using the x402 protocol.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm install @faremeter/middleware
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- Paywall any endpoint - Add `middleware` to any route
|
|
14
|
+
- Framework agnostic - Express, Hono, or custom
|
|
15
|
+
- Multi-chain support - Solana, EVM, extensible
|
|
16
|
+
- Fast validation - Payment requirements caching
|
|
17
|
+
- Facilitator integration - Handles settlement verification
|
|
18
|
+
|
|
19
|
+
## API Reference
|
|
20
|
+
|
|
21
|
+
<!-- TSDOC_START -->
|
|
22
|
+
|
|
23
|
+
<!-- TSDOC_END -->
|
|
24
|
+
|
|
25
|
+
## Examples
|
|
26
|
+
|
|
27
|
+
See working examples in the [faremeter repository](https://github.com/faremeter/faremeter/tree/main/scripts):
|
|
28
|
+
|
|
29
|
+
- [Express + Solana](https://github.com/faremeter/faremeter/blob/main/scripts/solana-example/server-express.ts)
|
|
30
|
+
- [Express + EVM](https://github.com/faremeter/faremeter/blob/main/scripts/evm-example/server-express.ts)
|
|
31
|
+
- [Hono + Solana](https://github.com/faremeter/faremeter/blob/main/scripts/solana-example/server-hono.ts)
|
|
32
|
+
|
|
33
|
+
## Related Packages
|
|
34
|
+
|
|
35
|
+
- [@faremeter/fetch](https://www.npmjs.com/package/@faremeter/fetch) - Client-side fetch wrapper
|
|
36
|
+
- [@faremeter/info](https://www.npmjs.com/package/@faremeter/info) - Network/asset configuration helpers
|
|
37
|
+
- [@faremeter/facilitator](https://www.npmjs.com/package/@faremeter/facilitator) - Payment facilitator service
|
|
38
|
+
|
|
39
|
+
## License
|
|
40
|
+
|
|
41
|
+
LGPL-3.0-only
|
package/dist/src/common.d.ts
CHANGED
|
@@ -49,6 +49,12 @@ export type HandleMiddlewareRequestArgs<MiddlewareResponse = unknown> = CommonMi
|
|
|
49
49
|
getHeader: (key: string) => string | undefined;
|
|
50
50
|
getPaymentRequiredResponse: typeof getPaymentRequiredResponse;
|
|
51
51
|
sendJSONResponse: (status: PossibleStatusCodes, obj: PossibleJSONResponse) => MiddlewareResponse;
|
|
52
|
+
body: (context: {
|
|
53
|
+
paymentRequirements: x402PaymentRequirements;
|
|
54
|
+
paymentPayload: x402PaymentPayload;
|
|
55
|
+
settle: () => Promise<MiddlewareResponse | undefined>;
|
|
56
|
+
verify: () => Promise<MiddlewareResponse | undefined>;
|
|
57
|
+
}) => Promise<MiddlewareResponse | undefined>;
|
|
52
58
|
};
|
|
53
59
|
export declare function handleMiddlewareRequest<MiddlewareResponse>(args: HandleMiddlewareRequestArgs<MiddlewareResponse>): Promise<MiddlewareResponse | undefined>;
|
|
54
60
|
export type createPaymentRequiredResponseCacheOpts = AgedLRUCacheOpts & {
|
package/dist/src/common.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../src/common.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,
|
|
1
|
+
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../src/common.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EAOxB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,KAAK,gBAAgB,EAAgB,MAAM,SAAS,CAAC;AAI9D,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,uBAAuB,EAAE,EAClC,OAAO,EAAE,kBAAkB;;;;;;;;;;;;cA6B5B;AAED,wBAAgB,8BAA8B,CAAC,GAAG,EAAE,QAAQ,QAS3D;AAED,MAAM,MAAM,mBAAmB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAC;AAEnE,KAAK,8BAA8B,GAAG;IACpC,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAC/B,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,8BAA8B;;;;;;;;;;;;oBAtCoB,CAAC;aACtC,CAAC;;;GAmErB;AAED,KAAK,mBAAmB,GAAG,GAAG,CAAC;AAC/B,KAAK,oBAAoB,GAAG,MAAM,CAAC;AAEnC,MAAM,MAAM,oBAAoB,GAAG;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,CAAC,mBAAmB,GAAG,mBAAmB,EAAE,CAAC,EAAE,CAAC;IACzD,WAAW,CAAC,EAAE,sCAAsC,CAAC;CACtD,CAAC;AAEF,MAAM,MAAM,2BAA2B,CAAC,kBAAkB,GAAG,OAAO,IAClE,oBAAoB,GAAG;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IAC/C,0BAA0B,EAAE,OAAO,0BAA0B,CAAC;IAC9D,gBAAgB,EAAE,CAChB,MAAM,EAAE,mBAAmB,EAC3B,GAAG,EAAE,oBAAoB,KACtB,kBAAkB,CAAC;IACxB,IAAI,EAAE,CAAC,OAAO,EAAE;QACd,mBAAmB,EAAE,uBAAuB,CAAC;QAC7C,cAAc,EAAE,kBAAkB,CAAC;QACnC,MAAM,EAAE,MAAM,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CAAC;QACtD,MAAM,EAAE,MAAM,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CAAC;KACvD,KAAK,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CAAC;CAC/C,CAAC;AAEJ,wBAAsB,uBAAuB,CAAC,kBAAkB,EAC9D,IAAI,EAAE,2BAA2B,CAAC,kBAAkB,CAAC,2CA4GtD;AAED,MAAM,MAAM,sCAAsC,GAAG,gBAAgB,GAAG;IACtE,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AACF,wBAAgB,kCAAkC,CAChD,IAAI,GAAE,sCAA2C;;EA8BlD"}
|
package/dist/src/common.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isValidationError } from "@faremeter/types";
|
|
2
|
-
import { x402PaymentRequiredResponse, x402PaymentHeaderToPayload, x402SettleRequest, x402SettleResponse, } from "@faremeter/types/x402";
|
|
2
|
+
import { x402PaymentRequiredResponse, x402PaymentHeaderToPayload, x402VerifyRequest, x402VerifyResponse, x402SettleRequest, x402SettleResponse, } from "@faremeter/types/x402";
|
|
3
3
|
import { AgedLRUCache } from "./cache.js";
|
|
4
4
|
import { logger } from "./logger.js";
|
|
5
5
|
export function findMatchingPaymentRequirements(accepts, payload) {
|
|
@@ -64,39 +64,74 @@ export async function handleMiddlewareRequest(args) {
|
|
|
64
64
|
if (!paymentHeader) {
|
|
65
65
|
return sendPaymentRequired();
|
|
66
66
|
}
|
|
67
|
-
const
|
|
68
|
-
if (isValidationError(
|
|
69
|
-
logger.debug(`couldn't validate client payload: ${
|
|
67
|
+
const paymentPayload = x402PaymentHeaderToPayload(paymentHeader);
|
|
68
|
+
if (isValidationError(paymentPayload)) {
|
|
69
|
+
logger.debug(`couldn't validate client payload: ${paymentPayload.summary}`);
|
|
70
70
|
return sendPaymentRequired();
|
|
71
71
|
}
|
|
72
|
-
const paymentRequirements = findMatchingPaymentRequirements(paymentRequiredResponse.accepts,
|
|
72
|
+
const paymentRequirements = findMatchingPaymentRequirements(paymentRequiredResponse.accepts, paymentPayload);
|
|
73
73
|
if (!paymentRequirements) {
|
|
74
|
-
logger.warning(`couldn't find matching payment requirements for payload`,
|
|
74
|
+
logger.warning(`couldn't find matching payment requirements for payload`, paymentPayload);
|
|
75
75
|
return sendPaymentRequired();
|
|
76
76
|
}
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
const settle = async () => {
|
|
78
|
+
const settleRequest = {
|
|
79
|
+
x402Version: 1,
|
|
80
|
+
paymentHeader,
|
|
81
|
+
paymentPayload,
|
|
82
|
+
paymentRequirements,
|
|
83
|
+
};
|
|
84
|
+
const t = await fetch(`${args.facilitatorURL}/settle`, {
|
|
85
|
+
method: "POST",
|
|
86
|
+
headers: {
|
|
87
|
+
Accept: "application/json",
|
|
88
|
+
"Content-Type": "application/json",
|
|
89
|
+
},
|
|
90
|
+
body: JSON.stringify(settleRequest),
|
|
91
|
+
});
|
|
92
|
+
const settlementResponse = x402SettleResponse(await t.json());
|
|
93
|
+
if (isValidationError(settlementResponse)) {
|
|
94
|
+
const msg = `error getting response from facilitator for settlement: ${settlementResponse.summary}`;
|
|
95
|
+
logger.error(msg);
|
|
96
|
+
throw new Error(msg);
|
|
97
|
+
}
|
|
98
|
+
if (!settlementResponse.success) {
|
|
99
|
+
logger.warning("failed to settle payment: {error}", settlementResponse);
|
|
100
|
+
return sendPaymentRequired();
|
|
101
|
+
}
|
|
81
102
|
};
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
103
|
+
const verify = async () => {
|
|
104
|
+
const verifyRequest = {
|
|
105
|
+
x402Version: 1,
|
|
106
|
+
paymentHeader,
|
|
107
|
+
paymentPayload,
|
|
108
|
+
paymentRequirements,
|
|
109
|
+
};
|
|
110
|
+
const t = await fetch(`${args.facilitatorURL}/verify`, {
|
|
111
|
+
method: "POST",
|
|
112
|
+
headers: {
|
|
113
|
+
Accept: "application/json",
|
|
114
|
+
"Content-Type": "application/json",
|
|
115
|
+
},
|
|
116
|
+
body: JSON.stringify(verifyRequest),
|
|
117
|
+
});
|
|
118
|
+
const verifyResponse = x402VerifyResponse(await t.json());
|
|
119
|
+
if (isValidationError(verifyResponse)) {
|
|
120
|
+
const msg = `error getting response from facilitator for verification: ${verifyResponse.summary}`;
|
|
121
|
+
logger.error(msg);
|
|
122
|
+
throw new Error(msg);
|
|
123
|
+
}
|
|
124
|
+
if (!verifyResponse.isValid) {
|
|
125
|
+
logger.warning("failed to settle payment: {invalidReason}", verifyResponse);
|
|
126
|
+
return sendPaymentRequired();
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
return await args.body({
|
|
130
|
+
paymentRequirements,
|
|
131
|
+
paymentPayload,
|
|
132
|
+
settle,
|
|
133
|
+
verify,
|
|
89
134
|
});
|
|
90
|
-
const settlementResponse = x402SettleResponse(await t.json());
|
|
91
|
-
if (isValidationError(settlementResponse)) {
|
|
92
|
-
const msg = `error getting response from facilitator for settlement: ${settlementResponse.summary}`;
|
|
93
|
-
logger.error(msg);
|
|
94
|
-
throw new Error(msg);
|
|
95
|
-
}
|
|
96
|
-
if (!settlementResponse.success) {
|
|
97
|
-
logger.warning("failed to settle payment: {error}", settlementResponse);
|
|
98
|
-
return sendPaymentRequired();
|
|
99
|
-
}
|
|
100
135
|
}
|
|
101
136
|
export function createPaymentRequiredResponseCache(opts = {}) {
|
|
102
137
|
if (opts.disable) {
|
package/dist/src/express.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type CommonMiddlewareArgs } from "./common.js";
|
|
2
2
|
import type { NextFunction, Request, Response } from "express";
|
|
3
3
|
type createMiddlewareArgs = CommonMiddlewareArgs;
|
|
4
|
-
export declare function createMiddleware(args: createMiddlewareArgs): Promise<(req: Request, res: Response, next: NextFunction) => Promise<
|
|
4
|
+
export declare function createMiddleware(args: createMiddlewareArgs): Promise<(req: Request, res: Response, next: NextFunction) => Promise<Response<any, Record<string, any>> | undefined>>;
|
|
5
5
|
export {};
|
|
6
6
|
//# sourceMappingURL=express.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"express.d.ts","sourceRoot":"","sources":["../../src/express.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,oBAAoB,EAE1B,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE/D,KAAK,oBAAoB,GAAG,oBAAoB,CAAC;AAEjD,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,oBAAoB,iBAK5C,OAAO,OAAO,QAAQ,QAAQ,YAAY,
|
|
1
|
+
{"version":3,"file":"express.d.ts","sourceRoot":"","sources":["../../src/express.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,oBAAoB,EAE1B,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE/D,KAAK,oBAAoB,GAAG,oBAAoB,CAAC;AAEjD,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,oBAAoB,iBAK5C,OAAO,OAAO,QAAQ,QAAQ,YAAY,8DAiB9D"}
|
package/dist/src/express.js
CHANGED
|
@@ -2,16 +2,19 @@ import { handleMiddlewareRequest, createPaymentRequiredResponseCache, } from "./
|
|
|
2
2
|
export async function createMiddleware(args) {
|
|
3
3
|
const { getPaymentRequiredResponse } = createPaymentRequiredResponseCache(args.cacheConfig);
|
|
4
4
|
return async (req, res, next) => {
|
|
5
|
-
|
|
5
|
+
return await handleMiddlewareRequest({
|
|
6
6
|
...args,
|
|
7
7
|
resource: `${req.protocol}://${req.headers.host}${req.path}`,
|
|
8
8
|
getPaymentRequiredResponse,
|
|
9
9
|
getHeader: (key) => req.header(key),
|
|
10
10
|
sendJSONResponse: (status, body) => res.status(status).json(body),
|
|
11
|
+
body: async ({ settle }) => {
|
|
12
|
+
const response = await settle();
|
|
13
|
+
if (response !== undefined) {
|
|
14
|
+
return response;
|
|
15
|
+
}
|
|
16
|
+
next();
|
|
17
|
+
},
|
|
11
18
|
});
|
|
12
|
-
if (response) {
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
next();
|
|
16
19
|
};
|
|
17
20
|
}
|
package/dist/src/hono.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { type CommonMiddlewareArgs } from "./common.js";
|
|
2
2
|
import type { MiddlewareHandler } from "hono";
|
|
3
|
-
type CreateMiddlewareArgs =
|
|
3
|
+
type CreateMiddlewareArgs = {
|
|
4
|
+
verifyBeforeSettle?: boolean;
|
|
5
|
+
} & CommonMiddlewareArgs;
|
|
4
6
|
export declare function createMiddleware(args: CreateMiddlewareArgs): Promise<MiddlewareHandler>;
|
|
5
7
|
export {};
|
|
6
8
|
//# sourceMappingURL=hono.d.ts.map
|
package/dist/src/hono.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hono.d.ts","sourceRoot":"","sources":["../../src/hono.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,oBAAoB,EAE1B,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAE9C,KAAK,oBAAoB,GAAG,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"hono.d.ts","sourceRoot":"","sources":["../../src/hono.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,oBAAoB,EAE1B,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAE9C,KAAK,oBAAoB,GAAG;IAC1B,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B,GAAG,oBAAoB,CAAC;AAEzB,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,oBAAoB,GACzB,OAAO,CAAC,iBAAiB,CAAC,CAoD5B"}
|
package/dist/src/hono.js
CHANGED
|
@@ -2,7 +2,7 @@ import { handleMiddlewareRequest, createPaymentRequiredResponseCache, } from "./
|
|
|
2
2
|
export async function createMiddleware(args) {
|
|
3
3
|
const { getPaymentRequiredResponse } = createPaymentRequiredResponseCache(args.cacheConfig);
|
|
4
4
|
return async (c, next) => {
|
|
5
|
-
|
|
5
|
+
return await handleMiddlewareRequest({
|
|
6
6
|
...args,
|
|
7
7
|
resource: c.req.url,
|
|
8
8
|
getHeader: (key) => c.req.header(key),
|
|
@@ -11,10 +11,39 @@ export async function createMiddleware(args) {
|
|
|
11
11
|
c.status(status);
|
|
12
12
|
return c.json(body);
|
|
13
13
|
},
|
|
14
|
+
body: async ({ verify, settle }) => {
|
|
15
|
+
if (args.verifyBeforeSettle) {
|
|
16
|
+
// If configured, try to verify the transaction before running
|
|
17
|
+
// the next operation.
|
|
18
|
+
const verifyResult = await verify();
|
|
19
|
+
if (verifyResult !== undefined) {
|
|
20
|
+
return verifyResult;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
// Otherwise just settle the payment beforehand, like we've
|
|
25
|
+
// done historically.
|
|
26
|
+
const settleResult = await settle();
|
|
27
|
+
if (settleResult !== undefined) {
|
|
28
|
+
return settleResult;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
await next();
|
|
32
|
+
if (args.verifyBeforeSettle) {
|
|
33
|
+
// Close out the verification, by actually settling the
|
|
34
|
+
// payment.
|
|
35
|
+
const settleResult = await settle();
|
|
36
|
+
if (settleResult !== undefined) {
|
|
37
|
+
// If the settlement fails, we need to explicitly
|
|
38
|
+
// overwrite the downstream result. See:
|
|
39
|
+
//
|
|
40
|
+
// https://hono.dev/docs/guides/middleware#modify-the-response-after-next
|
|
41
|
+
//
|
|
42
|
+
c.res = undefined;
|
|
43
|
+
c.res = settleResult;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
},
|
|
14
47
|
});
|
|
15
|
-
if (response) {
|
|
16
|
-
return response;
|
|
17
|
-
}
|
|
18
|
-
await next();
|
|
19
48
|
};
|
|
20
49
|
}
|