portal-payment-be/src/controllers/transferController.ts

504 lines
15 KiB
TypeScript

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),
});
}
}