ap2-payment-handler 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/README.md +133 -0
- package/dist/__tests__/handler.test.d.ts +2 -0
- package/dist/__tests__/handler.test.d.ts.map +1 -0
- package/dist/__tests__/handler.test.js +251 -0
- package/dist/handler.d.ts +14 -0
- package/dist/handler.d.ts.map +1 -0
- package/dist/handler.js +87 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/types.d.ts +47 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/validation.d.ts +6 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +61 -0
- package/dist/x402.d.ts +31 -0
- package/dist/x402.d.ts.map +1 -0
- package/dist/x402.js +74 -0
- package/package.json +35 -0
- package/src/__tests__/handler.test.ts +278 -0
- package/src/handler.ts +107 -0
- package/src/index.ts +4 -0
- package/src/types.ts +53 -0
- package/src/validation.ts +61 -0
- package/src/x402.ts +86 -0
- package/tsconfig.json +17 -0
package/src/x402.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { AP2PaymentResponse } from './types';
|
|
2
|
+
|
|
3
|
+
function generateId(): string {
|
|
4
|
+
return Math.random().toString(36).substring(2) + Date.now().toString(36);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export class X402Bridge {
|
|
8
|
+
/**
|
|
9
|
+
* Parse an HTTP 402 Payment Required response headers to extract payment details.
|
|
10
|
+
*/
|
|
11
|
+
async parsePaymentRequired(
|
|
12
|
+
headers: Record<string, string>
|
|
13
|
+
): Promise<{ amount: string; currency: string; address: string }> {
|
|
14
|
+
const paymentHeader = headers['x-payment-required'] || headers['X-Payment-Required'];
|
|
15
|
+
if (paymentHeader) {
|
|
16
|
+
try {
|
|
17
|
+
const parsed = JSON.parse(paymentHeader);
|
|
18
|
+
return {
|
|
19
|
+
amount: parsed.amount || '0',
|
|
20
|
+
currency: parsed.currency || 'USDC',
|
|
21
|
+
address: parsed.address || '',
|
|
22
|
+
};
|
|
23
|
+
} catch {
|
|
24
|
+
// fall through to individual headers
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const amount = headers['x-payment-amount'] || headers['X-Payment-Amount'] || '0';
|
|
29
|
+
const currency = headers['x-payment-currency'] || headers['X-Payment-Currency'] || 'USDC';
|
|
30
|
+
const address = headers['x-payment-address'] || headers['X-Payment-Address'] || '';
|
|
31
|
+
|
|
32
|
+
if (!address) {
|
|
33
|
+
throw new Error('Missing payment address in 402 response headers');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return { amount, currency, address };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Build a non-custodial payment proof for a given payment request.
|
|
41
|
+
*/
|
|
42
|
+
async buildPaymentProof(params: {
|
|
43
|
+
amount: string;
|
|
44
|
+
currency: string;
|
|
45
|
+
address: string;
|
|
46
|
+
agentId: string;
|
|
47
|
+
}): Promise<{ proof: string; timestamp: number }> {
|
|
48
|
+
const timestamp = Date.now();
|
|
49
|
+
const proofData = {
|
|
50
|
+
agentId: params.agentId,
|
|
51
|
+
amount: params.amount,
|
|
52
|
+
currency: params.currency,
|
|
53
|
+
address: params.address,
|
|
54
|
+
timestamp,
|
|
55
|
+
nonce: generateId(),
|
|
56
|
+
};
|
|
57
|
+
// In production this would be EIP-712 signed. Here we encode it.
|
|
58
|
+
const proof = Buffer.from(JSON.stringify(proofData)).toString('base64');
|
|
59
|
+
return { proof, timestamp };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Handle an HTTP response. Returns an AP2PaymentResponse if it's a 402, null otherwise.
|
|
64
|
+
*/
|
|
65
|
+
async handleResponse(response: {
|
|
66
|
+
status: number;
|
|
67
|
+
headers: Record<string, string>;
|
|
68
|
+
}): Promise<AP2PaymentResponse | null> {
|
|
69
|
+
if (response.status !== 402) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
const paymentDetails = await this.parsePaymentRequired(response.headers);
|
|
75
|
+
return {
|
|
76
|
+
success: false,
|
|
77
|
+
error: `Payment required: ${paymentDetails.amount} ${paymentDetails.currency} to ${paymentDetails.address}`,
|
|
78
|
+
};
|
|
79
|
+
} catch (err) {
|
|
80
|
+
return {
|
|
81
|
+
success: false,
|
|
82
|
+
error: `402 Payment Required: ${(err as Error).message}`,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"lib": ["ES2020"],
|
|
6
|
+
"outDir": "dist",
|
|
7
|
+
"rootDir": "src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"declaration": true,
|
|
10
|
+
"declarationMap": true,
|
|
11
|
+
"sourceMap": true,
|
|
12
|
+
"esModuleInterop": true,
|
|
13
|
+
"skipLibCheck": true
|
|
14
|
+
},
|
|
15
|
+
"include": ["src"],
|
|
16
|
+
"exclude": ["node_modules", "dist"]
|
|
17
|
+
}
|