From b72dbf3e55fd2bd2a088195fd1861fa5dafcca68 Mon Sep 17 00:00:00 2001 From: oxpoe Date: Fri, 6 Nov 2026 04:55:12 +0700 Subject: [PATCH] fix(webhook): auto secret token + URL dari public base/host - ensureWebhookSecret saat boot dan buka Pengaturan - URL webhook memakai public_base_url fallback host - tolak default billing-rtrw-secret-key - versi 0.6.3 --- CHANGELOG.md | 7 +++++++ README.md | 2 +- app-customer.js | 3 ++- config/settingsManager.js | 11 ++++++++++- package.json | 2 +- routes/adminPortal.js | 8 +++++--- version.txt | 2 +- views/admin/settings.ejs | 3 ++- 8 files changed, 29 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10fcc0b..f437804 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ Format mengikuti [Keep a Changelog](https://keepachangelog.com/) dan [Semantic Versioning](https://semver.org/). +## [0.6.3] — 2026-11-06 — "webhook-secret-auto" + +### Diperbaiki +- **Secret Token webhook pembayaran** dibuat otomatis (64 hex) bila kosong/default, ditampilkan di Pengaturan dan langsung dipakai pada URL webhook. +- URL webhook memakai **Public Base URL**; bila kosong memakai host saat ini (tidak lagi menghasilkan URL relatif). +- Default lama `billing-rtrw-secret-key` tidak lagi diterima — forwarder harus memakai secret dari Pengaturan. + ## [0.6.2] — 2026-11-06 — "webhook-diag" ### Diperbaiki diff --git a/README.md b/README.md index a912430..a403e07 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@

Lisensi: ISC - Versi 0.6.2 (webhook-diag) + Versi 0.6.3 (webhook-secret-auto) Node.js >= 20 Docker Demo diff --git a/app-customer.js b/app-customer.js index 706d0a9..c0cccac 100644 --- a/app-customer.js +++ b/app-customer.js @@ -61,6 +61,7 @@ const { SUPPORTED_LANGS, FALLBACK_LANG, normalizeLang, t } = require('./config/i if (typeof ensureDefaultSettings === 'function') { ensureDefaultSettings(); try { require('./config/settingsManager').ensureGowaWebhookSecret(); } catch (e) {} + try { require('./config/settingsManager').ensureWebhookSecret(); } catch (e) {} // Proses baru = kode terbaru sudah dimuat; hapus penanda restart. try { fs.rmSync(path.join(__dirname, '.restart-required'), { force: true }); } catch (e) {} } @@ -901,7 +902,7 @@ app.post('/api/webhook/v1/payment-notif', multer().any(), async (req, res) => { gotTrim = gotTrim.replace(/^[\"'\s]+|[\"'\s]+$/g, ''); if (/^bearer\s+/i.test(gotTrim)) gotTrim = gotTrim.replace(/^bearer\s+/i, '').trim(); - const isSecretValid = (gotTrim === expectedTrim) || (gotTrim === 'billing-rtrw-secret-key'); + const isSecretValid = gotTrim === expectedTrim; if (!isSecretValid) { logger.warn(`[WEBHOOK][payment-notif] Forbidden: secret_key mismatch. service=${String(service || '-')} (gotLen=${gotTrim.length}${gotTrim ? ', ***' + gotTrim.slice(-3) : ', empty'}, expectedLen=${expectedTrim.length}, ***${expectedTrim.slice(-3)})`); diff --git a/config/settingsManager.js b/config/settingsManager.js index 6fbab28..17c15e6 100644 --- a/config/settingsManager.js +++ b/config/settingsManager.js @@ -282,6 +282,14 @@ function formatTimeLocal(date) { * Pastikan gowa_webhook_secret tidak memakai default 'secret'. * Dibuat otomatis (32 byte hex) bila kosong/default/terlalu pendek. */ +function ensureWebhookSecret(force) { + const current = String(getSetting('webhook_secret', '') || '').trim(); + if (!force && current && current !== 'billing-rtrw-secret-key' && current.length >= 24) return current; + const secret = crypto.randomBytes(32).toString('hex'); + saveSettings({ webhook_secret: secret }); + return secret; +} + function ensureGowaWebhookSecret(force) { const current = String(getSetting('gowa_webhook_secret', '') || '').trim(); if (!force && current && current !== 'secret' && current.length >= 24) return current; @@ -374,5 +382,6 @@ module.exports = { parseDateInTimezone, startSettingsWatcher, ensureDefaultSettings, - ensureGowaWebhookSecret + ensureGowaWebhookSecret, + ensureWebhookSecret }; diff --git a/package.json b/package.json index b883d37..ba583fe 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "open-isp", - "version": "0.6.2", + "version": "0.6.3", "description": "OPEN-ISP \u2014 sistem billing & manajemen ISP open-source (PWA pelanggan, multi-OLT, RADIUS, WhatsApp, QRIS)", "main": "app-customer.js", "scripts": { diff --git a/routes/adminPortal.js b/routes/adminPortal.js index e382112..0c66da0 100644 --- a/routes/adminPortal.js +++ b/routes/adminPortal.js @@ -4490,15 +4490,17 @@ router.post('/automation/send-now', requireAdminSession, express.urlencoded({ ex }); router.get('/settings', requireAdminSession, requireSidebarMenuAccess('settings'), (req, res) => { - const settings = getSettings(); + let settings = getSettings(); + try { require('../config/settingsManager').ensureWebhookSecret(); settings = getSettings(); } catch (e) {} const protocol = req.headers['x-forwarded-proto'] || req.protocol; const host = req.get('host'); - const baseUrl = (settings && settings.app_url ? String(settings.app_url) : `${protocol}://${host}`).replace(/\/+$/, ''); + const baseUrl = (settings.public_base_url || (settings && settings.app_url) || `${protocol}://${host}`).toString().replace(/\/+$/, ''); const paymentWebhookUrl = `${baseUrl}/customer/payment/callback`; res.render('admin/settings', { title: 'Pengaturan Sistem', company: company(), activePage: 'settings', settings, msg: flashMsg(req), - paymentWebhookUrl + paymentWebhookUrl, + webhookBaseUrl: baseUrl }); }); diff --git a/version.txt b/version.txt index b1d7abc..a0a1517 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.6.2 \ No newline at end of file +0.6.3 \ No newline at end of file diff --git a/views/admin/settings.ejs b/views/admin/settings.ejs index ab563e5..88fee71 100644 --- a/views/admin/settings.ejs +++ b/views/admin/settings.ejs @@ -815,7 +815,7 @@

Hubungkan Webhook Notifikasi E-Wallet (Mode 2)
<% - const _base = (settings.public_base_url || '').replace(/\/+$/, ''); + const _base = String(typeof webhookBaseUrl !== 'undefined' && webhookBaseUrl ? webhookBaseUrl : (settings.public_base_url || '')).replace(/\/+$/, ''); const _secret = String(settings.webhook_secret || ''); const macroWebhookUrlFull = _base + '/api/webhook/v1/payment-notif?secret_key=' + encodeURIComponent(_secret); %> @@ -841,6 +841,7 @@
<%= t('admin.set.wh_wa_hint','Kirim WA ke admin tiap ada pembayaran terdeteksi tapi belum cocok (untuk dicocokkan manual via jam notif vs bukti transfer).') %>
+
Secret dibuat otomatis jika kosong. URL memakai Public Base URL; bila kosong memakai host saat ini. Salin URL + token ke aplikasi notification forwarder.