@relai-fi/x402 0.5.39 → 0.6.0-rc.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 +380 -21
- package/dist/index.cjs +75 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +74 -0
- package/dist/index.js.map +1 -1
- package/dist/plugins.cjs +21467 -45
- package/dist/plugins.cjs.map +1 -1
- package/dist/plugins.d.cts +2 -1
- package/dist/plugins.d.ts +2 -1
- package/dist/plugins.js +21477 -33
- package/dist/plugins.js.map +1 -1
- package/dist/relay-feedback.cjs +86 -0
- package/dist/relay-feedback.cjs.map +1 -0
- package/dist/relay-feedback.d.cts +63 -0
- package/dist/relay-feedback.d.ts +63 -0
- package/dist/relay-feedback.js +61 -0
- package/dist/relay-feedback.js.map +1 -0
- package/dist/server-Dr3JOA0-.d.ts +713 -0
- package/dist/server-t9nKvoKl.d.cts +713 -0
- package/dist/server.cjs +15 -0
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +2 -1
- package/dist/server.d.ts +2 -1
- package/dist/server.js +15 -0
- package/dist/server.js.map +1 -1
- package/package.json +10 -1
- package/dist/server-CaSmhDnd.d.ts +0 -312
- package/dist/server-CyfEHW9D.d.cts +0 -312
|
@@ -0,0 +1,713 @@
|
|
|
1
|
+
import { a as RelaiNetwork } from './types-Y9ni5XwY.cjs';
|
|
2
|
+
import './relay-feedback.cjs';
|
|
3
|
+
|
|
4
|
+
interface PluginContext {
|
|
5
|
+
/** Network for this endpoint */
|
|
6
|
+
network: RelaiNetwork;
|
|
7
|
+
/** Price in USD */
|
|
8
|
+
price: number;
|
|
9
|
+
/** Request path */
|
|
10
|
+
path: string;
|
|
11
|
+
/** HTTP method */
|
|
12
|
+
method: string;
|
|
13
|
+
}
|
|
14
|
+
interface PluginResult {
|
|
15
|
+
/** If true, skip payment and serve content for free */
|
|
16
|
+
skip?: boolean;
|
|
17
|
+
/** If true, reject the request entirely (e.g. service unhealthy) */
|
|
18
|
+
reject?: boolean;
|
|
19
|
+
/** HTTP status code when rejecting (default: 503) */
|
|
20
|
+
rejectStatus?: number;
|
|
21
|
+
/** Error message when rejecting */
|
|
22
|
+
rejectMessage?: string;
|
|
23
|
+
/** Extra response headers to set */
|
|
24
|
+
headers?: Record<string, string>;
|
|
25
|
+
/** Metadata attached to req.pluginMeta */
|
|
26
|
+
meta?: Record<string, unknown>;
|
|
27
|
+
}
|
|
28
|
+
interface RelaiPlugin {
|
|
29
|
+
/** Unique plugin name */
|
|
30
|
+
name: string;
|
|
31
|
+
/**
|
|
32
|
+
* Called before the 402 payment check.
|
|
33
|
+
* Return { skip: true } to bypass payment entirely.
|
|
34
|
+
*/
|
|
35
|
+
beforePaymentCheck?(req: any, ctx: PluginContext): Promise<PluginResult>;
|
|
36
|
+
/**
|
|
37
|
+
* Called after a successful payment settlement.
|
|
38
|
+
* Use for analytics, logging, webhooks, etc.
|
|
39
|
+
*/
|
|
40
|
+
afterSettled?(req: any, result: SettleResult, ctx: PluginContext): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Called once when the Relai instance initializes (server start).
|
|
43
|
+
* Use to sync config to RelAI backend or validate credentials.
|
|
44
|
+
*/
|
|
45
|
+
onInit?(): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Called before sending the 402 response. Allows plugins to add
|
|
48
|
+
* extensions or modify the response body (e.g. bridge info).
|
|
49
|
+
*/
|
|
50
|
+
enrich402Response?(response: any, ctx: PluginContext): any;
|
|
51
|
+
}
|
|
52
|
+
interface FreeTierPluginConfig {
|
|
53
|
+
/** Service key (sk_live_...) for syncing with RelAI backend. If omitted, runs in local in-memory mode. */
|
|
54
|
+
serviceKey?: string;
|
|
55
|
+
/** Max free calls per buyer per period */
|
|
56
|
+
perBuyerLimit: number;
|
|
57
|
+
/** Reset period for per-buyer counters */
|
|
58
|
+
resetPeriod?: 'none' | 'daily' | 'monthly';
|
|
59
|
+
/** Optional global cap across all buyers */
|
|
60
|
+
globalCap?: number;
|
|
61
|
+
/** Specific paths to apply free tier to (default: '*' = all) */
|
|
62
|
+
paths?: string[];
|
|
63
|
+
/** Override RelAI API base URL (default: https://api.relai.fi) */
|
|
64
|
+
baseUrl?: string;
|
|
65
|
+
/** Cache TTL in ms for check results (default: 5000) */
|
|
66
|
+
cacheTtlMs?: number;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Free Tier plugin - gives buyers a number of free API calls
|
|
70
|
+
* before requiring payment.
|
|
71
|
+
*
|
|
72
|
+
* State is stored in the RelAI backend, keyed by your service key.
|
|
73
|
+
* Config can be set here (SDK-side) or overridden in the relai.fi dashboard.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```typescript
|
|
77
|
+
* import Relai from '@relai-fi/x402/server';
|
|
78
|
+
* import { freeTier } from '@relai-fi/x402/plugins';
|
|
79
|
+
*
|
|
80
|
+
* const relai = new Relai({
|
|
81
|
+
* network: 'base',
|
|
82
|
+
* plugins: [
|
|
83
|
+
* freeTier({
|
|
84
|
+
* serviceKey: process.env.RELAI_SERVICE_KEY!,
|
|
85
|
+
* perBuyerLimit: 10,
|
|
86
|
+
* resetPeriod: 'daily',
|
|
87
|
+
* }),
|
|
88
|
+
* ],
|
|
89
|
+
* });
|
|
90
|
+
*
|
|
91
|
+
* app.get('/api/data', relai.protect({
|
|
92
|
+
* payTo: '0xYourWallet',
|
|
93
|
+
* price: 0.01,
|
|
94
|
+
* }), (req, res) => {
|
|
95
|
+
* res.json({ data: 'paid content' });
|
|
96
|
+
* });
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
interface BridgePluginConfig {
|
|
100
|
+
/** Service key (sk_live_...) for tracking bridge usage per API owner */
|
|
101
|
+
serviceKey?: string;
|
|
102
|
+
/** RelAI API base URL (default: https://api.relai.fi) */
|
|
103
|
+
baseUrl?: string;
|
|
104
|
+
/** Override settle endpoint (auto-discovered from /bridge/info if not set) */
|
|
105
|
+
settleEndpoint?: string;
|
|
106
|
+
/** Override supported source chains (auto-discovered if not set) */
|
|
107
|
+
supportedSourceChains?: string[];
|
|
108
|
+
/** Override supported source assets (auto-discovered if not set) */
|
|
109
|
+
supportedSourceAssets?: string[];
|
|
110
|
+
/** Override bridge payTo map: { [caip2]: address } */
|
|
111
|
+
payTo?: Record<string, string>;
|
|
112
|
+
/** Override Solana fee payer address (auto-discovered if not set) */
|
|
113
|
+
feePayerSvm?: string;
|
|
114
|
+
/** Override payment facilitator URL */
|
|
115
|
+
paymentFacilitator?: string;
|
|
116
|
+
/** Bridge fee in basis points (default: auto-discovered) */
|
|
117
|
+
feeBps?: number;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Bridge plugin - enables cross-chain payments via the RelAI bridge.
|
|
121
|
+
*
|
|
122
|
+
* When a buyer's wallet is on a different chain than the merchant accepts,
|
|
123
|
+
* the client SDK can automatically route the payment through the bridge.
|
|
124
|
+
* This plugin adds `extensions.bridge` to the 402 response with all the
|
|
125
|
+
* info the client needs to execute a cross-chain payment.
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* ```typescript
|
|
129
|
+
* import Relai from '@relai-fi/x402/server';
|
|
130
|
+
* import { bridge } from '@relai-fi/x402/plugins';
|
|
131
|
+
*
|
|
132
|
+
* const relai = new Relai({
|
|
133
|
+
* network: 'skale-base',
|
|
134
|
+
* plugins: [
|
|
135
|
+
* bridge(), // auto-discovers from https://api.relai.fi
|
|
136
|
+
* ],
|
|
137
|
+
* });
|
|
138
|
+
*
|
|
139
|
+
* // Buyer on Solana can now pay for a SKALE endpoint
|
|
140
|
+
* app.get('/api/data', relai.protect({
|
|
141
|
+
* payTo: '0xYourWallet',
|
|
142
|
+
* price: 0.05,
|
|
143
|
+
* }), (req, res) => {
|
|
144
|
+
* res.json({ data: 'paid content' });
|
|
145
|
+
* });
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
declare function bridge(config?: BridgePluginConfig): RelaiPlugin;
|
|
149
|
+
/**
|
|
150
|
+
* Extended plugin interface with data export for free tier.
|
|
151
|
+
*/
|
|
152
|
+
interface FreeTierPlugin extends RelaiPlugin {
|
|
153
|
+
/** Export all in-memory usage data (local mode only). Returns null when using cloud mode. */
|
|
154
|
+
getUsageData(): FreeTierUsageExport | null;
|
|
155
|
+
/** Whether plugin is running in local (in-memory) or cloud (RelAI backend) mode. */
|
|
156
|
+
readonly mode: 'local' | 'cloud';
|
|
157
|
+
}
|
|
158
|
+
interface FreeTierUsageExport {
|
|
159
|
+
mode: 'local';
|
|
160
|
+
config: {
|
|
161
|
+
perBuyerLimit: number;
|
|
162
|
+
resetPeriod: string;
|
|
163
|
+
globalCap: number | null;
|
|
164
|
+
paths: string[];
|
|
165
|
+
};
|
|
166
|
+
/** Per-buyer usage entries */
|
|
167
|
+
usage: Array<{
|
|
168
|
+
buyerId: string;
|
|
169
|
+
path: string;
|
|
170
|
+
count: number;
|
|
171
|
+
periodStart: string;
|
|
172
|
+
lastCall: string;
|
|
173
|
+
}>;
|
|
174
|
+
globalCount: number;
|
|
175
|
+
exportedAt: string;
|
|
176
|
+
}
|
|
177
|
+
declare function freeTier(config: FreeTierPluginConfig): FreeTierPlugin;
|
|
178
|
+
interface ShieldPluginConfig {
|
|
179
|
+
/**
|
|
180
|
+
* Custom health check function. Return true if the service is healthy.
|
|
181
|
+
* Takes priority over `healthUrl` if both are provided.
|
|
182
|
+
*/
|
|
183
|
+
healthCheck?: () => Promise<boolean> | boolean;
|
|
184
|
+
/**
|
|
185
|
+
* URL to probe. A 2xx response means healthy.
|
|
186
|
+
* Ignored if `healthCheck` is provided.
|
|
187
|
+
*/
|
|
188
|
+
healthUrl?: string;
|
|
189
|
+
/** Timeout in ms for the health probe (default: 5000) */
|
|
190
|
+
timeoutMs?: number;
|
|
191
|
+
/** Cache the health result for this many ms (default: 10000) */
|
|
192
|
+
cacheTtlMs?: number;
|
|
193
|
+
/** Message returned to the caller when unhealthy (default: 'Service temporarily unavailable. Please try again later.') */
|
|
194
|
+
unhealthyMessage?: string;
|
|
195
|
+
/** HTTP status code when unhealthy (default: 503) */
|
|
196
|
+
unhealthyStatus?: number;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Shield plugin — protects buyers from paying for unhealthy endpoints.
|
|
200
|
+
*
|
|
201
|
+
* Before the server returns a 402, Shield runs a health check (custom
|
|
202
|
+
* function or URL probe). If the service is down, the request is rejected
|
|
203
|
+
* with 503 instead of asking for payment.
|
|
204
|
+
*
|
|
205
|
+
* @example
|
|
206
|
+
* ```typescript
|
|
207
|
+
* import Relai from '@relai-fi/x402/server';
|
|
208
|
+
* import { shield } from '@relai-fi/x402/plugins';
|
|
209
|
+
*
|
|
210
|
+
* const relai = new Relai({
|
|
211
|
+
* network: 'base',
|
|
212
|
+
* plugins: [
|
|
213
|
+
* shield({
|
|
214
|
+
* healthUrl: 'https://my-api.com/health',
|
|
215
|
+
* timeoutMs: 3000,
|
|
216
|
+
* }),
|
|
217
|
+
* ],
|
|
218
|
+
* });
|
|
219
|
+
*
|
|
220
|
+
* // If /health is down, buyers get 503 instead of 402
|
|
221
|
+
* app.get('/api/data', relai.protect({
|
|
222
|
+
* payTo: '0xYourWallet',
|
|
223
|
+
* price: 0.01,
|
|
224
|
+
* }), (req, res) => {
|
|
225
|
+
* res.json({ data: 'premium content' });
|
|
226
|
+
* });
|
|
227
|
+
* ```
|
|
228
|
+
*/
|
|
229
|
+
declare function shield(config?: ShieldPluginConfig): RelaiPlugin;
|
|
230
|
+
interface PreflightPluginConfig {
|
|
231
|
+
/**
|
|
232
|
+
* Base URL of the API server (e.g. 'https://my-api.com').
|
|
233
|
+
* The plugin appends the request path to form the probe URL.
|
|
234
|
+
* **Required** — the plugin needs to know where to send the probe.
|
|
235
|
+
*/
|
|
236
|
+
baseUrl: string;
|
|
237
|
+
/** Timeout in ms for the preflight probe (default: 3000) */
|
|
238
|
+
timeoutMs?: number;
|
|
239
|
+
/** Cache the probe result for this many ms (default: 5000) */
|
|
240
|
+
cacheTtlMs?: number;
|
|
241
|
+
/** Custom unhealthy message (default: 'Endpoint not responding. Please try again later.') */
|
|
242
|
+
unhealthyMessage?: string;
|
|
243
|
+
/** HTTP status code when endpoint unreachable (default: 503) */
|
|
244
|
+
unhealthyStatus?: number;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Preflight plugin — verifies the specific endpoint responds before payment.
|
|
248
|
+
*
|
|
249
|
+
* Unlike Shield (global health check), Preflight probes the **actual endpoint**
|
|
250
|
+
* the buyer is requesting. It sends a HEAD request with `X-Preflight: true` —
|
|
251
|
+
* the Relai middleware responds 200 instantly without triggering payment.
|
|
252
|
+
* If the endpoint doesn't respond, the buyer gets 503 instead of 402.
|
|
253
|
+
*
|
|
254
|
+
* @example
|
|
255
|
+
* ```typescript
|
|
256
|
+
* import Relai from '@relai-fi/x402/server';
|
|
257
|
+
* import { preflight } from '@relai-fi/x402/plugins';
|
|
258
|
+
*
|
|
259
|
+
* const relai = new Relai({
|
|
260
|
+
* network: 'base',
|
|
261
|
+
* plugins: [
|
|
262
|
+
* preflight({ baseUrl: 'https://my-api.com' }),
|
|
263
|
+
* ],
|
|
264
|
+
* });
|
|
265
|
+
*
|
|
266
|
+
* app.get('/api/data', relai.protect({
|
|
267
|
+
* payTo: '0xYourWallet',
|
|
268
|
+
* price: 0.01,
|
|
269
|
+
* }), (req, res) => {
|
|
270
|
+
* res.json({ data: 'premium content' });
|
|
271
|
+
* });
|
|
272
|
+
* ```
|
|
273
|
+
*/
|
|
274
|
+
declare function preflight(config: PreflightPluginConfig): RelaiPlugin;
|
|
275
|
+
type CircuitState = 'closed' | 'open' | 'half-open';
|
|
276
|
+
interface CircuitBreakerPluginConfig {
|
|
277
|
+
/**
|
|
278
|
+
* Number of consecutive failures before the circuit opens.
|
|
279
|
+
* Default: 5
|
|
280
|
+
*/
|
|
281
|
+
failureThreshold?: number;
|
|
282
|
+
/**
|
|
283
|
+
* Time in ms the circuit stays open before moving to half-open.
|
|
284
|
+
* Default: 30000 (30 seconds)
|
|
285
|
+
*/
|
|
286
|
+
resetTimeMs?: number;
|
|
287
|
+
/**
|
|
288
|
+
* Number of successful requests in half-open state before closing the circuit.
|
|
289
|
+
* Default: 2
|
|
290
|
+
*/
|
|
291
|
+
halfOpenSuccesses?: number;
|
|
292
|
+
/** HTTP status code when circuit is open (default: 503) */
|
|
293
|
+
openStatus?: number;
|
|
294
|
+
/** Error message when circuit is open */
|
|
295
|
+
openMessage?: string;
|
|
296
|
+
/**
|
|
297
|
+
* HTTP status codes considered as failures.
|
|
298
|
+
* Default: [500, 502, 503, 504]
|
|
299
|
+
*/
|
|
300
|
+
failureCodes?: number[];
|
|
301
|
+
/**
|
|
302
|
+
* Track failures globally or per-path.
|
|
303
|
+
* Default: 'per-path'
|
|
304
|
+
*/
|
|
305
|
+
scope?: 'global' | 'per-path';
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Circuit Breaker plugin — tracks failure history and opens the circuit
|
|
309
|
+
* after repeated failures, preventing buyers from paying for broken endpoints.
|
|
310
|
+
*
|
|
311
|
+
* Unlike Shield/Preflight (real-time probes), Circuit Breaker is **zero-latency** —
|
|
312
|
+
* it makes no additional HTTP requests. Instead, it tracks `afterSettled` outcomes
|
|
313
|
+
* and rejects requests when the failure rate exceeds the threshold.
|
|
314
|
+
*
|
|
315
|
+
* States:
|
|
316
|
+
* - **closed** — normal operation, requests flow through
|
|
317
|
+
* - **open** — too many failures, requests are rejected with 503
|
|
318
|
+
* - **half-open** — after resetTime, allows a few test requests through
|
|
319
|
+
*
|
|
320
|
+
* @example
|
|
321
|
+
* ```typescript
|
|
322
|
+
* import Relai from '@relai-fi/x402/server';
|
|
323
|
+
* import { circuitBreaker } from '@relai-fi/x402/plugins';
|
|
324
|
+
*
|
|
325
|
+
* const relai = new Relai({
|
|
326
|
+
* network: 'base',
|
|
327
|
+
* plugins: [
|
|
328
|
+
* circuitBreaker({ failureThreshold: 5, resetTimeMs: 30000 }),
|
|
329
|
+
* ],
|
|
330
|
+
* });
|
|
331
|
+
* ```
|
|
332
|
+
*/
|
|
333
|
+
declare function circuitBreaker(config?: CircuitBreakerPluginConfig): RelaiPlugin;
|
|
334
|
+
interface RefundPluginConfig {
|
|
335
|
+
/**
|
|
336
|
+
* HTTP status codes that trigger a refund/credit after payment.
|
|
337
|
+
* Default: [500, 502, 503, 504]
|
|
338
|
+
*/
|
|
339
|
+
triggerCodes?: number[];
|
|
340
|
+
/**
|
|
341
|
+
* What to do when a refund is triggered.
|
|
342
|
+
* - 'credit': record a credit that the free-tier plugin can consume later
|
|
343
|
+
* - 'log': just log the event (useful with a custom onRefund callback)
|
|
344
|
+
* Default: 'credit'
|
|
345
|
+
*/
|
|
346
|
+
mode?: 'credit' | 'log';
|
|
347
|
+
/**
|
|
348
|
+
* Called whenever a refund event is triggered.
|
|
349
|
+
* Use for external refund processing, logging, or notifications.
|
|
350
|
+
*/
|
|
351
|
+
onRefund?: (event: RefundEvent) => void | Promise<void>;
|
|
352
|
+
/**
|
|
353
|
+
* Also trigger refund when settlement itself fails (result.success === false).
|
|
354
|
+
* Default: true
|
|
355
|
+
*/
|
|
356
|
+
refundOnSettlementFailure?: boolean;
|
|
357
|
+
}
|
|
358
|
+
interface RefundEvent {
|
|
359
|
+
/** Buyer wallet address */
|
|
360
|
+
payer: string;
|
|
361
|
+
/** Transaction ID of the original payment */
|
|
362
|
+
transactionId: string;
|
|
363
|
+
/** Network used */
|
|
364
|
+
network: string;
|
|
365
|
+
/** Amount paid in USD */
|
|
366
|
+
amount: number;
|
|
367
|
+
/** Request path */
|
|
368
|
+
path: string;
|
|
369
|
+
/** Reason for the refund */
|
|
370
|
+
reason: 'endpoint_error' | 'settlement_failure' | 'timeout';
|
|
371
|
+
/** HTTP status code that triggered the refund (if applicable) */
|
|
372
|
+
statusCode?: number;
|
|
373
|
+
/** Timestamp */
|
|
374
|
+
timestamp: number;
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Refund plugin — automatically handles refund/credit when a paid request fails.
|
|
378
|
+
*
|
|
379
|
+
* After payment settles, if the endpoint returns an error (e.g. 500),
|
|
380
|
+
* the plugin records a credit or calls your custom `onRefund` handler.
|
|
381
|
+
* Credits can be consumed by the free-tier plugin on the next request.
|
|
382
|
+
*
|
|
383
|
+
* @example
|
|
384
|
+
* ```typescript
|
|
385
|
+
* import Relai from '@relai-fi/x402/server';
|
|
386
|
+
* import { refund } from '@relai-fi/x402/plugins';
|
|
387
|
+
*
|
|
388
|
+
* const relai = new Relai({
|
|
389
|
+
* network: 'base',
|
|
390
|
+
* plugins: [
|
|
391
|
+
* refund({
|
|
392
|
+
* triggerCodes: [500, 502, 503],
|
|
393
|
+
* onRefund: (event) => {
|
|
394
|
+
* console.log(`Refund for ${event.payer}: $${event.amount} on ${event.path}`);
|
|
395
|
+
* // Notify buyer, record in DB, etc.
|
|
396
|
+
* },
|
|
397
|
+
* }),
|
|
398
|
+
* ],
|
|
399
|
+
* });
|
|
400
|
+
* ```
|
|
401
|
+
*/
|
|
402
|
+
declare function refund(config?: RefundPluginConfig): RelaiPlugin;
|
|
403
|
+
|
|
404
|
+
interface ScorePluginConfig {
|
|
405
|
+
/**
|
|
406
|
+
* ERC-8004 agentId (NFT tokenId) for this API.
|
|
407
|
+
* Find yours in the RelAI dashboard under "On-chain Identity".
|
|
408
|
+
*/
|
|
409
|
+
agentId: string | number;
|
|
410
|
+
/**
|
|
411
|
+
* SKALE Base Sepolia RPC URL.
|
|
412
|
+
* Default: https://base-sepolia-testnet.skalenodes.com/v1/jubilant-horrible-ancha
|
|
413
|
+
*/
|
|
414
|
+
rpcUrl?: string;
|
|
415
|
+
/**
|
|
416
|
+
* ERC-8004 IdentityRegistry contract address.
|
|
417
|
+
* Default: process.env.ERC8004_IDENTITY_REGISTRY
|
|
418
|
+
*/
|
|
419
|
+
identityRegistryAddress?: string;
|
|
420
|
+
/**
|
|
421
|
+
* ERC-8004 ReputationRegistry contract address.
|
|
422
|
+
* Default: process.env.ERC8004_REPUTATION_REGISTRY
|
|
423
|
+
*/
|
|
424
|
+
reputationRegistryAddress?: string;
|
|
425
|
+
/**
|
|
426
|
+
* Cache TTL in ms (default: 5 minutes).
|
|
427
|
+
*/
|
|
428
|
+
cacheTtlMs?: number;
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Score plugin — fetches ERC-8004 on-chain reputation for your API
|
|
432
|
+
* directly via RPC and injects it into the 402 response `extensions.score`.
|
|
433
|
+
*
|
|
434
|
+
* Agents can read the score **before paying**, enabling trust-based routing:
|
|
435
|
+
* - `feedbackCount` — number of on-chain feedback entries
|
|
436
|
+
* - `successRate` — 0–100 (% of successful calls)
|
|
437
|
+
* - `avgResponseMs` — average response time in milliseconds
|
|
438
|
+
* - `verified` — true if agentId is registered on-chain
|
|
439
|
+
*
|
|
440
|
+
* @example
|
|
441
|
+
* ```typescript
|
|
442
|
+
* import Relai from '@relai-fi/x402/server';
|
|
443
|
+
* import { score } from '@relai-fi/x402/plugins';
|
|
444
|
+
*
|
|
445
|
+
* const relai = new Relai({
|
|
446
|
+
* network: 'base',
|
|
447
|
+
* plugins: [
|
|
448
|
+
* score({ agentId: '5' }),
|
|
449
|
+
* ],
|
|
450
|
+
* });
|
|
451
|
+
* ```
|
|
452
|
+
*/
|
|
453
|
+
declare function score(config: ScorePluginConfig): RelaiPlugin;
|
|
454
|
+
interface FeedbackPluginConfig {
|
|
455
|
+
/**
|
|
456
|
+
* ERC-8004 agentId (NFT tokenId) for this API.
|
|
457
|
+
*/
|
|
458
|
+
agentId: string | number;
|
|
459
|
+
/**
|
|
460
|
+
* Private key of the wallet that signs feedback transactions.
|
|
461
|
+
* The wallet must hold CREDIT tokens on SKALE Base to pay gas.
|
|
462
|
+
* Default: process.env.BACKEND_WALLET_PRIVATE_KEY
|
|
463
|
+
*/
|
|
464
|
+
walletPrivateKey?: string;
|
|
465
|
+
/**
|
|
466
|
+
* SKALE Base Sepolia RPC URL.
|
|
467
|
+
* Default: process.env.ERC8004_RPC_URL or SKALE Base Sepolia public RPC.
|
|
468
|
+
*/
|
|
469
|
+
rpcUrl?: string;
|
|
470
|
+
/**
|
|
471
|
+
* ERC-8004 ReputationRegistry contract address.
|
|
472
|
+
* Default: process.env.ERC8004_REPUTATION_REGISTRY
|
|
473
|
+
*/
|
|
474
|
+
reputationRegistryAddress?: string;
|
|
475
|
+
/**
|
|
476
|
+
* Tag2 label for all feedback entries (default: 'x402').
|
|
477
|
+
*/
|
|
478
|
+
tag2?: string;
|
|
479
|
+
/**
|
|
480
|
+
* Whether to submit successRate feedback (default: true).
|
|
481
|
+
* Value: 1 (success) or 0 (failure).
|
|
482
|
+
*/
|
|
483
|
+
submitSuccessRate?: boolean;
|
|
484
|
+
/**
|
|
485
|
+
* Whether to submit responseTime feedback (default: true).
|
|
486
|
+
* Value: milliseconds elapsed since request start.
|
|
487
|
+
* Requires req.x402StartTime (set automatically if using Relai.protect()).
|
|
488
|
+
*/
|
|
489
|
+
submitResponseTime?: boolean;
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Feedback plugin — submits ERC-8004 on-chain reputation after every successful x402 settlement.
|
|
493
|
+
*
|
|
494
|
+
* Records `successRate` and `responseTime` signals to the ReputationRegistry on SKALE Base.
|
|
495
|
+
* This is the mechanism that **builds** the score that `score()` plugin later reads.
|
|
496
|
+
*
|
|
497
|
+
* Runs fire-and-forget — never blocks the response.
|
|
498
|
+
*
|
|
499
|
+
* @example
|
|
500
|
+
* ```typescript
|
|
501
|
+
* import Relai from '@relai-fi/x402/server';
|
|
502
|
+
* import { score, feedback } from '@relai-fi/x402/plugins';
|
|
503
|
+
*
|
|
504
|
+
* const relai = new Relai({
|
|
505
|
+
* network: 'base',
|
|
506
|
+
* plugins: [
|
|
507
|
+
* score({ agentId: '5' }),
|
|
508
|
+
* feedback({ agentId: '5' }), // needs BACKEND_WALLET_PRIVATE_KEY in env
|
|
509
|
+
* ],
|
|
510
|
+
* });
|
|
511
|
+
* ```
|
|
512
|
+
*/
|
|
513
|
+
declare function feedback(config: FeedbackPluginConfig): RelaiPlugin;
|
|
514
|
+
interface SolanaFeedbackPluginConfig {
|
|
515
|
+
/**
|
|
516
|
+
* Solana MPL Core NFT public key (base58) of the registered agent.
|
|
517
|
+
* This is the `solanaAgentAsset` stored after calling /api/solana8004/register.
|
|
518
|
+
*/
|
|
519
|
+
assetPubkey: string;
|
|
520
|
+
/**
|
|
521
|
+
* Private key of the feedback wallet — base58 or JSON array format.
|
|
522
|
+
* Should differ from the owner's registration wallet (avoids self-feedback restriction).
|
|
523
|
+
* Default: process.env.SOLANA_8004_FEEDBACK_KEY ?? process.env.SOLANA_8004_PRIVATE_KEY
|
|
524
|
+
*/
|
|
525
|
+
feedbackWalletPrivateKey?: string;
|
|
526
|
+
/**
|
|
527
|
+
* Solana cluster to use.
|
|
528
|
+
* Default: process.env.SOLANA_8004_CLUSTER ?? 'mainnet-beta'
|
|
529
|
+
*/
|
|
530
|
+
cluster?: 'mainnet-beta' | 'devnet';
|
|
531
|
+
/**
|
|
532
|
+
* Custom Solana RPC URL (Helius / QuickNode recommended).
|
|
533
|
+
* Default: process.env.SOLANA_8004_RPC_URL
|
|
534
|
+
*/
|
|
535
|
+
rpcUrl?: string;
|
|
536
|
+
/**
|
|
537
|
+
* Whether to submit successRate feedback (default: true).
|
|
538
|
+
*/
|
|
539
|
+
submitSuccessRate?: boolean;
|
|
540
|
+
/**
|
|
541
|
+
* Whether to submit responseTime feedback (default: true).
|
|
542
|
+
*/
|
|
543
|
+
submitResponseTime?: boolean;
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* Solana Feedback plugin — submits 8004-solana on-chain reputation after x402 settlement.
|
|
547
|
+
*
|
|
548
|
+
* Uses the native Solana `8004-solana` program (MPL Core NFT registry),
|
|
549
|
+
* separate from the SKALE EVM ReputationRegistry used by `feedback()`.
|
|
550
|
+
*
|
|
551
|
+
* Requires `8004-solana` package to be installed:
|
|
552
|
+
* ```
|
|
553
|
+
* npm install 8004-solana
|
|
554
|
+
* ```
|
|
555
|
+
*
|
|
556
|
+
* @example
|
|
557
|
+
* ```typescript
|
|
558
|
+
* import Relai from '@relai-fi/x402/server';
|
|
559
|
+
* import { solanaFeedback } from '@relai-fi/x402/plugins';
|
|
560
|
+
*
|
|
561
|
+
* const relai = new Relai({
|
|
562
|
+
* network: 'solana',
|
|
563
|
+
* plugins: [
|
|
564
|
+
* solanaFeedback({
|
|
565
|
+
* assetPubkey: process.env.SOLANA_AGENT_ASSET!, // e.g. 'GH93tGR8...'
|
|
566
|
+
* }),
|
|
567
|
+
* ],
|
|
568
|
+
* });
|
|
569
|
+
* ```
|
|
570
|
+
*/
|
|
571
|
+
declare function solanaFeedback(config: SolanaFeedbackPluginConfig): RelaiPlugin;
|
|
572
|
+
|
|
573
|
+
interface RelaiServerConfig {
|
|
574
|
+
/** Network to accept payments on */
|
|
575
|
+
network: RelaiNetwork;
|
|
576
|
+
/** RelAI facilitator URL (default: https://facilitator.x402.fi) */
|
|
577
|
+
facilitatorUrl?: string;
|
|
578
|
+
/** Plugins to extend protect() behavior (e.g. freeTier, rateLimit) */
|
|
579
|
+
plugins?: RelaiPlugin[];
|
|
580
|
+
}
|
|
581
|
+
type DynamicPrice = number | ((req: any) => number | Promise<number>);
|
|
582
|
+
type RelaiIntegritasFlow = 'single' | 'dual';
|
|
583
|
+
interface RelaiIntegritasOptions {
|
|
584
|
+
/** Enable Integritas by default for this endpoint */
|
|
585
|
+
enabled?: boolean;
|
|
586
|
+
/** Default flow when Integritas is enabled */
|
|
587
|
+
flow?: RelaiIntegritasFlow;
|
|
588
|
+
}
|
|
589
|
+
interface ProtectOptions {
|
|
590
|
+
/** Price in USD (e.g., 0.01 for 1 cent) */
|
|
591
|
+
price: DynamicPrice;
|
|
592
|
+
/** Optional token asset address for networks supporting multiple tokens */
|
|
593
|
+
asset?: string;
|
|
594
|
+
/** Wallet address to receive payments, or stripePayTo() for Stripe settlement */
|
|
595
|
+
payTo: string | StripePayTo;
|
|
596
|
+
/** Description shown to payer */
|
|
597
|
+
description?: string;
|
|
598
|
+
/** MIME type of the response (default: application/json) */
|
|
599
|
+
mimeType?: string;
|
|
600
|
+
/** Maximum timeout in seconds (default: 60) */
|
|
601
|
+
maxTimeoutSeconds?: number;
|
|
602
|
+
/** Integritas options (or simple boolean enable flag) */
|
|
603
|
+
integritas?: boolean | RelaiIntegritasOptions;
|
|
604
|
+
/** Override network for this endpoint */
|
|
605
|
+
network?: RelaiNetwork;
|
|
606
|
+
/** Override feePayer address (Solana only) — bypasses facilitator /supported fetch */
|
|
607
|
+
feePayer?: string;
|
|
608
|
+
/** Custom validation after payment is settled */
|
|
609
|
+
customRules?: (req: any) => boolean | Promise<boolean>;
|
|
610
|
+
/** Callback when 402 is returned (no payment provided) */
|
|
611
|
+
onPaymentRequired?: (req: any, info: {
|
|
612
|
+
price: number;
|
|
613
|
+
network: RelaiNetwork;
|
|
614
|
+
}) => void;
|
|
615
|
+
/** Callback when payment is settled successfully */
|
|
616
|
+
onPaymentSettled?: (req: any, result: SettleResult) => void;
|
|
617
|
+
/** Callback on error */
|
|
618
|
+
onError?: (req: any, error: unknown) => void;
|
|
619
|
+
}
|
|
620
|
+
interface SettleResult {
|
|
621
|
+
success: boolean;
|
|
622
|
+
transaction?: string;
|
|
623
|
+
payer?: string;
|
|
624
|
+
network?: string;
|
|
625
|
+
splitTransfers?: Record<string, unknown>;
|
|
626
|
+
integritasFeePaid?: boolean;
|
|
627
|
+
provenance?: Record<string, unknown>;
|
|
628
|
+
integritas?: Record<string, unknown>;
|
|
629
|
+
error?: string;
|
|
630
|
+
errorReason?: string;
|
|
631
|
+
}
|
|
632
|
+
interface PaymentInfo {
|
|
633
|
+
verified: boolean;
|
|
634
|
+
transactionId?: string;
|
|
635
|
+
payer?: string;
|
|
636
|
+
network: RelaiNetwork;
|
|
637
|
+
amount: number;
|
|
638
|
+
}
|
|
639
|
+
/** Config returned by stripePayTo() - used by protect() to create Stripe deposit addresses */
|
|
640
|
+
interface StripePayTo {
|
|
641
|
+
readonly __brand: 'stripePayTo';
|
|
642
|
+
readonly secretKey: string;
|
|
643
|
+
/** Stripe crypto deposits network (default: 'base') */
|
|
644
|
+
readonly stripeNetwork: string;
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* Create a Stripe pay-to configuration for x402 payments.
|
|
648
|
+
* Payments settle as USD in your Stripe Dashboard - no crypto knowledge required.
|
|
649
|
+
*
|
|
650
|
+
* Stripe creates a fresh PaymentIntent + deposit address per request.
|
|
651
|
+
* Network is auto-set to Base (Stripe settles USDC on Base).
|
|
652
|
+
*
|
|
653
|
+
* @example
|
|
654
|
+
* ```typescript
|
|
655
|
+
* import Relai, { stripePayTo } from '@relai-fi/x402/server';
|
|
656
|
+
*
|
|
657
|
+
* const relai = new Relai({ network: 'base' });
|
|
658
|
+
*
|
|
659
|
+
* app.get('/api/data', relai.protect({
|
|
660
|
+
* price: 0.01,
|
|
661
|
+
* payTo: stripePayTo(process.env.STRIPE_SECRET_KEY!),
|
|
662
|
+
* }), (req, res) => {
|
|
663
|
+
* res.json({ data: 'paid content' });
|
|
664
|
+
* });
|
|
665
|
+
* ```
|
|
666
|
+
*/
|
|
667
|
+
declare function stripePayTo(stripeSecretKey: string, options?: {
|
|
668
|
+
network?: string;
|
|
669
|
+
}): StripePayTo;
|
|
670
|
+
/**
|
|
671
|
+
* Server-side SDK for protecting Express endpoints with x402 micropayments.
|
|
672
|
+
* Settles payments through the RelAI facilitator (zero gas fees for users).
|
|
673
|
+
*
|
|
674
|
+
* Supports: Solana, Base, Avalanche, SKALE Base, SKALE Base Sepolia, SKALE BITE, Polygon, and Ethereum.
|
|
675
|
+
*
|
|
676
|
+
* @example
|
|
677
|
+
* ```typescript
|
|
678
|
+
* import Relai from '@relai-fi/x402/server';
|
|
679
|
+
*
|
|
680
|
+
* const relai = new Relai({ network: 'base' });
|
|
681
|
+
*
|
|
682
|
+
* app.get('/api/data', relai.protect({
|
|
683
|
+
* payTo: '0xYourWallet',
|
|
684
|
+
* price: 0.01, // $0.01 USDC
|
|
685
|
+
* }), (req, res) => {
|
|
686
|
+
* res.json({ data: 'Protected content', payment: req.payment });
|
|
687
|
+
* });
|
|
688
|
+
* ```
|
|
689
|
+
*/
|
|
690
|
+
declare class Relai {
|
|
691
|
+
private network;
|
|
692
|
+
private facilitatorUrl;
|
|
693
|
+
private feePayerCache;
|
|
694
|
+
private plugins;
|
|
695
|
+
private pluginInitPromise;
|
|
696
|
+
constructor(config: RelaiServerConfig);
|
|
697
|
+
private runPluginInit;
|
|
698
|
+
/**
|
|
699
|
+
* Get feePayer address for a network (cached)
|
|
700
|
+
*/
|
|
701
|
+
private getFeePayer;
|
|
702
|
+
/**
|
|
703
|
+
* Express middleware to protect an endpoint with x402 micropayments.
|
|
704
|
+
*
|
|
705
|
+
* Flow:
|
|
706
|
+
* 1. No payment header → returns 402 with payment requirements
|
|
707
|
+
* 2. Payment header present → calls RelAI facilitator `/settle`
|
|
708
|
+
* 3. Settlement success → sets `PAYMENT-RESPONSE` header, attaches `req.payment`, calls `next()`
|
|
709
|
+
*/
|
|
710
|
+
protect(options: ProtectOptions): (req: any, res: any, next: any) => Promise<any>;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
export { feedback as A, type BridgePluginConfig as B, type CircuitState as C, type DynamicPrice as D, solanaFeedback as E, type FreeTierPluginConfig as F, type ProtectOptions as P, Relai as R, type SettleResult as S, type RelaiServerConfig as a, type PaymentInfo as b, type StripePayTo as c, type RelaiIntegritasFlow as d, type RelaiIntegritasOptions as e, type RelaiPlugin as f, type PluginContext as g, type PluginResult as h, type ScorePluginConfig as i, type FeedbackPluginConfig as j, type SolanaFeedbackPluginConfig as k, bridge as l, type FreeTierPlugin as m, type FreeTierUsageExport as n, freeTier as o, type ShieldPluginConfig as p, shield as q, type PreflightPluginConfig as r, stripePayTo as s, preflight as t, type CircuitBreakerPluginConfig as u, circuitBreaker as v, type RefundPluginConfig as w, type RefundEvent as x, refund as y, score as z };
|