var stripe = Stripe('pk_live_51PLw9aK6h8aOTojnlB7Hh0Ql2zbHY28BpBDCwRcoldLw0xZ7zK55gfIPFkckLhjobySJyzrDnKvHxgUQhcrjEpAF00SV0bBGPO', { stripeAccount: 'acct_1S0aaDPYgUtXs99j' }); let elements; let subscriptionId = null; // Store subscription ID for PostHog tracking let connected_account_id = 'acct_1S0aaDPYgUtXs99j'; let customerId = null; document .querySelector("#payment-form") .addEventListener("submit", handleSubmit); let emailAddress = ''; // Fetches a payment intent and captures the client secret async function initialize(campaignId, designation, amount, interval, email, coverFees, anonymous, startDate = null, endDate = null, checkoutItems = '', checkoutItemsData = '') { var result; emailAddress = email; if (!('acct_1S0aaDPYgUtXs99j')) { document.querySelector("#payment-element").style.display = "none"; document.querySelector("#address-element").style.display = "none"; document.querySelector("#submit").style.display = "none"; document.querySelector("#payment-message").style.color = "red"; document.querySelector("#payment-message").style.fontSize = "1em"; window.posthog?.capture('invalid_payment_processor', { trigger: 'missing_connected_account', flow: 'donation_checkout', campaign_id: '43638', account_id: 'QEQg_6HV', connected_account_id: 'acct_1S0aaDPYgUtXs99j', environment: 'live', page_url: window.location.href, referrer: document.referrer, timestamp: new Date().toISOString(), }); showMessage("Unable to make a payment right now due to an invalid payment processor.", true); return; } amount = parseInt(amount); if (coverFees) { amount += Math.round(amount * 0.02 + amount * 0.022 + 30); } // Collect custom field values var customFields = {}; $('[name^="custom_fields"]').each(function() { var $field = $(this); var fieldId = $field.attr('name').match(/\[(\d+)\]/)[1]; if ($field.attr('type') === 'checkbox') { customFields[fieldId] = $field.is(':checked') ? '1' : '0'; } else if ($field.attr('type') === 'radio') { if ($field.is(':checked')) { customFields[fieldId] = $field.val(); } } else { customFields[fieldId] = $field.val(); } }); // Save custom fields for recurring payments const customFieldsData = JSON.stringify(customFields); // If start date is set and is after today, we aren't processing a payment immediately so just collect payment method through setup intent const today = new Date(); today.setHours(0, 0, 0, 0); const startDateObj = startDate ? new Date(startDate.replace(/-/g, '/')) : null; if (startDate != null && startDate != '' && startDateObj > today) { result = await fetch('/create-setupintent.php?campaign_id=43638', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ campaignId: campaignId, designation: designation, email: email, donationAmount: amount, interval: interval, startDate: startDate, endDate: endDate, anonymous: anonymous, customFields: customFieldsData }), }).then((r) => r.json()); } else { result = await fetch("/create.php?campaign_id=43638", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ campaignId: campaignId, designation: designation, interval: interval, donationAmount: amount, email: email, anonymous: anonymous, checkoutItems: checkoutItems, checkoutItemsData: checkoutItemsData, customFields: customFieldsData }), }).then((r) => r.json()); } if (result.connected_account_id) { connected_account_id = result.connected_account_id; } if (result.customer_id) { customerId = result.customer_id; } if (result.subscription_id) { subscriptionId = result.subscription_id; } if (result.error) { console.log(result.error); document.querySelector("#payment-element").style.display = "none"; document.querySelector("#address-element").style.display = "none"; document.querySelector("#submit").style.display = "none"; document.querySelector("#payment-message").style.color = "red"; document.querySelector("#payment-message").style.fontSize = "1em"; window.posthog?.capture('invalid_payment_processor', { trigger: 'create_intent_error', flow: 'donation_checkout', campaign_id: campaignId, account_id: 'QEQg_6HV', connected_account_id: connected_account_id, server_error: result.error, server_response: JSON.stringify(result), environment: 'live', email: email, amount: amount, interval: interval, designation: designation, page_url: window.location.href, referrer: document.referrer, timestamp: new Date().toISOString(), }); showMessage("Unable to make a payment right now due to an invalid payment processor.", true); return; } const clientSecret = result.clientSecret; stripe = Stripe('pk_live_51PLw9aK6h8aOTojnlB7Hh0Ql2zbHY28BpBDCwRcoldLw0xZ7zK55gfIPFkckLhjobySJyzrDnKvHxgUQhcrjEpAF00SV0bBGPO', {stripeAccount: result.connected_account_id}); elements = stripe.elements({ clientSecret, appearance: { variables: { borderRadius: '8px' } } }); const addressElement = elements.create('address', { mode: 'billing' }); addressElement.mount("#address-element"); const paymentElementOptions = { layout: { type: "accordion", defaultCollapsed: false, radios: true, spacedAccordionItems: false, }, }; const paymentElement = elements.create("payment", paymentElementOptions); paymentElement.mount("#payment-element"); // Express Checkout (Apple Pay / Google Pay / Link) — reuses the same elements + clientSecret. // On confirm we delegate to handleSubmit, which already branches setup-intent vs payment-intent // and handles the recurring subscription creation step. const expressCheckoutElement = elements.create('expressCheckout', { buttonHeight: 48, paymentMethods: { applePay: 'auto', googlePay: 'auto', link: 'auto', paypal: 'auto' }, paymentMethodOrder: ['applePay', 'googlePay', 'paypal', 'link'], layout: { overflow: 'never', maxColumns: 1, maxRows: 0 }, }); expressCheckoutElement.on('ready', function (event) { if (event.availablePaymentMethods) { document.getElementById('express-checkout-divider').style.display = 'flex'; } }); expressCheckoutElement.on('confirm', async function () { await handleSubmit({ preventDefault: function () {} }); }); expressCheckoutElement.mount('#express-checkout-element'); document.querySelector("#submit").disabled = false; paymentElement.on("ready", function(event) { // Hide the loading spinner //document.querySelector(".payment-loading-spinner").style.display = "none"; // Enable the submit button once payment form is ready console.log("Payment form ready"); }); } async function handleSubmit(e) { e.preventDefault(); setLoading(true); var startDate = (document.getElementById("customStartDate") != null ? document.getElementById("customStartDate").value : ''); var endDate = (document.getElementById("customEndDate") != null ? document.getElementById("customEndDate").value : ''); // If start date is set and is after today, we aren't processing a payment immediately so just collect payment method through setup intent const today = new Date(); today.setHours(0, 0, 0, 0); const startDateObj = startDate ? new Date(startDate.replace(/-/g, '/')) : null; if (startDate != "" && startDateObj > today) { var designation = (document.getElementById("designation") != null ? document.getElementById("designation").value : ''); var donationAmount = document.getElementById("amount").value; var interval = document.getElementById("interval").value; var email = document.getElementById("email").value; // startDate = // endDate = const { error, setupIntent } = await stripe.confirmSetup({ elements, confirmParams: { //return_url: "https://donations.swiftgive.org/donations/success?campaign_id=43638&donationAmount=" + donationAmount + "&interval=" + interval + "&designation=" + designation + "&start_date=" + startDate + "&end_date=" + endDate }, redirect: "if_required", }); if (error) { console.error(error); // handleError(); } else if (setupIntent && setupIntent.status === "succeeded") { posthog.capture( 'setup_succeeded', { setup_intent: setupIntent.id, connected_account_id: connected_account_id } ); var interval = document.getElementById("interval")?.value; // If interval is not one-time, we are creating a recurring plan if (interval != 'One-Time') { var designation = (document.getElementById("designation") != null ? document.getElementById("designation").value : ''); var donationAmount = document.getElementById("amount").value; var interval = document.getElementById("interval").value; var email = document.getElementById("email").value; var anonymous = (document.getElementById("anonymous") != null ? document.getElementById("anonymous").value : ''); var customFieldsData = (document.getElementById("customFieldsData") != null ? document.getElementById("customFieldsData").value : ''); result = await fetch('/create-sg-subscription.php?campaign_id=43638', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ customerId: customerId, campaignId: '43638', designation: designation, email: email, donationAmount: donationAmount, interval: interval, startDate: startDate, endDate: endDate, anonymous: anonymous, customFields: customFieldsData, setupIntent: setupIntent.id, paymentMethod: setupIntent.payment_method, subscriptionId: subscriptionId, }), }).then((r) => r.json()); if (result.subscriptionId) { subscriptionId = result.subscriptionId; console.log("Subscription created"); console.log(subscriptionId); window.posthog.capture( 'subscription_created', { subscription_id: subscriptionId, connected_account_id: connected_account_id } ); } } console.log("Setup succeeded"); // For recurring (setup intent) flows there is no payment intent ID; // expose the setup intent so DTD pending capture can still key off it. window.lastPaymentIntentId = setupIntent && setupIntent.id ? setupIntent.id : null; window.lastConnectedAccountId = connected_account_id || null; nextPage() // handleSuccess(); } else { console.log("Payment failed"); // handleOther(); } } else { const { error, paymentIntent } = await stripe.confirmPayment({ elements, confirmParams: { //return_url: "https://donations.swiftgive.org/donations/success", //receipt_email: emailAddress, }, redirect: "if_required", }); if (error) { console.error(error); // handleError(); } else if (paymentIntent && (paymentIntent.status === "succeeded" || paymentIntent.status === "processing")) { window.posthog.capture( 'payment_succeeded', { payment_intent: paymentIntent.id, connected_account_id: connected_account_id } ); var interval = document.getElementById("interval")?.value; // If interval is not one-time, we are creating a recurring plan if (interval != 'One-Time') { var designation = (document.getElementById("designation") != null ? document.getElementById("designation").value : ''); var donationAmount = document.getElementById("amount").value; var interval = document.getElementById("interval").value; var email = document.getElementById("email").value; var anonymous = (document.getElementById("anonymous") != null ? document.getElementById("anonymous").value : ''); var customFieldsData = (document.getElementById("customFieldsData") != null ? document.getElementById("customFieldsData").value : ''); result = await fetch('/create-sg-subscription.php?campaign_id=43638', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ customerId: customerId, campaignId: '43638', designation: designation, email: email, donationAmount: donationAmount, interval: interval, startDate: startDate, endDate: endDate, anonymous: anonymous, customFields: customFieldsData, paymentIntent: paymentIntent.id, paymentMethod: paymentIntent.payment_method, subscriptionId: subscriptionId, }), }).then((r) => r.json()); if (result.subscriptionId) { subscriptionId = result.subscriptionId; console.log("Subscription created"); console.log(subscriptionId); window.posthog.capture( 'subscription_created', { subscription_id: subscriptionId, connected_account_id: connected_account_id } ); } } console.log("Payment succeeded"); // Expose IDs for the DTD step (donation row hasn't been webhooked yet, // so we key the pending employer capture by payment intent + account). window.lastPaymentIntentId = paymentIntent.id; window.lastConnectedAccountId = connected_account_id || null; nextPage() // handleSuccess(); } else { console.log("Payment failed"); // handleOther(); } } setLoading(false); } // Fetches the payment intent status after payment submission async function checkStatus() { const clientSecret = new URLSearchParams(window.location.search).get( "payment_intent_client_secret" ); if (!clientSecret) { return; } const { paymentIntent } = await stripe.retrievePaymentIntent(clientSecret); switch (paymentIntent.status) { case "succeeded": showMessage("Payment succeeded!"); break; case "processing": showMessage("Your payment is processing."); break; case "requires_payment_method": showMessage("Your payment was not successful, please try again."); break; default: showMessage("Something went wrong."); break; } } // ------- UI helpers ------- function showMessage(messageText, permanent = false) { const messageContainer = document.querySelector("#payment-message"); messageContainer.classList.remove("hidden"); messageContainer.textContent = messageText; if (!permanent) { setTimeout(function () { messageContainer.classList.add("hidden"); messageContainer.textContent = ""; }, 4000); } } // Show a spinner on payment submission function setLoading(isLoading) { if (isLoading) { // Disable the button and show a spinner document.querySelector("#submit").disabled = true; document.querySelector("#spinner").classList.remove("hidden"); document.querySelector("#button-text").classList.add("hidden"); } else { document.querySelector("#submit").disabled = false; document.querySelector("#spinner").classList.add("hidden"); document.querySelector("#button-text").classList.remove("hidden"); } }