68 lines
2.4 KiB
JavaScript
68 lines
2.4 KiB
JavaScript
import { randomUUID } from "node:crypto";
|
|
import { createTransaction, addTransactionEvent, toTransactionPayload } from "../store/transactionStore";
|
|
function makePartnerReference(requestId) {
|
|
const clean = requestId.replace(/[^a-zA-Z0-9_-]/g, "").slice(0, 40);
|
|
return `DYN-${clean || randomUUID().slice(0, 12)}`;
|
|
}
|
|
function makeDynamicQrPayload(input) {
|
|
const amountMinor = Math.round(input.amount * 100);
|
|
const encoded = Buffer.from(JSON.stringify({
|
|
type: "QRIS_DYNAMIC_MOCK",
|
|
transaction_id: input.transactionId,
|
|
partner_reference: input.partnerReference,
|
|
amount_minor: amountMinor,
|
|
currency: input.currency,
|
|
expires_at: input.expiresAt
|
|
})).toString("base64url");
|
|
return `QRIS-DYNAMIC-MOCK.${encoded}`;
|
|
}
|
|
export async function createDynamicQrTransaction(input) {
|
|
const ttlSeconds = Math.min(Math.max(input.expires_in_seconds || 300, 60), 1800);
|
|
const expiresAt = new Date(Date.now() + ttlSeconds * 1000).toISOString();
|
|
const partnerReference = makePartnerReference(input.request_id);
|
|
const tx = await createTransaction({
|
|
merchant_id: input.merchant_id,
|
|
outlet_id: input.outlet_id,
|
|
terminal_id: input.terminal_id,
|
|
device_id: input.device_id,
|
|
partner_reference: partnerReference,
|
|
amount: input.amount,
|
|
currency: input.currency || "IDR",
|
|
qr_mode: "dynamic",
|
|
initiation_mode: input.initiation_mode || "dynamic_api",
|
|
status: "awaiting_payment",
|
|
expired_at: expiresAt
|
|
});
|
|
const qrPayload = makeDynamicQrPayload({
|
|
transactionId: tx.id,
|
|
partnerReference,
|
|
amount: tx.amount,
|
|
currency: tx.currency,
|
|
expiresAt
|
|
});
|
|
await addTransactionEvent({
|
|
transaction_id: tx.id,
|
|
event_type: "DYNAMIC_QR_CREATED",
|
|
source: "device",
|
|
payload_json: {
|
|
request_id: input.request_id,
|
|
correlation_id: input.request_id,
|
|
device_id: input.device_id,
|
|
qr_payload: qrPayload,
|
|
expires_at: expiresAt,
|
|
transaction: toTransactionPayload(tx)
|
|
}
|
|
});
|
|
return {
|
|
request_id: input.request_id,
|
|
correlation_id: input.request_id,
|
|
transaction_id: tx.id,
|
|
transaction_code: tx.transaction_code,
|
|
qr_type: "dynamic",
|
|
qr_payload: qrPayload,
|
|
expires_at: expiresAt,
|
|
status: "awaiting_payment",
|
|
partner_reference: partnerReference
|
|
};
|
|
}
|