@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 CHANGED
@@ -3,7 +3,7 @@
3
3
  [![Official Documentation](https://img.shields.io/badge/Docs-docs.paynode.dev-00ff88?style=for-the-badge&logo=readthedocs)](https://docs.paynode.dev)
4
4
  [![NPM Version](https://img.shields.io/npm/v/@paynodelabs/sdk-js.svg?style=for-the-badge)](https://www.npmjs.com/package/@paynodelabs/sdk-js)
5
5
 
6
- The official TypeScript/JavaScript SDK for the **PayNode Protocol (v3.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).
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`.
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,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;IAgCvB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,QAAQ,CAAC;YAoDjE,aAAa;IAuGrB,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"}
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 || !RETRYABLE_STATUS_CODES.has(response.status)) {
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
- if (contentType && contentType.includes('application/json')) {
79
- body = await response.clone().json();
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
- body = JSON.parse(decoded);
87
+ headerBody = JSON.parse(decoded);
87
88
  }
88
89
  catch (e) {
89
- console.debug('⚠️ [PayNode-JS] Failed to parse X-402-Required header:', e);
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
- version: "3.1",
136
- type: 'eip3009',
137
- orderId,
138
- payload: authorization
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
- version: "3.1",
166
- type: 'onchain',
167
- orderId,
168
- payload: { txHash }
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 json = JSON.stringify(payload);
209
+ const payloadJson = JSON.stringify(payload);
172
210
  const b64Payload = typeof globalThis.Buffer !== 'undefined'
173
- ? globalThis.Buffer.from(json).toString('base64')
174
- : btoa(json);
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
- 'X-402-Payload': b64Payload,
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 = {}) {
@@ -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: string[];
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
@@ -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;AACF,eAAO,MAAM,kBAAkB,UAAqsC,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() public", "function MIN_PAYMENT_AMOUNT() public", "function PROTOCOL_FEE_BPS() public", "function acceptOwnership() public", "function owner() public", "function pause() public", "function paused() public", "function pay(address token, address merchant, uint256 amount, bytes32 orderId) public", "function payWithPermit(address payer, address token, address merchant, uint256 amount, bytes32 orderId, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public", "function pendingOwner() public", "function protocolTreasury() public", "function renounceOwnership() public", "function transferOwnership(address newOwner) public", "function unpause() public", "function updateTreasury(address _newTreasury) public", "event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner)", "event OwnershipTransferred(address indexed previousOwner, address indexed newOwner)", "event Paused(address account)", "event PaymentReceived(bytes32 indexed orderId, address indexed merchant, address indexed payer, address token, uint256 amount, uint256 fee, uint256 chainId)", "event TreasuryUpdated(address indexed oldTreasury, address indexed newTreasury)", "event Unpaused(address account)"];
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": [] }];
@@ -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 'X-402-Payload' header.",
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,iBA8F1E,CAAC"}
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"}
@@ -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
- unifiedPayload = JSON.parse(Buffer.from(v2PayloadHeader, 'base64').toString());
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 X-402-Payload header:", e);
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: result.error?.code || errors_1.ErrorCode.InvalidReceipt,
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 X-402-Required
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: req.header('accept') || "application/json"
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
  }
@@ -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: "3.1";
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;
@@ -1 +1 @@
1
- {"version":3,"file":"x402.d.ts","sourceRoot":"","sources":["../../src/types/x402.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"x402.d.ts","sourceRoot":"","sources":["../../src/types/x402.ts"],"names":[],"mappings":"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"}
@@ -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;IA8BpD,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,gBAAgB,CAAA;KAAE,CAAC;IA0ElH;;;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,CAAA;KAAE,CAAC;CA0G3D"}
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"}
@@ -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.1.1",
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
- "express": "^4.19.2"
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",