Update to paypal express anti-spam blocks

This commit is contained in:
carpentryplus25
2026-02-26 14:20:17 -05:00
parent e497e47554
commit 1c884ed97f
2 changed files with 390 additions and 42 deletions

17
.gitattributes vendored
View File

@@ -1,2 +1,17 @@
# Auto detect text files and perform LF normalization # Auto-normalize line endings
* text=auto * text=auto
# Force text files to LF
*.php text eol=lf
*.js text eol=lf
*.css text eol=lf
*.md text eol=lf
*.txt text eol=lf
*.json text eol=lf
# Binaries
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary

View File

@@ -38,7 +38,7 @@ function dapper_activation() {
foreach ($existing_users as $user) { foreach ($existing_users as $user) {
update_mailing_list($user->ID, $user->user_email, 'subscribed'); update_mailing_list($user->ID, $user->user_email, 'subscribed');
} }
make_images_dir(); //make_images_dir();
make_backup_dir(); make_backup_dir();
// Mark setup completed // Mark setup completed
update_option('dapper_setup_complete', true); update_option('dapper_setup_complete', true);
@@ -47,6 +47,7 @@ function dapper_activation() {
register_activation_hook(__FILE__, 'dapper_activation'); register_activation_hook(__FILE__, 'dapper_activation');
/* /*
// Depreciated in v1.1.1 to be removed in later versions
// Checks for an images directory inside the dapper plugin // Checks for an images directory inside the dapper plugin
// Creates the 'images' folder when plugin gets activated // Creates the 'images' folder when plugin gets activated
// Used for custom logos on login page // Used for custom logos on login page
@@ -581,6 +582,31 @@ function dapper_woo_conditional_load() {
return $errors; return $errors;
} }
/**
* Returns true if the current posted payment method is any known PayPal variant
*
*
*/
function dapper_is_paypal_payment_method() {
if (!isset($_POST['payment_method'])) {
return false;
}
$method = $_POST['payment_method'];
$paypal_methods = [
'paypal',
'ppec_paypal', // older PayPal Express Checkout
'ppcp-gateway', // PayPal Payments (most common now)
'paypal_express',
'braintree_paypal', // if using Braintree integration
// add more here if you ever see other variants in logs
];
return in_array($method, $paypal_methods, true);
}
// =================== // ===================
// CHECKOUT HONEYPOT + ANTI-BOT LAYERS // CHECKOUT HONEYPOT + ANTI-BOT LAYERS
// =================== // ===================
@@ -650,7 +676,12 @@ function dapper_woo_conditional_load() {
add_action('woocommerce_checkout_before_customer_details', 'dapper_add_custom_human_check'); add_action('woocommerce_checkout_before_customer_details', 'dapper_add_custom_human_check');
function dapper_add_custom_human_check() { function dapper_add_custom_human_check() {
if (get_option('dapper_enable_paypal_human_check', 'on') !== 'on') return; if (get_option('dapper_enable_paypal_human_check', 'on') !== 'on') {
return;
}
if ( ! dapper_is_paypal_payment_method() && ! is_checkout() ) {
return;
}
?> ?>
<div id="dapper-human-check" style="margin:20px 0; padding:15px; background:#f8f9fa; border:1px solid #ddd; border-radius:4px; display:none;"> <div id="dapper-human-check" style="margin:20px 0; padding:15px; background:#f8f9fa; border:1px solid #ddd; border-radius:4px; display:none;">
@@ -663,62 +694,364 @@ function dapper_woo_conditional_load() {
<script> <script>
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
// Show check only if PayPal selected const paypalMethods = ['paypal', 'ppec_paypal', 'ppcp-gateway', 'paypal_express', 'braintree_paypal'];
const paymentMethods = document.querySelectorAll('input[name="payment_method"]'); const checkDiv = document.getElementById('dapper-human-check');
paymentMethods.forEach(radio => {
r adio.addEventListener('change', function() {
const checkDiv = document.getElementById('dapper-human-check');
if (this.value === 'paypal') {
checkDiv.style.display = 'block';
} else {
checkDiv.style.display = 'none';
}
});
});
// Set token when checkbox clicked // Function to show/hide based on current selected method
const checkbox = document.getElementById('dapper_human_checkbox'); function updateHumanCheckVisibility() {
if (checkbox) { const selectedRadio = document.querySelector('input[name="payment_method"]:checked');
checkbox.addEventListener('change', function() { if (selectedRadio && paypalMethods.includes(selectedRadio.value)) {
if (this.checked) { checkDiv.style.display = 'block';
const token = 'human_' + Math.random().toString(36).substring(2) + '_' + Date.now(); } else {
document.getElementById('dapper_human_token').value = token; checkDiv.style.display = 'none';
} else { }
document.getElementById('dapper_human_token').value = ''; }
}
}); // Run immediately on load
} updateHumanCheckVisibility();
});
</script> // Listen for changes on payment radios
document.querySelectorAll('input[name="payment_method"]').forEach(radio => {
radio.addEventListener('change', updateHumanCheckVisibility);
});
// Watch for dynamic payment section updates (WooCommerce often reloads parts via AJAX)
const paymentBox = document.querySelector('#payment') || document.body;
const observer = new MutationObserver(updateHumanCheckVisibility);
observer.observe(paymentBox, { childList: true, subtree: true });
// Token generation
const checkbox = document.getElementById('dapper_human_checkbox');
if (checkbox) {
checkbox.addEventListener('change', function() {
const tokenField = document.getElementById('dapper_human_token');
tokenField.value = this.checked
? 'human_' + Math.random().toString(36).substring(2) + '_' + Date.now()
: '';
});
}
});
</script>
<?php <?php
} }
// Validate on checkout // Validate on checkout
add_action('woocommerce_after_checkout_validation', 'dapper_validate_custom_human_check', 15, 2); add_action('woocommerce_after_checkout_validation', 'dapper_validate_custom_human_check', 15, 2);
function dapper_validate_custom_human_check($data, $errors) { function dapper_validate_custom_human_check($data, $errors) {
if (isset($_POST['payment_method']) && $_POST['payment_method'] === 'paypal') { if (!dapper_is_paypal_payment_method()) {
$token = $_POST['dapper_human_token'] ?? ''; return;
}
$token = trim( $_POST['dapper_human_token'] ?? '' );
$time = (int) ($_POST['dapper_human_time'] ?? 0); $time = (int) ($_POST['dapper_human_time'] ?? 0);
if (empty($token)) { if (empty($token)) {
$errors->add('human_check_error', __('Please verify you are human to use PayPal.', 'dapper')); $errors->add('human_check_error', __('Please check the "I\'m not a robot" box to continue with PayPal.', 'dapper'));
return; return;
} }
// Must be checked within 5 minutes
if (time() - $time > 300) { if (time() - $time > 300) {
$errors->add('human_check_error', __('Verification timed out. Please try again.', 'dapper')); $errors->add('human_check_error', __('Verification timed out. Please refresh and try again.', 'dapper'));
return; return;
} }
// Optional: token must start with 'human_' and be random-ish
if (strpos($token, 'human_') !== 0 || strlen($token) < 20) { if (strpos($token, 'human_') !== 0 || strlen($token) < 20) {
$errors->add('human_check_error', __('Invalid verification. Please try again.', 'dapper')); $errors->add('human_check_error', __('Invalid verification. Please try again.', 'dapper'));
} }
} }
// 1. Checkbox on SINGLE PRODUCT pages (near PayPal button)
add_action('woocommerce_paypal_payments_single_product_button', 'dapper_human_checkbox_product', 5);
// Product page checkbox - use core Woo hook + JS to force show near Add to Cart / PayPal area
add_action('woocommerce_after_add_to_cart_button', 'dapper_human_checkbox_product', 999); // High priority = late output
function dapper_human_checkbox_product() {
dapper_debug_log('Universal product checkbox output attempt - Product ID: ' . get_the_ID());
if (get_option('dapper_enable_paypal_human_check', 'on') !== 'on') {
return;
}
global $product;
if (!$product || !$product->is_purchasable()) return;
?>
<div id="dapper-human-check-product" style="margin:20px 0; padding:15px; background:#fff3cd; border:1px solid #ffeeba; border-radius:4px; font-weight:bold;">
<strong>Required: Human Verification for PayPal Express</strong><br>
<label style="display:block; margin:12px 0; font-size:1.1em;">
<input type="checkbox" id="dapper_human_checkbox_product" required style="transform:scale(1.6); margin-right:12px; vertical-align:middle;">
Confirm I'm not a robot to unlock PayPal
</label>
<p style="color:#856404; font-size:0.95em;">This prevents automated spam. Takes 2 seconds.</p>
</div>
<script>
(function() {
console.log('Universal Dapper product protection initialized');
const CHECKBOX_ID = 'dapper_human_checkbox_product';
const CHECKBOX_CONTAINER_ID = 'dapper-human-check-product';
let observer = null;
function disablePayPalButtons() {
// Target common PayPal Smart Button patterns universally
document.querySelectorAll(
'[id*="paypal"], [class*="paypal"], [data-paypal], [data-pp], ' +
'iframe[src*="paypal.com"], div[role="button"][aria-label*="PayPal"], ' +
'button[aria-label*="Pay with PayPal"], .pp-button, .smart-button'
).forEach(el => {
// Strong disable: remove pointer events + overlay
el.style.pointerEvents = 'none';
el.style.userSelect = 'none';
el.style.opacity = '0.45';
el.style.cursor = 'not-allowed';
el.style.position = 'relative';
// Add overlay if missing
if (!el.querySelector('.dapper-universal-overlay')) {
const overlay = document.createElement('div');
overlay.className = 'dapper-universal-overlay';
overlay.style.position = 'absolute';
overlay.style.inset = '0';
overlay.style.background = 'rgba(0,0,0,0.3)';
overlay.style.display = 'flex';
overlay.style.alignItems = 'center';
overlay.style.justifyContent = 'center';
overlay.style.color = '#fff';
overlay.style.fontWeight = 'bold';
overlay.style.fontSize = '1.3em';
overlay.style.textAlign = 'center';
overlay.style.padding = '15px';
overlay.style.zIndex = '10';
overlay.innerText = 'Check verification box above to enable PayPal';
el.appendChild(overlay);
}
});
}
function enablePayPalButtons() {
document.querySelectorAll('.dapper-universal-overlay').forEach(overlay => {
overlay.remove();
});
// Re-enable (PayPal may override, but overlay removal helps)
document.querySelectorAll(
'[id*="paypal"], [class*="paypal"], [data-paypal], [data-pp], ' +
'iframe[src*="paypal.com"], div[role="button"][aria-label*="PayPal"]'
).forEach(el => {
el.style.pointerEvents = 'auto';
el.style.opacity = '1';
el.style.cursor = 'pointer';
});
}
function checkAndProtect() {
const checkbox = document.getElementById(CHECKBOX_ID);
if (!checkbox) return;
const container = document.getElementById(CHECKBOX_CONTAINER_ID);
if (container) container.style.display = 'block';
// Disable until checked
if (!checkbox.checked) {
disablePayPalButtons();
}
checkbox.addEventListener('change', () => {
if (checkbox.checked) {
enablePayPalButtons();
console.log('Dapper: Checkbox checked → PayPal unlocked');
} else {
disablePayPalButtons();
console.log('Dapper: Checkbox unchecked → PayPal locked');
}
});
}
// Run immediately + watch for PayPal injection
checkAndProtect();
observer = new MutationObserver((mutations) => {
if (document.querySelector('[id*="paypal"], [class*="paypal"], iframe[src*="paypal.com"]')) {
checkAndProtect();
}
});
observer.observe(document.body, { childList: true, subtree: true });
// Retry safety net (Flatsome/PayPal can be very late)
setTimeout(checkAndProtect, 1500);
setTimeout(checkAndProtect, 4000);
setTimeout(checkAndProtect, 8000);
})();
</script>
<?php
} }
// 2. Checkbox on CART page (near PayPal button)
add_action('woocommerce_proceed_to_checkout', 'dapper_human_checkbox_cart', 15);
function dapper_human_checkbox_cart() {
dapper_debug_log('Attempting cart checkbox output');
if (get_option('dapper_enable_paypal_human_check', 'on') !== 'on') {
return;
}
?>
<div id="dapper-human-check-cart" style="margin:20px 0; padding:15px; background:#fff3cd; border:1px solid #ffeeba; border-radius:4px; font-weight:bold;">
<strong>Human Verification Required for PayPal Express</strong><br>
<label style="display:block; margin:12px 0; font-size:1.1em;">
<input type="checkbox" id="dapper_human_checkbox_cart" required style="transform:scale(1.6); margin-right:12px; vertical-align:middle;">
Confirm I'm not a robot to unlock PayPal
</label>
<p style="color:#856404; font-size:0.95em;">This quick check helps stop automated spam. Takes 2 seconds.</p>
</div>
<script>
(function() {
console.log('Universal Dapper cart protection initialized');
const CHECKBOX_ID = 'dapper_human_checkbox_cart';
const CONTAINER_ID = 'dapper-human-check-cart';
function initCartCheckbox() {
const checkbox = document.getElementById(CHECKBOX_ID);
const container = document.getElementById(CONTAINER_ID);
if (!checkbox || !container) {
console.log('Dapper cart: Checkbox or container missing');
return false;
}
// Universal PayPal selectors (same as product page)
const paypalContainers = document.querySelectorAll(
'[id*="paypal"], [class*="paypal"], [data-paypal], [data-pp], ' +
'iframe[src*="paypal.com"], div[role="button"][aria-label*="PayPal"], ' +
'button[aria-label*="Pay with PayPal"], .pp-button, .smart-button, ' +
'.woocommerce-proceed-to-checkout .paypal-smart-button, ' +
'.wc-proceed-to-checkout .paypal-button-container'
);
if (paypalContainers.length === 0) {
console.log('Dapper cart: No PayPal containers found yet - retrying');
return false;
}
console.log('Dapper cart: Found ' + paypalContainers.length + ' PayPal container(s)');
// Force checkbox visible
container.style.display = 'block';
function disablePayPal() {
paypalContainers.forEach(container => {
container.style.pointerEvents = 'none';
container.style.opacity = '0.45';
container.style.cursor = 'not-allowed';
container.style.position = 'relative';
if (!container.querySelector('.dapper-universal-overlay')) {
const overlay = document.createElement('div');
overlay.className = 'dapper-universal-overlay';
overlay.style.position = 'absolute';
overlay.style.inset = '0';
overlay.style.background = 'rgba(0,0,0,0.3)';
overlay.style.display = 'flex';
overlay.style.alignItems = 'center';
overlay.style.justifyContent = 'center';
overlay.style.color = '#fff';
overlay.style.fontWeight = 'bold';
overlay.style.fontSize = '1.3em';
overlay.style.textAlign = 'center';
overlay.style.padding = '15px';
overlay.style.zIndex = '10';
overlay.innerText = 'Check verification box above to enable PayPal';
container.appendChild(overlay);
}
});
}
function enablePayPal() {
document.querySelectorAll('.dapper-universal-overlay').forEach(overlay => overlay.remove());
paypalContainers.forEach(container => {
container.style.pointerEvents = 'auto';
container.style.opacity = '1';
container.style.cursor = 'pointer';
});
}
// Start disabled
disablePayPal();
checkbox.addEventListener('change', () => {
if (checkbox.checked) {
enablePayPal();
console.log('Dapper cart: Checkbox checked → PayPal unlocked');
} else {
disablePayPal();
console.log('Dapper cart: Checkbox unchecked → PayPal locked');
}
});
return true;
}
// Run now + retry for dynamic load
let attempts = 0;
const maxAttempts = 20;
const interval = setInterval(() => {
if (initCartCheckbox() || attempts >= maxAttempts) {
clearInterval(interval);
}
attempts++;
}, 500);
initCartCheckbox();
})();
</script>
<?php
}
// 3. Server-side block: cancel any PayPal order missing valid token or showing bot patterns
add_action('woocommerce_new_order', 'dapper_block_fake_paypal_orders', 10, 1);
function dapper_block_fake_paypal_orders($order_id) {
$order = wc_get_order($order_id);
if (!$order) return;
$method = $order->get_payment_method();
if (!in_array($method, ['ppcp-gateway', 'paypal', 'ppec_paypal', 'paypal_express'], true)) {
return;
}
// Check token from product or cart (whichever was used)
$token = trim($_POST['dapper_human_token_product'] ?? $_POST['dapper_human_token_cart'] ?? '');
$time = (int) ($_POST['dapper_human_time_product'] ?? $_POST['dapper_human_time_cart'] ?? 0);
$token_ok = !empty($token) &&
strpos($token, 'human_') === 0 &&
strlen($token) >= 20 &&
(time() - $time <= 600); // 10 min window
// If token is missing/invalid → check other bot signs
if (!$token_ok) {
$first = strtolower(trim($order->get_billing_first_name() ?: ''));
$last = strtolower(trim($order->get_billing_last_name() ?: ''));
$company = strtolower(trim($order->get_billing_company() ?: ''));
$lowercase_name = ($first === $order->get_billing_first_name() && $last === $order->get_billing_last_name());
$suspicious_company = in_array($company, ['pogab', 'rayazifi', 'lunom', 'topap', 'potanginamo', '']) ||
preg_match('/^[a-z]{5,10}$/', $company);
if ($lowercase_name || $suspicious_company || (float)$order->get_total_tax() === 0) {
$order->update_status('cancelled', 'Blocked: Invalid or missing human verification for PayPal Express (bot pattern detected).');
dapper_debug_log("Blocked fake PayPal order #$order_id - No valid token | Name: $first $last | Company: $company");
}
}
}
} }