diff --git a/frontend/src/components/ContributeModal.jsx b/frontend/src/components/ContributeModal.jsx index b54664f..4c82bf0 100644 --- a/frontend/src/components/ContributeModal.jsx +++ b/frontend/src/components/ContributeModal.jsx @@ -348,9 +348,39 @@ export default function ContributeModal({ campaign, onClose, onSuccess }) { ? await submitWithFreighter() : await submitWithCustodial(); if (paymentMethod === 'anchor') return; + setResult(data); - setPhase('success'); - onSuccess(); + setPhase('confirming'); + setLoadingLabel('Confirming on Stellar…'); + + // Poll finalization endpoint until status is 'finalized' or 'failed' + const pollFinalization = async (txHash, maxAttempts = 15) => { + for (let i = 0; i < maxAttempts; i++) { + await new Promise(r => setTimeout(r, 2000)); + try { + const finalizationResult = await api.getContributionFinalization(txHash, token); + if (finalizationResult.finalization_status === 'finalized') { + setPhase('success'); + onSuccess(); + return; + } + if (finalizationResult.finalization_status === 'failed') { + setPhase('success'); + setError('The contribution transaction failed on Stellar. Please try again.'); + onSuccess(); + return; + } + } catch (err) { + // Keep polling on error + } + } + // Timeout: show success screen with timeout message but allow user to view on Stellar Expert + setPhase('success'); + setError(null); + onSuccess(); + }; + + pollFinalization(data.tx_hash); } catch (err) { if (paymentMethod === 'anchor' && anchorPopupRef.current && !anchorPopupRef.current.closed) { anchorPopupRef.current.close(); @@ -683,6 +713,44 @@ export default function ContributeModal({ campaign, onClose, onSuccess }) { Close + ) : phase === 'confirming' ? ( +
+ Your payment was submitted. We're waiting for it to be confirmed on the Stellar ledger, which usually takes 3–5 seconds. +
++ {error} +
+ )} +