turnstile

Drupal paywall plugin
Log | Files | Refs | README | LICENSE

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:
Mjs/payment-button.js | 45++++++++++++++++++++++++++++++++++++++++-----
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); }); });