Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
15568bf
Integrate Stripe Connect proxy server for secure OAuth flow
superdav42 Dec 15, 2025
bd87731
Optimize Stripe OAuth performance and remove application fees
superdav42 Dec 15, 2025
c431f91
Remove platform_client_id - now handled by proxy server
superdav42 Dec 15, 2025
44f23c3
fix stripe
superdav42 Dec 31, 2025
b0155af
Merge branch 'main' of github.com:Multisite-Ultimate/ultimate-multisi…
superdav42 Jan 2, 2026
f980d7c
Hide redundant billing address fields when Stripe gateway is selected
superdav42 Jan 2, 2026
4f14632
Merge branch 'main' into stripe-claude
superdav42 Feb 8, 2026
18efb0b
Add Stripe checkout and subscription renewal E2E tests
superdav42 Feb 9, 2026
2e044f4
fix: use env var check instead of secrets context in workflow if cond…
superdav42 Feb 9, 2026
30e8f1c
fix: override wp-env plugins in CI to exclude missing domain-seller a…
superdav42 Feb 9, 2026
c5937c4
fix: rewrite wp-env.json in CI to strip addons and mappings
superdav42 Feb 9, 2026
568a608
Fix invoice PDF download failing with expired nonce
superdav42 Feb 9, 2026
01b2fae
Fix Stripe checkout test mock missing latest_charge field
superdav42 Feb 9, 2026
76b6b62
change the validation rules
superdav42 Feb 9, 2026
966176e
add attachement pdf in test emails
superdav42 Feb 10, 2026
7454593
Only check for memory errors
superdav42 Feb 10, 2026
4a125da
Merge branch 'main' into stripe-claude
superdav42 Feb 12, 2026
e58d57c
Actually get stripe to work
superdav42 Feb 13, 2026
52f9f17
fix test
superdav42 Feb 13, 2026
0a6982a
Fix duplicate Country/ZIP fields on Stripe checkout
superdav42 Feb 13, 2026
caf0854
Update tests/e2e/cypress/fixtures/process-stripe-renewal.php
superdav42 Feb 13, 2026
1ec49d7
Update inc/managers/class-gateway-manager.php
superdav42 Feb 13, 2026
827e78e
Move payment status CSS from inline to checkout.css
superdav42 Feb 13, 2026
2da2f14
Merge branch 'main' into stripe-claude
superdav42 Feb 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
273 changes: 219 additions & 54 deletions assets/js/gateways/stripe.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,115 +3,190 @@
let _stripe;
let stripeElement;
let card;
let paymentElement;
let elements;
let usePaymentElement = false;

const stripeElements = function(publicKey) {

_stripe = Stripe(publicKey);

const elements = _stripe.elements();

card = elements.create('card', {
hidePostalCode: true,
});
// Payment Element will be initialized dynamically after preflight response
// We'll detect which element to use based on what's available in the DOM

wp.hooks.addFilter('wu_before_form_submitted', 'nextpress/wp-ultimo', function(promises, checkout, gateway) {

const cardEl = document.getElementById('card-element');
const paymentEl = document.getElementById('payment-element');

if (gateway === 'stripe' && checkout.order.totals.total > 0 && cardEl && cardEl.offsetParent) {
if (gateway === 'stripe' && checkout.order.totals.total > 0) {

promises.push(new Promise( async (resolve, reject) => {
// If using Card Element (legacy/fallback)
if (cardEl && cardEl.offsetParent && !usePaymentElement) {

try {
promises.push(new Promise( async (resolve, reject) => {

const paymentMethod = await _stripe.createPaymentMethod({type: 'card', card});
try {

if (paymentMethod.error) {

reject(paymentMethod.error);

} // end if;
const paymentMethod = await _stripe.createPaymentMethod({type: 'card', card});

} catch(err) {
if (paymentMethod.error) {

} // end try;
reject(paymentMethod.error);

} // end if;

} catch(err) {

} // end try;

resolve();
resolve();

}));
}));
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

} // end if;

} // end if;

return promises;

});

wp.hooks.addAction('wu_on_form_success', 'nextpress/wp-ultimo', function(checkout, results) {

if (checkout.gateway === 'stripe' && (checkout.order.totals.total > 0 || checkout.order.totals.recurring.total > 0)) {

checkout.set_prevent_submission(false);

handlePayment(checkout, results, card);
// Check if we received a client_secret (required for Payment Element)
if (results.gateway.data.stripe_client_secret) {

const paymentEl = document.getElementById('payment-element');
const cardEl = document.getElementById('card-element');

// Initialize Payment Element with client_secret (preferred method)
if (paymentEl) {

usePaymentElement = true;

// Create elements instance with client_secret for dynamic mode
elements = _stripe.elements({
clientSecret: results.gateway.data.stripe_client_secret,
appearance: {
theme: 'stripe',
},
});

// Create and mount Payment Element
paymentElement = elements.create('payment');
paymentElement.mount('#payment-element');

// Apply custom styles
wu_stripe_update_payment_element_styles(paymentElement, '#field-payment_template');

checkout.set_prevent_submission(false);

// Handle payment with Payment Element
handlePaymentElement(checkout, results, elements);

} else if (cardEl && card) {

// Fallback to Card Element (legacy)
usePaymentElement = false;
checkout.set_prevent_submission(false);
handlePayment(checkout, results, card);

} // end if;

} else {

// No client_secret, use legacy Card Element flow
checkout.set_prevent_submission(false);

if (card) {
handlePayment(checkout, results, card);
}

} // end if;

} // end if;

});

wp.hooks.addAction('wu_on_form_updated', 'nextpress/wp-ultimo', function(form) {

if (form.gateway === 'stripe') {

try {
const cardEl = document.getElementById('card-element');
const paymentEl = document.getElementById('payment-element');

card.mount('#card-element');
// Only mount Card Element if Payment Element is not available
if (cardEl && !paymentEl && !usePaymentElement) {

wu_stripe_update_styles(card, '#field-payment_template');
try {

/*
* Prevents the from from submitting while Stripe is
* creating a payment source.
*/
form.set_prevent_submission(form.order && form.order.should_collect_payment && form.payment_method === 'add-new');
// Initialize Card Element for legacy support
if (!card) {
const cardElements = _stripe.elements();
card = cardElements.create('card', {
hidePostalCode: true,
});

} catch (error) {
// Element focus ring
card.on('focus', function() {
const el = document.getElementById('card-element');
el.classList.add('focused');
});

// Silence
card.on('blur', function() {
const el = document.getElementById('card-element');
el.classList.remove('focused');
});
}

} // end try;
card.mount('#card-element');

} else {
wu_stripe_update_styles(card, '#field-payment_template');

form.set_prevent_submission(false);
/*
* Prevents the form from submitting while Stripe is
* creating a payment source.
*/
form.set_prevent_submission(form.order && form.order.should_collect_payment && form.payment_method === 'add-new');

try {
} catch (error) {

card.unmount('#card-element');
// Silence

} catch (error) {
} // end try;

// Silence is golden
} else {

} // end try;
// Payment Element will be initialized in wu_on_form_success
// Set prevent submission for Payment Element
if (paymentEl) {
form.set_prevent_submission(form.order && form.order.should_collect_payment && form.payment_method === 'add-new');
} else {
form.set_prevent_submission(false);
}

} // end if;
} // end if;

});
} else {

// Element focus ring
card.on('focus', function() {
form.set_prevent_submission(false);

const el = document.getElementById('card-element');
try {

el.classList.add('focused');
if (card) {
card.unmount('#card-element');
}

});
} catch (error) {

card.on('blur', function() {
// Silence is golden

const el = document.getElementById('card-element');
} // end try;

el.classList.remove('focused');
} // end if;

});

Expand Down Expand Up @@ -214,6 +289,45 @@ function wu_stripe_update_styles(cardElement, selector) {

}

/**
* Update styles for Payment Element.
*
* @param {Object} paymentElement Stripe payment element.
* @param {string} selector Selector to copy styles from.
*
* @since 2.x.x
*/
function wu_stripe_update_payment_element_styles(paymentElement, selector) {

if (undefined === typeof selector) {
selector = '#field-payment_template';
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

const inputField = document.querySelector(selector);

if (null === inputField) {
return;
}

const inputStyles = window.getComputedStyle(inputField);

// Payment Element uses appearance API instead of style object
paymentElement.update({
appearance: {
theme: 'stripe',
variables: {
colorPrimary: inputStyles.getPropertyValue('border-color'),
colorBackground: inputStyles.getPropertyValue('background-color'),
colorText: inputStyles.getPropertyValue('color'),
fontFamily: inputStyles.getPropertyValue('font-family'),
fontSize: inputStyles.getPropertyValue('font-size'),
borderRadius: inputStyles.getPropertyValue('border-radius'),
},
},
});
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

}

function wu_stripe_handle_intent(handler, client_secret, args) {

const _handle_error = function (e) {
Expand All @@ -223,15 +337,15 @@ function wu_stripe_handle_intent(handler, client_secret, args) {
if (e.error) {

wu_checkout_form.errors.push(e.error);

} // end if;

} // end _handle_error;

try {

_stripe[handler](client_secret, args).then(function(results) {

if (results.error) {

_handle_error(results);
Expand All @@ -249,7 +363,58 @@ function wu_stripe_handle_intent(handler, client_secret, args) {
} // end if;

/**
* After registration has been processed, handle card payments.
* Handle payment with Payment Element (modern method).
*
* @param {Object} form Checkout form object.
* @param {Object} response Server response with client_secret.
* @param {Object} elements Stripe Elements instance.
*
* @since 2.x.x
*/
function handlePaymentElement(form, response, elements) {

const clientSecret = response.gateway.data.stripe_client_secret;

if (!clientSecret) {
return;
}

_stripe.confirmPayment({
elements: elements,
confirmParams: {
return_url: window.location.href,
payment_method_data: {
billing_details: {
name: response.customer.display_name,
email: response.customer.user_email,
address: {
country: response.customer.billing_address_data.billing_country,
postal_code: response.customer.billing_address_data.billing_zip_code,
},
},
},
},
redirect: 'if_required', // Stay on page if redirect not needed
}).then(function(result) {

if (result.error) {

wu_checkout_form.unblock();
wu_checkout_form.errors.push(result.error);

} else {

// Payment succeeded - resubmit form to complete checkout
wu_checkout_form.resubmit();

}

});

}

/**
* After registration has been processed, handle card payments (legacy Card Element).
*
* @param form
* @param response
Expand Down
Loading
Loading