forgelayer-node 1.1.0 → 1.1.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/server.js +11 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forgelayer-node",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "Node.js / Express middleware for crypto payments via ForgeLayer",
5
5
  "main": "index.js",
6
6
  "exports": {
package/src/server.js CHANGED
@@ -40,7 +40,7 @@ try {
40
40
 
41
41
  const FL_API_BASE = 'https://api.forgelayer.io/v1';
42
42
  const CG_API_BASE = 'https://api.coingecko.com/api/v3';
43
- const SDK_VERSION = '1.1.0';
43
+ const SDK_VERSION = '1.1.1';
44
44
 
45
45
  // Stablecoins pegged 1:1 to USD — skip CoinGecko for these
46
46
  const USD_STABLECOINS = new Set([
@@ -259,7 +259,8 @@ function createCheckout(config) {
259
259
  // ── Helpers ─────────────────────────────────────────────────────────────────
260
260
 
261
261
  function toSessionKey(orderId) {
262
- return 'fl_' + String(orderId).replace(/[^a-z0-9]/gi, '').toLowerCase();
262
+ // SHA-256 so ORDER-1 and ORDER_1 don't collapse to the same key
263
+ return 'fl_' + crypto.createHash('sha256').update(String(orderId)).digest('hex').slice(0, 32);
263
264
  }
264
265
 
265
266
  async function markConfirmed(sessionKey, order) {
@@ -410,7 +411,10 @@ function createCheckout(config) {
410
411
  }
411
412
 
412
413
  const expected = crypto.createHmac('sha256', webhookSecret).update(rawBody).digest('hex');
413
- if (!crypto.timingSafeEqual(Buffer.from(sig, 'utf8'), Buffer.from(expected, 'utf8'))) {
414
+ // timingSafeEqual throws if buffer lengths differ, so check length first
415
+ const sigBuf = Buffer.from(sig);
416
+ const expBuf = Buffer.from(expected);
417
+ if (sigBuf.length !== expBuf.length || !crypto.timingSafeEqual(sigBuf, expBuf)) {
414
418
  return res.status(401).json({ ok: false, error: 'Invalid signature.' });
415
419
  }
416
420
 
@@ -507,8 +511,10 @@ function readBody(req) {
507
511
 
508
512
  // ── Webhook secret/ID persistence ─────────────────────────────────────────────
509
513
 
510
- const SECRET_FILE = path.join(__dirname, '..', '.fl_webhook_secret');
511
- const ID_FILE = path.join(__dirname, '..', '.fl_webhook_id');
514
+ // Use process.cwd() so the file lands in the developer's project root,
515
+ // not inside node_modules/forgelayer-node/ where it gets wiped on reinstall.
516
+ const SECRET_FILE = path.join(process.cwd(), '.fl_webhook_secret');
517
+ const ID_FILE = path.join(process.cwd(), '.fl_webhook_id');
512
518
 
513
519
  function loadSavedWebhookSecret() {
514
520
  try { return fs.readFileSync(SECRET_FILE, 'utf8').trim(); } catch (_) { return ''; }