| 1 | <?php | 1 | <?php |
|---|
| 2 | /** | 2 | /** |
|---|
| 3 | * ========================= | 3 | * ========================= |
|---|
| 4 | * BACKEND (AJAX REQUEST) | 4 | * BACKEND (AJAX REQUEST) |
|---|
| 5 | * ========================= | 5 | * ========================= |
|---|
| 6 | */ | 6 | */ |
|---|
| 7 | if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['ajax'])) { | 7 | if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['ajax'])) { |
|---|
| 8 | | 8 | |
|---|
| 9 | header('Content-Type: application/json; charset=UTF-8'); | 9 | header('Content-Type: application/json; charset=UTF-8'); |
|---|
| 10 | | 10 | |
|---|
| 11 | function respond(bool $success, string $message = '', array $errors = []): void | 11 | function respond(bool $success, string $message = '', array $errors = []): void |
|---|
| 12 | { | 12 | { |
|---|
| 13 | echo json_encode([ | 13 | echo json_encode([ |
|---|
| 14 | 'success' => $success, | 14 | 'success' => $success, |
|---|
| 15 | 'message' => $message, | 15 | 'message' => $message, |
|---|
| 16 | 'errors' => $errors | 16 | 'errors' => $errors |
|---|
| 17 | ], JSON_UNESCAPED_UNICODE); | 17 | ], JSON_UNESCAPED_UNICODE); |
|---|
| 18 | exit; | 18 | exit; |
|---|
| 19 | } | 19 | } |
|---|
| 20 | | 20 | |
|---|
| 21 | $email = trim($_POST['email'] ?? ''); | 21 | $email = trim($_POST['email'] ?? ''); |
|---|
| 22 | $errors = []; | 22 | $errors = []; |
|---|
| 23 | | 23 | |
|---|
| 25 | if ($email === '') { | 25 | if ($email === '') { |
|---|
| 26 | $errors['email'] = 'Required field'; | 26 | $errors['email'] = 'Required field'; |
|---|
| 27 | } elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) { | 27 | } elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) { |
|---|
| 28 | $errors['email'] = 'Please enter a valid email address'; | 28 | $errors['email'] = 'Please enter a valid email address'; |
|---|
| 29 | } | 29 | } |
|---|
| 30 | | 30 | |
|---|
| 31 | if (!empty($errors)) { | 31 | if (!empty($errors)) { |
|---|
| 32 | respond(false, 'Please correct the highlighted fields.', $errors); | 32 | respond(false, 'Please correct the highlighted fields.', $errors); |
|---|
| 33 | } | 33 | } |
|---|
| 34 | | 34 | |
|---|
| 35 | // Process form | 35 | // Step 2: Check if CAPTCHA-Token exists |
|---|
| | 36 | if (empty($_POST['slidercaptcha_token'])) { |
|---|
| | 37 | // CAPTCHA required |
|---|
| | 38 | respond(true, 'captcha_required'); |
|---|
| | 39 | } |
|---|
| | 40 | |
|---|
| | 41 | // Step 3: Verify CAPTCHA Token |
|---|
| | 42 | $captchaToken = (string)($_POST['slidercaptcha_token'] ?? ''); |
|---|
| | 43 | $captchaServiceAvailable = true; |
|---|
| | 44 | |
|---|
| | 45 | if ($captchaToken === 'CAPTCHA_UNAVAILABLE') { |
|---|
| | 46 | // Special Token: Check if service is really down |
|---|
| | 47 | $ch = curl_init('https://slidercaptcha.net/api/v1/verify.php'); |
|---|
| | 48 | curl_setopt_array($ch, [ |
|---|
| | 49 | CURLOPT_POST => true, |
|---|
| | 50 | CURLOPT_RETURNTRANSFER => true, |
|---|
| | 51 | CURLOPT_HTTPHEADER => ['Content-Type: application/json'], |
|---|
| | 52 | CURLOPT_POSTFIELDS => json_encode(['token' => 'test', 'secret_key' => 'test']), |
|---|
| | 53 | CURLOPT_TIMEOUT => 3, |
|---|
| | 54 | CURLOPT_CONNECTTIMEOUT => 3 |
|---|
| | 55 | ]); |
|---|
| | 56 | |
|---|
| | 57 | $testResponse = curl_exec($ch); |
|---|
| | 58 | $testHttpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); |
|---|
| | 59 | curl_close($ch); |
|---|
| | 60 | |
|---|
| | 61 | if ($testResponse === false || $testHttpCode === 0) { |
|---|
| | 62 | // Service is down → Allow Bypass |
|---|
| | 63 | error_log('SliderCaptcha service unavailable - allowing bypass for email: ' . $email); |
|---|
| | 64 | $captchaVerified = true; |
|---|
| | 65 | $captchaServiceAvailable = false; |
|---|
| | 66 | } else { |
|---|
| | 67 | // Service is available → Bot attack! |
|---|
| | 68 | error_log('SECURITY WARNING: Attempted CAPTCHA bypass for email: ' . $email); |
|---|
| | 69 | respond(false, 'Invalid CAPTCHA token. Please try again.'); |
|---|
| | 70 | } |
|---|
| | 71 | |
|---|
| | 72 | } else { |
|---|
| | 73 | // Normal Token → Verify |
|---|
| | 74 | $verifyPayload = json_encode([ |
|---|
| | 75 | 'token' => $captchaToken, |
|---|
| | 76 | 'secret_key' => 'sk_live_26918...provided by DSLM IT-CONSULTING' |
|---|
| | 77 | ]); |
|---|
| | 78 | |
|---|
| | 79 | $ch = curl_init('https://slidercaptcha.net/api/v1/verify.php'); |
|---|
| | 80 | curl_setopt_array($ch, [ |
|---|
| | 81 | CURLOPT_POST => true, |
|---|
| | 82 | CURLOPT_RETURNTRANSFER => true, |
|---|
| | 83 | CURLOPT_HTTPHEADER => ['Content-Type: application/json'], |
|---|
| | 84 | CURLOPT_POSTFIELDS => $verifyPayload, |
|---|
| | 85 | CURLOPT_TIMEOUT => 5, |
|---|
| | 86 | CURLOPT_CONNECTTIMEOUT => 5 |
|---|
| | 87 | ]); |
|---|
| | 88 | |
|---|
| | 89 | $verifyResponse = curl_exec($ch); |
|---|
| | 90 | $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); |
|---|
| | 91 | curl_close($ch); |
|---|
| | 92 | |
|---|
| | 93 | if ($verifyResponse === false || $httpCode === 0) { |
|---|
| | 94 | // Service down → Allow |
|---|
| | 95 | error_log('SliderCaptcha service unavailable during verification - allowing bypass for email: ' . $email); |
|---|
| | 96 | $captchaVerified = true; |
|---|
| | 97 | $captchaServiceAvailable = false; |
|---|
| | 98 | } elseif ($httpCode !== 200) { |
|---|
| | 99 | respond(false, 'SliderCaptcha verification failed. Please try again.'); |
|---|
| | 100 | } else { |
|---|
| | 101 | $data = json_decode($verifyResponse, true); |
|---|
| | 102 | if (!is_array($data) || empty($data['success'])) { |
|---|
| | 103 | respond(false, 'SliderCaptcha verification failed. Please try again.'); |
|---|
| | 104 | } elseif (isset($data['score']) && $data['score'] < 0.5) { |
|---|
| | 105 | respond(false, 'SliderCaptcha verification failed. Please try again.'); |
|---|
| | 106 | } else { |
|---|
| | 107 | $captchaVerified = true; |
|---|
| | 108 | } |
|---|
| | 109 | } |
|---|
| | 110 | } |
|---|
| | 111 | |
|---|
| | 112 | // Step 4: Process form if CAPTCHA verified |
|---|
| | 113 | if ($captchaVerified) { |
|---|
| 36 | /* | 114 | /* |
|---|
| 37 | *** put your code here to check Email against database *** | 115 | *** put your code here to check Email against database *** |
|---|
| 38 | */ | 116 | */ |
|---|
| 39 | | 117 | |
|---|
| 40 | // Check if email already exists | 118 | // Check if email already exists |
|---|
| 41 | if ($email == "joe.doe@example.com") { | 119 | if ($email == "joe.doe@example.com") { |
|---|
| 42 | //respond(false, 'This email-address already exists in our database', ['email' => 'This email-address already exists in our database']); | 120 | //respond(false, 'This email-address already exists in our database', ['email' => 'This email-address already exists in our database']); |
|---|
| 43 | respond(false, '', ['email' => 'This email-address already exists in our database']); | 121 | respond(false, '', ['email' => 'This email-address already exists in our database']); |
|---|
| 44 | } | 122 | } |
|---|
| 45 | | 123 | |
|---|
| 46 | // Success | 124 | // Success |
|---|
| 47 | /* | 125 | /* |
|---|
| 48 | *** put your code here to store the email-address in your database *** | 126 | *** put your code here to store the email-address in your database *** |
|---|
| 49 | */ | 127 | */ |
|---|
| 50 | $successMessage = 'Thank you for your subscription!'; | 128 | $successMessage = 'Thank you for your subscription!'; |
|---|
| 99 | </ul> | 184 | </ul> |
|---|
| 100 | | 185 | |
|---|
| 101 | <div id="successMessage" class="success" style="display: none;"></div> | 186 | <div id="successMessage" class="success" style="display: none;"></div> |
|---|
| 102 | <div id="errorMessage" class="error" style="display: none;"></div> | 187 | <div id="errorMessage" class="error" style="display: none;"></div> |
|---|
| 103 | | 188 | |
|---|
| 104 | <form id="subscriptionForm" novalidate> | 189 | <form id="subscriptionForm" novalidate> |
|---|
| 105 | <!-- Email --> | 190 | <!-- Email --> |
|---|
| 106 | Email:<br> | 191 | Email:<br> |
|---|
| 107 | <input type="email" name="email" id="email" autocomplete="email"> | 192 | <input type="email" name="email" id="email" autocomplete="email"> |
|---|
| 108 | <div id="email-error" class="error" style="display: none;"></div> | 193 | <div id="email-error" class="error" style="display: none;"></div> |
|---|
| 109 | <br><br> | 194 | <br><br> |
|---|
| 110 | | 195 | |
|---|
| 111 | <button type="submit" id="submitBtn">Subscribe</button> | 196 | <button type="submit" id="submitBtn">Subscribe</button> |
|---|
| 112 | <span id="loadingIndicator" style="display: none; margin-left: 10px;">Processing...</span> | 197 | <span id="loadingIndicator" style="display: none; margin-left: 10px;">Processing...</span> |
|---|
| 113 | </form> | 198 | </form> |
|---|
| 114 | | 199 | |
|---|
| 115 | <script> | 200 | <script> |
|---|
| 116 | $(function() { | 201 | $(function() { |
|---|
| 117 | | 202 | |
|---|
| 118 | let isSubmitting = false; | 203 | let isSubmitting = false; |
|---|
| 119 | | 204 | |
|---|
| 120 | // Clear errors on input | 205 | // Clear errors on input |
|---|
| 121 | $('#email').on('input', function() { | 206 | $('#email').on('input', function() { |
|---|
| 122 | $('#email-error').hide(); | 207 | $('#email-error').hide(); |
|---|
| 123 | $('#errorMessage').hide(); | 208 | $('#errorMessage').hide(); |
|---|
| 124 | }); | 209 | }); |
|---|
| 125 | | 210 | |
|---|
| 126 | // Form submit handler | 211 | // Form submit handler |
|---|
| 127 | $('#subscriptionForm').on('submit', function(e) { | 212 | $('#subscriptionForm').on('submit', function(e) { |
|---|
| 128 | e.preventDefault(); | 213 | e.preventDefault(); |
|---|
| 129 | | 214 | |
|---|
| 130 | if (isSubmitting) return; | 215 | if (isSubmitting) return; |
|---|
| 131 | | 216 | |
|---|
| 132 | // Clear previous messages | 217 | // Clear previous messages |
|---|
| 133 | $('#successMessage').hide(); | 218 | $('#successMessage').hide(); |
|---|
| 134 | $('#errorMessage').hide(); | 219 | $('#errorMessage').hide(); |
|---|
| 135 | $('#email-error').hide(); | 220 | $('#email-error').hide(); |
|---|
| 136 | | 221 | |
|---|
| 137 | // Show loading | 222 | // Show loading |
|---|
| 138 | isSubmitting = true; | 223 | isSubmitting = true; |
|---|
| 139 | $('#submitBtn').prop('disabled', true); | 224 | $('#submitBtn').prop('disabled', true); |
|---|
| 140 | $('#loadingIndicator').show(); | 225 | $('#loadingIndicator').show(); |
|---|
| 141 | $('#subscriptionForm').addClass('loading'); | 226 | $('#subscriptionForm').addClass('loading'); |
|---|
| 142 | | 227 | |
|---|
| 143 | // First AJAX request: Validation | 228 | // First AJAX request: Validation |
|---|
| 144 | $.ajax({ | 229 | $.ajax({ |
|---|
| 145 | url: '', | 230 | url: '', |
|---|
| 146 | method: 'POST', | 231 | method: 'POST', |
|---|
| 147 | data: $(this).serialize() + '&ajax=1', | 232 | data: $(this).serialize() + '&ajax=1', |
|---|
| 148 | dataType: 'json', | 233 | dataType: 'json', |
|---|
| 149 | success: function(response) { | 234 | success: function(response) { |
|---|
| | 235 | |
|---|
| | 236 | // CAPTCHA required |
|---|
| | 237 | if (response.success && response.message === 'captcha_required') { |
|---|
| | 238 | |
|---|
| | 239 | // Check if SliderCaptcha is available |
|---|
| | 240 | if (typeof SliderCaptcha === 'undefined') { |
|---|
| | 241 | // Service unavailable - send special token |
|---|
| | 242 | console.warn('SliderCaptcha not loaded - sending CAPTCHA_UNAVAILABLE token'); |
|---|
| | 243 | submitWithToken('CAPTCHA_UNAVAILABLE'); |
|---|
| | 244 | } else { |
|---|
| | 245 | // Show CAPTCHA |
|---|
| | 246 | SliderCaptcha.execute() |
|---|
| | 247 | .then(function(captchaResponse) { |
|---|
| | 248 | // CAPTCHA solved - submit with token |
|---|
| | 249 | submitWithToken(captchaResponse.token); |
|---|
| | 250 | }) |
|---|
| | 251 | .catch(function(error) { |
|---|
| | 252 | // CAPTCHA cancelled |
|---|
| | 253 | console.error('SliderCaptcha error:', error); |
|---|
| | 254 | resetForm(); |
|---|
| | 255 | $('#errorMessage').text('CAPTCHA was cancelled. Please try again.').show(); |
|---|
| | 256 | }); |
|---|
| | 257 | } |
|---|
| | 258 | return; |
|---|
| | 259 | } |
|---|
| | 260 | |
|---|
| 167 | | 303 | |
|---|
| 168 | // Handle error responses | 304 | // Handle error responses |
|---|
| 169 | function handleErrors(response) { | 305 | function handleErrors(response) { |
|---|
| 170 | resetForm(); | 306 | resetForm(); |
|---|
| 171 | | 307 | |
|---|
| 172 | // Field-specific errors | 308 | // Field-specific errors |
|---|
| 173 | if (response.errors) { | 309 | if (response.errors) { |
|---|
| 174 | $.each(response.errors, function(field, message) { | 310 | $.each(response.errors, function(field, message) { |
|---|
| 175 | $('#' + field + '-error').text(message).show(); | 311 | $('#' + field + '-error').text(message).show(); |
|---|
| 176 | }); | 312 | }); |
|---|
| 177 | } | 313 | } |
|---|
| 178 | | 314 | |
|---|
| 179 | // General error message | 315 | // General error message |
|---|
| 180 | if (response.message && response.message !== 'Please correct the highlighted fields.') { | 316 | if (response.message && response.message !== 'Please correct the highlighted fields.') { |
|---|
| 181 | $('#errorMessage').text(response.message).show(); | 317 | $('#errorMessage').text(response.message).show(); |
|---|
| 182 | } | 318 | } |
|---|
| 183 | } | 319 | } |
|---|
| 184 | | 320 | |
|---|
| 185 | // Reset form state | 321 | // Reset form state |
|---|
| 186 | function resetForm() { | 322 | function resetForm() { |
|---|
| 187 | isSubmitting = false; | 323 | isSubmitting = false; |
|---|
| 188 | $('#submitBtn').prop('disabled', false); | 324 | $('#submitBtn').prop('disabled', false); |
|---|
| 189 | $('#loadingIndicator').hide(); | 325 | $('#loadingIndicator').hide(); |
|---|
| 190 | $('#subscriptionForm').removeClass('loading'); | 326 | $('#subscriptionForm').removeClass('loading'); |
|---|
| 191 | } | 327 | } |
|---|
| 192 | | 328 | |
|---|
| 193 | }); | 329 | }); |
|---|
| 194 | </script> | 330 | </script> |
|---|
| 195 | | 331 | |
|---|