Compare commits
2 Commits
9b8a62f1d8
...
d430e82d3f
| Author | SHA1 | Date |
|---|---|---|
|
|
d430e82d3f | |
|
|
6472e95310 |
|
|
@ -38,7 +38,6 @@ function parseEnable(v) {
|
|||
const ERP_NOTIFICATION_URL = process.env.ERP_NOTIFICATION_URL || ''
|
||||
const ERP_ENABLE_NOTIF = parseEnable(process.env.ERP_ENABLE_NOTIF)
|
||||
const ERP_CLIENT_ID = process.env.ERP_CLIENT_ID || ''
|
||||
const ERP_MERCANT_ID = process.env.ERP_MERCANT_ID || process.env.ERP_MERCHANT_ID || ''
|
||||
const notifiedOrders = new Set()
|
||||
|
||||
// --- Logger utilities
|
||||
|
|
@ -104,6 +103,8 @@ const PAYMENT_LINK_SECRET = process.env.PAYMENT_LINK_SECRET || ''
|
|||
const PAYMENT_LINK_TTL_MINUTES = parseInt(process.env.PAYMENT_LINK_TTL_MINUTES || '30', 10)
|
||||
const PAYMENT_LINK_BASE = process.env.PAYMENT_LINK_BASE || 'http://localhost:5174/pay'
|
||||
const activeOrders = new Map() // order_id -> expire_at
|
||||
// Map untuk menyimpan mercant_id per order_id agar notifikasi ERP bisa dinamis
|
||||
const orderMerchantId = new Map() // order_id -> mercant_id
|
||||
|
||||
function isDevEnv() { return (process.env.NODE_ENV || '').toLowerCase() !== 'production' }
|
||||
function verifyExternalKey(req) {
|
||||
|
|
@ -266,7 +267,27 @@ app.get('/api/payments/:orderId/status', async (req, res) => {
|
|||
logInfo('status.request', { id: req.id, orderId })
|
||||
const status = await core.transaction.status(orderId)
|
||||
logInfo('status.success', { id: req.id, orderId, transaction_status: status?.transaction_status })
|
||||
// Respond immediately with status
|
||||
res.json(status)
|
||||
|
||||
// Fallback: selain webhook, jika status di sini sudah sukses (settlement/capture+accept), kirim notifikasi ke ERP
|
||||
setImmediate(async () => {
|
||||
try {
|
||||
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 })
|
||||
} else {
|
||||
logInfo('erp.notify.skip', { orderId, reason: 'already_notified' })
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
logError('status.notify.error', { orderId, message: e?.message })
|
||||
}
|
||||
})
|
||||
} catch (e) {
|
||||
const msg = e?.message || 'Status check failed'
|
||||
logError('status.error', { id: req.id, orderId, message: msg })
|
||||
|
|
@ -298,6 +319,10 @@ app.post('/createtransaksi', async (req, res) => {
|
|||
(primaryItemId && mercantId) ? `${mercantId}:${primaryItemId}` :
|
||||
(primaryItemId || mercantId || req?.body?.order_id || req?.body?.item_id || '')
|
||||
)
|
||||
// Simpan mercant_id per order agar dapat digunakan saat notifikasi ERP
|
||||
if (mercantId) {
|
||||
try { orderMerchantId.set(order_id, mercantId) } catch {}
|
||||
}
|
||||
|
||||
// Bentuk customer dari field nama/no_telepon/email
|
||||
const customer = {
|
||||
|
|
@ -423,24 +448,43 @@ function computeErpSignature(mercantId, statusCode, nominal, clientId) {
|
|||
}
|
||||
}
|
||||
|
||||
async function notifyERP({ orderId, nominal }) {
|
||||
// Resolve mercant_id untuk sebuah order_id:
|
||||
// 1) gunakan map yang tersimpan dari createtransaksi
|
||||
// 2) jika order_id memakai skema "mercant_id:item_id", ambil prefix sebelum ':'
|
||||
// 3) fallback ke ERP_MERCANT_ID dari env (untuk kasus lama)
|
||||
function resolveMercantId(orderId) {
|
||||
try {
|
||||
if (orderMerchantId.has(orderId)) return orderMerchantId.get(orderId)
|
||||
if (typeof orderId === 'string' && orderId.includes(':')) {
|
||||
const [m] = orderId.split(':')
|
||||
if (m) return m
|
||||
}
|
||||
} catch {}
|
||||
return ''
|
||||
}
|
||||
|
||||
async function notifyERP({ orderId, nominal, mercantId }) {
|
||||
if (!ERP_ENABLE_NOTIF) {
|
||||
logInfo('erp.notify.skip', { reason: 'disabled' })
|
||||
return
|
||||
}
|
||||
if (!ERP_NOTIFICATION_URL || !ERP_CLIENT_ID || !ERP_MERCANT_ID) {
|
||||
logWarn('erp.notify.missing_config', { hasUrl: !!ERP_NOTIFICATION_URL, hasClientId: !!ERP_CLIENT_ID, hasMercantId: !!ERP_MERCANT_ID })
|
||||
// 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
|
||||
}
|
||||
const statusCode = '200'
|
||||
const signature = computeErpSignature(ERP_MERCANT_ID, statusCode, nominal, ERP_CLIENT_ID)
|
||||
const mId = mercantId || resolveMercantId(orderId)
|
||||
if (!mId) {
|
||||
logWarn('erp.notify.skip', { orderId, reason: 'missing_mercant_id' })
|
||||
return
|
||||
}
|
||||
const signature = computeErpSignature(mId, statusCode, nominal, ERP_CLIENT_ID)
|
||||
// Payload ERP harus flat: { mercant_id, nominal, status_code, signature }
|
||||
const payload = {
|
||||
data: {
|
||||
mercant_id: ERP_MERCANT_ID,
|
||||
mercant_id: mId,
|
||||
status_code: statusCode,
|
||||
nominal: nominal,
|
||||
client_id: ERP_CLIENT_ID,
|
||||
},
|
||||
signature,
|
||||
}
|
||||
logInfo('erp.notify.start', { orderId, url: ERP_NOTIFICATION_URL })
|
||||
|
|
|
|||
Loading…
Reference in New Issue