Show deposit progress after the invoice

- The invoice checks the server every 30 seconds and shows the amount
  detected, then the confirmation once the server is listed.
- The server page gets the same banner, with a 24-hour limit on checks.
- The invoice link is now "Follow your payment", so the invoice can be
  closed and the deposit followed from a fixed address.
This commit is contained in:
2026-11-01 12:36:46 +01:00
parent aa723acbca
commit 956e133b7c
2 changed files with 121 additions and 0 deletions

View File

@@ -639,7 +639,61 @@ window.addEventListener('popstate', () => {
fetchMinAmount();
});
// After an invoice is shown, ask the server page every 30 seconds whether the
// payment has been seen, so the visitor does not have to reload. The status only
// moves forward: unconfirmed_balance may go back to 0 before the deposit is
// credited, and that must not look as if the payment had disappeared.
const DEPOSIT_POLL_MS = 30000;
const DEPOSIT_POLL_MAX_MS = 24 * 60 * 60 * 1000;
let depositWatch = null;
function stopDepositWatch() {
if (depositWatch) clearInterval(depositWatch.timer);
depositWatch = null;
}
async function pollDeposit() {
const watch = depositWatch;
if (!watch) return;
if (Date.now() - watch.started > DEPOSIT_POLL_MAX_MS) return stopDepositWatch();
let data;
try {
const response = await fetch(`/weinfodata/${watch.chain}/${watch.id}`);
if (!response.ok) return;
data = await response.json();
} catch (err) {
return; // temporary network problem: try again at the next round
}
if (watch !== depositWatch || !data) return;
const expiry = data.expiry_date || null;
// The first answer is the reference: listing or renewal changes the expiry date.
if (watch.expiry === undefined) watch.expiry = expiry;
const status = document.getElementById('iv-status');
const bal = data.balance != null ? fmt(data.balance) : null;
const unc = data.unconfirmed_balance != null ? fmt(data.unconfirmed_balance) : null;
if (bal) document.getElementById('iv-balance').textContent = unc ? bal + ' (' + unc + ')' : bal;
if (expiry && expiry !== watch.expiry) {
status.className = 'ok-line';
status.textContent = '✓ Payment confirmed: your server is now listed.';
stopDepositWatch();
fetchData();
} else if (Number(data.unconfirmed_balance) > 0 && !watch.detected) {
watch.detected = true;
status.className = 'ok-line';
status.textContent = 'Payment detected: ' + fmt(data.unconfirmed_balance) + ' sats, waiting for 3 confirmations (about 30 minutes). This page updates by itself, or use the "Follow your payment" link below.';
}
}
function startDepositWatch(chain, id) {
stopDepositWatch();
if (!id) return;
depositWatch = { chain, id, started: Date.now(), detected: false, expiry: undefined };
depositWatch.timer = setInterval(pollDeposit, DEPOSIT_POLL_MS);
pollDeposit();
}
document.getElementById('submit').addEventListener('click', async () => {
stopDepositWatch();
const urlInput = document.getElementById('url');
let url = urlInput.value.trim();
if (!url) return;
@@ -739,6 +793,8 @@ document.getElementById('terms-accept').addEventListener('click', async function
inv.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
return;
}
// Fixed address: whoever pays can close this invoice and follow the deposit there.
if (weLink) weinfoEl.textContent = 'Follow your payment';
const amountSats = result.min_amount != null ? result.min_amount : minAmount;
const amountBtc = (amountSats / 1e8).toFixed(8).replace(/\.?0+$/, '');
const invoiceuri = `bitcoin:${result.address}?amount=${amountBtc}&label=BAL&message=Order+For+url+%26+${encodeURIComponent(url)}`;
@@ -773,6 +829,7 @@ document.getElementById('terms-accept').addEventListener('click', async function
const inv = document.getElementById('invoice');
inv.classList.add('show');
inv.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
startDepositWatch(result.chain || chain, result.id);
} catch (err) {
document.getElementById('iv-status').className = 'err-line';
document.getElementById('iv-status').textContent = 'Network error. Please try again.';