@paynodelabs/sdk-js 2.1.1 → 2.2.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 +2 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +85 -21
- package/dist/constants.d.ts +57 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +1 -1
- package/dist/errors/index.js +1 -1
- package/dist/middleware/x402.d.ts.map +1 -1
- package/dist/middleware/x402.js +64 -8
- package/dist/types/x402.d.ts +10 -2
- package/dist/types/x402.d.ts.map +1 -1
- package/dist/utils/verifier.d.ts +3 -0
- package/dist/utils/verifier.d.ts.map +1 -1
- package/dist/utils/verifier.js +5 -3
- package/package.json +11 -3
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[](https://docs.paynode.dev)
|
|
4
4
|
[](https://www.npmjs.com/package/@paynodelabs/sdk-js)
|
|
5
5
|
|
|
6
|
-
The official TypeScript/JavaScript SDK for the **PayNode Protocol (
|
|
6
|
+
The official TypeScript/JavaScript SDK for the **PayNode Protocol (v2.2.1)**. PayNode is a stateless, non-custodial M2M payment gateway that standardizes the HTTP 402 "Payment Required" flow for AI Agents, with support for both on-chain receipts and off-chain signatures (EIP-3009).
|
|
7
7
|
|
|
8
8
|
## 📖 Read the Docs
|
|
9
9
|
|
|
@@ -33,7 +33,7 @@ async function main() {
|
|
|
33
33
|
main();
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
### Key Features (v2.1)
|
|
36
|
+
### Key Features (v2.2.1)
|
|
37
37
|
- **Zero-Wait Checkout**: API response speed drops from 5 seconds to **under 50ms** by using local signatures instead of waiting for on-chain inclusion.
|
|
38
38
|
- **Double-Spend Protection**:
|
|
39
39
|
- **L1 (Memory)**: High-speed local replay protection via `IdempotencyStore`.
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAGA,OAAO,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAGA,OAAO,EAGL,eAAe,EAEhB,MAAM,cAAc,CAAC;AAEtB,MAAM,WAAW,cAAe,SAAQ,WAAW;IACjD,IAAI,CAAC,EAAE,GAAG,CAAC;CACZ;AAID,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,QAAQ,CAA0B;IAC1C,OAAO,CAAC,OAAO,CAAW;IAC1B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,SAAS,CAAoC;IAErD,OAAO,CAAC,SAAS,CAMf;IAEF,OAAO,CAAC,UAAU,CAAsB;gBAE5B,UAAU,EAAE,MAAM,EAAE,OAAO,GAAE,MAAM,GAAG,MAAM,EAAkB,EAAE,UAAU,GAAE,MAAU;YAepF,eAAe;IAoCvB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,QAAQ,CAAC;YA+DjE,aAAa;IA6JrB,6BAA6B,CACjC,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EACb,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,GAC9B,OAAO,CAAC,eAAe,CAAC;IA6CrB,GAAG,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAqBpH,aAAa,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,MAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IA8BrJ,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAE,MAAa,EAAE,OAAO,GAAE,MAAY;;;;;;YAyChH,UAAU;CAYzB"}
|
package/dist/client.js
CHANGED
|
@@ -36,7 +36,10 @@ class PayNodeAgentClient {
|
|
|
36
36
|
for (let attempt = 0; attempt < this.maxRetries; attempt++) {
|
|
37
37
|
try {
|
|
38
38
|
const response = await fetch(url, options);
|
|
39
|
-
if (!response
|
|
39
|
+
if (!response) {
|
|
40
|
+
throw new Error('fetch returned undefined');
|
|
41
|
+
}
|
|
42
|
+
if (!RETRYABLE_STATUS_CODES.has(response.status)) {
|
|
40
43
|
return response;
|
|
41
44
|
}
|
|
42
45
|
if (attempt < this.maxRetries - 1) {
|
|
@@ -72,26 +75,34 @@ class PayNodeAgentClient {
|
|
|
72
75
|
if (response.status === 402) {
|
|
73
76
|
console.log(`💡 [PayNode-JS] 402 Payment Required detected. Analyzing protocol version...`);
|
|
74
77
|
const contentType = response.headers.get('content-type');
|
|
75
|
-
const b64Required = response.headers.get('X-402-Required');
|
|
78
|
+
const b64Required = response.headers.get('PAYMENT-REQUIRED') || response.headers.get('X-402-Required');
|
|
76
79
|
const orderId = response.headers.get('X-402-Order-Id');
|
|
77
80
|
let body = null;
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
else if (b64Required) {
|
|
81
|
+
let headerBody = null;
|
|
82
|
+
if (b64Required) {
|
|
82
83
|
try {
|
|
83
84
|
const decoded = typeof globalThis.Buffer !== 'undefined'
|
|
84
85
|
? globalThis.Buffer.from(b64Required, 'base64').toString()
|
|
85
86
|
: atob(b64Required);
|
|
86
|
-
|
|
87
|
+
headerBody = JSON.parse(decoded);
|
|
87
88
|
}
|
|
88
89
|
catch (e) {
|
|
89
|
-
console.debug('⚠️ [PayNode-JS] Failed to parse
|
|
90
|
+
console.debug('⚠️ [PayNode-JS] Failed to parse PAYMENT-REQUIRED header:', e);
|
|
90
91
|
}
|
|
91
92
|
}
|
|
93
|
+
if (contentType && contentType.includes('application/json')) {
|
|
94
|
+
try {
|
|
95
|
+
body = await response.clone().json();
|
|
96
|
+
}
|
|
97
|
+
catch (e) { /* ignore */ }
|
|
98
|
+
}
|
|
99
|
+
// Robustness: Merge header info into body if body is missing critical bits
|
|
100
|
+
if (headerBody && (!body || !body.x402Version)) {
|
|
101
|
+
body = { ...body, ...headerBody };
|
|
102
|
+
}
|
|
92
103
|
if (body && body.x402Version === 2) {
|
|
93
104
|
console.log(`🚀 [PayNode-JS] x402 v2 detected. Handling autonomous payment...`);
|
|
94
|
-
if (orderId)
|
|
105
|
+
if (orderId && !body.orderId)
|
|
95
106
|
body.orderId = orderId;
|
|
96
107
|
return await this._handleX402V2(url, fetchOptions, body);
|
|
97
108
|
}
|
|
@@ -132,10 +143,23 @@ class PayNodeAgentClient {
|
|
|
132
143
|
const nonce = ethers_1.ethers.hexlify(ethers_1.ethers.randomBytes(32));
|
|
133
144
|
const authorization = await this.signTransferWithAuthorization(requirement.asset, requirement.payTo, BigInt(requirement.amount), validAfter, validBefore, nonce, requirement.extra);
|
|
134
145
|
payload = {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
146
|
+
x402Version: 2,
|
|
147
|
+
resource: requirements.resource,
|
|
148
|
+
accepted: {
|
|
149
|
+
scheme: requirement.scheme,
|
|
150
|
+
network: requirement.network,
|
|
151
|
+
amount: requirement.amount,
|
|
152
|
+
asset: requirement.asset,
|
|
153
|
+
payTo: requirement.payTo,
|
|
154
|
+
maxTimeoutSeconds: requirement.maxTimeoutSeconds,
|
|
155
|
+
extra: requirement.extra || {}
|
|
156
|
+
},
|
|
157
|
+
payload: authorization,
|
|
158
|
+
_paynode: {
|
|
159
|
+
version: "2.2.1",
|
|
160
|
+
type: 'eip3009',
|
|
161
|
+
orderId: orderId
|
|
162
|
+
}
|
|
139
163
|
};
|
|
140
164
|
}
|
|
141
165
|
else {
|
|
@@ -162,22 +186,37 @@ class PayNodeAgentClient {
|
|
|
162
186
|
txHash = await this.payWithPermit(routerAddr, requirement.asset, requirement.payTo, amount, orderId, requirement.extra?.version || '2');
|
|
163
187
|
}
|
|
164
188
|
payload = {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
189
|
+
x402Version: 2,
|
|
190
|
+
resource: requirements.resource,
|
|
191
|
+
accepted: {
|
|
192
|
+
scheme: requirement.scheme,
|
|
193
|
+
network: requirement.network,
|
|
194
|
+
amount: requirement.amount,
|
|
195
|
+
asset: requirement.asset,
|
|
196
|
+
payTo: requirement.payTo,
|
|
197
|
+
maxTimeoutSeconds: requirement.maxTimeoutSeconds,
|
|
198
|
+
router: requirement.router,
|
|
199
|
+
extra: requirement.extra || {}
|
|
200
|
+
},
|
|
201
|
+
payload: { txHash },
|
|
202
|
+
_paynode: {
|
|
203
|
+
version: "2.2.1",
|
|
204
|
+
type: 'onchain',
|
|
205
|
+
orderId: orderId
|
|
206
|
+
}
|
|
169
207
|
};
|
|
170
208
|
}
|
|
171
|
-
const
|
|
209
|
+
const payloadJson = JSON.stringify(payload);
|
|
172
210
|
const b64Payload = typeof globalThis.Buffer !== 'undefined'
|
|
173
|
-
? globalThis.Buffer.from(
|
|
174
|
-
: btoa(
|
|
211
|
+
? globalThis.Buffer.from(payloadJson).toString('base64')
|
|
212
|
+
: btoa(payloadJson);
|
|
175
213
|
const retryOptions = {
|
|
176
214
|
...options,
|
|
177
215
|
headers: {
|
|
178
216
|
...options.headers,
|
|
179
217
|
'Content-Type': 'application/json',
|
|
180
|
-
'
|
|
218
|
+
'PAYMENT-SIGNATURE': b64Payload,
|
|
219
|
+
'X-402-Payload': b64Payload, // Keep for backward compatibility
|
|
181
220
|
'X-402-Order-Id': orderId
|
|
182
221
|
}
|
|
183
222
|
};
|
|
@@ -185,6 +224,31 @@ class PayNodeAgentClient {
|
|
|
185
224
|
if (retryResponse.status === 402) {
|
|
186
225
|
throw new errors_1.PayNodeException(errors_1.ErrorCode.TransactionFailed, "Still 402 after payment attempt. The server may have rejected the payment or authorization.");
|
|
187
226
|
}
|
|
227
|
+
// Attempt to parse PAYMENT-RESPONSE header
|
|
228
|
+
const settleHeader = retryResponse.headers.get('PAYMENT-RESPONSE') || retryResponse.headers.get('X-PAYMENT-RESPONSE');
|
|
229
|
+
if (settleHeader) {
|
|
230
|
+
try {
|
|
231
|
+
let decoded;
|
|
232
|
+
if (settleHeader.trim().startsWith('{')) {
|
|
233
|
+
decoded = settleHeader;
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
decoded = typeof globalThis.Buffer !== 'undefined'
|
|
237
|
+
? globalThis.Buffer.from(settleHeader, 'base64').toString()
|
|
238
|
+
: atob(settleHeader);
|
|
239
|
+
}
|
|
240
|
+
const settleData = JSON.parse(decoded);
|
|
241
|
+
if (settleData.success) {
|
|
242
|
+
console.log(`✅ [PayNode-JS] Settlement confirmed: ${settleData.transaction}`);
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
console.warn(`⚠️ [PayNode-JS] Settlement failed: ${settleData.errorReason || 'Unknown error'}`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
catch (e) {
|
|
249
|
+
console.warn(`⚠️ [PayNode-JS] Failed to parse settlement response:`, e);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
188
252
|
return retryResponse;
|
|
189
253
|
}
|
|
190
254
|
async signTransferWithAuthorization(tokenAddr, to, amount, validAfter, validBefore, nonce, extra = {}) {
|
package/dist/constants.d.ts
CHANGED
|
@@ -9,5 +9,61 @@ export declare const MIN_PAYMENT_AMOUNT: bigint;
|
|
|
9
9
|
export declare const BASE_RPC_URLS: string[];
|
|
10
10
|
export declare const BASE_RPC_URLS_SANDBOX: string[];
|
|
11
11
|
export declare const ACCEPTED_TOKENS: Record<number, string[]>;
|
|
12
|
-
export declare const PAYNODE_ROUTER_ABI:
|
|
12
|
+
export declare const PAYNODE_ROUTER_ABI: ({
|
|
13
|
+
type: string;
|
|
14
|
+
inputs: {
|
|
15
|
+
name: string;
|
|
16
|
+
type: string;
|
|
17
|
+
internalType: string;
|
|
18
|
+
}[];
|
|
19
|
+
stateMutability: string;
|
|
20
|
+
name?: undefined;
|
|
21
|
+
outputs?: undefined;
|
|
22
|
+
anonymous?: undefined;
|
|
23
|
+
} | {
|
|
24
|
+
type: string;
|
|
25
|
+
name: string;
|
|
26
|
+
inputs: never[];
|
|
27
|
+
outputs: {
|
|
28
|
+
name: string;
|
|
29
|
+
type: string;
|
|
30
|
+
internalType: string;
|
|
31
|
+
}[];
|
|
32
|
+
stateMutability: string;
|
|
33
|
+
anonymous?: undefined;
|
|
34
|
+
} | {
|
|
35
|
+
type: string;
|
|
36
|
+
name: string;
|
|
37
|
+
inputs: {
|
|
38
|
+
name: string;
|
|
39
|
+
type: string;
|
|
40
|
+
internalType: string;
|
|
41
|
+
}[];
|
|
42
|
+
outputs: never[];
|
|
43
|
+
stateMutability: string;
|
|
44
|
+
anonymous?: undefined;
|
|
45
|
+
} | {
|
|
46
|
+
type: string;
|
|
47
|
+
name: string;
|
|
48
|
+
inputs: {
|
|
49
|
+
name: string;
|
|
50
|
+
type: string;
|
|
51
|
+
indexed: boolean;
|
|
52
|
+
internalType: string;
|
|
53
|
+
}[];
|
|
54
|
+
anonymous: boolean;
|
|
55
|
+
stateMutability?: undefined;
|
|
56
|
+
outputs?: undefined;
|
|
57
|
+
} | {
|
|
58
|
+
type: string;
|
|
59
|
+
name: string;
|
|
60
|
+
inputs: {
|
|
61
|
+
name: string;
|
|
62
|
+
type: string;
|
|
63
|
+
internalType: string;
|
|
64
|
+
}[];
|
|
65
|
+
stateMutability?: undefined;
|
|
66
|
+
outputs?: undefined;
|
|
67
|
+
anonymous?: undefined;
|
|
68
|
+
})[];
|
|
13
69
|
//# sourceMappingURL=constants.d.ts.map
|
package/dist/constants.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAC1C,eAAO,MAAM,sBAAsB,+CAA+C,CAAC;AACnF,eAAO,MAAM,8BAA8B,+CAA+C,CAAC;AAC3F,eAAO,MAAM,iBAAiB,+CAA+C,CAAC;AAC9E,eAAO,MAAM,yBAAyB,+CAA+C,CAAC;AACtF,eAAO,MAAM,iBAAiB,+CAA+C,CAAC;AAC9E,eAAO,MAAM,gBAAgB,MAAM,CAAC;AACpC,eAAO,MAAM,kBAAkB,QAAe,CAAC;AAE/C,eAAO,MAAM,aAAa,UAAmF,CAAC;AAC9G,eAAO,MAAM,qBAAqB,UAA0E,CAAC;AAE7G,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAGpD,CAAC;
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAC1C,eAAO,MAAM,sBAAsB,+CAA+C,CAAC;AACnF,eAAO,MAAM,8BAA8B,+CAA+C,CAAC;AAC3F,eAAO,MAAM,iBAAiB,+CAA+C,CAAC;AAC9E,eAAO,MAAM,yBAAyB,+CAA+C,CAAC;AACtF,eAAO,MAAM,iBAAiB,+CAA+C,CAAC;AAC9E,eAAO,MAAM,gBAAgB,MAAM,CAAC;AACpC,eAAO,MAAM,kBAAkB,QAAe,CAAC;AAE/C,eAAO,MAAM,aAAa,UAAmF,CAAC;AAC9G,eAAO,MAAM,qBAAqB,UAA0E,CAAC;AAE7G,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAGpD,CAAC;AAEF,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAA25K,CAAC"}
|
package/dist/constants.js
CHANGED
|
@@ -15,4 +15,4 @@ exports.ACCEPTED_TOKENS = {
|
|
|
15
15
|
8453: ["0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"],
|
|
16
16
|
84532: ["0x65c088EfBDB0E03185Dbe8e258Ad0cf4Ab7946b0"]
|
|
17
17
|
};
|
|
18
|
-
exports.PAYNODE_ROUTER_ABI = ["function MAX_BPS
|
|
18
|
+
exports.PAYNODE_ROUTER_ABI = [{ "type": "constructor", "inputs": [{ "name": "_protocolTreasury", "type": "address", "internalType": "address" }], "stateMutability": "nonpayable" }, { "type": "function", "name": "MAX_BPS", "inputs": [], "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], "stateMutability": "view" }, { "type": "function", "name": "MIN_PAYMENT_AMOUNT", "inputs": [], "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], "stateMutability": "view" }, { "type": "function", "name": "PROTOCOL_FEE_BPS", "inputs": [], "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], "stateMutability": "view" }, { "type": "function", "name": "acceptOwnership", "inputs": [], "outputs": [], "stateMutability": "nonpayable" }, { "type": "function", "name": "owner", "inputs": [], "outputs": [{ "name": "", "type": "address", "internalType": "address" }], "stateMutability": "view" }, { "type": "function", "name": "pause", "inputs": [], "outputs": [], "stateMutability": "nonpayable" }, { "type": "function", "name": "paused", "inputs": [], "outputs": [{ "name": "", "type": "bool", "internalType": "bool" }], "stateMutability": "view" }, { "type": "function", "name": "pay", "inputs": [{ "name": "token", "type": "address", "internalType": "address" }, { "name": "merchant", "type": "address", "internalType": "address" }, { "name": "amount", "type": "uint256", "internalType": "uint256" }, { "name": "orderId", "type": "bytes32", "internalType": "bytes32" }], "outputs": [], "stateMutability": "nonpayable" }, { "type": "function", "name": "payWithPermit", "inputs": [{ "name": "payer", "type": "address", "internalType": "address" }, { "name": "token", "type": "address", "internalType": "address" }, { "name": "merchant", "type": "address", "internalType": "address" }, { "name": "amount", "type": "uint256", "internalType": "uint256" }, { "name": "orderId", "type": "bytes32", "internalType": "bytes32" }, { "name": "deadline", "type": "uint256", "internalType": "uint256" }, { "name": "v", "type": "uint8", "internalType": "uint8" }, { "name": "r", "type": "bytes32", "internalType": "bytes32" }, { "name": "s", "type": "bytes32", "internalType": "bytes32" }], "outputs": [], "stateMutability": "nonpayable" }, { "type": "function", "name": "pendingOwner", "inputs": [], "outputs": [{ "name": "", "type": "address", "internalType": "address" }], "stateMutability": "view" }, { "type": "function", "name": "protocolTreasury", "inputs": [], "outputs": [{ "name": "", "type": "address", "internalType": "address" }], "stateMutability": "view" }, { "type": "function", "name": "renounceOwnership", "inputs": [], "outputs": [], "stateMutability": "nonpayable" }, { "type": "function", "name": "transferOwnership", "inputs": [{ "name": "newOwner", "type": "address", "internalType": "address" }], "outputs": [], "stateMutability": "nonpayable" }, { "type": "function", "name": "unpause", "inputs": [], "outputs": [], "stateMutability": "nonpayable" }, { "type": "function", "name": "updateTreasury", "inputs": [{ "name": "_newTreasury", "type": "address", "internalType": "address" }], "outputs": [], "stateMutability": "nonpayable" }, { "type": "event", "name": "OwnershipTransferStarted", "inputs": [{ "name": "previousOwner", "type": "address", "indexed": true, "internalType": "address" }, { "name": "newOwner", "type": "address", "indexed": true, "internalType": "address" }], "anonymous": false }, { "type": "event", "name": "OwnershipTransferred", "inputs": [{ "name": "previousOwner", "type": "address", "indexed": true, "internalType": "address" }, { "name": "newOwner", "type": "address", "indexed": true, "internalType": "address" }], "anonymous": false }, { "type": "event", "name": "Paused", "inputs": [{ "name": "account", "type": "address", "indexed": false, "internalType": "address" }], "anonymous": false }, { "type": "event", "name": "PaymentReceived", "inputs": [{ "name": "orderId", "type": "bytes32", "indexed": true, "internalType": "bytes32" }, { "name": "merchant", "type": "address", "indexed": true, "internalType": "address" }, { "name": "payer", "type": "address", "indexed": true, "internalType": "address" }, { "name": "token", "type": "address", "indexed": false, "internalType": "address" }, { "name": "amount", "type": "uint256", "indexed": false, "internalType": "uint256" }, { "name": "fee", "type": "uint256", "indexed": false, "internalType": "uint256" }, { "name": "chainId", "type": "uint256", "indexed": false, "internalType": "uint256" }], "anonymous": false }, { "type": "event", "name": "TreasuryUpdated", "inputs": [{ "name": "oldTreasury", "type": "address", "indexed": true, "internalType": "address" }, { "name": "newTreasury", "type": "address", "indexed": true, "internalType": "address" }], "anonymous": false }, { "type": "event", "name": "Unpaused", "inputs": [{ "name": "account", "type": "address", "indexed": false, "internalType": "address" }], "anonymous": false }, { "type": "error", "name": "AmountTooLow", "inputs": [] }, { "type": "error", "name": "EnforcedPause", "inputs": [] }, { "type": "error", "name": "ExpectedPause", "inputs": [] }, { "type": "error", "name": "InvalidAddress", "inputs": [] }, { "type": "error", "name": "OwnableInvalidOwner", "inputs": [{ "name": "owner", "type": "address", "internalType": "address" }] }, { "type": "error", "name": "OwnableUnauthorizedAccount", "inputs": [{ "name": "account", "type": "address", "internalType": "address" }] }, { "type": "error", "name": "SafeERC20FailedOperation", "inputs": [{ "name": "token", "type": "address", "internalType": "address" }] }, { "type": "error", "name": "UnauthorizedCaller", "inputs": [] }];
|
package/dist/errors/index.js
CHANGED
|
@@ -29,7 +29,7 @@ exports.ERROR_MESSAGES = {
|
|
|
29
29
|
"transaction_not_found": "Transaction not found on-chain.",
|
|
30
30
|
"wrong_contract": "Payment event was not emitted by the official PayNode contract.",
|
|
31
31
|
"order_mismatch": "OrderId in receipt does not match requested ID.",
|
|
32
|
-
"missing_receipt": "Please pay to PayNode contract and provide '
|
|
32
|
+
"missing_receipt": "Please pay to PayNode contract and provide 'PAYMENT-SIGNATURE' header.",
|
|
33
33
|
};
|
|
34
34
|
class PayNodeException extends Error {
|
|
35
35
|
code;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"x402.d.ts","sourceRoot":"","sources":["../../src/middleware/x402.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAG1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAcxD,MAAM,WAAW,wBAAwB;IACvC,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,KAAK,MAAM,CAAC;IACjD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,QAAQ,GAAI,SAAS,wBAAwB,MA6B1C,KAAK,OAAO,GAAG,GAAG,EAAE,KAAK,QAAQ,GAAG,GAAG,EAAE,MAAM,YAAY,
|
|
1
|
+
{"version":3,"file":"x402.d.ts","sourceRoot":"","sources":["../../src/middleware/x402.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAG1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAcxD,MAAM,WAAW,wBAAwB;IACvC,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,KAAK,MAAM,CAAC;IACjD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,QAAQ,GAAI,SAAS,wBAAwB,MA6B1C,KAAK,OAAO,GAAG,GAAG,EAAE,KAAK,QAAQ,GAAG,GAAG,EAAE,MAAM,YAAY,iBA0J1E,CAAC"}
|
package/dist/middleware/x402.js
CHANGED
|
@@ -40,7 +40,7 @@ const x402Gate = (options) => {
|
|
|
40
40
|
return req.headers[name.toLowerCase()] || req.headers[name];
|
|
41
41
|
return null;
|
|
42
42
|
};
|
|
43
|
-
const v2PayloadHeader = getHeader('X-402-Payload');
|
|
43
|
+
const v2PayloadHeader = getHeader('PAYMENT-SIGNATURE') || getHeader('X-402-Payload');
|
|
44
44
|
let orderId = getHeader('X-402-Order-Id');
|
|
45
45
|
if (!orderId) {
|
|
46
46
|
orderId = (options.generateOrderId || defaultOrderIdGen)(req);
|
|
@@ -49,10 +49,41 @@ const x402Gate = (options) => {
|
|
|
49
49
|
let unifiedPayload = null;
|
|
50
50
|
if (v2PayloadHeader) {
|
|
51
51
|
try {
|
|
52
|
-
|
|
52
|
+
const parsed = JSON.parse(Buffer.from(v2PayloadHeader, 'base64').toString());
|
|
53
|
+
if (parsed.x402Version === 2 && parsed.accepted) {
|
|
54
|
+
// Official X402 V2 format - convert to internal format
|
|
55
|
+
const internalOrderId = parsed._paynode?.orderId
|
|
56
|
+
|| orderId
|
|
57
|
+
|| `auto_${Date.now()}`;
|
|
58
|
+
let inferredType = "onchain";
|
|
59
|
+
if (parsed.payload?.signature || parsed.payload?.authorization) {
|
|
60
|
+
inferredType = "eip3009";
|
|
61
|
+
}
|
|
62
|
+
else if (parsed.payload?.txHash) {
|
|
63
|
+
inferredType = "onchain";
|
|
64
|
+
}
|
|
65
|
+
unifiedPayload = {
|
|
66
|
+
version: "2.2.1",
|
|
67
|
+
type: parsed._paynode?.type || inferredType,
|
|
68
|
+
orderId: internalOrderId,
|
|
69
|
+
router: parsed.accepted?.router,
|
|
70
|
+
payload: parsed.payload
|
|
71
|
+
};
|
|
72
|
+
orderId = internalOrderId;
|
|
73
|
+
}
|
|
74
|
+
else if (typeof parsed.version === 'string' && (parsed.version.startsWith("2.3") || parsed.version.startsWith("2.2"))) {
|
|
75
|
+
// Legacy PayNode format
|
|
76
|
+
unifiedPayload = parsed;
|
|
77
|
+
if (unifiedPayload?.orderId) {
|
|
78
|
+
orderId = unifiedPayload.orderId;
|
|
79
|
+
}
|
|
80
|
+
else if (unifiedPayload?.order_id) {
|
|
81
|
+
orderId = unifiedPayload.order_id;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
53
84
|
}
|
|
54
85
|
catch (e) {
|
|
55
|
-
console.error("❌ [PayNode-Middleware] Failed to decode
|
|
86
|
+
console.error("❌ [PayNode-Middleware] Failed to decode payment payload header:", e);
|
|
56
87
|
}
|
|
57
88
|
}
|
|
58
89
|
if (unifiedPayload) {
|
|
@@ -60,28 +91,52 @@ const x402Gate = (options) => {
|
|
|
60
91
|
merchantAddress: options.merchantAddress,
|
|
61
92
|
tokenAddress: tokenAddress,
|
|
62
93
|
amount: rawAmount.toString(),
|
|
63
|
-
orderId: orderId
|
|
94
|
+
orderId: orderId || undefined
|
|
64
95
|
}, unifiedPayload.type === 'eip3009' ? { name: currency, version: "2" } : {});
|
|
65
96
|
if (result.isValid) {
|
|
97
|
+
// Construct settlement response header
|
|
98
|
+
const settleResponse = {
|
|
99
|
+
success: true,
|
|
100
|
+
transaction: unifiedPayload.payload.txHash || "",
|
|
101
|
+
network: `eip155:${chainId}`,
|
|
102
|
+
payer: result.payer || ""
|
|
103
|
+
};
|
|
104
|
+
const b64Response = Buffer.from(JSON.stringify(settleResponse)).toString('base64');
|
|
105
|
+
if (res.set) {
|
|
106
|
+
res.set('PAYMENT-RESPONSE', b64Response);
|
|
107
|
+
res.set('X-PAYMENT-RESPONSE', b64Response); // Compatibility
|
|
108
|
+
}
|
|
66
109
|
req.paynode = { unifiedPayload, orderId };
|
|
67
110
|
return next();
|
|
68
111
|
}
|
|
69
112
|
else {
|
|
113
|
+
const errorReason = result.error?.code || errors_1.ErrorCode.InvalidReceipt;
|
|
114
|
+
const settleResponse = {
|
|
115
|
+
success: false,
|
|
116
|
+
errorReason: errorReason,
|
|
117
|
+
transaction: "",
|
|
118
|
+
network: `eip155:${chainId}`
|
|
119
|
+
};
|
|
120
|
+
const b64Response = Buffer.from(JSON.stringify(settleResponse)).toString('base64');
|
|
121
|
+
if (res.set) {
|
|
122
|
+
res.set('PAYMENT-RESPONSE', b64Response);
|
|
123
|
+
res.set('X-PAYMENT-RESPONSE', b64Response); // Compatibility
|
|
124
|
+
}
|
|
70
125
|
return res.status(403).json({
|
|
71
126
|
error: "Forbidden",
|
|
72
|
-
code:
|
|
127
|
+
code: errorReason,
|
|
73
128
|
message: result.error?.message || "Invalid X402 payment payload"
|
|
74
129
|
});
|
|
75
130
|
}
|
|
76
131
|
}
|
|
77
|
-
// No valid payment found, return 402 with
|
|
132
|
+
// No valid payment found, return 402 with appropriate headers
|
|
78
133
|
const v2Response = {
|
|
79
134
|
x402Version: 2,
|
|
80
135
|
error: "Payment Required by PayNode",
|
|
81
136
|
resource: {
|
|
82
|
-
url: req.protocol + '://' + req.get('host') + req.originalUrl,
|
|
137
|
+
url: req.protocol + '://' + req.get('host') + (req.originalUrl || req.url),
|
|
83
138
|
description: options.description || "Protected Resource",
|
|
84
|
-
mimeType:
|
|
139
|
+
mimeType: getHeader('accept') || "application/json"
|
|
85
140
|
},
|
|
86
141
|
accepts: [
|
|
87
142
|
{
|
|
@@ -111,6 +166,7 @@ const x402Gate = (options) => {
|
|
|
111
166
|
};
|
|
112
167
|
const b64Required = Buffer.from(JSON.stringify(v2Response)).toString('base64');
|
|
113
168
|
if (res.set) {
|
|
169
|
+
res.set('PAYMENT-REQUIRED', b64Required);
|
|
114
170
|
res.set('X-402-Required', b64Required);
|
|
115
171
|
res.set('X-402-Order-Id', orderId);
|
|
116
172
|
}
|
package/dist/types/x402.d.ts
CHANGED
|
@@ -32,8 +32,15 @@ export interface PaymentPayload {
|
|
|
32
32
|
x402Version: X402Version;
|
|
33
33
|
resource?: ResourceInfo;
|
|
34
34
|
accepted: PaymentRequirements;
|
|
35
|
-
payload: Record<string, any
|
|
35
|
+
payload: Record<string, any> | ExactEVMPayload | {
|
|
36
|
+
txHash: string;
|
|
37
|
+
};
|
|
36
38
|
extensions?: Record<string, any>;
|
|
39
|
+
_paynode?: {
|
|
40
|
+
version: string;
|
|
41
|
+
type: "onchain" | "eip3009";
|
|
42
|
+
orderId: string;
|
|
43
|
+
};
|
|
37
44
|
}
|
|
38
45
|
export interface ExactEVMPayload {
|
|
39
46
|
signature: string;
|
|
@@ -60,9 +67,10 @@ export interface VerifyResponse {
|
|
|
60
67
|
payer?: string;
|
|
61
68
|
}
|
|
62
69
|
export interface UnifiedPaymentPayload {
|
|
63
|
-
version: "
|
|
70
|
+
version: "2.2.1";
|
|
64
71
|
type: "onchain" | "eip3009";
|
|
65
72
|
orderId: string;
|
|
73
|
+
router?: string;
|
|
66
74
|
payload: {
|
|
67
75
|
txHash?: string;
|
|
68
76
|
signature?: string;
|
package/dist/types/x402.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"x402.d.ts","sourceRoot":"","sources":["../../src/types/x402.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC;AAE5B,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC7B;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC7B;AAED,MAAM,WAAW,uBAAuB;IACtC,WAAW,EAAE,WAAW,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,YAAY,CAAC;IACvB,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,WAAW,CAAC;IACzB,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"x402.d.ts","sourceRoot":"","sources":["../../src/types/x402.ts"],"names":[],"mappings":"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"}
|
package/dist/utils/verifier.d.ts
CHANGED
|
@@ -26,10 +26,12 @@ export declare class PayNodeVerifier {
|
|
|
26
26
|
verify(unifiedPayload: UnifiedPaymentPayload, expected: ExpectedPayment, extra?: any): Promise<{
|
|
27
27
|
isValid: boolean;
|
|
28
28
|
error?: PayNodeException;
|
|
29
|
+
payer?: string;
|
|
29
30
|
}>;
|
|
30
31
|
verifyOnchainPayment(txHash: string, expected: any): Promise<{
|
|
31
32
|
isValid: boolean;
|
|
32
33
|
error?: PayNodeException;
|
|
34
|
+
payer?: string;
|
|
33
35
|
}>;
|
|
34
36
|
/**
|
|
35
37
|
* 亚秒级离线签名验证 (V2 核心)
|
|
@@ -41,6 +43,7 @@ export declare class PayNodeVerifier {
|
|
|
41
43
|
}, extra?: Record<string, any>): Promise<{
|
|
42
44
|
isValid: boolean;
|
|
43
45
|
error?: PayNodeException;
|
|
46
|
+
payer?: string;
|
|
44
47
|
}>;
|
|
45
48
|
}
|
|
46
49
|
//# sourceMappingURL=verifier.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verifier.d.ts","sourceRoot":"","sources":["../../src/utils/verifier.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,gBAAgB,EAAE,MAAM,WAAW,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAA0B,MAAM,eAAe,CAAC;AAEzE,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAEvE,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB,oGAAoG;IACpG,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAqC;IACrD,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,OAAO,CAAC,CAAS;IACzB,OAAO,CAAC,KAAK,CAAC,CAAmB;IACjC,OAAO,CAAC,cAAc,CAAc;gBAExB,MAAM,EAAE,qBAAqB;IAyCzC,OAAO,CAAC,MAAM,CAAC,UAAU,CAEvB;IAEI,MAAM,CACV,cAAc,EAAE,qBAAqB,EACrC,QAAQ,EAAE,eAAe,EACzB,KAAK,CAAC,EAAE,GAAG,GACV,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,gBAAgB,CAAA;KAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"verifier.d.ts","sourceRoot":"","sources":["../../src/utils/verifier.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,gBAAgB,EAAE,MAAM,WAAW,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAA0B,MAAM,eAAe,CAAC;AAEzE,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAEvE,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB,oGAAoG;IACpG,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAqC;IACrD,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,OAAO,CAAC,CAAS;IACzB,OAAO,CAAC,KAAK,CAAC,CAAmB;IACjC,OAAO,CAAC,cAAc,CAAc;gBAExB,MAAM,EAAE,qBAAqB;IAyCzC,OAAO,CAAC,MAAM,CAAC,UAAU,CAEvB;IAEI,MAAM,CACV,cAAc,EAAE,qBAAqB,EACrC,QAAQ,EAAE,eAAe,EACzB,KAAK,CAAC,EAAE,GAAG,GACV,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IA8BpE,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IA4ElI;;;OAGG;IACG,+BAA+B,CACnC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,eAAe,EACxB,QAAQ,EAAE;QACR,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;KACjC,EACD,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,GAC9B,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CA0G3E"}
|
package/dist/utils/verifier.js
CHANGED
|
@@ -97,6 +97,7 @@ class PayNodeVerifier {
|
|
|
97
97
|
// convention: we hash the raw orderId string to bytes32 internally
|
|
98
98
|
const targetOrderId = ethers_1.ethers.id(expected.orderId);
|
|
99
99
|
let validEventFound = false;
|
|
100
|
+
let foundPayer = undefined;
|
|
100
101
|
let routerInteracted = false;
|
|
101
102
|
let orderIdMismatchFound = false;
|
|
102
103
|
for (const log of receipt.logs) {
|
|
@@ -106,7 +107,7 @@ class PayNodeVerifier {
|
|
|
106
107
|
routerInteracted = true;
|
|
107
108
|
const parsed = router.parseLog(log);
|
|
108
109
|
if (parsed && parsed.name === 'PaymentReceived') {
|
|
109
|
-
const { merchant, token, amount, orderId } = parsed.args;
|
|
110
|
+
const { merchant, token, amount, orderId, payer } = parsed.args;
|
|
110
111
|
const isMerchantMatch = merchant.toLowerCase() === expected.merchantAddress.toLowerCase();
|
|
111
112
|
const isTokenMatch = token.toLowerCase() === expected.tokenAddress.toLowerCase();
|
|
112
113
|
const isAmountMatch = BigInt(amount) >= BigInt(expected.amount);
|
|
@@ -114,6 +115,7 @@ class PayNodeVerifier {
|
|
|
114
115
|
if (isMerchantMatch && isTokenMatch && isAmountMatch) {
|
|
115
116
|
if (isOrderMatch) {
|
|
116
117
|
validEventFound = true;
|
|
118
|
+
foundPayer = payer;
|
|
117
119
|
break;
|
|
118
120
|
}
|
|
119
121
|
else {
|
|
@@ -141,7 +143,7 @@ class PayNodeVerifier {
|
|
|
141
143
|
return { isValid: false, error: new errors_1.PayNodeException(errors_1.ErrorCode.DuplicateTransaction) };
|
|
142
144
|
}
|
|
143
145
|
}
|
|
144
|
-
return { isValid: true };
|
|
146
|
+
return { isValid: true, payer: foundPayer };
|
|
145
147
|
}
|
|
146
148
|
catch (error) {
|
|
147
149
|
return { isValid: false, error: new errors_1.PayNodeException(errors_1.ErrorCode.RpcError, undefined, error) };
|
|
@@ -236,7 +238,7 @@ class PayNodeVerifier {
|
|
|
236
238
|
return { isValid: false, error: new errors_1.PayNodeException(errors_1.ErrorCode.DuplicateTransaction, "Nonce already consumed on-chain") };
|
|
237
239
|
}
|
|
238
240
|
// =======================================================================
|
|
239
|
-
return { isValid: true };
|
|
241
|
+
return { isValid: true, payer: from };
|
|
240
242
|
}
|
|
241
243
|
catch (e) {
|
|
242
244
|
if (isLocked && this.store)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@paynodelabs/sdk-js",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.1",
|
|
4
4
|
"description": "The official JavaScript/TypeScript SDK for PayNode x402 protocol on Base L2.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -26,8 +26,15 @@
|
|
|
26
26
|
"license": "MIT",
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"ethers": "^6.13.0",
|
|
29
|
-
"ioredis": "^5.10.1"
|
|
30
|
-
|
|
29
|
+
"ioredis": "^5.10.1"
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"express": ">=4.0.0"
|
|
33
|
+
},
|
|
34
|
+
"peerDependenciesMeta": {
|
|
35
|
+
"express": {
|
|
36
|
+
"optional": true
|
|
37
|
+
}
|
|
31
38
|
},
|
|
32
39
|
"devDependencies": {
|
|
33
40
|
"@types/express": "^5.0.6",
|
|
@@ -35,6 +42,7 @@
|
|
|
35
42
|
"@types/jest": "^29.5.12",
|
|
36
43
|
"@types/node": "^20.12.12",
|
|
37
44
|
"dotenv": "^16.4.5",
|
|
45
|
+
"express": "^4.19.2",
|
|
38
46
|
"jest": "^29.7.0",
|
|
39
47
|
"ts-jest": "^29.1.4",
|
|
40
48
|
"ts-node": "^10.9.2",
|