first commit

This commit is contained in:
2026-02-18 16:03:43 +07:00
commit c52ecd6514
54 changed files with 8270 additions and 0 deletions

View File

@@ -0,0 +1,24 @@
const notificationService = require('../services/notification.service');
const responseHelper = require('../../../helpers/response.helper');
class NotificationController {
static async handle(req, res) {
const payload = req.body;
try {
const isValid = notificationService.verifySignature(payload);
if (!isValid) {
return responseHelper.error(res, "Invalid Signature Key", 403);
}
await notificationService.processNotification(payload);
return responseHelper.success(res, "Notification Handled");
} catch (error) {
console.error("Webhook Error:", error.message);
return responseHelper.error(res, error.message);
}
}
}
module.exports = NotificationController;

View File

@@ -0,0 +1,8 @@
const express = require('express');
const router = express.Router();
const notificationController = require('../controllers/notification.controller');
router.post('/handling', notificationController.handle);
module.exports = router;

View File

@@ -0,0 +1,93 @@
const crypto = require('crypto');
const db = require('../../../../models/migration');
require('dotenv').config();
class NotificationService {
verifySignature(payload) {
const { order_id, status_code, gross_amount, signature_key } = payload;
const serverKey = process.env.MIDTRANS_SERVER_KEY;
const data = order_id + status_code + gross_amount + serverKey;
const hash = crypto.createHash('sha512').update(data).digest('hex');
return hash === signature_key;
}
async processNotification(payload) {
const { order_id, transaction_status, fraud_status } = payload;
console.log(`[Notification] Processing Order ${order_id}: status=${transaction_status}, fraud=${fraud_status}`);
try {
let payment = await db.Payment.findOne({
where: { external_id: order_id },
order: [['created_at', 'DESC']]
});
let order = null;
if (payment) {
order = await db.Order.findByPk(payment.order_id);
} else {
order = await db.Order.findOne({ where: { orderID: order_id } });
if (!order) {
order = await db.Order.findByPk(order_id);
}
}
if (!order) {
console.warn(`[Notification] Data Order/Payment untuk ID ${order_id} tidak ditemukan.`);
return;
}
let orderPaymentStatus = 'PENDING';
let orderStatus = order.status;
let paymentRecordStatus = 'PENDING';
if (transaction_status === 'settlement' || (transaction_status === 'capture' && fraud_status === 'accept')) {
orderPaymentStatus = 'PAID';
orderStatus = 'COMPLETED';
paymentRecordStatus = 'SUCCESS';
} else if (['deny', 'cancel', 'expire'].includes(transaction_status)) {
orderPaymentStatus = 'REJECT';
orderStatus = 'FAILED';
paymentRecordStatus = 'FAILED';
} else if (transaction_status === 'pending') {
orderPaymentStatus = 'PENDING';
paymentRecordStatus = 'PENDING';
}
if (order.payment_status === orderPaymentStatus && order.status === orderStatus) {
console.log(`[Notification] Order ${order.id} sudah sinkron. Skip update.`);
return;
}
await order.update({
payment_status: orderPaymentStatus,
status: orderStatus
});
if (!payment) {
payment = await db.Payment.findOne({
where: { order_id: order.id },
order: [['created_at', 'DESC']]
});
}
if (payment) {
await payment.update({
status: paymentRecordStatus,
response_data: JSON.stringify(payload)
});
}
console.log(`[Notification] SUCCESS: Order ${order.id} updated to ${orderStatus}, Payment updated to ${paymentRecordStatus}`);
} catch (error) {
console.error(`[Notification] Error: ${error.message}`);
throw error;
}
}
}
module.exports = new NotificationService();