fixing merchant id
This commit is contained in:
parent
4b43c61365
commit
6472e95310
|
|
@ -38,7 +38,6 @@ function parseEnable(v) {
|
||||||
const ERP_NOTIFICATION_URL = process.env.ERP_NOTIFICATION_URL || ''
|
const ERP_NOTIFICATION_URL = process.env.ERP_NOTIFICATION_URL || ''
|
||||||
const ERP_ENABLE_NOTIF = parseEnable(process.env.ERP_ENABLE_NOTIF)
|
const ERP_ENABLE_NOTIF = parseEnable(process.env.ERP_ENABLE_NOTIF)
|
||||||
const ERP_CLIENT_ID = process.env.ERP_CLIENT_ID || ''
|
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()
|
const notifiedOrders = new Set()
|
||||||
|
|
||||||
// --- Logger utilities
|
// --- 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_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 PAYMENT_LINK_BASE = process.env.PAYMENT_LINK_BASE || 'http://localhost:5174/pay'
|
||||||
const activeOrders = new Map() // order_id -> expire_at
|
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 isDevEnv() { return (process.env.NODE_ENV || '').toLowerCase() !== 'production' }
|
||||||
function verifyExternalKey(req) {
|
function verifyExternalKey(req) {
|
||||||
|
|
@ -266,7 +267,27 @@ app.get('/api/payments/:orderId/status', async (req, res) => {
|
||||||
logInfo('status.request', { id: req.id, orderId })
|
logInfo('status.request', { id: req.id, orderId })
|
||||||
const status = await core.transaction.status(orderId)
|
const status = await core.transaction.status(orderId)
|
||||||
logInfo('status.success', { id: req.id, orderId, transaction_status: status?.transaction_status })
|
logInfo('status.success', { id: req.id, orderId, transaction_status: status?.transaction_status })
|
||||||
|
// Respond immediately with status
|
||||||
res.json(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) {
|
} catch (e) {
|
||||||
const msg = e?.message || 'Status check failed'
|
const msg = e?.message || 'Status check failed'
|
||||||
logError('status.error', { id: req.id, orderId, message: msg })
|
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) ? `${mercantId}:${primaryItemId}` :
|
||||||
(primaryItemId || mercantId || req?.body?.order_id || req?.body?.item_id || '')
|
(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
|
// Bentuk customer dari field nama/no_telepon/email
|
||||||
const customer = {
|
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) {
|
if (!ERP_ENABLE_NOTIF) {
|
||||||
logInfo('erp.notify.skip', { reason: 'disabled' })
|
logInfo('erp.notify.skip', { reason: 'disabled' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!ERP_NOTIFICATION_URL || !ERP_CLIENT_ID || !ERP_MERCANT_ID) {
|
// Untuk notifikasi dinamis, hanya URL dan client secret yang wajib
|
||||||
logWarn('erp.notify.missing_config', { hasUrl: !!ERP_NOTIFICATION_URL, hasClientId: !!ERP_CLIENT_ID, hasMercantId: !!ERP_MERCANT_ID })
|
if (!ERP_NOTIFICATION_URL || !ERP_CLIENT_ID) {
|
||||||
|
logWarn('erp.notify.missing_config', { hasUrl: !!ERP_NOTIFICATION_URL, hasClientId: !!ERP_CLIENT_ID })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const statusCode = '200'
|
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 = {
|
const payload = {
|
||||||
data: {
|
mercant_id: mId,
|
||||||
mercant_id: ERP_MERCANT_ID,
|
status_code: statusCode,
|
||||||
status_code: statusCode,
|
nominal: nominal,
|
||||||
nominal: nominal,
|
|
||||||
client_id: ERP_CLIENT_ID,
|
|
||||||
},
|
|
||||||
signature,
|
signature,
|
||||||
}
|
}
|
||||||
logInfo('erp.notify.start', { orderId, url: ERP_NOTIFICATION_URL })
|
logInfo('erp.notify.start', { orderId, url: ERP_NOTIFICATION_URL })
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue