@pincerpay/merchant 0.4.1 → 0.5.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 +69 -45
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -2
- package/dist/index.js.map +1 -1
- package/dist/middleware/nextjs.d.ts +32 -0
- package/dist/middleware/nextjs.d.ts.map +1 -0
- package/dist/middleware/nextjs.js +192 -0
- package/dist/middleware/nextjs.js.map +1 -0
- package/package.json +8 -35
- package/dist/middleware/express.d.ts +0 -18
- package/dist/middleware/express.d.ts.map +0 -1
- package/dist/middleware/express.js +0 -62
- package/dist/middleware/express.js.map +0 -1
- package/dist/middleware/hono.d.ts +0 -18
- package/dist/middleware/hono.d.ts.map +0 -1
- package/dist/middleware/hono.js +0 -62
- package/dist/middleware/hono.js.map +0 -1
package/README.md
CHANGED
|
@@ -5,28 +5,29 @@
|
|
|
5
5
|
[](https://github.com/ds1/pincerpay/blob/master/LICENSE)
|
|
6
6
|
[](https://www.typescriptlang.org/)
|
|
7
7
|
|
|
8
|
-
Merchant SDK for accepting on-chain USDC payments from AI agents via the [x402 protocol](https://x402.org).
|
|
8
|
+
Merchant SDK for accepting on-chain USDC payments from AI agents via the [x402 protocol](https://x402.org). Ships a Hono middleware that runs natively in Hono apps and inside Next.js App Router via `hono/vercel`.
|
|
9
9
|
|
|
10
10
|
> **ESM Required:** Your project must have `"type": "module"` in package.json. This package is ESM-only.
|
|
11
11
|
|
|
12
12
|
## Install
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
|
-
npm install @pincerpay/merchant
|
|
15
|
+
npm install @pincerpay/merchant hono
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
## Quick Start
|
|
19
19
|
|
|
20
|
-
###
|
|
20
|
+
### Hono
|
|
21
21
|
|
|
22
22
|
```typescript
|
|
23
|
-
import
|
|
24
|
-
import {
|
|
23
|
+
import { Hono } from "hono";
|
|
24
|
+
import { createPincerPayMiddleware } from "@pincerpay/merchant/nextjs";
|
|
25
25
|
|
|
26
|
-
const app =
|
|
26
|
+
const app = new Hono();
|
|
27
27
|
|
|
28
28
|
app.use(
|
|
29
|
-
|
|
29
|
+
"*",
|
|
30
|
+
createPincerPayMiddleware({
|
|
30
31
|
apiKey: process.env.PINCERPAY_API_KEY!,
|
|
31
32
|
merchantAddress: "YOUR_SOLANA_ADDRESS",
|
|
32
33
|
routes: {
|
|
@@ -44,26 +45,29 @@ app.use(
|
|
|
44
45
|
})
|
|
45
46
|
);
|
|
46
47
|
|
|
47
|
-
app.get("/api/weather", (
|
|
48
|
-
res.json({ temp: 72, unit: "F" });
|
|
49
|
-
});
|
|
48
|
+
app.get("/api/weather", (c) => c.json({ temp: 72, unit: "F" }));
|
|
50
49
|
|
|
51
|
-
app
|
|
50
|
+
export default app;
|
|
52
51
|
```
|
|
53
52
|
|
|
54
|
-
###
|
|
53
|
+
### Next.js (App Router)
|
|
54
|
+
|
|
55
|
+
Next.js doesn't have native x402 middleware support. Use Hono as a lightweight handler inside a catch-all App Router route:
|
|
55
56
|
|
|
56
57
|
```typescript
|
|
58
|
+
// app/api/[...route]/route.ts
|
|
57
59
|
import { Hono } from "hono";
|
|
58
|
-
import {
|
|
60
|
+
import { handle } from "hono/vercel";
|
|
61
|
+
import { createPincerPayMiddleware } from "@pincerpay/merchant/nextjs";
|
|
59
62
|
|
|
60
|
-
const app = new Hono();
|
|
63
|
+
const app = new Hono().basePath("/api");
|
|
61
64
|
|
|
62
65
|
app.use(
|
|
63
66
|
"*",
|
|
64
|
-
|
|
67
|
+
createPincerPayMiddleware({
|
|
65
68
|
apiKey: process.env.PINCERPAY_API_KEY!,
|
|
66
69
|
merchantAddress: "YOUR_SOLANA_ADDRESS",
|
|
70
|
+
syncFacilitatorOnStart: false, // Avoids build-time network call during prerendering
|
|
67
71
|
routes: {
|
|
68
72
|
"GET /api/weather": {
|
|
69
73
|
price: "0.01",
|
|
@@ -74,58 +78,64 @@ app.use(
|
|
|
74
78
|
})
|
|
75
79
|
);
|
|
76
80
|
|
|
77
|
-
app.get("/
|
|
81
|
+
app.get("/weather", (c) => c.json({ temp: 72 }));
|
|
78
82
|
|
|
79
|
-
export
|
|
83
|
+
export const GET = handle(app);
|
|
84
|
+
export const POST = handle(app);
|
|
80
85
|
```
|
|
81
86
|
|
|
82
|
-
|
|
87
|
+
> **Note:** `basePath("/api")` must match the catch-all route location. Route handlers use paths relative to basePath (`/weather` serves `/api/weather`).
|
|
83
88
|
|
|
84
|
-
|
|
89
|
+
### Express
|
|
90
|
+
|
|
91
|
+
Express adapter is on the roadmap. Use Hono today — it runs anywhere Express does and is a drop-in for most paywall workloads. Track [the Express adapter issue](https://github.com/ds1/pincerpay/issues) for the upcoming release.
|
|
92
|
+
|
|
93
|
+
## Reading the Verified Payer
|
|
94
|
+
|
|
95
|
+
After successful settlement, the middleware surfaces the verified payer (and the rest of the settlement metadata) on the Hono request context under the `pincerpay` key. Your route handlers can attribute the action to the paying agent without re-decoding the `X-PAYMENT` request header.
|
|
85
96
|
|
|
86
97
|
```typescript
|
|
87
|
-
// app/api/[...route]/route.ts
|
|
88
98
|
import { Hono } from "hono";
|
|
89
|
-
import {
|
|
90
|
-
|
|
99
|
+
import {
|
|
100
|
+
createPincerPayMiddleware,
|
|
101
|
+
type PincerPayContextVariables,
|
|
102
|
+
} from "@pincerpay/merchant/nextjs";
|
|
91
103
|
|
|
92
|
-
const app = new Hono()
|
|
104
|
+
const app = new Hono<{ Variables: PincerPayContextVariables }>();
|
|
93
105
|
|
|
94
106
|
app.use(
|
|
95
107
|
"*",
|
|
96
|
-
|
|
108
|
+
createPincerPayMiddleware({
|
|
97
109
|
apiKey: process.env.PINCERPAY_API_KEY!,
|
|
98
110
|
merchantAddress: "YOUR_SOLANA_ADDRESS",
|
|
99
|
-
syncFacilitatorOnStart: false, // Avoids build-time network call during prerendering
|
|
100
111
|
routes: {
|
|
101
|
-
"
|
|
102
|
-
price: "0.01",
|
|
103
|
-
chain: "solana",
|
|
104
|
-
description: "Current weather data",
|
|
105
|
-
},
|
|
112
|
+
"POST /api/trade": { price: "0.05", chain: "solana", description: "Place trade" },
|
|
106
113
|
},
|
|
107
114
|
})
|
|
108
115
|
);
|
|
109
116
|
|
|
110
|
-
app.
|
|
117
|
+
app.post("/api/trade", async (c) => {
|
|
118
|
+
const { payer, transaction, network } = c.get("pincerpay");
|
|
119
|
+
// payer -> verified agent wallet (Solana base58 or EVM 0x-hex)
|
|
120
|
+
// transaction -> settlement tx hash
|
|
121
|
+
// network -> CAIP-2 network id (e.g., "solana:5eykt4...", "eip155:8453")
|
|
111
122
|
|
|
112
|
-
|
|
113
|
-
|
|
123
|
+
await recordTrade({ agentWallet: payer, txHash: transaction });
|
|
124
|
+
return c.json({ ok: true });
|
|
125
|
+
});
|
|
114
126
|
```
|
|
115
127
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
> **Note:** `basePath("/api")` must match the catch-all route location. Route handlers use paths relative to basePath (`/weather` serves `/api/weather`).
|
|
128
|
+
`payer` comes from the facilitator's verified settle response — not the unverified `X-PAYMENT` request header. It is canonical across schemes (EVM `authorization.from`, Solana signer, etc. are all normalized to a single string).
|
|
119
129
|
|
|
120
|
-
|
|
130
|
+
> **Don't re-decode `X-PAYMENT` to extract the payer.** The request header carries an unverified, scheme-specific payload. The middleware already verifies, settles, and surfaces the canonical payer on `c.get("pincerpay")`. If you find yourself probing `payload.authorization.from` / `payload.from` / `payload.signer`, stop — read `c.get("pincerpay").payer` instead.
|
|
121
131
|
|
|
122
|
-
|
|
132
|
+
The same `payer` field is also included in the base64-encoded `payment-response` response header for clients that bypass the middleware and post directly to `/v1/settle`.
|
|
123
133
|
|
|
124
|
-
|
|
134
|
+
## API Reference
|
|
125
135
|
|
|
126
|
-
### `
|
|
136
|
+
### `createPincerPayMiddleware(config): Hono.MiddlewareHandler`
|
|
127
137
|
|
|
128
|
-
Hono middleware with
|
|
138
|
+
Hono middleware that intercepts requests matching configured routes and returns HTTP 402 with x402 payment requirements. On a successful payment, settles via the PincerPay facilitator and sets `c.set("pincerpay", { payer, transaction, network })` before passing to the next handler. Used directly in Hono apps and inside Next.js App Router via `hono/vercel`.
|
|
129
139
|
|
|
130
140
|
### `PincerPayClient`
|
|
131
141
|
|
|
@@ -163,6 +173,16 @@ interface RoutePaywallConfig {
|
|
|
163
173
|
chains?: string[]; // Multiple chains
|
|
164
174
|
description?: string; // Human-readable description
|
|
165
175
|
}
|
|
176
|
+
|
|
177
|
+
interface PincerPayPaymentInfo {
|
|
178
|
+
payer: string; // Verified agent wallet (Solana base58 or EVM 0x-hex)
|
|
179
|
+
transaction: string; // Settlement transaction hash
|
|
180
|
+
network: string; // CAIP-2 network id
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
type PincerPayContextVariables = {
|
|
184
|
+
pincerpay: PincerPayPaymentInfo;
|
|
185
|
+
};
|
|
166
186
|
```
|
|
167
187
|
|
|
168
188
|
### Utility Functions
|
|
@@ -184,7 +204,7 @@ getUsdcAsset("base"); // USDC contract address for Base
|
|
|
184
204
|
### Multi-chain pricing
|
|
185
205
|
|
|
186
206
|
```typescript
|
|
187
|
-
|
|
207
|
+
createPincerPayMiddleware({
|
|
188
208
|
apiKey: process.env.PINCERPAY_API_KEY!,
|
|
189
209
|
merchantAddress: "YOUR_ADDRESS",
|
|
190
210
|
routes: {
|
|
@@ -202,7 +222,7 @@ pincerpay({
|
|
|
202
222
|
Routes not listed in `routes` pass through without payment. Only matching `METHOD /path` patterns trigger the 402 paywall.
|
|
203
223
|
|
|
204
224
|
```typescript
|
|
205
|
-
|
|
225
|
+
createPincerPayMiddleware({
|
|
206
226
|
apiKey: process.env.PINCERPAY_API_KEY!,
|
|
207
227
|
merchantAddress: "YOUR_ADDRESS",
|
|
208
228
|
routes: {
|
|
@@ -244,10 +264,10 @@ Your webhook secret is in the [dashboard settings](https://www.pincerpay.com/das
|
|
|
244
264
|
|
|
245
265
|
```typescript
|
|
246
266
|
// Bad
|
|
247
|
-
|
|
267
|
+
createPincerPayMiddleware({ apiKey: "pp_live_abc123...", ... });
|
|
248
268
|
|
|
249
269
|
// Good
|
|
250
|
-
|
|
270
|
+
createPincerPayMiddleware({ apiKey: process.env.PINCERPAY_API_KEY!, ... });
|
|
251
271
|
```
|
|
252
272
|
|
|
253
273
|
### Don't use the merchant SDK on the agent side
|
|
@@ -257,3 +277,7 @@ The merchant SDK is for servers accepting payments. Agents should use `@pincerpa
|
|
|
257
277
|
### Don't set price to "0"
|
|
258
278
|
|
|
259
279
|
A price of "0" will still trigger the 402 flow. If a route should be free, omit it from the `routes` config.
|
|
280
|
+
|
|
281
|
+
### Don't re-decode `X-PAYMENT` to find the payer
|
|
282
|
+
|
|
283
|
+
The verified payer is on `c.get("pincerpay").payer` after the middleware settles. Reading it from the request header bypasses verification and forces scheme-specific shape probing — see "Reading the Verified Payer" above.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export { pincerpayHono } from "./middleware/hono.js";
|
|
1
|
+
export { createPincerPayMiddleware } from "./middleware/nextjs.js";
|
|
3
2
|
export { PincerPayClient, toBaseUnits, resolveRouteChains, getUsdcAsset } from "./client.js";
|
|
4
3
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export { pincerpayHono } from "./middleware/hono.js";
|
|
1
|
+
export { createPincerPayMiddleware } from "./middleware/nextjs.js";
|
|
3
2
|
export { PincerPayClient, toBaseUnits, resolveRouteChains, getUsdcAsset } from "./client.js";
|
|
4
3
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { PincerPayConfig, PincerPayPaymentInfo, PincerPayContextVariables } from "@pincerpay/core";
|
|
2
|
+
export type { PincerPayPaymentInfo, PincerPayContextVariables };
|
|
3
|
+
/**
|
|
4
|
+
* Lightweight Next.js / Hono middleware for PincerPay x402 paywalls.
|
|
5
|
+
*
|
|
6
|
+
* Zero @x402/*, viem, @solana/kit imports — settlement is delegated
|
|
7
|
+
* entirely to the PincerPay facilitator via fetch().
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* // app/api/[...route]/route.ts
|
|
11
|
+
* import { Hono } from "hono";
|
|
12
|
+
* import { handle } from "hono/vercel";
|
|
13
|
+
* import { createPincerPayMiddleware } from "@pincerpay/merchant/nextjs";
|
|
14
|
+
*
|
|
15
|
+
* const app = new Hono().basePath("/api");
|
|
16
|
+
*
|
|
17
|
+
* app.use("*", createPincerPayMiddleware({
|
|
18
|
+
* apiKey: process.env.PINCERPAY_API_KEY!,
|
|
19
|
+
* merchantAddress: "YOUR_WALLET_ADDRESS",
|
|
20
|
+
* routes: {
|
|
21
|
+
* "GET /api/weather": { price: "0.01", chain: "solana", description: "Weather data" },
|
|
22
|
+
* },
|
|
23
|
+
* }));
|
|
24
|
+
*
|
|
25
|
+
* app.get("/weather", (c) => c.json({ temp: 72 }));
|
|
26
|
+
*
|
|
27
|
+
* export const GET = handle(app);
|
|
28
|
+
* export const POST = handle(app);
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare function createPincerPayMiddleware(config: PincerPayConfig): any;
|
|
32
|
+
//# sourceMappingURL=nextjs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nextjs.d.ts","sourceRoot":"","sources":["../../src/middleware/nextjs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,oBAAoB,EACpB,yBAAyB,EAC1B,MAAM,iBAAiB,CAAC;AASzB,YAAY,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,CAAC;AAahE;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,eAAe,GA+MzD,GAAG,CACV"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { resolveChain, DEFAULT_FACILITATOR_URL, API_KEY_HEADER, FACILITATOR_ROUTES, } from "@pincerpay/core";
|
|
2
|
+
/**
|
|
3
|
+
* Convert human-readable USDC price (e.g., "0.01") to base units (e.g., "10000").
|
|
4
|
+
*/
|
|
5
|
+
function toBaseUnits(amount) {
|
|
6
|
+
const parts = amount.split(".");
|
|
7
|
+
const whole = parts[0] ?? "0";
|
|
8
|
+
const frac = (parts[1] ?? "").padEnd(6, "0").slice(0, 6);
|
|
9
|
+
const result = BigInt(whole) * BigInt(1_000_000) + BigInt(frac);
|
|
10
|
+
return result.toString();
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Lightweight Next.js / Hono middleware for PincerPay x402 paywalls.
|
|
14
|
+
*
|
|
15
|
+
* Zero @x402/*, viem, @solana/kit imports — settlement is delegated
|
|
16
|
+
* entirely to the PincerPay facilitator via fetch().
|
|
17
|
+
*
|
|
18
|
+
* ```ts
|
|
19
|
+
* // app/api/[...route]/route.ts
|
|
20
|
+
* import { Hono } from "hono";
|
|
21
|
+
* import { handle } from "hono/vercel";
|
|
22
|
+
* import { createPincerPayMiddleware } from "@pincerpay/merchant/nextjs";
|
|
23
|
+
*
|
|
24
|
+
* const app = new Hono().basePath("/api");
|
|
25
|
+
*
|
|
26
|
+
* app.use("*", createPincerPayMiddleware({
|
|
27
|
+
* apiKey: process.env.PINCERPAY_API_KEY!,
|
|
28
|
+
* merchantAddress: "YOUR_WALLET_ADDRESS",
|
|
29
|
+
* routes: {
|
|
30
|
+
* "GET /api/weather": { price: "0.01", chain: "solana", description: "Weather data" },
|
|
31
|
+
* },
|
|
32
|
+
* }));
|
|
33
|
+
*
|
|
34
|
+
* app.get("/weather", (c) => c.json({ temp: 72 }));
|
|
35
|
+
*
|
|
36
|
+
* export const GET = handle(app);
|
|
37
|
+
* export const POST = handle(app);
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export function createPincerPayMiddleware(config) {
|
|
41
|
+
// Normalize: strip trailing /v1 if present — FACILITATOR_ROUTES already include the version prefix
|
|
42
|
+
const rawUrl = config.facilitatorUrl ?? DEFAULT_FACILITATOR_URL;
|
|
43
|
+
const facilitatorUrl = rawUrl.replace(/\/v1\/?$/, "");
|
|
44
|
+
// Warn if using devnet facilitator or default URL without explicit config
|
|
45
|
+
if (!config.facilitatorUrl) {
|
|
46
|
+
console.log("[pincerpay] Using production facilitator:", DEFAULT_FACILITATOR_URL);
|
|
47
|
+
}
|
|
48
|
+
// Fetch facilitator's supported schemes eagerly — provides correct `extra` fields
|
|
49
|
+
// (e.g., Solana feePayer = facilitator's address, EVM EIP-712 domain params)
|
|
50
|
+
const facilitatorExtraPromise = fetch(`${facilitatorUrl}${FACILITATOR_ROUTES.supported}`)
|
|
51
|
+
.then((res) => res.json())
|
|
52
|
+
.then((data) => {
|
|
53
|
+
const map = new Map();
|
|
54
|
+
for (const kind of data.kinds) {
|
|
55
|
+
if (kind.extra)
|
|
56
|
+
map.set(kind.network, kind.extra);
|
|
57
|
+
}
|
|
58
|
+
return map;
|
|
59
|
+
})
|
|
60
|
+
.catch(() => new Map());
|
|
61
|
+
const resolvedRoutes = new Map();
|
|
62
|
+
// Default extra fields per namespace (used as fallback if facilitator fetch fails)
|
|
63
|
+
function defaultExtra(namespace) {
|
|
64
|
+
return namespace === "solana"
|
|
65
|
+
? { feePayer: config.merchantAddress }
|
|
66
|
+
: { name: "USD Coin", version: "2" };
|
|
67
|
+
}
|
|
68
|
+
for (const [pattern, routeConfig] of Object.entries(config.routes)) {
|
|
69
|
+
const chains = routeConfig.chains ??
|
|
70
|
+
(routeConfig.chain ? [routeConfig.chain] : ["solana"]);
|
|
71
|
+
const accepts = chains.map((chainShorthand) => {
|
|
72
|
+
const chain = resolveChain(chainShorthand);
|
|
73
|
+
if (!chain)
|
|
74
|
+
throw new Error(`Unknown chain: ${chainShorthand}`);
|
|
75
|
+
return {
|
|
76
|
+
scheme: "exact",
|
|
77
|
+
network: chain.caip2Id,
|
|
78
|
+
amount: toBaseUnits(routeConfig.price),
|
|
79
|
+
asset: chain.usdcAddress,
|
|
80
|
+
payTo: config.merchantAddress,
|
|
81
|
+
maxTimeoutSeconds: 300,
|
|
82
|
+
extra: defaultExtra(chain.namespace),
|
|
83
|
+
};
|
|
84
|
+
});
|
|
85
|
+
resolvedRoutes.set(pattern, {
|
|
86
|
+
description: routeConfig.description ?? pattern,
|
|
87
|
+
accepts,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
// Once facilitator responds, merge its extra fields into resolved routes
|
|
91
|
+
let facilitatorExtraResolved = false;
|
|
92
|
+
facilitatorExtraPromise.then((extraMap) => {
|
|
93
|
+
if (extraMap.size === 0)
|
|
94
|
+
return;
|
|
95
|
+
for (const route of resolvedRoutes.values()) {
|
|
96
|
+
for (const accept of route.accepts) {
|
|
97
|
+
const extra = extraMap.get(accept.network);
|
|
98
|
+
if (extra)
|
|
99
|
+
accept.extra = { ...accept.extra, ...extra };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
facilitatorExtraResolved = true;
|
|
103
|
+
});
|
|
104
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
105
|
+
return (async (c, next) => {
|
|
106
|
+
const method = c.req.method;
|
|
107
|
+
const path = c.req.path;
|
|
108
|
+
const routeKey = `${method} ${path}`;
|
|
109
|
+
const route = resolvedRoutes.get(routeKey);
|
|
110
|
+
// Not a paywalled route — pass through
|
|
111
|
+
if (!route)
|
|
112
|
+
return next();
|
|
113
|
+
// Ensure facilitator extra fields are resolved before returning 402
|
|
114
|
+
if (!facilitatorExtraResolved)
|
|
115
|
+
await facilitatorExtraPromise;
|
|
116
|
+
// Check for x402 v2 payment signature header
|
|
117
|
+
const paymentHeader = c.req.header("payment-signature") ?? c.req.header("x-payment");
|
|
118
|
+
if (!paymentHeader) {
|
|
119
|
+
// Return 402 Payment Required
|
|
120
|
+
const paymentRequired = {
|
|
121
|
+
x402Version: 2,
|
|
122
|
+
error: "Payment required",
|
|
123
|
+
resource: {
|
|
124
|
+
resource: path,
|
|
125
|
+
description: route.description,
|
|
126
|
+
mimeType: "application/json",
|
|
127
|
+
},
|
|
128
|
+
accepts: route.accepts,
|
|
129
|
+
extensions: {},
|
|
130
|
+
};
|
|
131
|
+
const encoded = Buffer.from(JSON.stringify(paymentRequired)).toString("base64");
|
|
132
|
+
c.header("payment-required", encoded);
|
|
133
|
+
return c.json(paymentRequired, 402);
|
|
134
|
+
}
|
|
135
|
+
// Payment header present — settle via facilitator
|
|
136
|
+
try {
|
|
137
|
+
// x402 may send as raw JSON or base64-encoded JSON
|
|
138
|
+
let decoded;
|
|
139
|
+
try {
|
|
140
|
+
decoded = JSON.parse(paymentHeader);
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
decoded = JSON.parse(Buffer.from(paymentHeader, "base64").toString("utf-8"));
|
|
144
|
+
}
|
|
145
|
+
const paymentRequirements = decoded.accepted ?? decoded.paymentRequirements;
|
|
146
|
+
const settleRes = await fetch(`${facilitatorUrl}${FACILITATOR_ROUTES.settle}`, {
|
|
147
|
+
method: "POST",
|
|
148
|
+
headers: {
|
|
149
|
+
"content-type": "application/json",
|
|
150
|
+
[API_KEY_HEADER]: config.apiKey,
|
|
151
|
+
},
|
|
152
|
+
body: JSON.stringify({
|
|
153
|
+
paymentPayload: decoded,
|
|
154
|
+
paymentRequirements,
|
|
155
|
+
}),
|
|
156
|
+
});
|
|
157
|
+
const settle = (await settleRes.json());
|
|
158
|
+
if (!settle.success) {
|
|
159
|
+
return c.json({
|
|
160
|
+
error: "Payment settlement failed",
|
|
161
|
+
reason: settle.errorReason,
|
|
162
|
+
message: settle.errorMessage,
|
|
163
|
+
}, 402);
|
|
164
|
+
}
|
|
165
|
+
// Payment succeeded — surface verified payer on the request context
|
|
166
|
+
// so route handlers can attribute the action without re-decoding X-PAYMENT.
|
|
167
|
+
const paymentInfo = {
|
|
168
|
+
payer: settle.payer ?? "",
|
|
169
|
+
transaction: settle.transaction ?? "",
|
|
170
|
+
network: settle.network ?? "",
|
|
171
|
+
};
|
|
172
|
+
c.set("pincerpay", paymentInfo);
|
|
173
|
+
// Attach settlement response header (clients that bypass middleware
|
|
174
|
+
// can read the canonical verified payer here too).
|
|
175
|
+
const settleResponse = {
|
|
176
|
+
x402Version: 2,
|
|
177
|
+
success: true,
|
|
178
|
+
transaction: paymentInfo.transaction,
|
|
179
|
+
network: paymentInfo.network,
|
|
180
|
+
payer: paymentInfo.payer,
|
|
181
|
+
};
|
|
182
|
+
c.header("payment-response", Buffer.from(JSON.stringify(settleResponse)).toString("base64"));
|
|
183
|
+
return next();
|
|
184
|
+
}
|
|
185
|
+
catch (err) {
|
|
186
|
+
console.error("[pincerpay] settlement error:", err);
|
|
187
|
+
return c.json({ error: "Payment processing failed", detail: String(err) }, 500);
|
|
188
|
+
}
|
|
189
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=nextjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nextjs.js","sourceRoot":"","sources":["../../src/middleware/nextjs.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pincerpay/merchant",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "Merchant SDK for PincerPay.
|
|
5
|
+
"description": "Merchant SDK for PincerPay. Lightweight Next.js middleware for accepting USDC payments from AI agents via x402.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"solana",
|
|
26
26
|
"merchant-sdk",
|
|
27
27
|
"middleware",
|
|
28
|
-
"
|
|
28
|
+
"nextjs",
|
|
29
29
|
"hono",
|
|
30
30
|
"typescript",
|
|
31
31
|
"web3",
|
|
@@ -48,47 +48,20 @@
|
|
|
48
48
|
"types": "./dist/index.d.ts",
|
|
49
49
|
"import": "./dist/index.js"
|
|
50
50
|
},
|
|
51
|
-
"./
|
|
52
|
-
"types": "./dist/middleware/
|
|
53
|
-
"import": "./dist/middleware/
|
|
54
|
-
},
|
|
55
|
-
"./hono": {
|
|
56
|
-
"types": "./dist/middleware/hono.d.ts",
|
|
57
|
-
"import": "./dist/middleware/hono.js"
|
|
51
|
+
"./nextjs": {
|
|
52
|
+
"types": "./dist/middleware/nextjs.d.ts",
|
|
53
|
+
"import": "./dist/middleware/nextjs.js"
|
|
58
54
|
}
|
|
59
55
|
},
|
|
60
56
|
"dependencies": {
|
|
61
|
-
"@x402/core": "^2.6.0",
|
|
62
|
-
"@x402/evm": "^2.6.0",
|
|
63
|
-
"@x402/svm": "^2.6.0",
|
|
64
57
|
"zod": "^3.24",
|
|
65
|
-
"@pincerpay/core": "0.
|
|
58
|
+
"@pincerpay/core": "0.5.1"
|
|
66
59
|
},
|
|
67
60
|
"peerDependencies": {
|
|
68
|
-
"@x402/express": "^2.3",
|
|
69
|
-
"@x402/hono": "^2.3",
|
|
70
|
-
"express": "^4 || ^5",
|
|
71
61
|
"hono": "^4"
|
|
72
62
|
},
|
|
73
|
-
"peerDependenciesMeta": {
|
|
74
|
-
"@x402/express": {
|
|
75
|
-
"optional": true
|
|
76
|
-
},
|
|
77
|
-
"@x402/hono": {
|
|
78
|
-
"optional": true
|
|
79
|
-
},
|
|
80
|
-
"express": {
|
|
81
|
-
"optional": true
|
|
82
|
-
},
|
|
83
|
-
"hono": {
|
|
84
|
-
"optional": true
|
|
85
|
-
}
|
|
86
|
-
},
|
|
87
63
|
"devDependencies": {
|
|
88
|
-
"@types/
|
|
89
|
-
"@x402/express": "^2.6.0",
|
|
90
|
-
"@x402/hono": "^2.6.0",
|
|
91
|
-
"express": "^5",
|
|
64
|
+
"@types/node": "^22",
|
|
92
65
|
"hono": "^4.12.0",
|
|
93
66
|
"typescript": "^5.7"
|
|
94
67
|
},
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { PincerPayConfig } from "@pincerpay/core";
|
|
2
|
-
import { type PaywallConfig } from "@x402/express";
|
|
3
|
-
/**
|
|
4
|
-
* Express middleware factory — the dead-simple API from the plan:
|
|
5
|
-
*
|
|
6
|
-
* ```ts
|
|
7
|
-
* app.use(pincerpay({
|
|
8
|
-
* apiKey: process.env.PINCERPAY_API_KEY!,
|
|
9
|
-
* merchantAddress: "0xYourAddress",
|
|
10
|
-
* routes: {
|
|
11
|
-
* "GET /api/weather": { price: "0.01", chain: "base", description: "Weather data" },
|
|
12
|
-
* },
|
|
13
|
-
* }));
|
|
14
|
-
* ```
|
|
15
|
-
*/
|
|
16
|
-
export declare function pincerpay(config: PincerPayConfig, paywallConfig?: PaywallConfig): (req: import("express").Request, res: import("express").Response, next: import("express").NextFunction) => Promise<void>;
|
|
17
|
-
export { PincerPayClient } from "../client.js";
|
|
18
|
-
//# sourceMappingURL=express.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"express.d.ts","sourceRoot":"","sources":["../../src/middleware/express.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEvD,OAAO,EAEL,KAAK,aAAa,EACnB,MAAM,eAAe,CAAC;AAMvB;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,eAAe,EAAE,aAAa,CAAC,EAAE,aAAa,4HA+D/E;AAED,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { resolveChain, DEFAULT_FACILITATOR_URL } from "@pincerpay/core";
|
|
2
|
-
import { paymentMiddlewareFromConfig, } from "@x402/express";
|
|
3
|
-
import { HTTPFacilitatorClient } from "@x402/core/server";
|
|
4
|
-
import { ExactEvmScheme } from "@x402/evm/exact/server";
|
|
5
|
-
import { ExactSvmScheme } from "@x402/svm/exact/server";
|
|
6
|
-
/**
|
|
7
|
-
* Express middleware factory — the dead-simple API from the plan:
|
|
8
|
-
*
|
|
9
|
-
* ```ts
|
|
10
|
-
* app.use(pincerpay({
|
|
11
|
-
* apiKey: process.env.PINCERPAY_API_KEY!,
|
|
12
|
-
* merchantAddress: "0xYourAddress",
|
|
13
|
-
* routes: {
|
|
14
|
-
* "GET /api/weather": { price: "0.01", chain: "base", description: "Weather data" },
|
|
15
|
-
* },
|
|
16
|
-
* }));
|
|
17
|
-
* ```
|
|
18
|
-
*/
|
|
19
|
-
export function pincerpay(config, paywallConfig) {
|
|
20
|
-
const facilitatorUrl = config.facilitatorUrl ?? DEFAULT_FACILITATOR_URL;
|
|
21
|
-
// Build x402-compatible routes config
|
|
22
|
-
// Pass price as Money (string) so the EVM server scheme handles conversion
|
|
23
|
-
// and automatically includes EIP-712 domain parameters (name, version)
|
|
24
|
-
const x402Routes = {};
|
|
25
|
-
for (const [pattern, routeConfig] of Object.entries(config.routes)) {
|
|
26
|
-
const chains = routeConfig.chains ?? (routeConfig.chain ? [routeConfig.chain] : ["solana"]);
|
|
27
|
-
const accepts = chains.map((chainShorthand) => {
|
|
28
|
-
const chain = resolveChain(chainShorthand);
|
|
29
|
-
if (!chain)
|
|
30
|
-
throw new Error(`Unknown chain: ${chainShorthand}`);
|
|
31
|
-
return {
|
|
32
|
-
scheme: "exact",
|
|
33
|
-
network: chain.caip2Id,
|
|
34
|
-
payTo: config.merchantAddress,
|
|
35
|
-
price: routeConfig.price,
|
|
36
|
-
maxTimeoutSeconds: 300,
|
|
37
|
-
};
|
|
38
|
-
});
|
|
39
|
-
x402Routes[pattern] = {
|
|
40
|
-
accepts,
|
|
41
|
-
description: routeConfig.description ?? pattern,
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
// Create facilitator client pointing to PincerPay
|
|
45
|
-
const facilitatorClient = new HTTPFacilitatorClient({
|
|
46
|
-
url: facilitatorUrl,
|
|
47
|
-
createAuthHeaders: async () => ({
|
|
48
|
-
verify: { "x-pincerpay-api-key": config.apiKey },
|
|
49
|
-
settle: { "x-pincerpay-api-key": config.apiKey },
|
|
50
|
-
supported: { "x-pincerpay-api-key": config.apiKey },
|
|
51
|
-
}),
|
|
52
|
-
});
|
|
53
|
-
// Register server schemes so the resource server can build payment requirements
|
|
54
|
-
const schemes = [
|
|
55
|
-
{ network: "eip155:*", server: new ExactEvmScheme() },
|
|
56
|
-
{ network: "solana:*", server: new ExactSvmScheme() },
|
|
57
|
-
];
|
|
58
|
-
return paymentMiddlewareFromConfig(x402Routes, facilitatorClient, schemes, paywallConfig, undefined, // paywall provider
|
|
59
|
-
config.syncFacilitatorOnStart ?? true);
|
|
60
|
-
}
|
|
61
|
-
export { PincerPayClient } from "../client.js";
|
|
62
|
-
//# sourceMappingURL=express.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"express.js","sourceRoot":"","sources":["../../src/middleware/express.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AACxE,OAAO,EACL,2BAA2B,GAE5B,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,SAAS,CAAC,MAAuB,EAAE,aAA6B;IAC9E,MAAM,cAAc,GAAG,MAAM,CAAC,cAAc,IAAI,uBAAuB,CAAC;IAExE,sCAAsC;IACtC,2EAA2E;IAC3E,uEAAuE;IACvE,MAAM,UAAU,GASX,EAAE,CAAC;IAER,KAAK,MAAM,CAAC,OAAO,EAAE,WAAW,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;QACnE,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;QAE5F,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,cAAc,EAAE,EAAE;YAC5C,MAAM,KAAK,GAAG,YAAY,CAAC,cAAc,CAAC,CAAC;YAC3C,IAAI,CAAC,KAAK;gBAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,cAAc,EAAE,CAAC,CAAC;YAEhE,OAAO;gBACL,MAAM,EAAE,OAAgB;gBACxB,OAAO,EAAE,KAAK,CAAC,OAAkB;gBACjC,KAAK,EAAE,MAAM,CAAC,eAAe;gBAC7B,KAAK,EAAE,WAAW,CAAC,KAAK;gBACxB,iBAAiB,EAAE,GAAG;aACvB,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,UAAU,CAAC,OAAO,CAAC,GAAG;YACpB,OAAO;YACP,WAAW,EAAE,WAAW,CAAC,WAAW,IAAI,OAAO;SAChD,CAAC;IACJ,CAAC;IAED,kDAAkD;IAClD,MAAM,iBAAiB,GAAG,IAAI,qBAAqB,CAAC;QAClD,GAAG,EAAE,cAAc;QACnB,iBAAiB,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;YAC9B,MAAM,EAAE,EAAE,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE;YAChD,MAAM,EAAE,EAAE,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE;YAChD,SAAS,EAAE,EAAE,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE;SACpD,CAAC;KACH,CAAC,CAAC;IAEH,gFAAgF;IAChF,MAAM,OAAO,GAAG;QACd,EAAE,OAAO,EAAE,UAAqB,EAAE,MAAM,EAAE,IAAI,cAAc,EAAE,EAAE;QAChE,EAAE,OAAO,EAAE,UAAqB,EAAE,MAAM,EAAE,IAAI,cAAc,EAAE,EAAE;KACjE,CAAC;IAEF,OAAO,2BAA2B,CAChC,UAAU,EACV,iBAAiB,EACjB,OAAO,EACP,aAAa,EACb,SAAS,EAAE,mBAAmB;IAC9B,MAAM,CAAC,sBAAsB,IAAI,IAAI,CACtC,CAAC;AACJ,CAAC;AAED,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { PincerPayConfig } from "@pincerpay/core";
|
|
2
|
-
import { type PaywallConfig } from "@x402/hono";
|
|
3
|
-
/**
|
|
4
|
-
* Hono middleware factory for PincerPay merchants.
|
|
5
|
-
*
|
|
6
|
-
* ```ts
|
|
7
|
-
* app.use("*", pincerpayHono({
|
|
8
|
-
* apiKey: process.env.PINCERPAY_API_KEY!,
|
|
9
|
-
* merchantAddress: "0xYourAddress",
|
|
10
|
-
* routes: {
|
|
11
|
-
* "GET /api/weather": { price: "0.01", chain: "base", description: "Weather data" },
|
|
12
|
-
* },
|
|
13
|
-
* }));
|
|
14
|
-
* ```
|
|
15
|
-
*/
|
|
16
|
-
export declare function pincerpayHono(config: PincerPayConfig, paywallConfig?: PaywallConfig): import("hono").MiddlewareHandler;
|
|
17
|
-
export { PincerPayClient } from "../client.js";
|
|
18
|
-
//# sourceMappingURL=hono.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hono.d.ts","sourceRoot":"","sources":["../../src/middleware/hono.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEvD,OAAO,EAEL,KAAK,aAAa,EACnB,MAAM,YAAY,CAAC;AAMpB;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,eAAe,EAAE,aAAa,CAAC,EAAE,aAAa,oCA+DnF;AAED,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/middleware/hono.js
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { resolveChain, DEFAULT_FACILITATOR_URL } from "@pincerpay/core";
|
|
2
|
-
import { paymentMiddlewareFromConfig, } from "@x402/hono";
|
|
3
|
-
import { HTTPFacilitatorClient } from "@x402/core/server";
|
|
4
|
-
import { ExactEvmScheme } from "@x402/evm/exact/server";
|
|
5
|
-
import { ExactSvmScheme } from "@x402/svm/exact/server";
|
|
6
|
-
/**
|
|
7
|
-
* Hono middleware factory for PincerPay merchants.
|
|
8
|
-
*
|
|
9
|
-
* ```ts
|
|
10
|
-
* app.use("*", pincerpayHono({
|
|
11
|
-
* apiKey: process.env.PINCERPAY_API_KEY!,
|
|
12
|
-
* merchantAddress: "0xYourAddress",
|
|
13
|
-
* routes: {
|
|
14
|
-
* "GET /api/weather": { price: "0.01", chain: "base", description: "Weather data" },
|
|
15
|
-
* },
|
|
16
|
-
* }));
|
|
17
|
-
* ```
|
|
18
|
-
*/
|
|
19
|
-
export function pincerpayHono(config, paywallConfig) {
|
|
20
|
-
const facilitatorUrl = config.facilitatorUrl ?? DEFAULT_FACILITATOR_URL;
|
|
21
|
-
// Build x402-compatible routes config
|
|
22
|
-
// Pass price as Money (string) so the EVM server scheme handles conversion
|
|
23
|
-
// and automatically includes EIP-712 domain parameters (name, version)
|
|
24
|
-
const x402Routes = {};
|
|
25
|
-
for (const [pattern, routeConfig] of Object.entries(config.routes)) {
|
|
26
|
-
const chains = routeConfig.chains ?? (routeConfig.chain ? [routeConfig.chain] : ["solana"]);
|
|
27
|
-
const accepts = chains.map((chainShorthand) => {
|
|
28
|
-
const chain = resolveChain(chainShorthand);
|
|
29
|
-
if (!chain)
|
|
30
|
-
throw new Error(`Unknown chain: ${chainShorthand}`);
|
|
31
|
-
return {
|
|
32
|
-
scheme: "exact",
|
|
33
|
-
network: chain.caip2Id,
|
|
34
|
-
payTo: config.merchantAddress,
|
|
35
|
-
price: routeConfig.price,
|
|
36
|
-
maxTimeoutSeconds: 300,
|
|
37
|
-
};
|
|
38
|
-
});
|
|
39
|
-
x402Routes[pattern] = {
|
|
40
|
-
accepts,
|
|
41
|
-
description: routeConfig.description ?? pattern,
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
// Create facilitator client pointing to PincerPay
|
|
45
|
-
const facilitatorClient = new HTTPFacilitatorClient({
|
|
46
|
-
url: facilitatorUrl,
|
|
47
|
-
createAuthHeaders: async () => ({
|
|
48
|
-
verify: { "x-pincerpay-api-key": config.apiKey },
|
|
49
|
-
settle: { "x-pincerpay-api-key": config.apiKey },
|
|
50
|
-
supported: { "x-pincerpay-api-key": config.apiKey },
|
|
51
|
-
}),
|
|
52
|
-
});
|
|
53
|
-
// Register server schemes so the resource server can build payment requirements
|
|
54
|
-
const schemes = [
|
|
55
|
-
{ network: "eip155:*", server: new ExactEvmScheme() },
|
|
56
|
-
{ network: "solana:*", server: new ExactSvmScheme() },
|
|
57
|
-
];
|
|
58
|
-
return paymentMiddlewareFromConfig(x402Routes, facilitatorClient, schemes, paywallConfig, undefined, // paywall provider
|
|
59
|
-
config.syncFacilitatorOnStart ?? true);
|
|
60
|
-
}
|
|
61
|
-
export { PincerPayClient } from "../client.js";
|
|
62
|
-
//# sourceMappingURL=hono.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hono.js","sourceRoot":"","sources":["../../src/middleware/hono.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AACxE,OAAO,EACL,2BAA2B,GAE5B,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,aAAa,CAAC,MAAuB,EAAE,aAA6B;IAClF,MAAM,cAAc,GAAG,MAAM,CAAC,cAAc,IAAI,uBAAuB,CAAC;IAExE,sCAAsC;IACtC,2EAA2E;IAC3E,uEAAuE;IACvE,MAAM,UAAU,GASX,EAAE,CAAC;IAER,KAAK,MAAM,CAAC,OAAO,EAAE,WAAW,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;QACnE,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;QAE5F,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,cAAc,EAAE,EAAE;YAC5C,MAAM,KAAK,GAAG,YAAY,CAAC,cAAc,CAAC,CAAC;YAC3C,IAAI,CAAC,KAAK;gBAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,cAAc,EAAE,CAAC,CAAC;YAEhE,OAAO;gBACL,MAAM,EAAE,OAAgB;gBACxB,OAAO,EAAE,KAAK,CAAC,OAAkB;gBACjC,KAAK,EAAE,MAAM,CAAC,eAAe;gBAC7B,KAAK,EAAE,WAAW,CAAC,KAAK;gBACxB,iBAAiB,EAAE,GAAG;aACvB,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,UAAU,CAAC,OAAO,CAAC,GAAG;YACpB,OAAO;YACP,WAAW,EAAE,WAAW,CAAC,WAAW,IAAI,OAAO;SAChD,CAAC;IACJ,CAAC;IAED,kDAAkD;IAClD,MAAM,iBAAiB,GAAG,IAAI,qBAAqB,CAAC;QAClD,GAAG,EAAE,cAAc;QACnB,iBAAiB,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;YAC9B,MAAM,EAAE,EAAE,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE;YAChD,MAAM,EAAE,EAAE,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE;YAChD,SAAS,EAAE,EAAE,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE;SACpD,CAAC;KACH,CAAC,CAAC;IAEH,gFAAgF;IAChF,MAAM,OAAO,GAAG;QACd,EAAE,OAAO,EAAE,UAAqB,EAAE,MAAM,EAAE,IAAI,cAAc,EAAE,EAAE;QAChE,EAAE,OAAO,EAAE,UAAqB,EAAE,MAAM,EAAE,IAAI,cAAc,EAAE,EAAE;KACjE,CAAC;IAEF,OAAO,2BAA2B,CAChC,UAAU,EACV,iBAAiB,EACjB,OAAO,EACP,aAAa,EACb,SAAS,EAAE,mBAAmB;IAC9B,MAAM,CAAC,sBAAsB,IAAI,IAAI,CACtC,CAAC;AACJ,CAAC;AAED,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC"}
|