@pincerpay/merchant 0.2.0 → 0.4.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/LICENSE +21 -21
- package/README.md +259 -233
- package/package.json +6 -6
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 PincerPay
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 PincerPay
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,233 +1,259 @@
|
|
|
1
|
-
# @pincerpay/merchant
|
|
2
|
-
|
|
3
|
-
[](https://www.npmjs.com/package/@pincerpay/merchant)
|
|
4
|
-
[](https://www.npmjs.com/package/@pincerpay/merchant)
|
|
5
|
-
[](https://github.com/ds1/pincerpay/blob/master/LICENSE)
|
|
6
|
-
[](https://www.typescriptlang.org/)
|
|
7
|
-
|
|
8
|
-
Merchant SDK for accepting on-chain USDC payments from AI agents via the [x402 protocol](https://x402.org). Supports Express, Hono, and Next.js.
|
|
9
|
-
|
|
10
|
-
> **ESM Required:** Your project must have `"type": "module"` in package.json. This package is ESM-only.
|
|
11
|
-
|
|
12
|
-
## Install
|
|
13
|
-
|
|
14
|
-
```bash
|
|
15
|
-
npm install @pincerpay/merchant
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
## Quick Start
|
|
19
|
-
|
|
20
|
-
### Express
|
|
21
|
-
|
|
22
|
-
```typescript
|
|
23
|
-
import express from "express";
|
|
24
|
-
import { pincerpay } from "@pincerpay/merchant/express";
|
|
25
|
-
|
|
26
|
-
const app = express();
|
|
27
|
-
|
|
28
|
-
app.use(
|
|
29
|
-
pincerpay({
|
|
30
|
-
apiKey: process.env.PINCERPAY_API_KEY!,
|
|
31
|
-
merchantAddress: "YOUR_SOLANA_ADDRESS",
|
|
32
|
-
routes: {
|
|
33
|
-
"GET /api/weather": {
|
|
34
|
-
price: "0.01",
|
|
35
|
-
chain: "solana",
|
|
36
|
-
description: "Current weather data",
|
|
37
|
-
},
|
|
38
|
-
"POST /api/analyze": {
|
|
39
|
-
price: "0.10",
|
|
40
|
-
chains: ["solana", "base"],
|
|
41
|
-
description: "AI text analysis",
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
})
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
app.get("/api/weather", (req, res) => {
|
|
48
|
-
res.json({ temp: 72, unit: "F" });
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
app.listen(3000);
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
### Hono
|
|
55
|
-
|
|
56
|
-
```typescript
|
|
57
|
-
import { Hono } from "hono";
|
|
58
|
-
import { pincerpayHono } from "@pincerpay/merchant/hono";
|
|
59
|
-
|
|
60
|
-
const app = new Hono();
|
|
61
|
-
|
|
62
|
-
app.use(
|
|
63
|
-
"*",
|
|
64
|
-
pincerpayHono({
|
|
65
|
-
apiKey: process.env.PINCERPAY_API_KEY!,
|
|
66
|
-
merchantAddress: "YOUR_SOLANA_ADDRESS",
|
|
67
|
-
routes: {
|
|
68
|
-
"GET /api/weather": {
|
|
69
|
-
price: "0.01",
|
|
70
|
-
chain: "solana",
|
|
71
|
-
description: "Current weather data",
|
|
72
|
-
},
|
|
73
|
-
},
|
|
74
|
-
})
|
|
75
|
-
);
|
|
76
|
-
|
|
77
|
-
app.get("/api/weather", (c) => c.json({ temp: 72 }));
|
|
78
|
-
|
|
79
|
-
export default app;
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
### Next.js (Hono Adapter)
|
|
83
|
-
|
|
84
|
-
Next.js doesn't have native x402 middleware support. Use Hono as a lightweight handler inside a catch-all App Router route:
|
|
85
|
-
|
|
86
|
-
```typescript
|
|
87
|
-
// app/api/[...route]/route.ts
|
|
88
|
-
import { Hono } from "hono";
|
|
89
|
-
import { handle } from "hono/vercel";
|
|
90
|
-
import { pincerpayHono } from "@pincerpay/merchant/hono";
|
|
91
|
-
|
|
92
|
-
const app = new Hono().basePath("/api");
|
|
93
|
-
|
|
94
|
-
app.use(
|
|
95
|
-
"*",
|
|
96
|
-
pincerpayHono({
|
|
97
|
-
apiKey: process.env.PINCERPAY_API_KEY!,
|
|
98
|
-
merchantAddress: "YOUR_SOLANA_ADDRESS",
|
|
99
|
-
syncFacilitatorOnStart: false, // Avoids build-time network call during prerendering
|
|
100
|
-
routes: {
|
|
101
|
-
"GET /api/weather": {
|
|
102
|
-
price: "0.01",
|
|
103
|
-
chain: "solana",
|
|
104
|
-
description: "Current weather data",
|
|
105
|
-
},
|
|
106
|
-
},
|
|
107
|
-
})
|
|
108
|
-
);
|
|
109
|
-
|
|
110
|
-
app.get("/weather", (c) => c.json({ temp: 72 }));
|
|
111
|
-
|
|
112
|
-
export const GET = handle(app);
|
|
113
|
-
export const POST = handle(app);
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
Install: `npm install @pincerpay/merchant hono`
|
|
117
|
-
|
|
118
|
-
> **Note:** `basePath("/api")` must match the catch-all route location. Route handlers use paths relative to basePath (`/weather` serves `/api/weather`).
|
|
119
|
-
|
|
120
|
-
## API Reference
|
|
121
|
-
|
|
122
|
-
### `pincerpay(config): Express.RequestHandler`
|
|
123
|
-
|
|
124
|
-
Express middleware that intercepts requests matching configured routes and returns HTTP 402 with x402 payment requirements.
|
|
125
|
-
|
|
126
|
-
### `pincerpayHono(config): HonoMiddleware`
|
|
127
|
-
|
|
128
|
-
Hono middleware with identical behavior. Also used for Next.js via the Hono adapter pattern (see Next.js example above).
|
|
129
|
-
|
|
130
|
-
### `PincerPayClient`
|
|
131
|
-
|
|
132
|
-
Low-level client for direct facilitator API access.
|
|
133
|
-
|
|
134
|
-
```typescript
|
|
135
|
-
import { PincerPayClient } from "@pincerpay/merchant";
|
|
136
|
-
|
|
137
|
-
const client = new PincerPayClient({
|
|
138
|
-
apiKey: process.env.PINCERPAY_API_KEY!,
|
|
139
|
-
merchantAddress: "YOUR_ADDRESS",
|
|
140
|
-
facilitatorUrl: "https://facilitator.pincerpay.com", // default
|
|
141
|
-
routes: {},
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
const result = await client.settle(paymentPayload, paymentRequirements);
|
|
145
|
-
const status = await client.getStatus(txHash);
|
|
146
|
-
const supported = await client.getSupported();
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
### Config
|
|
150
|
-
|
|
151
|
-
```typescript
|
|
152
|
-
interface PincerPayConfig {
|
|
153
|
-
apiKey: string;
|
|
154
|
-
merchantAddress: string;
|
|
155
|
-
facilitatorUrl?: string; // defaults to https://facilitator.pincerpay.com
|
|
156
|
-
routes: Record<string, RoutePaywallConfig>;
|
|
157
|
-
syncFacilitatorOnStart?: boolean; // defer facilitator sync to first request (default: false)
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
interface RoutePaywallConfig {
|
|
161
|
-
price: string; // USDC amount (e.g., "0.01")
|
|
162
|
-
chain?: string; // Chain shorthand (e.g., "solana", "base")
|
|
163
|
-
chains?: string[]; // Multiple chains
|
|
164
|
-
description?: string; // Human-readable description
|
|
165
|
-
}
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
### Utility Functions
|
|
169
|
-
|
|
170
|
-
```typescript
|
|
171
|
-
import { toBaseUnits, resolveRouteChains, getUsdcAsset } from "@pincerpay/merchant";
|
|
172
|
-
|
|
173
|
-
toBaseUnits("0.01"); // "10000" (USDC has 6 decimals)
|
|
174
|
-
toBaseUnits("1.00"); // "1000000"
|
|
175
|
-
|
|
176
|
-
resolveRouteChains(routeConfig); // ["solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1"]
|
|
177
|
-
|
|
178
|
-
getUsdcAsset("solana-devnet"); // USDC mint address for Solana devnet
|
|
179
|
-
getUsdcAsset("base"); // USDC contract address for Base
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
## Common Patterns
|
|
183
|
-
|
|
184
|
-
### Multi-chain pricing
|
|
185
|
-
|
|
186
|
-
```typescript
|
|
187
|
-
pincerpay({
|
|
188
|
-
apiKey: process.env.PINCERPAY_API_KEY!,
|
|
189
|
-
merchantAddress: "YOUR_ADDRESS",
|
|
190
|
-
routes: {
|
|
191
|
-
"GET /api/data": {
|
|
192
|
-
price: "0.05",
|
|
193
|
-
chains: ["solana", "base", "polygon"],
|
|
194
|
-
description: "Accept USDC on any supported chain",
|
|
195
|
-
},
|
|
196
|
-
},
|
|
197
|
-
});
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
### Free routes alongside paid routes
|
|
201
|
-
|
|
202
|
-
Routes not listed in `routes` pass through without payment. Only matching `METHOD /path` patterns trigger the 402 paywall.
|
|
203
|
-
|
|
204
|
-
```typescript
|
|
205
|
-
pincerpay({
|
|
206
|
-
apiKey: process.env.PINCERPAY_API_KEY!,
|
|
207
|
-
merchantAddress: "YOUR_ADDRESS",
|
|
208
|
-
routes: {
|
|
209
|
-
"GET /api/premium": { price: "1.00", chain: "solana" },
|
|
210
|
-
// GET /api/free is not listed -- no paywall
|
|
211
|
-
},
|
|
212
|
-
});
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
##
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
```typescript
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
1
|
+
# @pincerpay/merchant
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@pincerpay/merchant)
|
|
4
|
+
[](https://www.npmjs.com/package/@pincerpay/merchant)
|
|
5
|
+
[](https://github.com/ds1/pincerpay/blob/master/LICENSE)
|
|
6
|
+
[](https://www.typescriptlang.org/)
|
|
7
|
+
|
|
8
|
+
Merchant SDK for accepting on-chain USDC payments from AI agents via the [x402 protocol](https://x402.org). Supports Express, Hono, and Next.js.
|
|
9
|
+
|
|
10
|
+
> **ESM Required:** Your project must have `"type": "module"` in package.json. This package is ESM-only.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install @pincerpay/merchant
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
### Express
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import express from "express";
|
|
24
|
+
import { pincerpay } from "@pincerpay/merchant/express";
|
|
25
|
+
|
|
26
|
+
const app = express();
|
|
27
|
+
|
|
28
|
+
app.use(
|
|
29
|
+
pincerpay({
|
|
30
|
+
apiKey: process.env.PINCERPAY_API_KEY!,
|
|
31
|
+
merchantAddress: "YOUR_SOLANA_ADDRESS",
|
|
32
|
+
routes: {
|
|
33
|
+
"GET /api/weather": {
|
|
34
|
+
price: "0.01",
|
|
35
|
+
chain: "solana",
|
|
36
|
+
description: "Current weather data",
|
|
37
|
+
},
|
|
38
|
+
"POST /api/analyze": {
|
|
39
|
+
price: "0.10",
|
|
40
|
+
chains: ["solana", "base"],
|
|
41
|
+
description: "AI text analysis",
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
})
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
app.get("/api/weather", (req, res) => {
|
|
48
|
+
res.json({ temp: 72, unit: "F" });
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
app.listen(3000);
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Hono
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
import { Hono } from "hono";
|
|
58
|
+
import { pincerpayHono } from "@pincerpay/merchant/hono";
|
|
59
|
+
|
|
60
|
+
const app = new Hono();
|
|
61
|
+
|
|
62
|
+
app.use(
|
|
63
|
+
"*",
|
|
64
|
+
pincerpayHono({
|
|
65
|
+
apiKey: process.env.PINCERPAY_API_KEY!,
|
|
66
|
+
merchantAddress: "YOUR_SOLANA_ADDRESS",
|
|
67
|
+
routes: {
|
|
68
|
+
"GET /api/weather": {
|
|
69
|
+
price: "0.01",
|
|
70
|
+
chain: "solana",
|
|
71
|
+
description: "Current weather data",
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
})
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
app.get("/api/weather", (c) => c.json({ temp: 72 }));
|
|
78
|
+
|
|
79
|
+
export default app;
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Next.js (Hono Adapter)
|
|
83
|
+
|
|
84
|
+
Next.js doesn't have native x402 middleware support. Use Hono as a lightweight handler inside a catch-all App Router route:
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
// app/api/[...route]/route.ts
|
|
88
|
+
import { Hono } from "hono";
|
|
89
|
+
import { handle } from "hono/vercel";
|
|
90
|
+
import { pincerpayHono } from "@pincerpay/merchant/hono";
|
|
91
|
+
|
|
92
|
+
const app = new Hono().basePath("/api");
|
|
93
|
+
|
|
94
|
+
app.use(
|
|
95
|
+
"*",
|
|
96
|
+
pincerpayHono({
|
|
97
|
+
apiKey: process.env.PINCERPAY_API_KEY!,
|
|
98
|
+
merchantAddress: "YOUR_SOLANA_ADDRESS",
|
|
99
|
+
syncFacilitatorOnStart: false, // Avoids build-time network call during prerendering
|
|
100
|
+
routes: {
|
|
101
|
+
"GET /api/weather": {
|
|
102
|
+
price: "0.01",
|
|
103
|
+
chain: "solana",
|
|
104
|
+
description: "Current weather data",
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
})
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
app.get("/weather", (c) => c.json({ temp: 72 }));
|
|
111
|
+
|
|
112
|
+
export const GET = handle(app);
|
|
113
|
+
export const POST = handle(app);
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Install: `npm install @pincerpay/merchant hono`
|
|
117
|
+
|
|
118
|
+
> **Note:** `basePath("/api")` must match the catch-all route location. Route handlers use paths relative to basePath (`/weather` serves `/api/weather`).
|
|
119
|
+
|
|
120
|
+
## API Reference
|
|
121
|
+
|
|
122
|
+
### `pincerpay(config): Express.RequestHandler`
|
|
123
|
+
|
|
124
|
+
Express middleware that intercepts requests matching configured routes and returns HTTP 402 with x402 payment requirements.
|
|
125
|
+
|
|
126
|
+
### `pincerpayHono(config): HonoMiddleware`
|
|
127
|
+
|
|
128
|
+
Hono middleware with identical behavior. Also used for Next.js via the Hono adapter pattern (see Next.js example above).
|
|
129
|
+
|
|
130
|
+
### `PincerPayClient`
|
|
131
|
+
|
|
132
|
+
Low-level client for direct facilitator API access.
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
import { PincerPayClient } from "@pincerpay/merchant";
|
|
136
|
+
|
|
137
|
+
const client = new PincerPayClient({
|
|
138
|
+
apiKey: process.env.PINCERPAY_API_KEY!,
|
|
139
|
+
merchantAddress: "YOUR_ADDRESS",
|
|
140
|
+
facilitatorUrl: "https://facilitator.pincerpay.com", // default
|
|
141
|
+
routes: {},
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
const result = await client.settle(paymentPayload, paymentRequirements);
|
|
145
|
+
const status = await client.getStatus(txHash);
|
|
146
|
+
const supported = await client.getSupported();
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Config
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
interface PincerPayConfig {
|
|
153
|
+
apiKey: string;
|
|
154
|
+
merchantAddress: string;
|
|
155
|
+
facilitatorUrl?: string; // defaults to https://facilitator.pincerpay.com
|
|
156
|
+
routes: Record<string, RoutePaywallConfig>;
|
|
157
|
+
syncFacilitatorOnStart?: boolean; // defer facilitator sync to first request (default: false)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
interface RoutePaywallConfig {
|
|
161
|
+
price: string; // USDC amount (e.g., "0.01")
|
|
162
|
+
chain?: string; // Chain shorthand (e.g., "solana", "base")
|
|
163
|
+
chains?: string[]; // Multiple chains
|
|
164
|
+
description?: string; // Human-readable description
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Utility Functions
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
import { toBaseUnits, resolveRouteChains, getUsdcAsset } from "@pincerpay/merchant";
|
|
172
|
+
|
|
173
|
+
toBaseUnits("0.01"); // "10000" (USDC has 6 decimals)
|
|
174
|
+
toBaseUnits("1.00"); // "1000000"
|
|
175
|
+
|
|
176
|
+
resolveRouteChains(routeConfig); // ["solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1"]
|
|
177
|
+
|
|
178
|
+
getUsdcAsset("solana-devnet"); // USDC mint address for Solana devnet
|
|
179
|
+
getUsdcAsset("base"); // USDC contract address for Base
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Common Patterns
|
|
183
|
+
|
|
184
|
+
### Multi-chain pricing
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
pincerpay({
|
|
188
|
+
apiKey: process.env.PINCERPAY_API_KEY!,
|
|
189
|
+
merchantAddress: "YOUR_ADDRESS",
|
|
190
|
+
routes: {
|
|
191
|
+
"GET /api/data": {
|
|
192
|
+
price: "0.05",
|
|
193
|
+
chains: ["solana", "base", "polygon"],
|
|
194
|
+
description: "Accept USDC on any supported chain",
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Free routes alongside paid routes
|
|
201
|
+
|
|
202
|
+
Routes not listed in `routes` pass through without payment. Only matching `METHOD /path` patterns trigger the 402 paywall.
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
pincerpay({
|
|
206
|
+
apiKey: process.env.PINCERPAY_API_KEY!,
|
|
207
|
+
merchantAddress: "YOUR_ADDRESS",
|
|
208
|
+
routes: {
|
|
209
|
+
"GET /api/premium": { price: "1.00", chain: "solana" },
|
|
210
|
+
// GET /api/free is not listed -- no paywall
|
|
211
|
+
},
|
|
212
|
+
});
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Webhook Verification
|
|
216
|
+
|
|
217
|
+
PincerPay signs every webhook delivery with your webhook secret (HMAC-SHA256). Verify the `X-PincerPay-Signature` header to ensure requests are authentic:
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
import crypto from "node:crypto";
|
|
221
|
+
|
|
222
|
+
function verifyWebhook(payload: string, header: string, secret: string): boolean {
|
|
223
|
+
const parts = Object.fromEntries(
|
|
224
|
+
header.split(",").map((p) => p.split("=") as [string, string])
|
|
225
|
+
);
|
|
226
|
+
const age = Math.floor(Date.now() / 1000) - Number(parts.t);
|
|
227
|
+
if (age > 300) return false; // Reject replays > 5 min
|
|
228
|
+
|
|
229
|
+
const expected = crypto
|
|
230
|
+
.createHmac("sha256", secret)
|
|
231
|
+
.update(`${parts.t}.${payload}`)
|
|
232
|
+
.digest("hex");
|
|
233
|
+
return crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Header format: `t=<unix-timestamp>,v1=<hmac-sha256-hex>`
|
|
238
|
+
|
|
239
|
+
Your webhook secret is in the [dashboard settings](https://www.pincerpay.com/dashboard/settings). See [full docs](https://www.pincerpay.com/docs/testing) for Python examples and Express integration patterns.
|
|
240
|
+
|
|
241
|
+
## Anti-Patterns
|
|
242
|
+
|
|
243
|
+
### Don't hardcode API keys
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
// Bad
|
|
247
|
+
pincerpay({ apiKey: "pp_live_abc123...", ... });
|
|
248
|
+
|
|
249
|
+
// Good
|
|
250
|
+
pincerpay({ apiKey: process.env.PINCERPAY_API_KEY!, ... });
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Don't use the merchant SDK on the agent side
|
|
254
|
+
|
|
255
|
+
The merchant SDK is for servers accepting payments. Agents should use `@pincerpay/agent` to make payments.
|
|
256
|
+
|
|
257
|
+
### Don't set price to "0"
|
|
258
|
+
|
|
259
|
+
A price of "0" will still trigger the 402 flow. If a route should be free, omit it from the `routes` config.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pincerpay/merchant",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Merchant SDK for PincerPay. Express and Hono middleware for accepting USDC payments from AI agents.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -58,9 +58,9 @@
|
|
|
58
58
|
}
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
|
-
"@x402/core": "^2.
|
|
62
|
-
"@x402/evm": "^2.
|
|
63
|
-
"@x402/svm": "^2.
|
|
61
|
+
"@x402/core": "^2.6.0",
|
|
62
|
+
"@x402/evm": "^2.6.0",
|
|
63
|
+
"@x402/svm": "^2.6.0",
|
|
64
64
|
"zod": "^3.24",
|
|
65
65
|
"@pincerpay/core": "0.2.0"
|
|
66
66
|
},
|
|
@@ -86,8 +86,8 @@
|
|
|
86
86
|
},
|
|
87
87
|
"devDependencies": {
|
|
88
88
|
"@types/express": "^5",
|
|
89
|
-
"@x402/express": "^2.
|
|
90
|
-
"@x402/hono": "^2.
|
|
89
|
+
"@x402/express": "^2.6.0",
|
|
90
|
+
"@x402/hono": "^2.6.0",
|
|
91
91
|
"express": "^5",
|
|
92
92
|
"hono": "^4.12.0",
|
|
93
93
|
"typescript": "^5.7"
|