fix(qris): kode unik lintas order + pelunasan multi-tagihan; fix kirim tagihan manual
Some checks failed
CI / test (push) Has been cancelled
Some checks failed
CI / test (push) Has been cancelled
- webhook QRIS: nominal total tagihan + kode -> lunaskan semua tagihan pelanggan
- isQrisAmountTaken() cek invoice+voucher+donasi (cegah ambigu)
- billingSendService: require('qrcode'), nominal unik + {{qris_kode}}, fallback link
- pilih tagihan terlama; pesan pengingat manual yang jujur saat di-skip
- versi 0.5.5
This commit is contained in:
@@ -7,6 +7,29 @@ const db = require('../config/database');
|
||||
const qrisUtil = require('../utils/qrisUtil');
|
||||
const { getSettings } = require('../config/settingsManager');
|
||||
|
||||
/**
|
||||
* Cek apakah nominal unik sudah dipakai order lain yang masih menunggu bayar.
|
||||
* WAJIB mencakup invoice, voucher, dan donasi: webhook mencocokkan ketiganya
|
||||
* sekaligus, jadi nominal yang dipakai dua order berbeda akan dianggap ambigu
|
||||
* dan pembayarannya tidak diproses.
|
||||
*/
|
||||
function isQrisAmountTaken(amount, opts = {}) {
|
||||
const amt = Number(amount || 0);
|
||||
if (!Number.isFinite(amt) || amt <= 0) return false;
|
||||
const exInv = Number(opts.excludeInvoiceId || 0);
|
||||
const exVoucher = Number(opts.excludeVoucherOrderId || 0);
|
||||
const exDonation = Number(opts.excludeDonationOrderId || 0);
|
||||
try {
|
||||
const inv = db.prepare('SELECT id FROM invoices WHERE status=? AND qris_amount_unique=? AND id!=? LIMIT 1').get('unpaid', amt, exInv);
|
||||
if (inv && inv.id) return true;
|
||||
const voc = db.prepare('SELECT id FROM public_voucher_orders WHERE status=? AND qris_amount_unique=? AND id!=? LIMIT 1').get('pending', amt, exVoucher);
|
||||
if (voc && voc.id) return true;
|
||||
const don = db.prepare('SELECT id FROM public_donation_orders WHERE status=? AND qris_amount_unique=? AND id!=? LIMIT 1').get('pending', amt, exDonation);
|
||||
if (don && don.id) return true;
|
||||
} catch (e) { /* tabel opsional belum ada */ }
|
||||
return false;
|
||||
}
|
||||
|
||||
function ensureInvoiceQris(inv) {
|
||||
const settings = getSettings() || {};
|
||||
const invId = Number((inv && inv.id) || 0);
|
||||
@@ -17,11 +40,10 @@ function ensureInvoiceQris(inv) {
|
||||
let amountUnique = Number(inv.qris_amount_unique || 0);
|
||||
|
||||
if (!(amountUnique > 0 && code > 0 && (amountUnique - code) === base)) {
|
||||
const exists = db.prepare('SELECT id FROM invoices WHERE status=? AND qris_amount_unique=? AND id!=? LIMIT 1');
|
||||
let chosen = 0, chosenAmt = 0;
|
||||
for (let c = 1; c <= 999; c++) {
|
||||
const a = base + c;
|
||||
if (!exists.get('unpaid', a, invId)) { chosen = c; chosenAmt = a; break; }
|
||||
if (!isQrisAmountTaken(a, { excludeInvoiceId: invId })) { chosen = c; chosenAmt = a; break; }
|
||||
}
|
||||
if (chosen) {
|
||||
code = chosen;
|
||||
@@ -43,4 +65,4 @@ function ensureInvoiceQris(inv) {
|
||||
return { uniqueCode: code, amount: finalAmount, payload };
|
||||
}
|
||||
|
||||
module.exports = { ensureInvoiceQris };
|
||||
module.exports = { ensureInvoiceQris, isQrisAmountTaken };
|
||||
|
||||
Reference in New Issue
Block a user