From 80fb683dcc27d215afcbe34d38633ede806d6fd7 Mon Sep 17 00:00:00 2001 From: CIFO Dev Date: Fri, 14 Nov 2025 10:04:53 +0700 Subject: [PATCH 1/2] Remove dev-only status mock; always query Midtrans API. Add helper script to compute Midtrans webhook signature for local tests. --- scripts/midtrans-sig.cjs | 15 +++++++++++++++ server/index.cjs | 27 +++++++++++++++++++-------- 2 files changed, 34 insertions(+), 8 deletions(-) create mode 100644 scripts/midtrans-sig.cjs diff --git a/scripts/midtrans-sig.cjs b/scripts/midtrans-sig.cjs new file mode 100644 index 0000000..2bd7821 --- /dev/null +++ b/scripts/midtrans-sig.cjs @@ -0,0 +1,15 @@ +// Utility to compute Midtrans webhook signature: sha512(order_id + status_code + gross_amount + server_key) +const crypto = require('crypto') + +function main() { + const [orderId, statusCode, grossAmount, serverKey] = process.argv.slice(2) + if (!orderId || !statusCode || !grossAmount || !serverKey) { + console.error('Usage: node scripts/midtrans-sig.js ') + process.exit(1) + } + const raw = String(orderId) + String(statusCode) + String(grossAmount) + String(serverKey) + const sig = crypto.createHash('sha512').update(raw).digest('hex') + process.stdout.write(sig) +} + +main() \ No newline at end of file diff --git a/server/index.cjs b/server/index.cjs index e9494f9..b135b60 100644 --- a/server/index.cjs +++ b/server/index.cjs @@ -276,10 +276,14 @@ app.get('/api/payments/:orderId/status', async (req, res) => { if (isSuccessfulMidtransStatus(status)) { const nominal = String(status?.gross_amount || '') if (!notifiedOrders.has(orderId)) { - notifiedOrders.add(orderId) activeOrders.delete(orderId) logInfo('status.notify.erp.trigger', { orderId, transaction_status: status?.transaction_status }) - await notifyERP({ orderId, nominal }) + const ok = await notifyERP({ orderId, nominal }) + if (ok) { + notifiedOrders.add(orderId) + } else { + logWarn('erp.notify.defer', { orderId, reason: 'post_failed_or_missing_data' }) + } } else { logInfo('erp.notify.skip', { orderId, reason: 'already_notified' }) } @@ -466,18 +470,18 @@ function resolveMercantId(orderId) { async function notifyERP({ orderId, nominal, mercantId }) { if (!ERP_ENABLE_NOTIF) { logInfo('erp.notify.skip', { reason: 'disabled' }) - return + return false } // Untuk notifikasi dinamis, hanya URL dan client secret yang wajib if (!ERP_NOTIFICATION_URL || !ERP_CLIENT_ID) { logWarn('erp.notify.missing_config', { hasUrl: !!ERP_NOTIFICATION_URL, hasClientId: !!ERP_CLIENT_ID }) - return + return false } const statusCode = '200' const mId = mercantId || resolveMercantId(orderId) if (!mId) { logWarn('erp.notify.skip', { orderId, reason: 'missing_mercant_id' }) - return + return false } const signature = computeErpSignature(mId, statusCode, nominal, ERP_CLIENT_ID) // Payload ERP harus flat: { mercant_id, nominal, status_code, signature } @@ -491,8 +495,10 @@ async function notifyERP({ orderId, nominal, mercantId }) { try { const res = await postJson(ERP_NOTIFICATION_URL, payload) logInfo('erp.notify.success', { orderId, status: res.status }) + return true } catch (e) { logError('erp.notify.error', { orderId, message: e?.message }) + return false } } @@ -518,12 +524,17 @@ app.post('/api/payments/webhook', async (req, res) => { // Process success callbacks asynchronously if (isSuccessfulMidtransStatus(body)) { + logInfo('webhook.success_status', { order_id: orderId, transaction_status: body?.transaction_status, fraud_status: body?.fraud_status }) const nominal = String(grossAmount) if (!notifiedOrders.has(orderId)) { - notifiedOrders.add(orderId) // Mark order inactive upon completion activeOrders.delete(orderId) - await notifyERP({ orderId, nominal }) + const ok = await notifyERP({ orderId, nominal }) + if (ok) { + notifiedOrders.add(orderId) + } else { + logWarn('erp.notify.defer', { orderId, reason: 'post_failed_or_missing_data' }) + } } else { logInfo('erp.notify.skip', { orderId, reason: 'already_notified' }) } @@ -539,4 +550,4 @@ app.post('/api/payments/webhook', async (req, res) => { const port = process.env.PORT || 8000 app.listen(port, () => { console.log(`[server] listening on http://localhost:${port}/ (production=${isProduction})`) -}) \ No newline at end of file +}) From 9edcd6191ad010eb411185e68c2313d1cf02fb6f Mon Sep 17 00:00:00 2001 From: CIFO Dev Date: Fri, 14 Nov 2025 10:07:11 +0700 Subject: [PATCH 2/2] notify erp --- tmp-sig.txt | 1 + tmp-sig2.txt | Bin 0 -> 262 bytes tmp-sig3.txt | 1 + 3 files changed, 2 insertions(+) create mode 100644 tmp-sig.txt create mode 100644 tmp-sig2.txt create mode 100644 tmp-sig3.txt diff --git a/tmp-sig.txt b/tmp-sig.txt new file mode 100644 index 0000000..5ebb743 --- /dev/null +++ b/tmp-sig.txt @@ -0,0 +1 @@ +417582e9fb7105b479e3e7aee99a285dbee0f2ec3238869f8f6fc36b6a098dbee411cf0d3e7637b69f41803518e640a6c9ae71a66b414b29e2182f5aed2ea55a \ No newline at end of file diff --git a/tmp-sig2.txt b/tmp-sig2.txt new file mode 100644 index 0000000000000000000000000000000000000000..68890c021ce19aabaceb541d196fc2b4f7ee2a7d GIT binary patch literal 262 zcmW-bQ4+);2t)I|GkX_pwc>HK)cc>HyHAEekc5QaYw9{QRMoUp=q|cPiD}i%Y{;W# znI?<|V#?H;G|&kr1@fTQ!4e85ugA~OOvEHByorK?Z>9a;K2G0)