import { Request, Response } from "express"; import getTokenAuth from "../lib/getTokenAuth"; import { getUserFromToken } from "../middleware/auth"; import { get, isEmpty } from "lodash"; import db from "../db"; import { v7 } from "uuid"; import { formatTimestamp } from "../lib/formatTimestamp"; import { formatMoney } from "../lib/bank/mandiri/getInvoiceVirtualAccount"; import callGate from "../lib/gate"; import { getParameter } from "../lib/getParameter"; import { dbQueryClient } from "../lib/dbQueryClient"; import sequence from "../lib/sequence"; export async function inquiry(req: Request, res: Response) { try { const { version } = req.params; if (version === "v1.0") { const body = req.body || {}; const requiredBody = [ "partnerServiceId", "customerNo", "virtualAccountNo", "trxDateInit", "inquiryRequestId", ]; for (const f of requiredBody) { if (body[f] === undefined || body[f] === null) { return res.status(400).json({ error: `missing body field ${f}` }); } } await db.activity_logs.create({ data: { action: "GET", status: "SUCCESS", message: "Get Parameter User PartnerServiceId", }, }); const parameters = await db.parameters.findFirst({ where: { param_key: "partnerServiceId", param_value: get(body, "partnerServiceId"), }, }); const user = await db.users.findFirst({ where: { user_id: parameters?.user_id }, include: { database: true, banks: true, parameters: true, tenants: true, }, }); await db.activity_logs.create({ data: { action: "GET", status: "SUCCESS", message: "Get User", }, }); const result = await callGate({ client: "mandiri", action: "getInvoiceVirtualAccount", data: { ...body }, user: user, }); return res.status(200).json({ responseCode: "2002400", responseMessage: "Successful", virtualAccountData: result, }); } else { return res.status(400).json({ error: "unsupported version" }); } } catch (err) { const message: any = get(err, "message", "internal_error"); if ( message === "No outstanding amount found for the given Virtual Account No" ) { return res.status(404).json({ responseCode: "4042412", responseMessage: "Invalid Bill not found", }); } else if ( message === "C_BPartner_ID not found for the given Virtual Account No" ) { return res.status(404).json({ responseCode: "4042401", responseMessage: "Virtual Account not found", }); } else if (message === "User database information is missing") { return res.status(500).json({ responseCode: "5002401", responseMessage: "Internal Server Error", }); } console.error("transfer inquiry error:", err); return res.status(500).json({ responseCode: "5002401", responseMessage: "Internal Server Error", }); } } export async function MidsuitPayment(req: Request, res: Response) { const data = req.body || {}; const timestamp = formatTimestamp(); let token = await getTokenAuth(req); let user = null as any; if (!token) { const authClient = req.header("Authorization-Client") || ""; user = await db.users.findFirst({ where: { token_access: authClient, }, include: { tenants: true, database: true, banks: true, parameters: true, }, }); } else { const userToken = await getUserFromToken(token); user = get(userToken, "user"); } if (!user) { return res.status(401).json({ error: "invalid token" }); } if (!get(user, "database")) { return res.status(400).json({ error: "user has no database assigned" }); } if (get(data, "amount") <= 0) { return res.status(400).json({ error: "invalid amount" }); } if ( isEmpty(get(data, "originatorBankCode")) || isEmpty(get(data, "beneficiaryBankCode")) ) { return res.status(400).json({ error: "invalid bank code" }); } const bankFrom = await db.banks.findFirst({ where: { code: String(get(data, "originatorBankCode")) }, }); if (!bankFrom) { return res .status(400) .json({ error: "unsupported originator bank code midsuit" }); } const bankTo = await db.banks.findFirst({ where: { code: String(get(data, "beneficiaryBankCode")) }, }); if (!bankTo) { return res .status(400) .json({ error: "unsupported beneficiary bank code midsuit" }); } const bankCodeUser = String(get(data, "originatorBankCode")); if (isEmpty(bankCodeUser)) { return res.status(400).json({ error: "user has no bank assigned" }); } const partnerReferenceNo = v7(); const external_id = await sequence("midsuit_payment_external_id"); let requestBody: any = { partnerReferenceNo: partnerReferenceNo, amount: { value: formatMoney(get(data, "amount")), currency: "IDR", }, beneficiaryAccountNo: get(data, "beneficiaryAccountNo"), beneficiaryEmail: get(data, "beneficiaryEmail"), currency: "IDR", customerReference: get(data, "documentno"), // feeType: "BEN", BIAYA ADMIN remark: get(data, "documentno"), sourceAccountNo: get(data, "sourceAccountNo"), transactionDate: timestamp, originatorInfos: [ { originatorCustomerNo: get(data, "originatorCustomerNo"), originatorCustomerName: get(data, "originatorCustomerName"), originatorBankCode: bankCodeUser, }, ], }; let isRTKGS = get(data, "amount") >= 100000000; if (isRTKGS || get(data, "beneficiaryBankCode") !== bankCodeUser) { requestBody = { ...requestBody, beneficiaryAccountName: get(data, "beneficiaryAccountName"), beneficiaryBankCode: get(data, "beneficiaryBankCode"), beneficiaryCustomerResidence: "1", beneficiaryCustomerType: get(data, "beneficiaryCustomerType", "1"), senderCustomerResidence: "1", senderCustomerType: get(data, "beneficiaryCustomerType", "2"), }; } const transaction = await db.transactions.create({ data: { db_id: user.db_id, user_id: user.user_id, documentno: get(data, "documentno"), tenant_id: user.tenant_id, bank_id: get(bankTo, "bank_id") as any, bankto_id: get(bankFrom, "bank_id") as any, external_id: external_id, partnerReferenceNo: partnerReferenceNo, amount: get(data, "amount"), description: `Payment for ${get(data, "documentno")}`, status: "DRAFT", beneficiaryAccountName: get(data, "beneficiaryAccountName"), beneficiaryAccountNo: get(data, "beneficiaryAccountNo"), sourceAccountNo: get(data, "sourceAccountNo"), originatorCustomerName: get(data, "originatorCustomerName"), originatorCustomerNo: get(data, "originatorCustomerNo"), method: isRTKGS ? "RTGS" : get(data, "beneficiaryBankCode") !== bankCodeUser ? "SKN" : "Inhouse", }, }); await db.transactions_lines.create({ data: { transaction_id: transaction.id, documentno: get(data, "documentno"), amount: get(data, "amount"), description: `Payment for ${get(data, "documentno")}`, tenant_id: user.tenant_id, line_no: 1, }, }); if (isRTKGS) { // method RTGS const result = await callGate({ action: "TransferRTGS", client: "mandiri", data: { clientbank_id: get(user, "clientbank_id"), requestBody, external_id: getParameter(user, "external_id"), channel_id: getParameter(user, "channel_id"), }, }); await db.transactions.update({ where: { id: transaction.id }, data: { status: "COMPLETED", referenceNo: get(result, "referenceNo"), trxDateTime: get(result, "timestamp") ? new Date(get(result, "timestamp")) : null, }, }); return res.status(200).json(result); } else if (get(data, "beneficiaryBankCode") !== bankCodeUser) { // method SKN const result = await callGate({ action: "TransferSKN", client: "mandiri", data: { clientbank_id: get(user, "clientbank_id"), requestBody, external_id: external_id, channel_id: getParameter(user, "channel_id"), }, }); await db.transactions.update({ where: { id: transaction.id }, data: { status: "COMPLETED", referenceNo: get(result, "referenceNo"), trxDateTime: get(result, "timestamp") ? new Date(get(result, "timestamp")) : null, }, }); return res.status(200).json(result); } else if (get(data, "beneficiaryBankCode") === bankCodeUser) { // method Inhouse const result = await callGate({ action: "TransferInhouse", client: "mandiri", data: { clientbank_id: get(user, "clientbank_id"), requestBody, external_id: external_id, channel_id: getParameter(user, "channel_id"), }, }); await db.transactions.update({ where: { id: transaction.id }, data: { status: "COMPLETED", referenceNo: get(result, "referenceNo"), trxDateTime: get(result, "timestamp") ? new Date(get(result, "timestamp")) : null, }, }); return res.status(200).json(result); } } export async function MidsuitPaymentV2(req: Request, res: Response) { const data = req.body || {}; const timestamp = formatTimestamp(); let token = await getTokenAuth(req); let user = null as any; if (!token) { const authClient = req.header("Authorization-Client") || ""; user = await db.users.findFirst({ where: { token_access: authClient, }, include: { tenants: true, database: true, banks: true, parameters: true, }, }); } else { const userToken = await getUserFromToken(token); user = get(userToken, "user"); } if (!user) { return res.status(401).json({ error: "invalid token" }); } if (!get(user, "database")) { return res.status(400).json({ error: "user has no database assigned" }); } const result = await dbQueryClient({ name: get(user, "database.name") || "", action: "first", query: ` select * from c_payment where c_payment_id = ${get( data, "c_payment_id", 0 )}`, }); if (!result) { return res.status(404).json({ error: "payment not found" }); } if (get(data, "amount") != get(result, "payamt")) { return res.status(400).json({ error: "amount mismatch" }); } if (get(data, "amount") <= 0) { return res.status(400).json({ error: "invalid amount" }); } if (isEmpty(get(data, "bank_code"))) { return res.status(400).json({ error: "invalid bank code" }); } const bankCode = await db.bank_code.findFirst({ where: { code: String(get(data, "bank_code")) }, }); if (!bankCode) { return res.status(400).json({ error: "unsupported bank code midsuit" }); } const bankCodeUser = String(get(user, "banks.code")); if (isEmpty(bankCodeUser)) { return res.status(400).json({ error: "user has no bank assigned" }); } const partnerReferenceNo = v7(); let requestBody: any = { partnerReferenceNo: partnerReferenceNo, amount: { value: formatMoney(get(data, "amount")), currency: "IDR", }, beneficiaryAccountNo: get(data, "accountno"), beneficiaryEmail: get(data, "email"), currency: "IDR", customerReference: get(data, "customerReference") || get(result, "documentno"), // feeType: "BEN", BIAYA ADMIN remark: get(result, "documentno"), sourceAccountNo: getParameter(user, "sourceaccountno"), transactionDate: timestamp, originatorInfos: [ { originatorCustomerNo: getParameter(user, "originatorCustomerNo"), originatorCustomerName: getParameter(user, "originatorCustomerName"), originatorBankCode: bankCodeUser, }, ], }; let isRTKGS = get(data, "amount") >= 100000000; if (isRTKGS || bankCode.code !== bankCodeUser) { requestBody = { ...requestBody, beneficiaryAccountName: get(data, "accountname"), beneficiaryBankCode: get(bankCode, "code"), beneficiaryCustomerResidence: "1", beneficiaryCustomerType: "1", senderCustomerResidence: "1", senderCustomerType: "2", }; } if (isRTKGS) { // method RTGS const result = await callGate({ action: "TransferRTGS", client: "mandiri", data: { clientbank_id: get(user, "clientbank_id"), requestBody, external_id: getParameter(user, "external_id"), channel_id: getParameter(user, "channel_id"), }, }); return res.status(200).json(result); } else if (bankCode.code !== bankCodeUser) { // method SKN const result = await callGate({ action: "TransferSKN", client: "mandiri", data: { clientbank_id: get(user, "clientbank_id"), requestBody, external_id: getParameter(user, "external_id"), channel_id: getParameter(user, "channel_id"), }, }); return res.status(200).json(result); } else if (bankCode.code === bankCodeUser) { // method Inhouse const result = await callGate({ action: "TransferInhouse", client: "mandiri", data: { clientbank_id: get(user, "clientbank_id"), requestBody, external_id: getParameter(user, "external_id"), channel_id: getParameter(user, "channel_id"), }, }); return res.status(200).json(result); } } export async function paymentStatus(req: Request, res: Response) { try { const { c_invoice_id, startdate, enddate } = req.body as any; const where: any = {}; if (startdate || enddate) { if (!startdate || !enddate) { return res.status(400).json({ error: "startdate and enddate are required when filtering by date", }); } where.created_at = {} as any; if (startdate) { const s = new Date(String(startdate)); where.created_at.gte = s; } if (enddate) { const e = new Date(String(enddate)); // include the whole end day by setting to end of day if time not provided if (!String(enddate).includes("T")) { e.setHours(23, 59, 59, 999); } where.created_at.lte = e; } } if (c_invoice_id) { where.c_invoice_id = Number(c_invoice_id); } const rows = await db.transactions_lines.findMany({ where: { ...where, AND: [ { NOT: { c_invoice_id: null, }, }, { transactions: { status: "COMPLETED", }, }, ], }, include: { transactions: true }, orderBy: { created_at: "desc" }, }); if (rows.length === 0) { return res .status(404) .json({ responseCode: "404", responseMessage: "no payment found" }); } return res.json({ count: rows.length, data: rows }); } catch (err) { console.error("paymentStatus error", err); return res.status(500).json({ error: "internal_error", detail: err instanceof Error ? err.message : String(err), }); } }