diff --git a/CHANGELOG.md b/CHANGELOG.md index f437804..8fac1da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ Format mengikuti [Keep a Changelog](https://keepachangelog.com/) dan [Semantic Versioning](https://semver.org/). +## [0.6.4] — 2026-11-06 — "qr-custom-ux" + +### Diperbaiki +- QR pada link publik kini **center**. +- Timer hanya tampil saat QR masih aktif; berhenti/disembunyikan ketika LUNAS, ditolak, atau kedaluwarsa. +- **Reject / Refund** kini tersedia juga untuk pembayaran yang sudah LUNAS. +- QR kedaluwarsa: **nominal dan gambar QR disembunyikan** di link publik maupun detail dashboard. +- Tombol **Regenerate** untuk membuat QR baru + kode unik baru dari QR yang kedaluwarsa (juga tersedia di tabel histori). + ## [0.6.3] — 2026-11-06 — "webhook-secret-auto" ### Diperbaiki diff --git a/README.md b/README.md index a403e07..704cb73 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@

Lisensi: ISC - Versi 0.6.3 (webhook-secret-auto) + Versi 0.6.4 (qr-custom-ux) Node.js >= 20 Docker Demo diff --git a/app-customer.js b/app-customer.js index c0cccac..72cff5d 100644 --- a/app-customer.js +++ b/app-customer.js @@ -2116,7 +2116,11 @@ app.get('/custom-qr/:token', (req, res) => { const company = settings.company_header || 'OPEN-ISP'; const statusLabels = { pending: 'Menunggu Pembayaran', paid: 'LUNAS', expired: 'Kedaluwarsa', warning: 'Perlu Verifikasi Admin', rejected: 'Ditolak / Refund' }; const status = String(row.status || 'pending'); - const showQr = status !== 'paid' && status !== 'rejected'; + const expiresMs = row.expires_at ? new Date(row.expires_at).getTime() : 0; + const expiredNow = status === 'expired' || (status === 'pending' && expiresMs && expiresMs < Date.now()); + const showAmount = !expiredNow; + const showQr = !expiredNow && status !== 'paid' && status !== 'rejected'; + const showTimer = status === 'pending' && !expiredNow; const amountText = 'Rp ' + Number(row.amount_unique || 0).toLocaleString('id-ID'); const baseAmountText = 'Rp ' + Number(row.amount || 0).toLocaleString('id-ID'); const html = ` @@ -2126,7 +2130,7 @@ body{margin:0;background:#0b1220;color:#e5e7eb;font-family:system-ui,Segoe UI,Ar .card{max-width:460px;width:100%;background:#0f172a;border:1px solid rgba(148,163,184,.2);border-radius:16px;padding:22px;text-align:center} h1{font-size:19px;margin:0 0 4px}p{margin:6px 0;color:#94a3b8;font-size:13px} .amt{font-size:26px;font-weight:800;color:#ADFF2F;margin:12px 0 2px}.base{font-size:12px;color:#64748b} -.qr{background:#fff;padding:12px;border-radius:14px;display:inline-block;margin:14px 0}.qr img{display:block;width:240px;height:240px} +.qr{background:#fff;padding:12px;border-radius:14px;display:block;width:max-content;margin:14px auto}.qr img{display:block;width:240px;height:240px} .badge{display:inline-block;padding:5px 10px;border-radius:999px;font-size:12px;font-weight:700;margin-top:6px} .pending{background:rgba(245,158,11,.15);color:#fbbf24}.paid{background:rgba(34,197,94,.15);color:#4ade80} .expired{background:rgba(239,68,68,.15);color:#f87171}.warning{background:rgba(249,115,22,.15);color:#fb923c}.rejected{background:rgba(148,163,184,.15);color:#cbd5e1} @@ -2134,20 +2138,22 @@ h1{font-size:19px;margin:0 0 4px}p{margin:6px 0;color:#94a3b8;font-size:13px}

${cqEscape(row.category || 'Pembayaran')}

${cqEscape(row.title)}

-
${amountText}
-
Nominal dasar ${baseAmountText} + kode unik ${Number(row.unique_code || 0)}
-
${cqEscape(statusLabels[status] || status)}
+${showAmount + ? `
${amountText}
Nominal dasar ${baseAmountText} + kode unik ${Number(row.unique_code || 0)}
` + : `
QR Kedaluwarsa
Nominal dan QR disembunyikan. Minta admin membuat QR baru.
`} +
${cqEscape(expiredNow ? 'Kedaluwarsa' : (statusLabels[status] || status))}
${showQr ? `
QRIS
` : ''} -
+${showTimer ? `
` : ''} ${row.note ? `
${cqEscape(row.note)}
` : ''} -
Bayar dengan nominal persis seperti di atas agar pembayaran otomatis tercocokkan. QR berlaku ${require('./services/customQrService').DURATION_MINUTES} menit.
+${showAmount ? `
Bayar dengan nominal persis seperti di atas agar pembayaran otomatis tercocokkan. QR berlaku ${require('./services/customQrService').DURATION_MINUTES} menit.
` : ''}
${cqEscape(company)}