@4mica/x402 0.1.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/.eslintrc.cjs +29 -0
- package/.prettierignore +3 -0
- package/.prettierrc +6 -0
- package/CHANGELOG.md +8 -0
- package/LICENSE +21 -0
- package/README.md +389 -0
- package/demo/.env.example +8 -0
- package/demo/README.md +125 -0
- package/demo/package.json +26 -0
- package/demo/src/client.ts +54 -0
- package/demo/src/deposit.ts +39 -0
- package/demo/src/server.ts +74 -0
- package/demo/tsconfig.json +8 -0
- package/demo/yarn.lock +925 -0
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.js +1 -0
- package/dist/client/scheme.d.ts +11 -0
- package/dist/client/scheme.js +65 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/server/express/adapter.d.ts +71 -0
- package/dist/server/express/adapter.js +90 -0
- package/dist/server/express/index.d.ts +122 -0
- package/dist/server/express/index.js +340 -0
- package/dist/server/facilitator.d.ts +35 -0
- package/dist/server/facilitator.js +52 -0
- package/dist/server/index.d.ts +6 -0
- package/dist/server/index.js +4 -0
- package/dist/server/scheme.d.ts +93 -0
- package/dist/server/scheme.js +179 -0
- package/eslint.config.mjs +22 -0
- package/package.json +79 -0
- package/src/client/index.ts +1 -0
- package/src/client/scheme.ts +95 -0
- package/src/index.ts +7 -0
- package/src/server/express/adapter.ts +100 -0
- package/src/server/express/index.ts +466 -0
- package/src/server/facilitator.ts +90 -0
- package/src/server/index.ts +10 -0
- package/src/server/scheme.ts +223 -0
- package/tsconfig.build.json +5 -0
- package/tsconfig.json +17 -0
- package/vitest.config.ts +12 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './scheme.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './scheme.js';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { SchemeNetworkClient, PaymentRequirements, PaymentPayload } from '@x402/core/types';
|
|
2
|
+
import { Account } from 'viem/accounts';
|
|
3
|
+
export declare class FourMicaEvmScheme implements SchemeNetworkClient {
|
|
4
|
+
private readonly signer;
|
|
5
|
+
private readonly x402Flows;
|
|
6
|
+
readonly scheme = "4mica-credit";
|
|
7
|
+
private constructor();
|
|
8
|
+
private static createX402Flow;
|
|
9
|
+
static create(signer: Account): Promise<FourMicaEvmScheme>;
|
|
10
|
+
createPaymentPayload(x402Version: number, paymentRequirements: PaymentRequirements): Promise<Pick<PaymentPayload, 'x402Version' | 'payload'>>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Client, ConfigBuilder, X402Flow, } from '@4mica/sdk';
|
|
2
|
+
import { SUPPORTED_NETWORKS } from '../server/scheme.js';
|
|
3
|
+
const NETWORK_RPC_URLS = {
|
|
4
|
+
'eip155:11155111': 'https://ethereum.sepolia.api.4mica.xyz',
|
|
5
|
+
'eip155:80002': 'https://api.4mica.xyz',
|
|
6
|
+
};
|
|
7
|
+
export class FourMicaEvmScheme {
|
|
8
|
+
constructor(signer,
|
|
9
|
+
// rpcUrl -> x402Flow
|
|
10
|
+
x402Flows) {
|
|
11
|
+
this.signer = signer;
|
|
12
|
+
this.x402Flows = x402Flows;
|
|
13
|
+
this.scheme = '4mica-credit';
|
|
14
|
+
}
|
|
15
|
+
static async createX402Flow(signer, rpcUrl) {
|
|
16
|
+
const cfg = new ConfigBuilder().rpcUrl(rpcUrl).signer(signer).build();
|
|
17
|
+
const client = await Client.new(cfg);
|
|
18
|
+
return X402Flow.fromClient(client);
|
|
19
|
+
}
|
|
20
|
+
static async create(signer) {
|
|
21
|
+
const x402Flows = new Map();
|
|
22
|
+
for (const network of SUPPORTED_NETWORKS) {
|
|
23
|
+
const rpcUrl = NETWORK_RPC_URLS[network];
|
|
24
|
+
if (!rpcUrl)
|
|
25
|
+
continue;
|
|
26
|
+
x402Flows.set(rpcUrl, await FourMicaEvmScheme.createX402Flow(signer, rpcUrl));
|
|
27
|
+
}
|
|
28
|
+
return new FourMicaEvmScheme(signer, x402Flows);
|
|
29
|
+
}
|
|
30
|
+
async createPaymentPayload(x402Version, paymentRequirements) {
|
|
31
|
+
const network = paymentRequirements.network;
|
|
32
|
+
if (!network) {
|
|
33
|
+
throw new Error('Network is required in PaymentRequirements');
|
|
34
|
+
}
|
|
35
|
+
const rpcUrl = paymentRequirements.extra?.rpcUrl ?? NETWORK_RPC_URLS[network];
|
|
36
|
+
if (!rpcUrl) {
|
|
37
|
+
throw new Error(`No RPC URL configured for network ${network}`);
|
|
38
|
+
}
|
|
39
|
+
let x402Flow = this.x402Flows.get(rpcUrl);
|
|
40
|
+
if (!x402Flow) {
|
|
41
|
+
x402Flow = await FourMicaEvmScheme.createX402Flow(this.signer, rpcUrl);
|
|
42
|
+
this.x402Flows.set(rpcUrl, x402Flow);
|
|
43
|
+
}
|
|
44
|
+
if (x402Version === 1) {
|
|
45
|
+
const signed = await x402Flow.signPayment(paymentRequirements, this.signer.address);
|
|
46
|
+
return {
|
|
47
|
+
x402Version: 1,
|
|
48
|
+
payload: signed.payload,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
else if (x402Version === 2) {
|
|
52
|
+
const paymentRequired = {
|
|
53
|
+
x402Version: 2,
|
|
54
|
+
resource: { url: '', description: '', mimeType: '' },
|
|
55
|
+
accepts: [paymentRequirements],
|
|
56
|
+
};
|
|
57
|
+
const signed = await x402Flow.signPaymentV2(paymentRequired, paymentRequirements, this.signer.address);
|
|
58
|
+
return {
|
|
59
|
+
x402Version: 2,
|
|
60
|
+
payload: signed.payload,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
throw new Error(`Unsupported x402Version: ${x402Version}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type { PaymentRequired, PaymentRequirements, PaymentPayload, Network, SchemeNetworkServer, } from '@x402/core/types';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { HTTPAdapter } from '@x402/core/server';
|
|
2
|
+
import { Request } from 'express';
|
|
3
|
+
/**
|
|
4
|
+
* Express adapter implementation
|
|
5
|
+
*/
|
|
6
|
+
export declare class ExpressAdapter implements HTTPAdapter {
|
|
7
|
+
private req;
|
|
8
|
+
/**
|
|
9
|
+
* Creates a new ExpressAdapter instance.
|
|
10
|
+
*
|
|
11
|
+
* @param req - The Express request object
|
|
12
|
+
*/
|
|
13
|
+
constructor(req: Request);
|
|
14
|
+
/**
|
|
15
|
+
* Gets a header value from the request.
|
|
16
|
+
*
|
|
17
|
+
* @param name - The header name
|
|
18
|
+
* @returns The header value or undefined
|
|
19
|
+
*/
|
|
20
|
+
getHeader(name: string): string | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* Gets the HTTP method of the request.
|
|
23
|
+
*
|
|
24
|
+
* @returns The HTTP method
|
|
25
|
+
*/
|
|
26
|
+
getMethod(): string;
|
|
27
|
+
/**
|
|
28
|
+
* Gets the path of the request.
|
|
29
|
+
*
|
|
30
|
+
* @returns The request path
|
|
31
|
+
*/
|
|
32
|
+
getPath(): string;
|
|
33
|
+
/**
|
|
34
|
+
* Gets the full URL of the request.
|
|
35
|
+
*
|
|
36
|
+
* @returns The full request URL
|
|
37
|
+
*/
|
|
38
|
+
getUrl(): string;
|
|
39
|
+
/**
|
|
40
|
+
* Gets the Accept header from the request.
|
|
41
|
+
*
|
|
42
|
+
* @returns The Accept header value or empty string
|
|
43
|
+
*/
|
|
44
|
+
getAcceptHeader(): string;
|
|
45
|
+
/**
|
|
46
|
+
* Gets the User-Agent header from the request.
|
|
47
|
+
*
|
|
48
|
+
* @returns The User-Agent header value or empty string
|
|
49
|
+
*/
|
|
50
|
+
getUserAgent(): string;
|
|
51
|
+
/**
|
|
52
|
+
* Gets all query parameters from the request URL.
|
|
53
|
+
*
|
|
54
|
+
* @returns Record of query parameter key-value pairs
|
|
55
|
+
*/
|
|
56
|
+
getQueryParams(): Record<string, string | string[]>;
|
|
57
|
+
/**
|
|
58
|
+
* Gets a specific query parameter by name.
|
|
59
|
+
*
|
|
60
|
+
* @param name - The query parameter name
|
|
61
|
+
* @returns The query parameter value(s) or undefined
|
|
62
|
+
*/
|
|
63
|
+
getQueryParam(name: string): string | string[] | undefined;
|
|
64
|
+
/**
|
|
65
|
+
* Gets the parsed request body.
|
|
66
|
+
* Requires express.json() or express.urlencoded() middleware.
|
|
67
|
+
*
|
|
68
|
+
* @returns The parsed request body
|
|
69
|
+
*/
|
|
70
|
+
getBody(): unknown;
|
|
71
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Express adapter implementation
|
|
3
|
+
*/
|
|
4
|
+
export class ExpressAdapter {
|
|
5
|
+
/**
|
|
6
|
+
* Creates a new ExpressAdapter instance.
|
|
7
|
+
*
|
|
8
|
+
* @param req - The Express request object
|
|
9
|
+
*/
|
|
10
|
+
constructor(req) {
|
|
11
|
+
this.req = req;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Gets a header value from the request.
|
|
15
|
+
*
|
|
16
|
+
* @param name - The header name
|
|
17
|
+
* @returns The header value or undefined
|
|
18
|
+
*/
|
|
19
|
+
getHeader(name) {
|
|
20
|
+
const value = this.req.header(name);
|
|
21
|
+
return Array.isArray(value) ? value[0] : value;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Gets the HTTP method of the request.
|
|
25
|
+
*
|
|
26
|
+
* @returns The HTTP method
|
|
27
|
+
*/
|
|
28
|
+
getMethod() {
|
|
29
|
+
return this.req.method;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Gets the path of the request.
|
|
33
|
+
*
|
|
34
|
+
* @returns The request path
|
|
35
|
+
*/
|
|
36
|
+
getPath() {
|
|
37
|
+
return this.req.path;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Gets the full URL of the request.
|
|
41
|
+
*
|
|
42
|
+
* @returns The full request URL
|
|
43
|
+
*/
|
|
44
|
+
getUrl() {
|
|
45
|
+
return `${this.req.protocol}://${this.req.headers.host}${this.req.originalUrl}`;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Gets the Accept header from the request.
|
|
49
|
+
*
|
|
50
|
+
* @returns The Accept header value or empty string
|
|
51
|
+
*/
|
|
52
|
+
getAcceptHeader() {
|
|
53
|
+
return this.req.header('Accept') || '';
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Gets the User-Agent header from the request.
|
|
57
|
+
*
|
|
58
|
+
* @returns The User-Agent header value or empty string
|
|
59
|
+
*/
|
|
60
|
+
getUserAgent() {
|
|
61
|
+
return this.req.header('User-Agent') || '';
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Gets all query parameters from the request URL.
|
|
65
|
+
*
|
|
66
|
+
* @returns Record of query parameter key-value pairs
|
|
67
|
+
*/
|
|
68
|
+
getQueryParams() {
|
|
69
|
+
return this.req.query;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Gets a specific query parameter by name.
|
|
73
|
+
*
|
|
74
|
+
* @param name - The query parameter name
|
|
75
|
+
* @returns The query parameter value(s) or undefined
|
|
76
|
+
*/
|
|
77
|
+
getQueryParam(name) {
|
|
78
|
+
const value = this.req.query[name];
|
|
79
|
+
return value;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Gets the parsed request body.
|
|
83
|
+
* Requires express.json() or express.urlencoded() middleware.
|
|
84
|
+
*
|
|
85
|
+
* @returns The parsed request body
|
|
86
|
+
*/
|
|
87
|
+
getBody() {
|
|
88
|
+
return this.req.body;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { PaywallConfig, PaywallProvider, x402HTTPResourceServer, x402ResourceServer, RoutesConfig, FacilitatorClient } from '@x402/core/server';
|
|
2
|
+
import { SchemeNetworkServer, Network } from '@x402/core/types';
|
|
3
|
+
import { NextFunction, Request, Response } from 'express';
|
|
4
|
+
/**
|
|
5
|
+
* Configuration for payment tab handling
|
|
6
|
+
*/
|
|
7
|
+
interface TabConfig {
|
|
8
|
+
/**
|
|
9
|
+
* The full URL endpoint for opening payment tabs. This URL is injected into
|
|
10
|
+
* paymentRequirements.extra and clients use it to open a payment tab.
|
|
11
|
+
* When a request matches this endpoint's path, the middleware will parse
|
|
12
|
+
* the request body and call the 4mica facilitator to open a tab.
|
|
13
|
+
*
|
|
14
|
+
* @example "https://api.example.com/x402/tab"
|
|
15
|
+
*/
|
|
16
|
+
advertisedEndpoint: string;
|
|
17
|
+
/**
|
|
18
|
+
* The lifetime of the payment tab in seconds. Defines how long the tab
|
|
19
|
+
* remains valid before expiring.
|
|
20
|
+
*
|
|
21
|
+
* @example 3600 // 1 hour
|
|
22
|
+
*/
|
|
23
|
+
ttlSeconds?: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Configuration for registering a payment scheme with a specific network
|
|
27
|
+
*/
|
|
28
|
+
export interface SchemeRegistration {
|
|
29
|
+
/**
|
|
30
|
+
* The network identifier (e.g., 'eip155:84532', 'solana:mainnet')
|
|
31
|
+
*/
|
|
32
|
+
network: Network;
|
|
33
|
+
/**
|
|
34
|
+
* The scheme server implementation for this network
|
|
35
|
+
*/
|
|
36
|
+
server: SchemeNetworkServer;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Express payment middleware for x402 protocol (direct HTTP server instance).
|
|
40
|
+
*
|
|
41
|
+
* Use this when you need to configure HTTP-level hooks.
|
|
42
|
+
*
|
|
43
|
+
* @param httpServer - Pre-configured x402HTTPResourceServer instance
|
|
44
|
+
* @param tabConfig - Configuration for payment tab handling (endpoint URL and TTL)
|
|
45
|
+
* @param paywallConfig - Optional configuration for the built-in paywall UI
|
|
46
|
+
* @param paywall - Optional custom paywall provider (overrides default)
|
|
47
|
+
* @param syncFacilitatorOnStart - Whether to sync with the facilitator on startup (defaults to true)
|
|
48
|
+
* @returns Express middleware handler
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```typescript
|
|
52
|
+
* import { paymentMiddlewareFromHTTPServer, x402ResourceServer, x402HTTPResourceServer } from "@x402/express";
|
|
53
|
+
*
|
|
54
|
+
* const resourceServer = new x402ResourceServer(facilitatorClient)
|
|
55
|
+
* .register(NETWORK, new ExactEvmScheme())
|
|
56
|
+
*
|
|
57
|
+
* const httpServer = new x402HTTPResourceServer(resourceServer, routes)
|
|
58
|
+
* .onProtectedRequest(requestHook);
|
|
59
|
+
*
|
|
60
|
+
* app.use(paymentMiddlewareFromHTTPServer(
|
|
61
|
+
* httpServer,
|
|
62
|
+
* { advertisedEndpoint: "https://api.example.com/x402/tab" },
|
|
63
|
+
* )); * ```
|
|
64
|
+
*/
|
|
65
|
+
export declare function paymentMiddlewareFromHTTPServer(httpServer: x402HTTPResourceServer, tabConfig: TabConfig, paywallConfig?: PaywallConfig, paywall?: PaywallProvider, syncFacilitatorOnStart?: boolean): (req: Request, res: Response, next: NextFunction) => Promise<void | Response<any, Record<string, any>>>;
|
|
66
|
+
/**
|
|
67
|
+
* Express payment middleware for x402 protocol (direct server instance).
|
|
68
|
+
*
|
|
69
|
+
* Use this when you want to pass a pre-configured x402ResourceServer instance.
|
|
70
|
+
* This provides more flexibility for testing, custom configuration, and reusing
|
|
71
|
+
* server instances across multiple middlewares.
|
|
72
|
+
*
|
|
73
|
+
* @param routes - Route configurations for protected endpoints
|
|
74
|
+
* @param server - Pre-configured x402ResourceServer instance
|
|
75
|
+
* @param tabConfig - Configuration for payment tab handling (endpoint URL and TTL)
|
|
76
|
+
* @param paywallConfig - Optional configuration for the built-in paywall UI
|
|
77
|
+
* @param paywall - Optional custom paywall provider (overrides default)
|
|
78
|
+
* @param syncFacilitatorOnStart - Whether to sync with the facilitator on startup (defaults to true)
|
|
79
|
+
* @returns Express middleware handler
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```typescript
|
|
83
|
+
* import { paymentMiddleware } from "@x402/express";
|
|
84
|
+
*
|
|
85
|
+
* const server = new x402ResourceServer(myFacilitatorClient)
|
|
86
|
+
* .register(NETWORK, new ExactEvmScheme());
|
|
87
|
+
*
|
|
88
|
+
* app.use(paymentMiddleware(
|
|
89
|
+
* routes,
|
|
90
|
+
* server,
|
|
91
|
+
* { advertisedEndpoint: "https://api.example.com/x402/tab" },
|
|
92
|
+
* ));
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export declare function paymentMiddleware(routes: RoutesConfig, server: x402ResourceServer, tabConfig: TabConfig, paywallConfig?: PaywallConfig, paywall?: PaywallProvider, syncFacilitatorOnStart?: boolean): (req: Request, res: Response, next: NextFunction) => Promise<void | Response<any, Record<string, any>>>;
|
|
96
|
+
/**
|
|
97
|
+
* Express payment middleware for x402 protocol (configuration-based).
|
|
98
|
+
*
|
|
99
|
+
* Use this when you want to quickly set up middleware with simple configuration.
|
|
100
|
+
* This function creates and configures the x402ResourceServer internally.
|
|
101
|
+
*
|
|
102
|
+
* @param routes - Route configurations for protected endpoints
|
|
103
|
+
* @param tabConfig - Configuration for payment tab handling
|
|
104
|
+
* @param facilitatorClients - Optional facilitator client(s) for payment processing
|
|
105
|
+
* @param schemes - Optional array of scheme registrations for server-side payment processing
|
|
106
|
+
* @param paywallConfig - Optional configuration for the built-in paywall UI
|
|
107
|
+
* @param paywall - Optional custom paywall provider (overrides default)
|
|
108
|
+
* @param syncFacilitatorOnStart - Whether to sync with the facilitator on startup (defaults to true)
|
|
109
|
+
* @returns Express middleware handler
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```typescript
|
|
113
|
+
* import { paymentMiddlewareFromConfig } from "@x402/express";
|
|
114
|
+
*
|
|
115
|
+
* app.use(paymentMiddlewareFromConfig(
|
|
116
|
+
* routes,
|
|
117
|
+
* { advertisedEndpoint: "https://api.example.com/x402/tab" },
|
|
118
|
+
* ));
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
export declare function paymentMiddlewareFromConfig(routes: RoutesConfig, tabConfig: TabConfig, facilitatorClients?: FacilitatorClient | FacilitatorClient[], schemes?: SchemeRegistration[], paywallConfig?: PaywallConfig, paywall?: PaywallProvider, syncFacilitatorOnStart?: boolean): (req: Request, res: Response, next: NextFunction) => Promise<void | Response<any, Record<string, any>>>;
|
|
122
|
+
export { ExpressAdapter } from './adapter.js';
|