Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
5468ce2
Refactor PayPal gateway with modern REST API and subscription support
superdav42 Jan 12, 2026
bc0bed6
Security fixes, PHPCS compliance, and unit tests for PayPal REST gateway
superdav42 Feb 27, 2026
ce610f9
Refactor PayPal OAuth to use proxy for partner credentials
superdav42 Feb 27, 2026
87373bc
Move inline scripts to admin_footer to fix settings page rendering
superdav42 Feb 27, 2026
58566a3
Embed nonces in JS instead of data attributes for wp_kses compatibility
superdav42 Feb 28, 2026
ccd82ab
Add 3% platform fee for PayPal Connect (mirrors Stripe)
superdav42 Feb 28, 2026
b7b72e1
Fix code review findings for PayPal REST gateway
superdav42 Feb 28, 2026
a89b35b
Match PayPal settings UI to Stripe Connect pattern
superdav42 Feb 28, 2026
74383c9
Hide legacy PayPal gateway when no existing configuration
superdav42 Feb 28, 2026
0160deb
Add unit tests for PayPal OAuth handler and E2E Cypress test
superdav42 Feb 28, 2026
8a7f27f
Gate PayPal OAuth Connect behind proxy-controlled feature flag
superdav42 Feb 28, 2026
1f2c815
fix(paypal): update BN code to ULTIMATE_SP_PPCP
superdav42 Mar 12, 2026
71ea8af
Merge branch 'main' into fix/paypal-checkout-loop-193
superdav42 Mar 13, 2026
c3e777e
fix(integrations): defer translations in integration constructors to …
superdav42 Mar 23, 2026
d617a82
Fix problems with types
superdav42 Mar 24, 2026
d71a9a0
fix(tests): fix PHPCS style violations in PayPal_Webhook_Handler_Test
superdav42 Mar 24, 2026
2f961d3
fix(tests): fix PHPCS violations in setup-paypal-gateway.php
superdav42 Mar 24, 2026
95a46fd
chore: trigger CI
superdav42 Mar 24, 2026
661513f
chore: merge main into fix/paypal-checkout-loop-193 to resolve conflicts
superdav42 Mar 24, 2026
7d366c3
fix: restore Base_Host_Provider class required by BunnyNet and Larave…
superdav42 Mar 24, 2026
3c0ff3d
finish paypal
superdav42 Mar 24, 2026
aa4483c
Merge branch 'fix/paypal-checkout-loop-193' of github.com:Ultimate-Mu…
superdav42 Mar 24, 2026
7d6c70d
fix: replace dashes with underscores in host provider setting IDs
superdav42 Mar 24, 2026
fe52583
Add js for paypal
superdav42 Mar 24, 2026
a491fc2
Merge branch 'fix/paypal-checkout-loop-193' of github.com:Ultimate-Mu…
superdav42 Mar 24, 2026
4d2b26a
Merge branch 'main' of github.com:Ultimate-Multisite/ultimate-multisi…
superdav42 Mar 24, 2026
15b25bf
Merge branch 'main' of github.com:Ultimate-Multisite/ultimate-multisi…
superdav42 Mar 25, 2026
60cb041
Add all the stuff
superdav42 Mar 25, 2026
b1aa8f6
prevent infinite refresh
superdav42 Mar 25, 2026
92a5224
Fix paypal and polling
superdav42 Mar 27, 2026
5ed653d
Merge branch 'main' into fix/paypal-checkout-loop-193
superdav42 Mar 28, 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
83 changes: 83 additions & 0 deletions assets/css/checkout.css
Original file line number Diff line number Diff line change
Expand Up @@ -260,3 +260,86 @@
outline: 2px solid var(--wu-accent-color);
outline-offset: 2px;
}

/**
* Radio option card styling.
*
* Provides a consistent, visually polished radio button appearance for
* payment method and other radio fields. Works across classic, block,
* and page-builder themes by relying on the CSS custom properties
* defined at the top of this file.
*
* The custom radio dot is drawn via box-shadow so no extra markup or
* pseudo-elements are needed.
*
* @since 2.4.0
*/

/* Card wrapper for each radio option */
.wu-styling .wu-radio-option {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 14px;
border: 1.5px solid var(--wu-input-border-color);
border-radius: var(--wu-input-border-radius);
cursor: pointer;
margin-bottom: 6px;
background: var(--wu-input-bg);
color: var(--wu-input-color);
font-weight: normal;
font-size: inherit;
line-height: 1.4;
transition: border-color 0.15s ease, background-color 0.15s ease;
user-select: none;
-webkit-user-select: none;
}

.wu-styling .wu-radio-option:last-child {
margin-bottom: 0;
}

.wu-styling .wu-radio-option:hover {
border-color: var(--wu-accent-color);
}

/* Highlight the selected card — :has() is widely supported (Chrome 105+, Firefox 121+, Safari 15.4+) */
.wu-styling .wu-radio-option:has(.wu-radio-input:checked) {
border-color: var(--wu-accent-color);
background-color: color-mix(in srgb, var(--wu-accent-color) 6%, var(--wu-input-bg));
}

/* Custom radio circle — replaces the native browser control */
.wu-styling .wu-radio-input {
appearance: none;
-webkit-appearance: none;
flex-shrink: 0;
width: 18px;
height: 18px;
border: 2px solid var(--wu-input-border-color);
border-radius: 50%;
margin: 0;
cursor: pointer;
background-color: var(--wu-input-bg);
box-sizing: border-box;
transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.wu-styling .wu-radio-input:focus-visible {
outline: 2px solid var(--wu-accent-color);
outline-offset: 2px;
}

/* Filled dot drawn via layered box-shadow — no pseudo-elements needed */
.wu-styling .wu-radio-input:checked {
border-color: var(--wu-accent-color);
box-shadow:
inset 0 0 0 3px var(--wu-input-bg),
inset 0 0 0 10px var(--wu-accent-color);
}

/* Ensure images (e.g. PayPal logo) inside a radio option align neatly */
.wu-styling .wu-radio-option img {
vertical-align: middle;
max-height: 20px;
}
72 changes: 72 additions & 0 deletions assets/js/payment-status-poll.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
(() => {
"use strict";

const config = window.wu_payment_poll;

if (!config || !config.should_poll) {
return;
}

let attempts = 0;
const max_attempts = parseInt(config.max_attempts, 10) || 20;
const poll_interval = parseInt(config.poll_interval, 10) || 3000;

function show_status(message, css_class) {
const el = document.querySelector(config.status_selector);
if (!el) {
return;
}
el.textContent = message;
el.className = "wu-payment-status wu-p-3 wu-rounded wu-mt-3 wu-block wu-text-sm " + (css_class || "");
el.style.display = "block";
}

async function check_payment_status() {
attempts++;

if (attempts > max_attempts) {
show_status(config.messages.timeout, "wu-bg-yellow-100 wu-text-yellow-800");
return;
}

show_status(config.messages.checking, "wu-bg-gray-100 wu-text-gray-600");

try {
const params = new URLSearchParams({
action: "wu_check_payment_status",
nonce: config.nonce,
payment_hash: config.payment_hash,
});

const response = await fetch(config.ajax_url, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: params.toString(),
});

const data = await response.json();

if (data.success && data.data.status === "completed") {
show_status(config.messages.completed, "wu-bg-green-100 wu-text-green-800");
if (config.success_redirect) {
setTimeout(() => { window.location.href = config.success_redirect; }, 1500);
} else {
setTimeout(() => { window.location.reload(); }, 1500);
}
return;
}

// Still pending — continue polling
show_status(config.messages.pending, "wu-bg-blue-100 wu-text-blue-800");
setTimeout(check_payment_status, poll_interval);

} catch (_e) {
show_status(config.messages.error, "wu-bg-red-100 wu-text-red-800");
setTimeout(check_payment_status, poll_interval);
}
}

document.addEventListener("DOMContentLoaded", () => {
setTimeout(check_payment_status, poll_interval);
});
})();
15 changes: 10 additions & 5 deletions assets/js/thank-you.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,16 @@ document.addEventListener("DOMContentLoaded", () => {
if (response.publish_status === "completed") {
this.creating = false;
this.site_ready = true;
// Reload with cache buster so CDN/cache plugins don't serve stale page
setTimeout(() => {
var sep = window.location.href.indexOf("?") > -1 ? "&" : "?";
window.location.href = window.location.href.split("#")[0] + sep + "_t=" + Date.now();
}, 1500);
// Only reload to bust cache if we actually watched the site transition
// through "running" during this page load. Without this guard, PayPal
// (and any other gateway that completes before the thank-you page loads)
// would trigger a reload on every poll, causing an infinite refresh loop.
if (this.running_count > 0) {
setTimeout(() => {
var sep = window.location.href.indexOf("?") > -1 ? "&" : "?";
window.location.href = window.location.href.split("#")[0] + sep + "_t=" + Date.now();
}, 1500);
}
} else if (response.publish_status === "running") {
this.creating = true;
this.stopped_count = 0;
Expand Down
26 changes: 26 additions & 0 deletions assets/js/wu-password-strength.js
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,15 @@
colorClass = 'wu-bg-green-300 wu-border-green-400';
label = this.getStrengthLabel('super_strong');
}
} else if (strength === 4 && strength !== 5) {
// Even when enforce_rules is off, show Super Strong if the password
// voluntarily meets all the super-strong criteria.
const password = this.options.pass1.val();

if (this.checkSuperStrongRules(password)) {
colorClass = 'wu-bg-green-300 wu-border-green-400';
label = this.getStrengthLabel('super_strong');
}
}

this.options.result.addClass(colorClass).html(label);
Expand Down Expand Up @@ -394,6 +403,23 @@
};
},

/**
* Check if a password meets all super-strong criteria unconditionally.
*
* Used to display "Super Strong" even when the site minimum is set below
* super_strong — a reward for users who go above and beyond.
*
* @param {string} password
* @return {boolean}
*/
checkSuperStrongRules(password) {
return password.length >= 12
&& /[A-Z]/.test(password)
&& /[a-z]/.test(password)
&& /[0-9]/.test(password)
&& /[!@#$%^&*()_+\-={};:'",.<>?~\[\]\/|`\\]/.test(password);
},

/**
* Get failed rules for external access.
*
Expand Down
Loading
Loading