commit 5f16d08d31b9b69c2623d2f8e6c8f9d0fdd3cb1a
parent b8c5548e0fc334cf5edc33e46a36acda9ecc9e7d
Author: Christian Grothoff <christian@grothoff.org>
Date: Wed, 20 May 2026 22:29:05 +0200
limit retries on backend failure
Diffstat:
1 file changed, 40 insertions(+), 5 deletions(-)
diff --git a/js/payment-button.js b/js/payment-button.js
@@ -139,7 +139,16 @@
});
}
+ // Stop after this many consecutive hard failures (4xx other than
+ // 404, 5xx, or network errors). 404 and 202 are expected
+ // intermediate states and reset the counter.
+ var MAX_POLL_ERRORS = 5;
+
+
function pollSession(ctx) {
+ if (typeof ctx.errorCount !== 'number') {
+ ctx.errorCount = 0;
+ }
var pollUrl = ctx.merchantBackend.replace(/\/$/, '')
+ '/sessions/' + encodeURIComponent(ctx.paivanaId)
+ '?timeout_ms=30000'
@@ -148,18 +157,44 @@
fetch(pollUrl, { cache: 'no-store' })
.then(function (res) {
if (res.status === 200) {
- return res.json().then(function (j) {
- return confirmPayment(ctx, j.order_id);
+ ctx.errorCount = 0;
+ return res.json().then(function (j) {
+ if (!j || !j.order_id) {
+ throw new Error('200 without order_id');
+ }
+ return confirmPayment(ctx, j.order_id);
});
}
- // 202 = unpaid (long poll returned), 404 = no order yet,
- // anything else is treated as transient.
+ // 202 = unpaid (long poll returned), 404 = no order yet.
+ // Everything else (400, 401/403, 5xx, ...) is a hard error.
+ var transient = (res.status === 202 || res.status === 404);
+ if (transient) {
+ ctx.errorCount = 0;
+ }
+ else {
+ ctx.errorCount++;
+ console.warn('[turnstile] poll HTTP',
+ res.status,
+ '(', ctx.errorCount, '/', MAX_POLL_ERRORS, ')');
+ if (ctx.errorCount >= MAX_POLL_ERRORS) {
+ setStatus(ctx.$container, Drupal.t(
+ 'Payment service is unavailable. Please reload the page to try again.'));
+ return;
+ }
+ }
var rem = 30000 - (performance.now() - start);
if (rem < 0) rem = 0;
return waitMs(rem).then(function () { pollSession(ctx); });
})
.catch(function (e) {
- console.warn('[turnstile] poll error:', e);
+ ctx.errorCount = (ctx.errorCount || 0) + 1;
+ console.warn('[turnstile] poll error:', e,
+ '(', ctx.errorCount, '/', MAX_POLL_ERRORS, ')');
+ if (ctx.errorCount >= MAX_POLL_ERRORS) {
+ setStatus(ctx.$container, Drupal.t(
+ 'Network unavailable. Please reload the page to try again.'));
+ return;
+ }
setStatus(ctx.$container, Drupal.t('Network error. Retrying...'));
waitMs(5000).then(function () { pollSession(ctx); });
});