@dexterai/x402 1.0.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/LICENSE +22 -0
- package/README.md +254 -0
- package/assets/dexter-wordmark.svg +30 -0
- package/dist/adapters/index.cjs +481 -0
- package/dist/adapters/index.cjs.map +1 -0
- package/dist/adapters/index.d.cts +15 -0
- package/dist/adapters/index.d.ts +15 -0
- package/dist/adapters/index.js +473 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/client/index.cjs +602 -0
- package/dist/client/index.cjs.map +1 -0
- package/dist/client/index.d.cts +4 -0
- package/dist/client/index.d.ts +4 -0
- package/dist/client/index.js +583 -0
- package/dist/client/index.js.map +1 -0
- package/dist/evm-B4mhmeNZ.d.cts +120 -0
- package/dist/evm-av6iEAW7.d.ts +120 -0
- package/dist/react/index.cjs +827 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +108 -0
- package/dist/react/index.d.ts +108 -0
- package/dist/react/index.js +814 -0
- package/dist/react/index.js.map +1 -0
- package/dist/server/index.cjs +295 -0
- package/dist/server/index.cjs.map +1 -0
- package/dist/server/index.d.cts +191 -0
- package/dist/server/index.d.ts +191 -0
- package/dist/server/index.js +262 -0
- package/dist/server/index.js.map +1 -0
- package/dist/types-DkTxHOns.d.cts +119 -0
- package/dist/types-DkTxHOns.d.ts +119 -0
- package/dist/types-uljmYAuY.d.ts +118 -0
- package/dist/types-vWD8uj2B.d.cts +118 -0
- package/dist/x402-client-BSWNMJbm.d.ts +84 -0
- package/dist/x402-client-DUipGiRr.d.cts +84 -0
- package/package.json +99 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { P as PaymentAccept, V as VerifyResponse, S as SettleResponse, a as PaymentRequired } from '../types-DkTxHOns.cjs';
|
|
2
|
+
export { B as BASE_MAINNET_NETWORK, D as DEXTER_FACILITATOR_URL, b as SOLANA_MAINNET_NETWORK, c as USDC_BASE, U as USDC_MINT } from '../types-DkTxHOns.cjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Facilitator Client
|
|
6
|
+
*
|
|
7
|
+
* Communicates with the x402 facilitator for:
|
|
8
|
+
* - /supported - Get supported payment schemes and fee payer addresses
|
|
9
|
+
* - /verify - Verify a payment signature before processing
|
|
10
|
+
* - /settle - Submit the payment for execution
|
|
11
|
+
*
|
|
12
|
+
* Works with any x402 v2 facilitator (Dexter or others).
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Supported payment kind from facilitator /supported endpoint
|
|
17
|
+
*/
|
|
18
|
+
interface SupportedKind {
|
|
19
|
+
x402Version: number;
|
|
20
|
+
scheme: string;
|
|
21
|
+
network: string;
|
|
22
|
+
extra?: {
|
|
23
|
+
feePayer?: string;
|
|
24
|
+
decimals?: number;
|
|
25
|
+
name?: string;
|
|
26
|
+
version?: string;
|
|
27
|
+
[key: string]: unknown;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Response from facilitator /supported endpoint
|
|
32
|
+
*/
|
|
33
|
+
interface SupportedResponse {
|
|
34
|
+
kinds: SupportedKind[];
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Client for communicating with an x402 v2 facilitator
|
|
38
|
+
*/
|
|
39
|
+
declare class FacilitatorClient {
|
|
40
|
+
private facilitatorUrl;
|
|
41
|
+
private cachedSupported;
|
|
42
|
+
private cacheTime;
|
|
43
|
+
private readonly CACHE_TTL_MS;
|
|
44
|
+
constructor(facilitatorUrl?: string);
|
|
45
|
+
/**
|
|
46
|
+
* Get supported payment kinds from the facilitator
|
|
47
|
+
* Results are cached for 1 minute to reduce network calls
|
|
48
|
+
*/
|
|
49
|
+
getSupported(): Promise<SupportedResponse>;
|
|
50
|
+
/**
|
|
51
|
+
* Get the fee payer address for a specific network
|
|
52
|
+
*
|
|
53
|
+
* @param network - CAIP-2 network identifier
|
|
54
|
+
* @returns Fee payer address
|
|
55
|
+
*/
|
|
56
|
+
getFeePayer(network: string): Promise<string>;
|
|
57
|
+
/**
|
|
58
|
+
* Get extra data for a network (feePayer, decimals, EIP-712 data, etc.)
|
|
59
|
+
*
|
|
60
|
+
* @param network - CAIP-2 network identifier
|
|
61
|
+
* @returns Extra data from /supported
|
|
62
|
+
*/
|
|
63
|
+
getNetworkExtra(network: string): Promise<SupportedKind['extra']>;
|
|
64
|
+
/**
|
|
65
|
+
* Verify a payment with the facilitator
|
|
66
|
+
*
|
|
67
|
+
* @param paymentSignatureHeader - Base64-encoded PAYMENT-SIGNATURE header value
|
|
68
|
+
* @param requirements - The payment requirements that were sent to the client
|
|
69
|
+
* @returns Verification response
|
|
70
|
+
*/
|
|
71
|
+
verifyPayment(paymentSignatureHeader: string, requirements: PaymentAccept): Promise<VerifyResponse>;
|
|
72
|
+
/**
|
|
73
|
+
* Settle a payment with the facilitator
|
|
74
|
+
*
|
|
75
|
+
* @param paymentSignatureHeader - Base64-encoded PAYMENT-SIGNATURE header value
|
|
76
|
+
* @param requirements - The payment requirements that were sent to the client
|
|
77
|
+
* @returns Settlement response with transaction signature on success
|
|
78
|
+
*/
|
|
79
|
+
settlePayment(paymentSignatureHeader: string, requirements: PaymentAccept): Promise<SettleResponse>;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* x402 v2 Server
|
|
84
|
+
*
|
|
85
|
+
* Server-side helpers for accepting x402 payments.
|
|
86
|
+
* Chain-agnostic - works with Solana, Base, and any x402-compatible network.
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```typescript
|
|
90
|
+
* import { createX402Server } from '@dexterai/x402/server';
|
|
91
|
+
*
|
|
92
|
+
* const server = createX402Server({
|
|
93
|
+
* payTo: 'YourAddress...',
|
|
94
|
+
* network: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
|
|
95
|
+
* });
|
|
96
|
+
*
|
|
97
|
+
* // Handle 402 responses
|
|
98
|
+
* if (!paymentSignature) {
|
|
99
|
+
* const requirements = await server.buildRequirements({
|
|
100
|
+
* amountAtomic: '50000',
|
|
101
|
+
* resourceUrl: '/api/protected',
|
|
102
|
+
* });
|
|
103
|
+
* res.setHeader('PAYMENT-REQUIRED', server.encodeRequirements(requirements));
|
|
104
|
+
* res.status(402).json({});
|
|
105
|
+
* return;
|
|
106
|
+
* }
|
|
107
|
+
*
|
|
108
|
+
* // Verify and settle
|
|
109
|
+
* const verify = await server.verifyPayment(paymentSignature);
|
|
110
|
+
* if (!verify.isValid) throw new Error(verify.invalidReason);
|
|
111
|
+
*
|
|
112
|
+
* const settle = await server.settlePayment(paymentSignature);
|
|
113
|
+
* if (!settle.success) throw new Error(settle.errorReason);
|
|
114
|
+
*
|
|
115
|
+
* // Payment successful!
|
|
116
|
+
* res.json({ transaction: settle.transaction });
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Asset configuration
|
|
122
|
+
*/
|
|
123
|
+
interface AssetConfig {
|
|
124
|
+
/** Token address (mint on Solana, contract on EVM) */
|
|
125
|
+
address: string;
|
|
126
|
+
/** Token decimals */
|
|
127
|
+
decimals: number;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Server configuration
|
|
131
|
+
*/
|
|
132
|
+
interface X402ServerConfig {
|
|
133
|
+
/** Address to receive payments */
|
|
134
|
+
payTo: string;
|
|
135
|
+
/** Facilitator URL (defaults to Dexter) */
|
|
136
|
+
facilitatorUrl?: string;
|
|
137
|
+
/** CAIP-2 network identifier */
|
|
138
|
+
network?: string;
|
|
139
|
+
/** Asset configuration (defaults to USDC) */
|
|
140
|
+
asset?: AssetConfig;
|
|
141
|
+
/** Default payment timeout in seconds */
|
|
142
|
+
defaultTimeoutSeconds?: number;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Options for building payment requirements
|
|
146
|
+
*/
|
|
147
|
+
interface BuildRequirementsOptions {
|
|
148
|
+
/** Amount in atomic units (e.g., '50000' for 0.05 USDC) */
|
|
149
|
+
amountAtomic: string;
|
|
150
|
+
/** Full URL of the resource */
|
|
151
|
+
resourceUrl: string;
|
|
152
|
+
/** Human-readable description */
|
|
153
|
+
description?: string;
|
|
154
|
+
/** MIME type of the response */
|
|
155
|
+
mimeType?: string;
|
|
156
|
+
/** Override timeout for this request */
|
|
157
|
+
timeoutSeconds?: number;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* x402 Server interface
|
|
161
|
+
*/
|
|
162
|
+
interface X402Server {
|
|
163
|
+
/** Build payment requirements (fetches feePayer from facilitator) */
|
|
164
|
+
buildRequirements(options: BuildRequirementsOptions): Promise<PaymentRequired>;
|
|
165
|
+
/** Encode requirements for PAYMENT-REQUIRED header */
|
|
166
|
+
encodeRequirements(requirements: PaymentRequired): string;
|
|
167
|
+
/** Create complete 402 response object */
|
|
168
|
+
create402Response(requirements: PaymentRequired): {
|
|
169
|
+
status: 402;
|
|
170
|
+
headers: {
|
|
171
|
+
'PAYMENT-REQUIRED': string;
|
|
172
|
+
};
|
|
173
|
+
body: Record<string, unknown>;
|
|
174
|
+
};
|
|
175
|
+
/** Verify payment with facilitator */
|
|
176
|
+
verifyPayment(paymentSignatureHeader: string, requirements?: PaymentAccept): Promise<VerifyResponse>;
|
|
177
|
+
/** Settle payment via facilitator */
|
|
178
|
+
settlePayment(paymentSignatureHeader: string, requirements?: PaymentAccept): Promise<SettleResponse>;
|
|
179
|
+
/** Get PaymentAccept for verify/settle */
|
|
180
|
+
getPaymentAccept(options: BuildRequirementsOptions): Promise<PaymentAccept>;
|
|
181
|
+
/** Get network this server is configured for */
|
|
182
|
+
readonly network: string;
|
|
183
|
+
/** Get facilitator client for advanced usage */
|
|
184
|
+
readonly facilitator: FacilitatorClient;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Create an x402 server for accepting payments
|
|
188
|
+
*/
|
|
189
|
+
declare function createX402Server(config: X402ServerConfig): X402Server;
|
|
190
|
+
|
|
191
|
+
export { type AssetConfig, type BuildRequirementsOptions, FacilitatorClient, PaymentAccept, PaymentRequired, SettleResponse, type SupportedKind, type SupportedResponse, VerifyResponse, type X402Server, type X402ServerConfig, createX402Server };
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { P as PaymentAccept, V as VerifyResponse, S as SettleResponse, a as PaymentRequired } from '../types-DkTxHOns.js';
|
|
2
|
+
export { B as BASE_MAINNET_NETWORK, D as DEXTER_FACILITATOR_URL, b as SOLANA_MAINNET_NETWORK, c as USDC_BASE, U as USDC_MINT } from '../types-DkTxHOns.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Facilitator Client
|
|
6
|
+
*
|
|
7
|
+
* Communicates with the x402 facilitator for:
|
|
8
|
+
* - /supported - Get supported payment schemes and fee payer addresses
|
|
9
|
+
* - /verify - Verify a payment signature before processing
|
|
10
|
+
* - /settle - Submit the payment for execution
|
|
11
|
+
*
|
|
12
|
+
* Works with any x402 v2 facilitator (Dexter or others).
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Supported payment kind from facilitator /supported endpoint
|
|
17
|
+
*/
|
|
18
|
+
interface SupportedKind {
|
|
19
|
+
x402Version: number;
|
|
20
|
+
scheme: string;
|
|
21
|
+
network: string;
|
|
22
|
+
extra?: {
|
|
23
|
+
feePayer?: string;
|
|
24
|
+
decimals?: number;
|
|
25
|
+
name?: string;
|
|
26
|
+
version?: string;
|
|
27
|
+
[key: string]: unknown;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Response from facilitator /supported endpoint
|
|
32
|
+
*/
|
|
33
|
+
interface SupportedResponse {
|
|
34
|
+
kinds: SupportedKind[];
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Client for communicating with an x402 v2 facilitator
|
|
38
|
+
*/
|
|
39
|
+
declare class FacilitatorClient {
|
|
40
|
+
private facilitatorUrl;
|
|
41
|
+
private cachedSupported;
|
|
42
|
+
private cacheTime;
|
|
43
|
+
private readonly CACHE_TTL_MS;
|
|
44
|
+
constructor(facilitatorUrl?: string);
|
|
45
|
+
/**
|
|
46
|
+
* Get supported payment kinds from the facilitator
|
|
47
|
+
* Results are cached for 1 minute to reduce network calls
|
|
48
|
+
*/
|
|
49
|
+
getSupported(): Promise<SupportedResponse>;
|
|
50
|
+
/**
|
|
51
|
+
* Get the fee payer address for a specific network
|
|
52
|
+
*
|
|
53
|
+
* @param network - CAIP-2 network identifier
|
|
54
|
+
* @returns Fee payer address
|
|
55
|
+
*/
|
|
56
|
+
getFeePayer(network: string): Promise<string>;
|
|
57
|
+
/**
|
|
58
|
+
* Get extra data for a network (feePayer, decimals, EIP-712 data, etc.)
|
|
59
|
+
*
|
|
60
|
+
* @param network - CAIP-2 network identifier
|
|
61
|
+
* @returns Extra data from /supported
|
|
62
|
+
*/
|
|
63
|
+
getNetworkExtra(network: string): Promise<SupportedKind['extra']>;
|
|
64
|
+
/**
|
|
65
|
+
* Verify a payment with the facilitator
|
|
66
|
+
*
|
|
67
|
+
* @param paymentSignatureHeader - Base64-encoded PAYMENT-SIGNATURE header value
|
|
68
|
+
* @param requirements - The payment requirements that were sent to the client
|
|
69
|
+
* @returns Verification response
|
|
70
|
+
*/
|
|
71
|
+
verifyPayment(paymentSignatureHeader: string, requirements: PaymentAccept): Promise<VerifyResponse>;
|
|
72
|
+
/**
|
|
73
|
+
* Settle a payment with the facilitator
|
|
74
|
+
*
|
|
75
|
+
* @param paymentSignatureHeader - Base64-encoded PAYMENT-SIGNATURE header value
|
|
76
|
+
* @param requirements - The payment requirements that were sent to the client
|
|
77
|
+
* @returns Settlement response with transaction signature on success
|
|
78
|
+
*/
|
|
79
|
+
settlePayment(paymentSignatureHeader: string, requirements: PaymentAccept): Promise<SettleResponse>;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* x402 v2 Server
|
|
84
|
+
*
|
|
85
|
+
* Server-side helpers for accepting x402 payments.
|
|
86
|
+
* Chain-agnostic - works with Solana, Base, and any x402-compatible network.
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```typescript
|
|
90
|
+
* import { createX402Server } from '@dexterai/x402/server';
|
|
91
|
+
*
|
|
92
|
+
* const server = createX402Server({
|
|
93
|
+
* payTo: 'YourAddress...',
|
|
94
|
+
* network: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
|
|
95
|
+
* });
|
|
96
|
+
*
|
|
97
|
+
* // Handle 402 responses
|
|
98
|
+
* if (!paymentSignature) {
|
|
99
|
+
* const requirements = await server.buildRequirements({
|
|
100
|
+
* amountAtomic: '50000',
|
|
101
|
+
* resourceUrl: '/api/protected',
|
|
102
|
+
* });
|
|
103
|
+
* res.setHeader('PAYMENT-REQUIRED', server.encodeRequirements(requirements));
|
|
104
|
+
* res.status(402).json({});
|
|
105
|
+
* return;
|
|
106
|
+
* }
|
|
107
|
+
*
|
|
108
|
+
* // Verify and settle
|
|
109
|
+
* const verify = await server.verifyPayment(paymentSignature);
|
|
110
|
+
* if (!verify.isValid) throw new Error(verify.invalidReason);
|
|
111
|
+
*
|
|
112
|
+
* const settle = await server.settlePayment(paymentSignature);
|
|
113
|
+
* if (!settle.success) throw new Error(settle.errorReason);
|
|
114
|
+
*
|
|
115
|
+
* // Payment successful!
|
|
116
|
+
* res.json({ transaction: settle.transaction });
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Asset configuration
|
|
122
|
+
*/
|
|
123
|
+
interface AssetConfig {
|
|
124
|
+
/** Token address (mint on Solana, contract on EVM) */
|
|
125
|
+
address: string;
|
|
126
|
+
/** Token decimals */
|
|
127
|
+
decimals: number;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Server configuration
|
|
131
|
+
*/
|
|
132
|
+
interface X402ServerConfig {
|
|
133
|
+
/** Address to receive payments */
|
|
134
|
+
payTo: string;
|
|
135
|
+
/** Facilitator URL (defaults to Dexter) */
|
|
136
|
+
facilitatorUrl?: string;
|
|
137
|
+
/** CAIP-2 network identifier */
|
|
138
|
+
network?: string;
|
|
139
|
+
/** Asset configuration (defaults to USDC) */
|
|
140
|
+
asset?: AssetConfig;
|
|
141
|
+
/** Default payment timeout in seconds */
|
|
142
|
+
defaultTimeoutSeconds?: number;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Options for building payment requirements
|
|
146
|
+
*/
|
|
147
|
+
interface BuildRequirementsOptions {
|
|
148
|
+
/** Amount in atomic units (e.g., '50000' for 0.05 USDC) */
|
|
149
|
+
amountAtomic: string;
|
|
150
|
+
/** Full URL of the resource */
|
|
151
|
+
resourceUrl: string;
|
|
152
|
+
/** Human-readable description */
|
|
153
|
+
description?: string;
|
|
154
|
+
/** MIME type of the response */
|
|
155
|
+
mimeType?: string;
|
|
156
|
+
/** Override timeout for this request */
|
|
157
|
+
timeoutSeconds?: number;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* x402 Server interface
|
|
161
|
+
*/
|
|
162
|
+
interface X402Server {
|
|
163
|
+
/** Build payment requirements (fetches feePayer from facilitator) */
|
|
164
|
+
buildRequirements(options: BuildRequirementsOptions): Promise<PaymentRequired>;
|
|
165
|
+
/** Encode requirements for PAYMENT-REQUIRED header */
|
|
166
|
+
encodeRequirements(requirements: PaymentRequired): string;
|
|
167
|
+
/** Create complete 402 response object */
|
|
168
|
+
create402Response(requirements: PaymentRequired): {
|
|
169
|
+
status: 402;
|
|
170
|
+
headers: {
|
|
171
|
+
'PAYMENT-REQUIRED': string;
|
|
172
|
+
};
|
|
173
|
+
body: Record<string, unknown>;
|
|
174
|
+
};
|
|
175
|
+
/** Verify payment with facilitator */
|
|
176
|
+
verifyPayment(paymentSignatureHeader: string, requirements?: PaymentAccept): Promise<VerifyResponse>;
|
|
177
|
+
/** Settle payment via facilitator */
|
|
178
|
+
settlePayment(paymentSignatureHeader: string, requirements?: PaymentAccept): Promise<SettleResponse>;
|
|
179
|
+
/** Get PaymentAccept for verify/settle */
|
|
180
|
+
getPaymentAccept(options: BuildRequirementsOptions): Promise<PaymentAccept>;
|
|
181
|
+
/** Get network this server is configured for */
|
|
182
|
+
readonly network: string;
|
|
183
|
+
/** Get facilitator client for advanced usage */
|
|
184
|
+
readonly facilitator: FacilitatorClient;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Create an x402 server for accepting payments
|
|
188
|
+
*/
|
|
189
|
+
declare function createX402Server(config: X402ServerConfig): X402Server;
|
|
190
|
+
|
|
191
|
+
export { type AssetConfig, type BuildRequirementsOptions, FacilitatorClient, PaymentAccept, PaymentRequired, SettleResponse, type SupportedKind, type SupportedResponse, VerifyResponse, type X402Server, type X402ServerConfig, createX402Server };
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
// src/types.ts
|
|
2
|
+
var SOLANA_MAINNET_NETWORK = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
|
|
3
|
+
var BASE_MAINNET_NETWORK = "eip155:8453";
|
|
4
|
+
var USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
|
|
5
|
+
var USDC_BASE = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
6
|
+
var DEXTER_FACILITATOR_URL = "https://x402.dexter.cash";
|
|
7
|
+
|
|
8
|
+
// src/utils.ts
|
|
9
|
+
function decodeBase64Json(encoded) {
|
|
10
|
+
return JSON.parse(atob(encoded));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// src/server/facilitator-client.ts
|
|
14
|
+
var FacilitatorClient = class {
|
|
15
|
+
facilitatorUrl;
|
|
16
|
+
cachedSupported = null;
|
|
17
|
+
cacheTime = 0;
|
|
18
|
+
CACHE_TTL_MS = 6e4;
|
|
19
|
+
// 1 minute cache
|
|
20
|
+
constructor(facilitatorUrl = DEXTER_FACILITATOR_URL) {
|
|
21
|
+
this.facilitatorUrl = facilitatorUrl.replace(/\/$/, "");
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Get supported payment kinds from the facilitator
|
|
25
|
+
* Results are cached for 1 minute to reduce network calls
|
|
26
|
+
*/
|
|
27
|
+
async getSupported() {
|
|
28
|
+
const now = Date.now();
|
|
29
|
+
if (this.cachedSupported && now - this.cacheTime < this.CACHE_TTL_MS) {
|
|
30
|
+
return this.cachedSupported;
|
|
31
|
+
}
|
|
32
|
+
const response = await fetch(`${this.facilitatorUrl}/supported`);
|
|
33
|
+
if (!response.ok) {
|
|
34
|
+
throw new Error(`Facilitator /supported returned ${response.status}`);
|
|
35
|
+
}
|
|
36
|
+
this.cachedSupported = await response.json();
|
|
37
|
+
this.cacheTime = now;
|
|
38
|
+
return this.cachedSupported;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Get the fee payer address for a specific network
|
|
42
|
+
*
|
|
43
|
+
* @param network - CAIP-2 network identifier
|
|
44
|
+
* @returns Fee payer address
|
|
45
|
+
*/
|
|
46
|
+
async getFeePayer(network) {
|
|
47
|
+
const supported = await this.getSupported();
|
|
48
|
+
const kind = supported.kinds.find(
|
|
49
|
+
(k) => k.x402Version === 2 && k.scheme === "exact" && k.network === network
|
|
50
|
+
);
|
|
51
|
+
if (!kind?.extra?.feePayer) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`Facilitator does not support network "${network}" with scheme "exact", or feePayer not provided`
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
return kind.extra.feePayer;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Get extra data for a network (feePayer, decimals, EIP-712 data, etc.)
|
|
60
|
+
*
|
|
61
|
+
* @param network - CAIP-2 network identifier
|
|
62
|
+
* @returns Extra data from /supported
|
|
63
|
+
*/
|
|
64
|
+
async getNetworkExtra(network) {
|
|
65
|
+
const supported = await this.getSupported();
|
|
66
|
+
const kind = supported.kinds.find(
|
|
67
|
+
(k) => k.x402Version === 2 && k.scheme === "exact" && k.network === network
|
|
68
|
+
);
|
|
69
|
+
return kind?.extra;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Verify a payment with the facilitator
|
|
73
|
+
*
|
|
74
|
+
* @param paymentSignatureHeader - Base64-encoded PAYMENT-SIGNATURE header value
|
|
75
|
+
* @param requirements - The payment requirements that were sent to the client
|
|
76
|
+
* @returns Verification response
|
|
77
|
+
*/
|
|
78
|
+
async verifyPayment(paymentSignatureHeader, requirements) {
|
|
79
|
+
try {
|
|
80
|
+
const paymentPayload = decodeBase64Json(paymentSignatureHeader);
|
|
81
|
+
const verifyPayload = {
|
|
82
|
+
paymentPayload,
|
|
83
|
+
paymentRequirements: requirements
|
|
84
|
+
};
|
|
85
|
+
const response = await fetch(`${this.facilitatorUrl}/verify`, {
|
|
86
|
+
method: "POST",
|
|
87
|
+
headers: {
|
|
88
|
+
"Content-Type": "application/json"
|
|
89
|
+
},
|
|
90
|
+
body: JSON.stringify(verifyPayload)
|
|
91
|
+
});
|
|
92
|
+
if (!response.ok) {
|
|
93
|
+
const errorText = await response.text();
|
|
94
|
+
console.error(`Facilitator /verify returned ${response.status}:`, errorText);
|
|
95
|
+
return {
|
|
96
|
+
isValid: false,
|
|
97
|
+
invalidReason: `facilitator_error_${response.status}`
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
return await response.json();
|
|
101
|
+
} catch (error) {
|
|
102
|
+
console.error("Payment verification failed:", error);
|
|
103
|
+
return {
|
|
104
|
+
isValid: false,
|
|
105
|
+
invalidReason: error instanceof Error ? error.message : "unexpected_verify_error"
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Settle a payment with the facilitator
|
|
111
|
+
*
|
|
112
|
+
* @param paymentSignatureHeader - Base64-encoded PAYMENT-SIGNATURE header value
|
|
113
|
+
* @param requirements - The payment requirements that were sent to the client
|
|
114
|
+
* @returns Settlement response with transaction signature on success
|
|
115
|
+
*/
|
|
116
|
+
async settlePayment(paymentSignatureHeader, requirements) {
|
|
117
|
+
try {
|
|
118
|
+
const paymentPayload = decodeBase64Json(paymentSignatureHeader);
|
|
119
|
+
const settlePayload = {
|
|
120
|
+
paymentPayload,
|
|
121
|
+
paymentRequirements: requirements
|
|
122
|
+
};
|
|
123
|
+
const response = await fetch(`${this.facilitatorUrl}/settle`, {
|
|
124
|
+
method: "POST",
|
|
125
|
+
headers: {
|
|
126
|
+
"Content-Type": "application/json"
|
|
127
|
+
},
|
|
128
|
+
body: JSON.stringify(settlePayload)
|
|
129
|
+
});
|
|
130
|
+
if (!response.ok) {
|
|
131
|
+
const errorText = await response.text();
|
|
132
|
+
console.error(`Facilitator /settle returned ${response.status}:`, errorText);
|
|
133
|
+
return {
|
|
134
|
+
success: false,
|
|
135
|
+
network: requirements.network,
|
|
136
|
+
errorReason: `facilitator_error_${response.status}`
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
const result = await response.json();
|
|
140
|
+
return {
|
|
141
|
+
...result,
|
|
142
|
+
network: requirements.network
|
|
143
|
+
};
|
|
144
|
+
} catch (error) {
|
|
145
|
+
console.error("Payment settlement failed:", error);
|
|
146
|
+
return {
|
|
147
|
+
success: false,
|
|
148
|
+
network: requirements.network,
|
|
149
|
+
errorReason: error instanceof Error ? error.message : "unexpected_settle_error"
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// src/server/x402-server.ts
|
|
156
|
+
function createX402Server(config) {
|
|
157
|
+
const {
|
|
158
|
+
payTo,
|
|
159
|
+
facilitatorUrl = DEXTER_FACILITATOR_URL,
|
|
160
|
+
network = SOLANA_MAINNET_NETWORK,
|
|
161
|
+
asset = { address: USDC_MINT, decimals: 6 },
|
|
162
|
+
defaultTimeoutSeconds = 60
|
|
163
|
+
} = config;
|
|
164
|
+
const facilitator = new FacilitatorClient(facilitatorUrl);
|
|
165
|
+
let cachedExtra = null;
|
|
166
|
+
async function getNetworkExtra() {
|
|
167
|
+
if (!cachedExtra) {
|
|
168
|
+
cachedExtra = await facilitator.getNetworkExtra(network);
|
|
169
|
+
}
|
|
170
|
+
if (!cachedExtra?.feePayer) {
|
|
171
|
+
throw new Error(`Facilitator does not provide feePayer for network "${network}"`);
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
feePayer: cachedExtra.feePayer,
|
|
175
|
+
decimals: cachedExtra.decimals ?? asset.decimals,
|
|
176
|
+
// Include any additional EIP-712 data for EVM chains
|
|
177
|
+
name: cachedExtra.name,
|
|
178
|
+
version: cachedExtra.version
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
async function getPaymentAccept(options) {
|
|
182
|
+
const {
|
|
183
|
+
amountAtomic,
|
|
184
|
+
timeoutSeconds = defaultTimeoutSeconds
|
|
185
|
+
} = options;
|
|
186
|
+
const extra = await getNetworkExtra();
|
|
187
|
+
return {
|
|
188
|
+
scheme: "exact",
|
|
189
|
+
network,
|
|
190
|
+
amount: amountAtomic,
|
|
191
|
+
asset: asset.address,
|
|
192
|
+
payTo,
|
|
193
|
+
maxTimeoutSeconds: timeoutSeconds,
|
|
194
|
+
extra
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
async function buildRequirements(options) {
|
|
198
|
+
const {
|
|
199
|
+
resourceUrl,
|
|
200
|
+
description,
|
|
201
|
+
mimeType = "application/json"
|
|
202
|
+
} = options;
|
|
203
|
+
const resource = {
|
|
204
|
+
url: resourceUrl,
|
|
205
|
+
description,
|
|
206
|
+
mimeType
|
|
207
|
+
};
|
|
208
|
+
const accept = await getPaymentAccept(options);
|
|
209
|
+
return {
|
|
210
|
+
x402Version: 2,
|
|
211
|
+
resource,
|
|
212
|
+
accepts: [accept],
|
|
213
|
+
error: "Payment required"
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
function encodeRequirements(requirements) {
|
|
217
|
+
return btoa(JSON.stringify(requirements));
|
|
218
|
+
}
|
|
219
|
+
function create402Response(requirements) {
|
|
220
|
+
return {
|
|
221
|
+
status: 402,
|
|
222
|
+
headers: {
|
|
223
|
+
"PAYMENT-REQUIRED": encodeRequirements(requirements)
|
|
224
|
+
},
|
|
225
|
+
body: {}
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
async function verifyPayment(paymentSignatureHeader, requirements) {
|
|
229
|
+
const req = requirements || await getPaymentAccept({
|
|
230
|
+
amountAtomic: "0",
|
|
231
|
+
resourceUrl: ""
|
|
232
|
+
});
|
|
233
|
+
return facilitator.verifyPayment(paymentSignatureHeader, req);
|
|
234
|
+
}
|
|
235
|
+
async function settlePayment(paymentSignatureHeader, requirements) {
|
|
236
|
+
const req = requirements || await getPaymentAccept({
|
|
237
|
+
amountAtomic: "0",
|
|
238
|
+
resourceUrl: ""
|
|
239
|
+
});
|
|
240
|
+
return facilitator.settlePayment(paymentSignatureHeader, req);
|
|
241
|
+
}
|
|
242
|
+
return {
|
|
243
|
+
buildRequirements,
|
|
244
|
+
encodeRequirements,
|
|
245
|
+
create402Response,
|
|
246
|
+
verifyPayment,
|
|
247
|
+
settlePayment,
|
|
248
|
+
getPaymentAccept,
|
|
249
|
+
network,
|
|
250
|
+
facilitator
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
export {
|
|
254
|
+
BASE_MAINNET_NETWORK,
|
|
255
|
+
DEXTER_FACILITATOR_URL,
|
|
256
|
+
FacilitatorClient,
|
|
257
|
+
SOLANA_MAINNET_NETWORK,
|
|
258
|
+
USDC_BASE,
|
|
259
|
+
USDC_MINT,
|
|
260
|
+
createX402Server
|
|
261
|
+
};
|
|
262
|
+
//# sourceMappingURL=index.js.map
|