/* global Stripe */
const publishableKey = document.querySelector('meta[name="stripe-publishable-key"]')?.content || '';
const stripe = publishableKey ? Stripe(publishableKey) : null;
let elements = null;
let selectedCents = 500; // default $5
const presetBtns = document.querySelectorAll('.amount-preset-btn');
const customInput = document.getElementById('customAmount');
const submitBtn = document.getElementById('donateSubmitBtn');
const errorEl = document.getElementById('donateError');
const formEl = document.getElementById('donateForm');
const successEl = document.getElementById('donateSuccess');
/** @param {string} msg - Error message to display, or empty string to clear. */
function setError(msg) {
if (errorEl) errorEl.textContent = msg || '';
}
/** Updates donate button label to reflect current amount. */
function updateSubmitLabel() {
const dollars = (selectedCents / 100).toFixed(2);
if (submitBtn) submitBtn.textContent = `Donate $${dollars}`;
}
/** @param {number} cents - Donation amount in cents. */
function selectPreset(cents) {
selectedCents = cents;
presetBtns.forEach(b => b.classList.toggle('active', parseInt(b.dataset.amount, 10) === cents));
if (customInput) customInput.value = '';
updateSubmitLabel();
reinitElements();
}
/** @param {string} dollars - Dollar amount entered by user. */
function selectCustom(dollars) {
const cents = Math.round(parseFloat(dollars) * 100);
if (isNaN(cents) || cents < 100) return;
selectedCents = cents;
presetBtns.forEach(b => b.classList.remove('active'));
updateSubmitLabel();
reinitElements();
}
/** Fetches new PaymentIntent and mounts Stripe payment element. */
async function reinitElements() {
if (!stripe) return;
setError('');
if (submitBtn) submitBtn.disabled = true;
// Create a new PaymentIntent for the selected amount
let clientSecret;
try {
const res = await fetch('/api/stripe/create-donation-intent', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ amount_cents: selectedCents }),
});
const data = await res.json();
if (!res.ok) { setError(data.error || 'Failed to initialise payment.'); return; }
clientSecret = data.client_secret;
} catch {
setError('Network error — please try again.');
return;
}
const paymentEl = document.getElementById('payment-element');
if (paymentEl) paymentEl.innerHTML = '';
elements = stripe.elements({ clientSecret });
const paymentElement = elements.create('payment');
paymentElement.mount('#payment-element');
paymentElement.on('ready', () => { if (submitBtn) submitBtn.disabled = false; });
}
// ── Wire preset buttons ──────────────────────────────────────────────────────
presetBtns.forEach(btn => {
btn.addEventListener('click', () => selectPreset(parseInt(btn.dataset.amount, 10)));
});
// ── Wire custom input ────────────────────────────────────────────────────────
let customDebounce;
customInput?.addEventListener('input', (e) => {
clearTimeout(customDebounce);
customDebounce = setTimeout(() => selectCustom(e.target.value), 600);
});
// ── Wire submit ──────────────────────────────────────────────────────────────
submitBtn?.addEventListener('click', async () => {
if (!stripe || !elements) return;
setError('');
submitBtn.disabled = true;
submitBtn.textContent = 'Processing…';
const { error } = await stripe.confirmPayment({
elements,
redirect: 'if_required',
});
if (error) {
setError(error.message || 'Payment failed. Please try again.');
submitBtn.disabled = false;
updateSubmitLabel();
} else {
if (formEl) formEl.style.display = 'none';
if (successEl) successEl.style.display = '';
}
});
// ── Initialise on load ───────────────────────────────────────────────────────
if (stripe) {
reinitElements();
} else {
setError('Stripe is not configured. Donations are not available yet.');
}