paygate 0.0.1 → 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/README.md +127 -0
- package/dist/core.d.ts +95 -0
- package/dist/core.d.ts.map +1 -0
- package/dist/core.js +182 -0
- package/dist/core.js.map +1 -0
- package/dist/express.d.ts +36 -0
- package/dist/express.d.ts.map +1 -0
- package/dist/express.js +41 -0
- package/dist/express.js.map +1 -0
- package/dist/fastify.d.ts +44 -0
- package/dist/fastify.d.ts.map +1 -0
- package/dist/fastify.js +51 -0
- package/dist/fastify.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/package.json +78 -1
- package/index.js +0 -1
package/README.md
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# paygate
|
|
2
|
+
|
|
3
|
+
Gate your API with USDC micropayments in one line of code. AI agents pay per-request using the [x402 protocol](https://x402.org) — **no accounts, no API keys, no subscriptions needed.**
|
|
4
|
+
|
|
5
|
+
Works with Express and Fastify. Settlement and gas sponsorship handled by ArisPay.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install paygate
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Express
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
import express from 'express';
|
|
17
|
+
import { paygate } from 'paygate/express';
|
|
18
|
+
|
|
19
|
+
const app = express();
|
|
20
|
+
const pw = paygate({ wallet: '0xYourUSDCWallet' });
|
|
21
|
+
|
|
22
|
+
// $0.10 per request
|
|
23
|
+
app.get('/api/data', pw({ price: 0.10 }), (req, res) => {
|
|
24
|
+
res.json({ data: 'premium content' });
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Dynamic pricing
|
|
28
|
+
app.post('/api/analyze', pw({
|
|
29
|
+
price: (req) => req.body.depth === 'deep' ? 0.50 : 0.10,
|
|
30
|
+
description: 'AI analysis',
|
|
31
|
+
}), (req, res) => {
|
|
32
|
+
res.json({ result: '...' });
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
app.listen(3000);
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Fastify
|
|
39
|
+
|
|
40
|
+
```js
|
|
41
|
+
import Fastify from 'fastify';
|
|
42
|
+
import paygate from 'paygate/fastify';
|
|
43
|
+
|
|
44
|
+
const app = Fastify();
|
|
45
|
+
|
|
46
|
+
await app.register(paygate, { wallet: '0xYourUSDCWallet' });
|
|
47
|
+
|
|
48
|
+
// Auto-paywall via route config
|
|
49
|
+
app.get('/api/data', {
|
|
50
|
+
config: { x402: { price: 0.10 } }
|
|
51
|
+
}, async (req, reply) => {
|
|
52
|
+
reply.send({ data: 'premium content' });
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Or imperative API
|
|
56
|
+
app.get('/api/research', async (req, reply) => {
|
|
57
|
+
const { paid } = await req.x402Pay({ price: 0.05, description: 'Research query' });
|
|
58
|
+
if (!paid) return; // 402 challenge already sent
|
|
59
|
+
reply.send({ results: '...' });
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
await app.listen({ port: 3000 });
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## How it works
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
Agent Your API ArisPay Facilitator
|
|
69
|
+
│ │ │
|
|
70
|
+
├─── GET /api/data ──────►│ │
|
|
71
|
+
│ │ (no X-Payment header) │
|
|
72
|
+
│◄── 402 + requirements ──┤ │
|
|
73
|
+
│ │ │
|
|
74
|
+
│ (agent signs EIP-3009 USDC transfer) │
|
|
75
|
+
│ │ │
|
|
76
|
+
├─── GET /api/data ──────►│ │
|
|
77
|
+
│ + X-Payment header ├── POST /settle ────────►│
|
|
78
|
+
│ │ │ verify + settle
|
|
79
|
+
│ │◄── { success, txHash } ─┤ sponsor gas
|
|
80
|
+
│◄── 200 + data ──────────┤ │
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The agent SDKs ([payagent](https://npmjs.com/package/payagent)) handle the signing automatically — agents don't need to understand the protocol.
|
|
84
|
+
|
|
85
|
+
## Config
|
|
86
|
+
|
|
87
|
+
| Option | Required | Default | Description |
|
|
88
|
+
|---|---|---|---|
|
|
89
|
+
| `wallet` | Yes | — | Your USDC wallet address |
|
|
90
|
+
| `network` | No | `eip155:84532` | CAIP-2 network ID (Base Sepolia testnet) |
|
|
91
|
+
| `asset` | No | Auto | USDC contract address |
|
|
92
|
+
| `facilitatorUrl` | No | ArisPay production | Override the settlement facilitator |
|
|
93
|
+
| `timeout` | No | `30000` | Facilitator call timeout (ms) |
|
|
94
|
+
|
|
95
|
+
## Networks
|
|
96
|
+
|
|
97
|
+
| Network | ID | Status |
|
|
98
|
+
|---|---|---|
|
|
99
|
+
| Base Sepolia | `eip155:84532` | **Default (testnet)** |
|
|
100
|
+
| Base Mainnet | `eip155:8453` | Production |
|
|
101
|
+
| Ethereum | `eip155:1` | Production |
|
|
102
|
+
| Polygon | `eip155:137` | Production |
|
|
103
|
+
|
|
104
|
+
## Agent-side
|
|
105
|
+
|
|
106
|
+
Agents use [payagent](https://npmjs.com/package/payagent) — a drop-in fetch wrapper that handles 402 payments automatically:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
npm install payagent
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
```js
|
|
113
|
+
import { createPayagentFetch } from 'payagent';
|
|
114
|
+
|
|
115
|
+
const fetch = createPayagentFetch({
|
|
116
|
+
apiKey: 'ap_live_...', // ArisPay API key
|
|
117
|
+
agentId: 'agent-123', // Your agent ID
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// This call will automatically handle any 402 challenges
|
|
121
|
+
const res = await fetch('https://your-api.com/api/data');
|
|
122
|
+
const data = await res.json();
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## License
|
|
126
|
+
|
|
127
|
+
MIT
|
package/dist/core.d.ts
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* paygate — Core x402 paywall logic.
|
|
3
|
+
*
|
|
4
|
+
* Framework-agnostic. Express and Fastify adapters wrap this.
|
|
5
|
+
* Self-contained — no monorepo dependencies.
|
|
6
|
+
*/
|
|
7
|
+
export declare const DEFAULT_FACILITATOR = "https://paygate.arispay.app";
|
|
8
|
+
export interface PaygateConfig {
|
|
9
|
+
/**
|
|
10
|
+
* Your USDC wallet address — where payments are sent.
|
|
11
|
+
* Required.
|
|
12
|
+
*/
|
|
13
|
+
wallet: string;
|
|
14
|
+
/**
|
|
15
|
+
* CAIP-2 network ID.
|
|
16
|
+
* Default: "eip155:84532" (Base Sepolia).
|
|
17
|
+
* Production: "eip155:8453" (Base Mainnet).
|
|
18
|
+
*/
|
|
19
|
+
network?: string;
|
|
20
|
+
/**
|
|
21
|
+
* USDC contract address. Auto-derived from network if omitted.
|
|
22
|
+
*/
|
|
23
|
+
asset?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Override the default AgFac facilitator URL.
|
|
26
|
+
* Leave unset to use the production ArisPay facilitator.
|
|
27
|
+
*/
|
|
28
|
+
facilitatorUrl?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Timeout for facilitator calls in ms. Default: 30000.
|
|
31
|
+
*/
|
|
32
|
+
timeout?: number;
|
|
33
|
+
/**
|
|
34
|
+
* Rate limit for unpaid discovery probes (402 responses without payment).
|
|
35
|
+
* Agents can probe pricing without a wallet, but this prevents abuse.
|
|
36
|
+
* Default: 10 probes per 60s per IP.
|
|
37
|
+
*/
|
|
38
|
+
discoveryRateLimit?: DiscoveryRateLimitConfig;
|
|
39
|
+
}
|
|
40
|
+
export interface RoutePrice {
|
|
41
|
+
/**
|
|
42
|
+
* Price in USDC (e.g. 0.10 for $0.10).
|
|
43
|
+
* Can be a static number or a function for dynamic pricing.
|
|
44
|
+
*/
|
|
45
|
+
price: number | ((request: unknown) => number | Promise<number>);
|
|
46
|
+
/**
|
|
47
|
+
* Optional description shown in the 402 response.
|
|
48
|
+
* Helps agents understand what they're paying for.
|
|
49
|
+
*/
|
|
50
|
+
description?: string;
|
|
51
|
+
}
|
|
52
|
+
export interface DiscoveryRateLimitConfig {
|
|
53
|
+
maxProbes: number;
|
|
54
|
+
windowMs: number;
|
|
55
|
+
}
|
|
56
|
+
interface X402PaymentAccept {
|
|
57
|
+
scheme: string;
|
|
58
|
+
network: string;
|
|
59
|
+
maxAmountRequired: string;
|
|
60
|
+
resource: string;
|
|
61
|
+
asset: string;
|
|
62
|
+
payTo: string;
|
|
63
|
+
extra?: Record<string, string>;
|
|
64
|
+
}
|
|
65
|
+
interface FacilitatorSettleResponse {
|
|
66
|
+
success: boolean;
|
|
67
|
+
txHash?: string;
|
|
68
|
+
errorReason?: string;
|
|
69
|
+
errorMessage?: string;
|
|
70
|
+
}
|
|
71
|
+
export interface PaywallResult {
|
|
72
|
+
paid: boolean;
|
|
73
|
+
settlement?: FacilitatorSettleResponse;
|
|
74
|
+
challenge?: {
|
|
75
|
+
statusCode: 402;
|
|
76
|
+
headers: Record<string, string>;
|
|
77
|
+
body: {
|
|
78
|
+
error: string;
|
|
79
|
+
x402Version: number;
|
|
80
|
+
accepts: X402PaymentAccept[];
|
|
81
|
+
facilitator: string;
|
|
82
|
+
gasSponsored: boolean;
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
error?: {
|
|
86
|
+
statusCode: number;
|
|
87
|
+
body: {
|
|
88
|
+
error: string;
|
|
89
|
+
code: string;
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
export declare function handlePaywall(config: PaygateConfig, routePrice: RoutePrice, resourceUrl: string, paymentHeader: string | undefined, clientIp?: string): Promise<PaywallResult>;
|
|
94
|
+
export {};
|
|
95
|
+
//# sourceMappingURL=core.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,eAAO,MAAM,mBAAmB,gCAAgC,CAAC;AAajE,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,wBAAwB,CAAC;CAC/C;AAED,MAAM,WAAW,UAAU;IACzB;;;OAGG;IACH,KAAK,EAAE,MAAM,GAAG,CAAC,CAAC,OAAO,EAAE,OAAO,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IAEjE;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAID,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AA+CD,UAAU,iBAAiB;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED,UAAU,yBAAyB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAsCD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,CAAC,EAAE,yBAAyB,CAAC;IACvC,SAAS,CAAC,EAAE;QACV,UAAU,EAAE,GAAG,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAChC,IAAI,EAAE;YACJ,KAAK,EAAE,MAAM,CAAC;YACd,WAAW,EAAE,MAAM,CAAC;YACpB,OAAO,EAAE,iBAAiB,EAAE,CAAC;YAC7B,WAAW,EAAE,MAAM,CAAC;YACpB,YAAY,EAAE,OAAO,CAAC;SACvB,CAAC;KACH,CAAC;IACF,KAAK,CAAC,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;CACvE;AAID,wBAAsB,aAAa,CACjC,MAAM,EAAE,aAAa,EACrB,UAAU,EAAE,UAAU,EACtB,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,GAAG,SAAS,EACjC,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,aAAa,CAAC,CAiIxB"}
|
package/dist/core.js
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* paygate — Core x402 paywall logic.
|
|
3
|
+
*
|
|
4
|
+
* Framework-agnostic. Express and Fastify adapters wrap this.
|
|
5
|
+
* Self-contained — no monorepo dependencies.
|
|
6
|
+
*/
|
|
7
|
+
// ── Default facilitator ─────────────────────────────
|
|
8
|
+
export const DEFAULT_FACILITATOR = 'https://paygate.arispay.app';
|
|
9
|
+
// ── Known USDC addresses per network ───────────────
|
|
10
|
+
const USDC_ADDRESSES = {
|
|
11
|
+
'eip155:84532': '0x036CbD53842c5426634e7929541eC2318f3dCF7e', // Base Sepolia (default/testnet)
|
|
12
|
+
'eip155:8453': '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // Base Mainnet
|
|
13
|
+
'eip155:1': '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // Ethereum
|
|
14
|
+
'eip155:137': '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359', // Polygon
|
|
15
|
+
};
|
|
16
|
+
const DEFAULT_DISCOVERY_LIMIT = {
|
|
17
|
+
maxProbes: 10,
|
|
18
|
+
windowMs: 60_000,
|
|
19
|
+
};
|
|
20
|
+
const discoveryWindows = new Map();
|
|
21
|
+
function checkDiscoveryRateLimit(key, config = DEFAULT_DISCOVERY_LIMIT) {
|
|
22
|
+
const now = Date.now();
|
|
23
|
+
const cutoff = now - config.windowMs;
|
|
24
|
+
let timestamps = discoveryWindows.get(key);
|
|
25
|
+
if (!timestamps) {
|
|
26
|
+
timestamps = [];
|
|
27
|
+
discoveryWindows.set(key, timestamps);
|
|
28
|
+
}
|
|
29
|
+
while (timestamps.length > 0 && timestamps[0] < cutoff)
|
|
30
|
+
timestamps.shift();
|
|
31
|
+
if (timestamps.length >= config.maxProbes)
|
|
32
|
+
return false;
|
|
33
|
+
timestamps.push(now);
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
// ── Payment requirements builder ────────────────────
|
|
37
|
+
function buildPaymentRequirements(opts) {
|
|
38
|
+
return {
|
|
39
|
+
accepts: [
|
|
40
|
+
{
|
|
41
|
+
scheme: 'exact',
|
|
42
|
+
network: opts.network,
|
|
43
|
+
maxAmountRequired: opts.feeBaseUnits,
|
|
44
|
+
resource: opts.resource,
|
|
45
|
+
asset: opts.asset,
|
|
46
|
+
payTo: opts.payTo,
|
|
47
|
+
extra: { name: 'USDC', version: '2' },
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function decodePaymentHeader(header) {
|
|
53
|
+
try {
|
|
54
|
+
const decoded = Buffer.from(header, 'base64').toString('utf8');
|
|
55
|
+
return JSON.parse(decoded);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// Try raw JSON
|
|
59
|
+
return JSON.parse(header);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// ── Core paywall handler ───────────────────────────
|
|
63
|
+
export async function handlePaywall(config, routePrice, resourceUrl, paymentHeader, clientIp) {
|
|
64
|
+
const network = config.network || 'eip155:84532';
|
|
65
|
+
const asset = config.asset || USDC_ADDRESSES[network];
|
|
66
|
+
const facilitatorUrl = config.facilitatorUrl || DEFAULT_FACILITATOR;
|
|
67
|
+
if (!asset) {
|
|
68
|
+
return {
|
|
69
|
+
paid: false,
|
|
70
|
+
error: {
|
|
71
|
+
statusCode: 500,
|
|
72
|
+
body: { error: `No USDC address for network ${network}`, code: 'UNSUPPORTED_NETWORK' },
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const resolvedPrice = typeof routePrice.price === 'function'
|
|
77
|
+
? await routePrice.price(undefined)
|
|
78
|
+
: routePrice.price;
|
|
79
|
+
const priceBaseUnits = Math.round(resolvedPrice * 1e6).toString();
|
|
80
|
+
// No payment header → return 402 challenge
|
|
81
|
+
if (!paymentHeader) {
|
|
82
|
+
if (clientIp) {
|
|
83
|
+
if (!checkDiscoveryRateLimit(clientIp, config.discoveryRateLimit)) {
|
|
84
|
+
return {
|
|
85
|
+
paid: false,
|
|
86
|
+
error: {
|
|
87
|
+
statusCode: 429,
|
|
88
|
+
body: { error: 'Too many discovery requests. Try again later.', code: 'DISCOVERY_RATE_LIMITED' },
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const requirements = buildPaymentRequirements({
|
|
94
|
+
feeBaseUnits: priceBaseUnits,
|
|
95
|
+
resource: resourceUrl,
|
|
96
|
+
payTo: config.wallet,
|
|
97
|
+
asset,
|
|
98
|
+
network,
|
|
99
|
+
});
|
|
100
|
+
const requirementsStr = JSON.stringify({
|
|
101
|
+
x402Version: 1,
|
|
102
|
+
accepts: requirements.accepts,
|
|
103
|
+
}).replace(/[\r\n\0]/g, ' ');
|
|
104
|
+
return {
|
|
105
|
+
paid: false,
|
|
106
|
+
challenge: {
|
|
107
|
+
statusCode: 402,
|
|
108
|
+
headers: {
|
|
109
|
+
'X-Payment-Requirements': requirementsStr,
|
|
110
|
+
},
|
|
111
|
+
body: {
|
|
112
|
+
error: 'Payment Required',
|
|
113
|
+
x402Version: 1,
|
|
114
|
+
accepts: requirements.accepts,
|
|
115
|
+
facilitator: facilitatorUrl,
|
|
116
|
+
gasSponsored: true,
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
// Decode X-Payment header
|
|
122
|
+
let paymentPayload;
|
|
123
|
+
try {
|
|
124
|
+
paymentPayload = decodePaymentHeader(paymentHeader);
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
return {
|
|
128
|
+
paid: false,
|
|
129
|
+
error: {
|
|
130
|
+
statusCode: 400,
|
|
131
|
+
body: { error: 'Invalid X-Payment header', code: 'INVALID_PAYMENT' },
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
// Settle via facilitator
|
|
136
|
+
const accept = {
|
|
137
|
+
scheme: 'exact',
|
|
138
|
+
network,
|
|
139
|
+
maxAmountRequired: priceBaseUnits,
|
|
140
|
+
resource: resourceUrl,
|
|
141
|
+
asset,
|
|
142
|
+
payTo: config.wallet,
|
|
143
|
+
extra: { name: 'USDC', version: '2' },
|
|
144
|
+
};
|
|
145
|
+
try {
|
|
146
|
+
const res = await fetch(`${facilitatorUrl}/facilitator/settle`, {
|
|
147
|
+
method: 'POST',
|
|
148
|
+
headers: { 'Content-Type': 'application/json' },
|
|
149
|
+
body: JSON.stringify({
|
|
150
|
+
x402Version: 1,
|
|
151
|
+
paymentPayload,
|
|
152
|
+
paymentRequirements: accept,
|
|
153
|
+
}),
|
|
154
|
+
signal: AbortSignal.timeout(config.timeout || 30_000),
|
|
155
|
+
});
|
|
156
|
+
const settlement = (await res.json());
|
|
157
|
+
if (!settlement.success) {
|
|
158
|
+
return {
|
|
159
|
+
paid: false,
|
|
160
|
+
error: {
|
|
161
|
+
statusCode: 402,
|
|
162
|
+
body: {
|
|
163
|
+
error: settlement.errorMessage || settlement.errorReason || 'Payment settlement failed',
|
|
164
|
+
code: settlement.errorReason || 'SETTLEMENT_FAILED',
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
return { paid: true, settlement };
|
|
170
|
+
}
|
|
171
|
+
catch (err) {
|
|
172
|
+
const message = err instanceof Error ? err.message : 'Facilitator unreachable';
|
|
173
|
+
return {
|
|
174
|
+
paid: false,
|
|
175
|
+
error: {
|
|
176
|
+
statusCode: 502,
|
|
177
|
+
body: { error: `Facilitator error: ${message}`, code: 'FACILITATOR_ERROR' },
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=core.js.map
|
package/dist/core.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.js","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* paygate/express — Express middleware for x402 paywalls.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* import express from 'express';
|
|
6
|
+
* import { paygate } from 'paygate/express';
|
|
7
|
+
*
|
|
8
|
+
* const app = express();
|
|
9
|
+
*
|
|
10
|
+
* const pw = paygate({ wallet: '0xYourUSDCWallet' });
|
|
11
|
+
*
|
|
12
|
+
* // Static pricing:
|
|
13
|
+
* app.get('/api/data', pw({ price: 0.10 }), (req, res) => {
|
|
14
|
+
* res.json({ data: 'premium content' });
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* // Dynamic pricing:
|
|
18
|
+
* app.post('/api/search', pw({
|
|
19
|
+
* price: (req) => req.body.deep ? 0.015 : 0.007,
|
|
20
|
+
* description: 'AI-powered search',
|
|
21
|
+
* }), (req, res) => {
|
|
22
|
+
* res.json({ results: [] });
|
|
23
|
+
* });
|
|
24
|
+
*/
|
|
25
|
+
import type { RequestHandler } from 'express';
|
|
26
|
+
import { type PaygateConfig, type RoutePrice } from './core.js';
|
|
27
|
+
export type { PaygateConfig, RoutePrice };
|
|
28
|
+
/**
|
|
29
|
+
* Create a paywall middleware factory.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* const pw = paygate({ wallet: '0x...' });
|
|
33
|
+
* app.get('/api/data', pw({ price: 0.10 }), handler);
|
|
34
|
+
*/
|
|
35
|
+
export declare function paygate(config: PaygateConfig): (routePrice: RoutePrice) => RequestHandler;
|
|
36
|
+
//# sourceMappingURL=express.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"express.d.ts","sourceRoot":"","sources":["../src/express.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,KAAK,EAAmC,cAAc,EAAE,MAAM,SAAS,CAAC;AAC/E,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,UAAU,EAAiB,MAAM,WAAW,CAAC;AAE/E,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC;AAQ1C;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,aAAa,IACnB,YAAY,UAAU,KAAG,cAAc,CA6BhE"}
|
package/dist/express.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { handlePaywall } from './core.js';
|
|
2
|
+
function getClientIp(req) {
|
|
3
|
+
const forwarded = req.headers['x-forwarded-for'];
|
|
4
|
+
if (typeof forwarded === 'string')
|
|
5
|
+
return forwarded.split(',')[0].trim();
|
|
6
|
+
return req.ip || req.socket.remoteAddress || 'unknown';
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Create a paywall middleware factory.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* const pw = paygate({ wallet: '0x...' });
|
|
13
|
+
* app.get('/api/data', pw({ price: 0.10 }), handler);
|
|
14
|
+
*/
|
|
15
|
+
export function paygate(config) {
|
|
16
|
+
return function paywall(routePrice) {
|
|
17
|
+
return async (req, res, next) => {
|
|
18
|
+
const resourceUrl = `${req.protocol}://${req.get('host')}${req.originalUrl}`;
|
|
19
|
+
const paymentHeader = req.headers['x-payment'];
|
|
20
|
+
const clientIp = getClientIp(req);
|
|
21
|
+
const resolved = typeof routePrice.price === 'function'
|
|
22
|
+
? { ...routePrice, price: await routePrice.price(req) }
|
|
23
|
+
: routePrice;
|
|
24
|
+
const result = await handlePaywall(config, resolved, resourceUrl, paymentHeader, clientIp);
|
|
25
|
+
if (result.paid) {
|
|
26
|
+
req.x402 = result.settlement;
|
|
27
|
+
return next();
|
|
28
|
+
}
|
|
29
|
+
if (result.challenge) {
|
|
30
|
+
for (const [key, value] of Object.entries(result.challenge.headers)) {
|
|
31
|
+
res.setHeader(key, value);
|
|
32
|
+
}
|
|
33
|
+
return res.status(402).json(result.challenge.body);
|
|
34
|
+
}
|
|
35
|
+
if (result.error) {
|
|
36
|
+
return res.status(result.error.statusCode).json(result.error.body);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=express.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"express.js","sourceRoot":"","sources":["../src/express.ts"],"names":[],"mappings":"AAyBA,OAAO,EAAuC,aAAa,EAAE,MAAM,WAAW,CAAC;AAI/E,SAAS,WAAW,CAAC,GAAY;IAC/B,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACjD,IAAI,OAAO,SAAS,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAC;IAC1E,OAAO,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC,MAAM,CAAC,aAAa,IAAI,SAAS,CAAC;AACzD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,OAAO,CAAC,MAAqB;IAC3C,OAAO,SAAS,OAAO,CAAC,UAAsB;QAC5C,OAAO,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;YAC/D,MAAM,WAAW,GAAG,GAAG,GAAG,CAAC,QAAQ,MAAM,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;YAC7E,MAAM,aAAa,GAAG,GAAG,CAAC,OAAO,CAAC,WAAW,CAAuB,CAAC;YACrE,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;YAElC,MAAM,QAAQ,GAAe,OAAO,UAAU,CAAC,KAAK,KAAK,UAAU;gBACjE,CAAC,CAAC,EAAE,GAAG,UAAU,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;gBACvD,CAAC,CAAC,UAAU,CAAC;YAEf,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,aAAa,EAAE,QAAQ,CAAC,CAAC;YAE3F,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;gBACf,GAAW,CAAC,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC;gBACtC,OAAO,IAAI,EAAE,CAAC;YAChB,CAAC;YAED,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;gBACrB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;oBACpE,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;gBAC5B,CAAC;gBACD,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YACrD,CAAC;YAED,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;gBACjB,OAAO,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACrE,CAAC;QACH,CAAC,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* paygate/fastify — Fastify plugin for x402 paywalls.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* import Fastify from 'fastify';
|
|
6
|
+
* import paygate from 'paygate/fastify';
|
|
7
|
+
*
|
|
8
|
+
* const app = Fastify();
|
|
9
|
+
*
|
|
10
|
+
* await app.register(paygate, { wallet: '0xYourUSDCWallet' });
|
|
11
|
+
*
|
|
12
|
+
* // Static pricing via route config:
|
|
13
|
+
* app.get('/api/data', { config: { x402: { price: 0.10 } } }, async (req, reply) => {
|
|
14
|
+
* reply.send({ data: 'premium content' });
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* // Or imperative API:
|
|
18
|
+
* app.get('/api/joke', async (req, reply) => {
|
|
19
|
+
* const { paid } = await req.x402Pay({ price: 0.05 });
|
|
20
|
+
* if (!paid) return; // 402 already sent
|
|
21
|
+
* reply.send({ joke: '...' });
|
|
22
|
+
* });
|
|
23
|
+
*/
|
|
24
|
+
import type { FastifyPluginAsync } from 'fastify';
|
|
25
|
+
import { type PaygateConfig, type RoutePrice } from './core.js';
|
|
26
|
+
export type { PaygateConfig, RoutePrice };
|
|
27
|
+
declare module 'fastify' {
|
|
28
|
+
interface FastifyRequest {
|
|
29
|
+
x402?: {
|
|
30
|
+
paid: boolean;
|
|
31
|
+
txHash?: string;
|
|
32
|
+
};
|
|
33
|
+
x402Pay: (routePrice: RoutePrice) => Promise<{
|
|
34
|
+
paid: boolean;
|
|
35
|
+
}>;
|
|
36
|
+
}
|
|
37
|
+
interface FastifyContextConfig {
|
|
38
|
+
x402?: RoutePrice;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
declare const paygatePlugin: FastifyPluginAsync<PaygateConfig>;
|
|
42
|
+
export default paygatePlugin;
|
|
43
|
+
export { paygatePlugin };
|
|
44
|
+
//# sourceMappingURL=fastify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fastify.d.ts","sourceRoot":"","sources":["../src/fastify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,KAAK,EAEV,kBAAkB,EAGnB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,UAAU,EAAiB,MAAM,WAAW,CAAC;AAE/E,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC;AAE1C,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,cAAc;QACtB,IAAI,CAAC,EAAE;YACL,IAAI,EAAE,OAAO,CAAC;YACd,MAAM,CAAC,EAAE,MAAM,CAAC;SACjB,CAAC;QACF,OAAO,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,OAAO,CAAC;YAAE,IAAI,EAAE,OAAO,CAAA;SAAE,CAAC,CAAC;KACjE;IACD,UAAU,oBAAoB;QAC5B,IAAI,CAAC,EAAE,UAAU,CAAC;KACnB;CACF;AAcD,QAAA,MAAM,aAAa,EAAE,kBAAkB,CAAC,aAAa,CA2CpD,CAAC;AAEF,eAAe,aAAa,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,CAAC"}
|
package/dist/fastify.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { handlePaywall } from './core.js';
|
|
2
|
+
function getClientIp(request) {
|
|
3
|
+
const forwarded = request.headers['x-forwarded-for'];
|
|
4
|
+
if (typeof forwarded === 'string')
|
|
5
|
+
return forwarded.split(',')[0].trim();
|
|
6
|
+
return request.ip;
|
|
7
|
+
}
|
|
8
|
+
async function resolvePrice(routePrice, request) {
|
|
9
|
+
if (typeof routePrice.price !== 'function')
|
|
10
|
+
return routePrice;
|
|
11
|
+
const resolved = await routePrice.price(request);
|
|
12
|
+
return { ...routePrice, price: resolved };
|
|
13
|
+
}
|
|
14
|
+
const paygatePlugin = async (fastify, config) => {
|
|
15
|
+
fastify.decorateRequest('x402', undefined);
|
|
16
|
+
fastify.decorateRequest('x402Pay', (async (_routePrice) => ({ paid: false })));
|
|
17
|
+
fastify.addHook('onRequest', async (request, reply) => {
|
|
18
|
+
const clientIp = getClientIp(request);
|
|
19
|
+
request.x402Pay = async (routePrice) => {
|
|
20
|
+
const resourceUrl = `${request.protocol}://${request.hostname}${request.url}`;
|
|
21
|
+
const paymentHeader = request.headers['x-payment'];
|
|
22
|
+
const resolved = await resolvePrice(routePrice, request);
|
|
23
|
+
const result = await handlePaywall(config, resolved, resourceUrl, paymentHeader, clientIp);
|
|
24
|
+
if (result.paid) {
|
|
25
|
+
request.x402 = { paid: true, txHash: result.settlement?.txHash };
|
|
26
|
+
return { paid: true };
|
|
27
|
+
}
|
|
28
|
+
if (result.challenge) {
|
|
29
|
+
for (const [key, value] of Object.entries(result.challenge.headers)) {
|
|
30
|
+
reply.header(key, value);
|
|
31
|
+
}
|
|
32
|
+
reply.status(402).send(result.challenge.body);
|
|
33
|
+
return { paid: false };
|
|
34
|
+
}
|
|
35
|
+
if (result.error) {
|
|
36
|
+
reply.status(result.error.statusCode).send(result.error.body);
|
|
37
|
+
return { paid: false };
|
|
38
|
+
}
|
|
39
|
+
return { paid: false };
|
|
40
|
+
};
|
|
41
|
+
const routePrice = request.routeOptions?.config?.x402;
|
|
42
|
+
if (routePrice) {
|
|
43
|
+
const { paid } = await request.x402Pay(routePrice);
|
|
44
|
+
if (!paid)
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
export default paygatePlugin;
|
|
50
|
+
export { paygatePlugin };
|
|
51
|
+
//# sourceMappingURL=fastify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fastify.js","sourceRoot":"","sources":["../src/fastify.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* paygate — x402 paywall middleware for Express and Fastify.
|
|
3
|
+
*
|
|
4
|
+
* Gate your API with USDC micropayments in one line of code.
|
|
5
|
+
* AI agents pay per-request — no accounts, no API keys needed.
|
|
6
|
+
*
|
|
7
|
+
* Express:
|
|
8
|
+
* import { paygate } from 'paygate/express';
|
|
9
|
+
* const pw = paygate({ wallet: '0xYourWallet' });
|
|
10
|
+
* app.get('/api/data', pw({ price: 0.10 }), handler);
|
|
11
|
+
*
|
|
12
|
+
* Fastify:
|
|
13
|
+
* import paygate from 'paygate/fastify';
|
|
14
|
+
* await app.register(paygate, { wallet: '0xYourWallet' });
|
|
15
|
+
* app.get('/api/data', { config: { x402: { price: 0.10 } } }, handler);
|
|
16
|
+
*/
|
|
17
|
+
export { handlePaywall, DEFAULT_FACILITATOR } from './core.js';
|
|
18
|
+
export type { PaygateConfig, RoutePrice, PaywallResult, DiscoveryRateLimitConfig } from './core.js';
|
|
19
|
+
export { paygate } from './express.js';
|
|
20
|
+
export { paygatePlugin } from './fastify.js';
|
|
21
|
+
export { default as paygatePluginDefault } from './fastify.js';
|
|
22
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAC/D,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,aAAa,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAGpG,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* paygate — x402 paywall middleware for Express and Fastify.
|
|
3
|
+
*
|
|
4
|
+
* Gate your API with USDC micropayments in one line of code.
|
|
5
|
+
* AI agents pay per-request — no accounts, no API keys needed.
|
|
6
|
+
*
|
|
7
|
+
* Express:
|
|
8
|
+
* import { paygate } from 'paygate/express';
|
|
9
|
+
* const pw = paygate({ wallet: '0xYourWallet' });
|
|
10
|
+
* app.get('/api/data', pw({ price: 0.10 }), handler);
|
|
11
|
+
*
|
|
12
|
+
* Fastify:
|
|
13
|
+
* import paygate from 'paygate/fastify';
|
|
14
|
+
* await app.register(paygate, { wallet: '0xYourWallet' });
|
|
15
|
+
* app.get('/api/data', { config: { x402: { price: 0.10 } } }, handler);
|
|
16
|
+
*/
|
|
17
|
+
// Core (framework-agnostic)
|
|
18
|
+
export { handlePaywall, DEFAULT_FACILITATOR } from './core.js';
|
|
19
|
+
// Express
|
|
20
|
+
export { paygate } from './express.js';
|
|
21
|
+
// Fastify
|
|
22
|
+
export { paygatePlugin } from './fastify.js';
|
|
23
|
+
export { default as paygatePluginDefault } from './fastify.js';
|
|
24
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,4BAA4B;AAC5B,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAG/D,UAAU;AACV,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,UAAU;AACV,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,cAAc,CAAC"}
|
package/package.json
CHANGED
|
@@ -1 +1,78 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
|
+
"name": "paygate",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Gate your API with USDC micropayments in one line. Add x402 payment enforcement to any Express or Fastify server — agents pay per-request, no accounts needed.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
},
|
|
13
|
+
"./express": {
|
|
14
|
+
"import": "./dist/express.js",
|
|
15
|
+
"types": "./dist/express.d.ts"
|
|
16
|
+
},
|
|
17
|
+
"./fastify": {
|
|
18
|
+
"import": "./dist/fastify.js",
|
|
19
|
+
"types": "./dist/fastify.d.ts"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsc",
|
|
24
|
+
"dev": "tsc --watch",
|
|
25
|
+
"prepublishOnly": "pnpm build"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"ethers": "^6.13.0"
|
|
29
|
+
},
|
|
30
|
+
"peerDependencies": {
|
|
31
|
+
"express": ">=4.0.0",
|
|
32
|
+
"fastify": ">=4.0.0"
|
|
33
|
+
},
|
|
34
|
+
"peerDependenciesMeta": {
|
|
35
|
+
"express": {
|
|
36
|
+
"optional": true
|
|
37
|
+
},
|
|
38
|
+
"fastify": {
|
|
39
|
+
"optional": true
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/express": "^5.0.0",
|
|
44
|
+
"@types/node": "^22.0.0",
|
|
45
|
+
"express": "^5.0.0",
|
|
46
|
+
"fastify": "^5.0.0",
|
|
47
|
+
"typescript": "^5.7.0"
|
|
48
|
+
},
|
|
49
|
+
"files": [
|
|
50
|
+
"dist",
|
|
51
|
+
"README.md"
|
|
52
|
+
],
|
|
53
|
+
"license": "MIT",
|
|
54
|
+
"keywords": [
|
|
55
|
+
"x402",
|
|
56
|
+
"paywall",
|
|
57
|
+
"micropayments",
|
|
58
|
+
"usdc",
|
|
59
|
+
"api",
|
|
60
|
+
"middleware",
|
|
61
|
+
"express",
|
|
62
|
+
"fastify",
|
|
63
|
+
"ai-agent",
|
|
64
|
+
"http-402",
|
|
65
|
+
"stablecoin",
|
|
66
|
+
"base",
|
|
67
|
+
"ethereum",
|
|
68
|
+
"polygon",
|
|
69
|
+
"web3",
|
|
70
|
+
"eip-3009",
|
|
71
|
+
"payment-gateway"
|
|
72
|
+
],
|
|
73
|
+
"homepage": "https://arispay.app",
|
|
74
|
+
"repository": {
|
|
75
|
+
"type": "git",
|
|
76
|
+
"url": "https://github.com/stevemilton/ArisPay"
|
|
77
|
+
}
|
|
78
|
+
}
|
package/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
// Reserved
|