Preserve absolute invoice deadline across reloads
This commit is contained in:
@@ -45,6 +45,7 @@ $data = $urls[$code];
|
|||||||
$hash = is_array($data) ? ($data['h'] ?? '') : $data;
|
$hash = is_array($data) ? ($data['h'] ?? '') : $data;
|
||||||
$hash = is_string($hash) ? $hash : '';
|
$hash = is_string($hash) ? $hash : '';
|
||||||
$signature = is_array($data) ? $data['s'] : null;
|
$signature = is_array($data) ? $data['s'] : null;
|
||||||
|
$expiryTs = is_array($data) ? intval($data['e'] ?? 0) : 0;
|
||||||
|
|
||||||
// Re-derive expected signature so client can verify
|
// Re-derive expected signature so client can verify
|
||||||
$expected = $signature ? hash_hmac('sha256', $hash, get_hmac_secret()) : null;
|
$expected = $signature ? hash_hmac('sha256', $hash, get_hmac_secret()) : null;
|
||||||
@@ -52,5 +53,6 @@ $expected = $signature ? hash_hmac('sha256', $hash, get_hmac_secret()) : null;
|
|||||||
echo json_encode([
|
echo json_encode([
|
||||||
'code' => $code,
|
'code' => $code,
|
||||||
'hash' => $hash,
|
'hash' => $hash,
|
||||||
'signature' => $expected
|
'signature' => $expected,
|
||||||
|
'expiry_ts' => $expiryTs > 0 ? $expiryTs : null
|
||||||
]);
|
]);
|
||||||
|
|||||||
34
app.js
34
app.js
@@ -29,6 +29,7 @@
|
|||||||
const timerCustom = $('#timerCustom');
|
const timerCustom = $('#timerCustom');
|
||||||
const deadlineBadges = $('#deadlineBadges');
|
const deadlineBadges = $('#deadlineBadges');
|
||||||
let selectedDays = 0;
|
let selectedDays = 0;
|
||||||
|
let deadlineEndMs = null;
|
||||||
const generateBtn = $('#generate');
|
const generateBtn = $('#generate');
|
||||||
const resultSection = $('#result');
|
const resultSection = $('#result');
|
||||||
const qrContainer = $('#qr');
|
const qrContainer = $('#qr');
|
||||||
@@ -131,11 +132,13 @@
|
|||||||
if (btn.classList.contains('active')) {
|
if (btn.classList.contains('active')) {
|
||||||
btn.classList.remove('active');
|
btn.classList.remove('active');
|
||||||
selectedDays = 0;
|
selectedDays = 0;
|
||||||
|
deadlineEndMs = null;
|
||||||
timerCustom.value = '';
|
timerCustom.value = '';
|
||||||
} else {
|
} else {
|
||||||
deadlineBadges.querySelectorAll('.badge').forEach(function (b) { b.classList.remove('active'); });
|
deadlineBadges.querySelectorAll('.badge').forEach(function (b) { b.classList.remove('active'); });
|
||||||
btn.classList.add('active');
|
btn.classList.add('active');
|
||||||
selectedDays = days;
|
selectedDays = days;
|
||||||
|
deadlineEndMs = null;
|
||||||
timerCustom.value = '';
|
timerCustom.value = '';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -143,6 +146,7 @@
|
|||||||
timerCustom.addEventListener('input', function () {
|
timerCustom.addEventListener('input', function () {
|
||||||
deadlineBadges.querySelectorAll('.badge').forEach(function (b) { b.classList.remove('active'); });
|
deadlineBadges.querySelectorAll('.badge').forEach(function (b) { b.classList.remove('active'); });
|
||||||
selectedDays = parseInt(timerCustom.value) || 0;
|
selectedDays = parseInt(timerCustom.value) || 0;
|
||||||
|
deadlineEndMs = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
// PDF
|
// PDF
|
||||||
@@ -162,6 +166,7 @@
|
|||||||
currencySelect.value = 'EUR';
|
currencySelect.value = 'EUR';
|
||||||
descInput.value = '';
|
descInput.value = '';
|
||||||
selectedDays = 0;
|
selectedDays = 0;
|
||||||
|
deadlineEndMs = null;
|
||||||
timerCustom.value = '';
|
timerCustom.value = '';
|
||||||
deadlineBadges.querySelectorAll('.badge').forEach(function (b) { b.classList.remove('active'); });
|
deadlineBadges.querySelectorAll('.badge').forEach(function (b) { b.classList.remove('active'); });
|
||||||
fiatHint.textContent = '';
|
fiatHint.textContent = '';
|
||||||
@@ -271,12 +276,13 @@
|
|||||||
return uri;
|
return uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildHash(addr, xmrAmount, desc, timer) {
|
function buildHash(addr, xmrAmount, desc, timer, deadlineTs) {
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
params.set('a', addr);
|
params.set('a', addr);
|
||||||
if (xmrAmount) params.set('x', xmrAmount.toFixed(12));
|
if (xmrAmount) params.set('x', xmrAmount.toFixed(12));
|
||||||
if (desc) params.set('d', desc);
|
if (desc) params.set('d', desc);
|
||||||
if (timer) params.set('t', timer);
|
if (timer) params.set('t', timer);
|
||||||
|
if (deadlineTs) params.set('te', deadlineTs);
|
||||||
return params.toString();
|
return params.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,7 +327,14 @@
|
|||||||
updatePageTitle(xmrAmount, desc);
|
updatePageTitle(xmrAmount, desc);
|
||||||
|
|
||||||
// Share link — keep existing short URL if present; otherwise shorten new hash
|
// Share link — keep existing short URL if present; otherwise shorten new hash
|
||||||
const hash = buildHash(addr, xmrAmount, desc, timer);
|
var deadlineTs = null;
|
||||||
|
if (timer && timer > 0) {
|
||||||
|
if (!deadlineEndMs) {
|
||||||
|
deadlineEndMs = Date.now() + timer * 86400000;
|
||||||
|
}
|
||||||
|
deadlineTs = Math.floor(deadlineEndMs / 1000);
|
||||||
|
}
|
||||||
|
const hash = buildHash(addr, xmrAmount, desc, timer, deadlineTs);
|
||||||
if (invoiceCode) {
|
if (invoiceCode) {
|
||||||
shareLinkInput.value = location.origin + '/s/' + invoiceCode;
|
shareLinkInput.value = location.origin + '/s/' + invoiceCode;
|
||||||
} else {
|
} else {
|
||||||
@@ -388,6 +401,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const deadlineTs = parseInt(params.get('te') || '0');
|
||||||
|
if (deadlineTs > 0) {
|
||||||
|
deadlineEndMs = deadlineTs * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
// Check for short URL code and load payment status
|
// Check for short URL code and load payment status
|
||||||
const code = params.get('c');
|
const code = params.get('c');
|
||||||
if (code) {
|
if (code) {
|
||||||
@@ -416,6 +434,13 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data.expiry_ts && parseInt(data.expiry_ts) > 0) {
|
||||||
|
deadlineEndMs = parseInt(data.expiry_ts) * 1000;
|
||||||
|
if (resultSection.classList.contains('visible')) {
|
||||||
|
startCountdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var params = new URLSearchParams(currentHash);
|
var params = new URLSearchParams(currentHash);
|
||||||
params.delete('c');
|
params.delete('c');
|
||||||
var normalizedHash = params.toString();
|
var normalizedHash = params.toString();
|
||||||
@@ -462,9 +487,10 @@
|
|||||||
countdownEl.textContent = '';
|
countdownEl.textContent = '';
|
||||||
countdownEl.className = 'countdown';
|
countdownEl.className = 'countdown';
|
||||||
|
|
||||||
if (!selectedDays || selectedDays <= 0) return;
|
if ((!selectedDays || selectedDays <= 0) && !deadlineEndMs) return;
|
||||||
|
|
||||||
const end = Date.now() + selectedDays * 86400000;
|
const end = deadlineEndMs || (Date.now() + selectedDays * 86400000);
|
||||||
|
deadlineEndMs = end;
|
||||||
countdownEl.classList.add('active');
|
countdownEl.classList.add('active');
|
||||||
|
|
||||||
function tick() {
|
function tick() {
|
||||||
|
|||||||
2
app.min.js
vendored
2
app.min.js
vendored
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user