From 7d6818a8d4dce2cb7d4e95b75eb342fa70bc9531 Mon Sep 17 00:00:00 2001 From: martbost Date: Tue, 8 Sep 2026 10:08:34 -0500 Subject: [PATCH] Buy flow: survive mobile app-switch fetch drops Mobile drops in-flight fetches when the page returns from the wallet app, which aborted the purchase before the tx ("Failed to fetch"). Retry /api/me and /api/sponsor, and make waitTx keep polling through transient fetch failures. Co-Authored-By: Claude Opus 4.8 --- public/assets/my.js | 7 +++++-- public/assets/wallet.js | 8 +++++--- public/index.html | 2 +- public/my.html | 4 ++-- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/public/assets/my.js b/public/assets/my.js index c366963..339b211 100644 --- a/public/assets/my.js +++ b/public/assets/my.js @@ -1223,12 +1223,15 @@ // the buyer's credits are read by the member id of their LINKED wallet. // If they only connected a wallet (e.g. for the faucet) but never linked // it, link it first (one signature) so the purchase's credits show up. - const meNow = await (await fetch('/api/me')).json(); + // retry these through transient failures — mobile drops in-flight + // fetches when the page returns from the wallet app-switch + const jretry = async url => { let err; for (let i = 0; i < 4; i++) { try { return await (await fetch(url)).json(); } catch (e) { err = e; await new Promise(s => setTimeout(s, 500 * (i + 1))); } } throw err; }; + const meNow = await jretry('/api/me'); if (!meNow.address) { IAP.status('Link your wallet first — one quick signature…'); await IAPWallet.signIn(); } - const spNow = await (await fetch('/api/sponsor')).json(); + const spNow = await jretry('/api/sponsor'); IAP.status('Confirm the purchase in your wallet…'); const r = await IAPWallet.buy(Number(b.dataset.id), spNow.sponsorId || 0, b.dataset.cost); if (r.status !== '0x1' && r.receipt && r.receipt.status !== '0x1') throw new Error('Transaction reverted.'); diff --git a/public/assets/wallet.js b/public/assets/wallet.js index 484b4dd..ab982b6 100644 --- a/public/assets/wallet.js +++ b/public/assets/wallet.js @@ -109,9 +109,11 @@ window.IAPWallet = (function () { } async function waitTx(hash) { - for (let i = 0; i < 60; i++) { - const r = await (await fetch('/api/tx/' + hash)).json(); - if (r.found) return { status: r.status, blockNumber: r.blockNumber }; + for (let i = 0; i < 90; i++) { + try { + const r = await (await fetch('/api/tx/' + hash)).json(); + if (r.found) return { status: r.status, blockNumber: r.blockNumber }; + } catch (e) { /* transient fetch failure (e.g. mobile app-switch) — keep polling */ } await new Promise(res => setTimeout(res, 2500)); } throw new Error('Timed out waiting for the transaction. Check the explorer.'); diff --git a/public/index.html b/public/index.html index 521410f..d4e9802 100644 --- a/public/index.html +++ b/public/index.html @@ -439,7 +439,7 @@ - + diff --git a/public/my.html b/public/my.html index d2eb292..bd48373 100644 --- a/public/my.html +++ b/public/my.html @@ -696,8 +696,8 @@ - - + +