@paynodelabs/sdk-js 2.2.3 β 2.3.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 +36 -5
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/merchant/index.d.ts +37 -0
- package/dist/merchant/index.d.ts.map +1 -0
- package/dist/merchant/index.js +112 -0
- package/dist/merchant/middleware.d.ts +12 -0
- package/dist/merchant/middleware.d.ts.map +1 -0
- package/dist/merchant/middleware.js +73 -0
- package/dist/merchant/types.d.ts +27 -0
- package/dist/merchant/types.d.ts.map +1 -0
- package/dist/merchant/types.js +2 -0
- package/dist/utils/signature.d.ts +11 -0
- package/dist/utils/signature.d.ts.map +1 -0
- package/dist/utils/signature.js +33 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -33,9 +33,36 @@ async function main() {
|
|
|
33
33
|
main();
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
+
### Market Merchant (Seller)
|
|
37
|
+
|
|
38
|
+
Starting from **v2.3.0**, PayNode SDK provides a unified merchant gateway for seamless [PayNode Market](https://mk.paynode.dev) integration.
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { PayNodeMerchant } from "@paynodelabs/sdk-js";
|
|
42
|
+
import express from "express";
|
|
43
|
+
|
|
44
|
+
const app = express();
|
|
45
|
+
const merchant = new PayNodeMerchant({
|
|
46
|
+
sharedSecret: "YOUR_MARKET_SECRET" // From Market Hub
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
// 1. Unified Middleware (Strict Market Auth + Discovery + Body Unwrap)
|
|
51
|
+
app.post("/my-api", merchant.middleware({
|
|
52
|
+
manifest: { slug: "my-tool", price_per_call: "0.1" },
|
|
53
|
+
strict: true // Enforce Market Proxy (Ensures you & Market get fees)
|
|
54
|
+
}), (req, res) => {
|
|
55
|
+
// req.body is automatically unwrapped from market data structure.
|
|
56
|
+
// Payment details available in req.paynode.txHash
|
|
57
|
+
res.json({ data: "premium_content" });
|
|
58
|
+
}
|
|
59
|
+
);
|
|
60
|
+
```
|
|
61
|
+
|
|
36
62
|
### Key Features (v2.2.1)
|
|
63
|
+
|
|
37
64
|
- **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
|
-
- **Double-Spend Protection**:
|
|
65
|
+
- **Double-Spend Protection**:
|
|
39
66
|
- **L1 (Memory)**: High-speed local replay protection via `IdempotencyStore`.
|
|
40
67
|
- **L2 (RPC)**: Real-time on-chain `authorizationState` verification.
|
|
41
68
|
- **Empty-Wallet Proof**: Integrated `balanceOf` probes to block malicious agents using empty wallets to generate valid signatures.
|
|
@@ -44,6 +71,7 @@ main();
|
|
|
44
71
|
- **Dual Flow**: Automatic switch between V1 (on-chain receipts) and V2 (off-chain signatures).
|
|
45
72
|
|
|
46
73
|
## πΊοΈ Roadmap
|
|
74
|
+
|
|
47
75
|
- **TRON Support**: USDT (TRC-20) payment integration.
|
|
48
76
|
- **Solana Support**: SPL USDC/USDT payment integration.
|
|
49
77
|
- **Cross-chain**: Universal settlement via bridges.
|
|
@@ -108,10 +136,13 @@ To publish a new version of the SDK:
|
|
|
108
136
|
|
|
109
137
|
PayNode uses two distinct versioning schemes:
|
|
110
138
|
|
|
111
|
-
| Version Type
|
|
112
|
-
|
|
|
113
|
-
| **Protocol Version**
|
|
114
|
-
| **SDK/Product Version** | Software package version (npm/SemVer).
|
|
139
|
+
| Version Type | Description | Current Value |
|
|
140
|
+
| :---------------------- | :------------------------------------------ | :------------ |
|
|
141
|
+
| **Protocol Version** | On-chain contracts & core x402 data schema. | `1.4.0` |
|
|
142
|
+
| **SDK/Product Version** | Software package version (npm/SemVer). | `2.2.3` |
|
|
143
|
+
|
|
144
|
+
> [!IMPORTANT]
|
|
145
|
+
> **Production Hardening (v2.2.3+)**: This version includes critical fixes for Base Mainnet transaction stability (Inconsistent Quorum/Code 9) by enforcing `staticNetwork` and optimized provider settings. It is the minimum recommended version for mainnet deployment.
|
|
115
146
|
|
|
116
147
|
> [!NOTE]
|
|
117
148
|
> Protocol Version changes imply breaking changes in verification or contract interfaces. SDK Version changes may include features, fixes, or performance improvements without altering protocol logic.
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -26,3 +26,5 @@ __exportStar(require("./utils/payload"), exports);
|
|
|
26
26
|
__exportStar(require("./types/x402"), exports);
|
|
27
27
|
var ethers_1 = require("ethers");
|
|
28
28
|
Object.defineProperty(exports, "ethers", { enumerable: true, get: function () { return ethers_1.ethers; } });
|
|
29
|
+
__exportStar(require("./merchant"), exports);
|
|
30
|
+
__exportStar(require("./merchant/types"), exports);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { MerchantConfig, MerchantMiddlewareOptions, ApiManifest } from './types';
|
|
2
|
+
import { PayNodeMiddlewareOptions } from '../middleware/x402';
|
|
3
|
+
/**
|
|
4
|
+
* PayNodeMerchant: The high-level SDK class for Merchant Integration.
|
|
5
|
+
*/
|
|
6
|
+
export declare class PayNodeMerchant {
|
|
7
|
+
private config;
|
|
8
|
+
constructor(config: MerchantConfig);
|
|
9
|
+
/**
|
|
10
|
+
* Registers or syncs the API manifest with the PayNode Market.
|
|
11
|
+
* This ensures the market shows the correct price and input schema.
|
|
12
|
+
*/
|
|
13
|
+
sync(manifest: ApiManifest): Promise<boolean>;
|
|
14
|
+
/**
|
|
15
|
+
* Returns a unified middleware that handles:
|
|
16
|
+
* 1. Market Proxy (Strict Signature Check + Body Unwrap)
|
|
17
|
+
* 2. Auto-Discovery (Market Sync Probe)
|
|
18
|
+
* 3. (Optional) Direct X402 payment
|
|
19
|
+
*/
|
|
20
|
+
middleware(options?: MerchantMiddlewareOptions & Partial<PayNodeMiddlewareOptions>): (req: import("express").Request | any, res: import("express").Response | any, next: import("express").NextFunction) => Promise<any>;
|
|
21
|
+
/**
|
|
22
|
+
* Manual verification for Next.js or other non-Express environments.
|
|
23
|
+
* Extracts headers and verifies signature. Returns the unwrapped body and context.
|
|
24
|
+
*/
|
|
25
|
+
verify(req: any): Promise<{
|
|
26
|
+
isValid: boolean;
|
|
27
|
+
error: string;
|
|
28
|
+
body?: undefined;
|
|
29
|
+
paynodeContext?: undefined;
|
|
30
|
+
} | {
|
|
31
|
+
isValid: boolean;
|
|
32
|
+
body: any;
|
|
33
|
+
paynodeContext: any;
|
|
34
|
+
error?: undefined;
|
|
35
|
+
}>;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/merchant/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,yBAAyB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEjF,OAAO,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAG9D;;GAEG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAiB;gBAEnB,MAAM,EAAE,cAAc;IAOlC;;;OAGG;IACG,IAAI,CAAC,QAAQ,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;IA4BnD;;;;;OAKG;IACH,UAAU,CAAC,OAAO,GAAE,yBAAyB,GAAG,OAAO,CAAC,wBAAwB,CAAM;IAOtF;;;OAGG;IACG,MAAM,CAAC,GAAG,EAAE,GAAG;;;;;;;;;;;CAiDtB"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PayNodeMerchant = void 0;
|
|
4
|
+
const middleware_1 = require("./middleware");
|
|
5
|
+
const signature_1 = require("../utils/signature");
|
|
6
|
+
/**
|
|
7
|
+
* PayNodeMerchant: The high-level SDK class for Merchant Integration.
|
|
8
|
+
*/
|
|
9
|
+
class PayNodeMerchant {
|
|
10
|
+
config;
|
|
11
|
+
constructor(config) {
|
|
12
|
+
this.config = {
|
|
13
|
+
marketUrl: 'https://mk.paynode.dev',
|
|
14
|
+
...config
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Registers or syncs the API manifest with the PayNode Market.
|
|
19
|
+
* This ensures the market shows the correct price and input schema.
|
|
20
|
+
*/
|
|
21
|
+
async sync(manifest) {
|
|
22
|
+
console.log(`[PayNode-SDK] Syncing API manifest for ${manifest.slug} to ${this.config.marketUrl}`);
|
|
23
|
+
try {
|
|
24
|
+
const response = await fetch(`${this.config.marketUrl}/api/v1/merchant/apis`, {
|
|
25
|
+
method: 'POST',
|
|
26
|
+
headers: { 'Content-Type': 'application/json' },
|
|
27
|
+
body: JSON.stringify({
|
|
28
|
+
...manifest,
|
|
29
|
+
gateway_url: manifest.slug
|
|
30
|
+
})
|
|
31
|
+
});
|
|
32
|
+
const result = await response.json();
|
|
33
|
+
if (response.ok && result.success) {
|
|
34
|
+
console.log(`[PayNode-SDK] Successfully synced ${manifest.slug}. Status: ${result.api_id}`);
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
console.warn(`[PayNode-SDK] Sync failed for ${manifest.slug}: ${result.error || response.statusText}`);
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
console.error(`[PayNode-SDK] Network error during sync for ${manifest.slug}:`, err.message);
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Returns a unified middleware that handles:
|
|
49
|
+
* 1. Market Proxy (Strict Signature Check + Body Unwrap)
|
|
50
|
+
* 2. Auto-Discovery (Market Sync Probe)
|
|
51
|
+
* 3. (Optional) Direct X402 payment
|
|
52
|
+
*/
|
|
53
|
+
middleware(options = {}) {
|
|
54
|
+
return (0, middleware_1.createMerchantMiddleware)(this.config, {
|
|
55
|
+
price: options.manifest?.price_per_call || '0.01',
|
|
56
|
+
...options
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Manual verification for Next.js or other non-Express environments.
|
|
61
|
+
* Extracts headers and verifies signature. Returns the unwrapped body and context.
|
|
62
|
+
*/
|
|
63
|
+
async verify(req) {
|
|
64
|
+
const getHeader = (name) => {
|
|
65
|
+
if (typeof req.getHeader === 'function')
|
|
66
|
+
return req.getHeader(name);
|
|
67
|
+
if (typeof req.get === 'function')
|
|
68
|
+
return req.get(name);
|
|
69
|
+
if (req.headers?.get && typeof req.headers.get === 'function')
|
|
70
|
+
return req.headers.get(name);
|
|
71
|
+
if (req.headers)
|
|
72
|
+
return req.headers[name.toLowerCase()] || req.headers[name];
|
|
73
|
+
return null;
|
|
74
|
+
};
|
|
75
|
+
const signature = getHeader('X-PayNode-Signature');
|
|
76
|
+
const timestamp = getHeader('X-PayNode-Timestamp');
|
|
77
|
+
const requestId = getHeader('X-PayNode-Request-Id') || getHeader('X-402-Order-Id');
|
|
78
|
+
const isValid = (0, signature_1.verifyMarketSignature)({
|
|
79
|
+
signature,
|
|
80
|
+
orderId: requestId,
|
|
81
|
+
timestamp,
|
|
82
|
+
sharedSecret: this.config.sharedSecret,
|
|
83
|
+
});
|
|
84
|
+
if (!isValid) {
|
|
85
|
+
return { isValid: false, error: 'Invalid PayNode Market Signature' };
|
|
86
|
+
}
|
|
87
|
+
// Handle Body Unwrap if it's a JSON body
|
|
88
|
+
let body = {};
|
|
89
|
+
try {
|
|
90
|
+
if (typeof req.json === 'function') {
|
|
91
|
+
body = await req.json();
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
body = req.body;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch (e) { }
|
|
98
|
+
let paynodeContext = { orderId: requestId };
|
|
99
|
+
if (body && body.payload && typeof body.payload === 'object') {
|
|
100
|
+
paynodeContext = {
|
|
101
|
+
...paynodeContext,
|
|
102
|
+
txHash: getHeader('X-PayNode-Transaction-Hash') || body.tx_hash,
|
|
103
|
+
amount: getHeader('X-PayNode-Amount') || body.amount,
|
|
104
|
+
network: getHeader('X-PayNode-Network') || body.network,
|
|
105
|
+
chainId: getHeader('X-PayNode-Chain-Id') || body.chain_id?.toString(),
|
|
106
|
+
};
|
|
107
|
+
body = body.payload;
|
|
108
|
+
}
|
|
109
|
+
return { isValid: true, body, paynodeContext };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
exports.PayNodeMerchant = PayNodeMerchant;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
import { MerchantConfig, MerchantMiddlewareOptions } from './types';
|
|
3
|
+
import { PayNodeMiddlewareOptions } from '../middleware/x402';
|
|
4
|
+
/**
|
|
5
|
+
* Unified PayNode Merchant Middleware
|
|
6
|
+
* Handles:
|
|
7
|
+
* 1. Market Proxy (Strict HMAC Signature + Body Unwrapping)
|
|
8
|
+
* 2. Discovery Probes (Auto-respond with API Manifest)
|
|
9
|
+
* 3. Direct Agent Access (Optional X402 Fallback)
|
|
10
|
+
*/
|
|
11
|
+
export declare const createMerchantMiddleware: (config: MerchantConfig, options: MerchantMiddlewareOptions & PayNodeMiddlewareOptions) => (req: Request | any, res: Response | any, next: NextFunction) => Promise<any>;
|
|
12
|
+
//# sourceMappingURL=middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/merchant/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,yBAAyB,EAAE,MAAM,SAAS,CAAC;AAEpE,OAAO,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAE9D;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,GAAI,QAAQ,cAAc,EAAE,SAAS,yBAAyB,GAAG,wBAAwB,MAG9G,KAAK,OAAO,GAAG,GAAG,EAAE,KAAK,QAAQ,GAAG,GAAG,EAAE,MAAM,YAAY,iBAiE1E,CAAC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createMerchantMiddleware = void 0;
|
|
4
|
+
const signature_1 = require("../utils/signature");
|
|
5
|
+
/**
|
|
6
|
+
* Unified PayNode Merchant Middleware
|
|
7
|
+
* Handles:
|
|
8
|
+
* 1. Market Proxy (Strict HMAC Signature + Body Unwrapping)
|
|
9
|
+
* 2. Discovery Probes (Auto-respond with API Manifest)
|
|
10
|
+
* 3. Direct Agent Access (Optional X402 Fallback)
|
|
11
|
+
*/
|
|
12
|
+
const createMerchantMiddleware = (config, options) => {
|
|
13
|
+
const { manifest, strict = true } = options;
|
|
14
|
+
return async (req, res, next) => {
|
|
15
|
+
// 1. Check for Market Proxy Headers
|
|
16
|
+
const signature = req.header('X-PayNode-Signature');
|
|
17
|
+
const timestamp = req.header('X-PayNode-Timestamp');
|
|
18
|
+
const requestId = req.header('X-PayNode-Request-Id') || req.header('X-402-Order-Id');
|
|
19
|
+
const isDiscovery = req.header('X-PayNode-Discovery') === 'true';
|
|
20
|
+
if (signature && requestId && timestamp) {
|
|
21
|
+
// β
Verify Signature from PayNode Market
|
|
22
|
+
const isValid = (0, signature_1.verifyMarketSignature)({
|
|
23
|
+
signature,
|
|
24
|
+
orderId: requestId,
|
|
25
|
+
timestamp,
|
|
26
|
+
sharedSecret: config.sharedSecret,
|
|
27
|
+
});
|
|
28
|
+
if (!isValid) {
|
|
29
|
+
console.error(`[PayNode-SDK] Invalid Market Proxy Signature for request ${requestId}`);
|
|
30
|
+
return res.status(401).json({ error: 'unauthorized', message: 'PayNode Market Signature verification failed.' });
|
|
31
|
+
}
|
|
32
|
+
// --- Scene A: Discovery Probe ---
|
|
33
|
+
if (isDiscovery) {
|
|
34
|
+
return res.status(200).json({
|
|
35
|
+
status: 'DISCOVERED',
|
|
36
|
+
version: '2.0.0',
|
|
37
|
+
manifest: manifest || {},
|
|
38
|
+
last_synced: new Date().toISOString()
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
// --- Scene B: Proxy Flow - Body Unwrapping ---
|
|
42
|
+
// The Market Proxy wraps original body in { payload: { ... } }
|
|
43
|
+
if (req.body && req.body.payload && typeof req.body.payload === 'object') {
|
|
44
|
+
const metadata = { ...req.body };
|
|
45
|
+
delete metadata.payload;
|
|
46
|
+
// Enrich request context with Proxy details
|
|
47
|
+
req.paynode = {
|
|
48
|
+
orderId: requestId,
|
|
49
|
+
txHash: req.header('X-PayNode-Transaction-Hash') || req.body.tx_hash,
|
|
50
|
+
amount: req.header('X-PayNode-Amount') || req.body.amount,
|
|
51
|
+
network: req.header('X-PayNode-Network') || req.body.network,
|
|
52
|
+
chainId: req.header('X-PayNode-Chain-Id') || req.body.chain_id?.toString(),
|
|
53
|
+
proxyMetadata: metadata
|
|
54
|
+
};
|
|
55
|
+
// Transparently Unwrap Body
|
|
56
|
+
req.body = req.body.payload;
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
// Direct call via Proxy (unlikely for POST, but possible for some flows)
|
|
60
|
+
req.paynode = { orderId: requestId };
|
|
61
|
+
}
|
|
62
|
+
return next();
|
|
63
|
+
}
|
|
64
|
+
// 2. Scene C: Direct Agent Call (Rejected)
|
|
65
|
+
// PayNodeMerchant component REQUIRES Market Proxy to ensure protocol consistency and fee collection.
|
|
66
|
+
// Use x402Gate directly for standalone/direct 402 integration.
|
|
67
|
+
return res.status(403).json({
|
|
68
|
+
error: 'forbidden',
|
|
69
|
+
message: 'PayNode Market Auth required. This API must be accessed via PayNode Market Proxy for verification.'
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
exports.createMerchantMiddleware = createMerchantMiddleware;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface MerchantConfig {
|
|
2
|
+
sharedSecret: string;
|
|
3
|
+
marketUrl?: string;
|
|
4
|
+
}
|
|
5
|
+
export interface ApiManifest {
|
|
6
|
+
slug: string;
|
|
7
|
+
name: string;
|
|
8
|
+
description: string;
|
|
9
|
+
price_per_call: string;
|
|
10
|
+
currency?: string;
|
|
11
|
+
network?: 'mainnet' | 'testnet';
|
|
12
|
+
input_schema?: Record<string, any>;
|
|
13
|
+
sample_response?: Record<string, any>;
|
|
14
|
+
}
|
|
15
|
+
export interface PayNodeRequestContext {
|
|
16
|
+
orderId: string;
|
|
17
|
+
txHash?: string;
|
|
18
|
+
payer?: string;
|
|
19
|
+
amount?: string;
|
|
20
|
+
network?: string;
|
|
21
|
+
chainId?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface MerchantMiddlewareOptions {
|
|
24
|
+
manifest?: Partial<ApiManifest>;
|
|
25
|
+
strict?: boolean;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/merchant/types.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACnC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IAChC,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface SignatureContext {
|
|
2
|
+
signature: string;
|
|
3
|
+
orderId: string;
|
|
4
|
+
timestamp: string;
|
|
5
|
+
sharedSecret: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Verifies the HMAC-SHA256 signature from PayNode Market Proxy
|
|
9
|
+
*/
|
|
10
|
+
export declare function verifyMarketSignature(context: SignatureContext): boolean;
|
|
11
|
+
//# sourceMappingURL=signature.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signature.d.ts","sourceRoot":"","sources":["../../src/utils/signature.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CA4BxE"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.verifyMarketSignature = verifyMarketSignature;
|
|
7
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
8
|
+
/**
|
|
9
|
+
* Verifies the HMAC-SHA256 signature from PayNode Market Proxy
|
|
10
|
+
*/
|
|
11
|
+
function verifyMarketSignature(context) {
|
|
12
|
+
const { signature, orderId, timestamp, sharedSecret } = context;
|
|
13
|
+
if (!signature || !orderId || !timestamp || !sharedSecret) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
// Check for timestamp drift (default 5 minutes to prevent replay)
|
|
17
|
+
const tsDate = new Date(timestamp);
|
|
18
|
+
const now = new Date();
|
|
19
|
+
// Accept both ISO string and milliseconds
|
|
20
|
+
const tsMs = isNaN(tsDate.getTime()) ? parseInt(timestamp) : tsDate.getTime();
|
|
21
|
+
if (isNaN(tsMs))
|
|
22
|
+
return false;
|
|
23
|
+
const drift = Math.abs(now.getTime() - tsMs);
|
|
24
|
+
if (drift > 5 * 60 * 1000) {
|
|
25
|
+
console.warn(`[PayNode-SDK] Signature timestamp drift too high: ${drift}ms`);
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
const expectedSig = crypto_1.default
|
|
29
|
+
.createHmac('sha256', sharedSecret)
|
|
30
|
+
.update(`${orderId}${timestamp}`)
|
|
31
|
+
.digest('hex');
|
|
32
|
+
return signature === expectedSig;
|
|
33
|
+
}
|