PluginProbe ʕ •ᴥ•ʔ
GiveWP – Donation Plugin and Fundraising Platform / 4.16.7.2
GiveWP – Donation Plugin and Fundraising Platform v4.16.7.2
4.16.7.2 4.16.7.1 4.16.7 4.16.6.1 4.16.6 4.16.5.1 4.16.5 4.16.4 4.16.3 4.16.2 4.16.1 4.16.0 4.15.5 4.15.4 4.15.3 4.15.2 4.15.1 4.15.0 2.3.0 2.3.1 2.3.2 2.30.0 2.31.0 2.31.1 2.32.0 2.33.0 2.33.1 2.33.2 2.33.3 2.33.4 2.33.5 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.4.7 2.5.0 2.5.1 2.5.10 2.5.11 2.5.12 2.5.13 2.5.2 2.5.3 2.5.4 2.5.5 2.5.6 2.5.7 2.5.8 2.5.9 2.6.0 2.6.1 2.6.2 2.6.3 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.8.0 2.8.1 2.9.0 2.9.1 2.9.2 2.9.3 2.9.4 2.9.5 2.9.6 2.9.7 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.1.2 3.10.0 3.11.0 3.12.0 3.12.1 3.12.2 3.12.3 3.13.0 3.14.0 3.14.1 3.14.2 3.15.0 3.15.1 3.16.0 3.16.1 3.16.2 3.16.3 3.16.4 3.16.5 3.17.0 3.17.1 3.17.2 3.18.0 3.19.0 3.19.1 3.19.2 3.19.3 3.19.4 3.2.0 3.2.1 3.2.2 3.20.0 3.21.0 3.21.1 3.22.0 3.22.1 3.22.2 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 3.6.0 3.6.1 3.6.2 3.7.0 3.8.0 3.9.0 4.0.0 4.1.0 4.1.1 4.10.0 4.10.1 4.11.0 4.12.0 4.13.0 4.13.1 4.13.2 4.14.0 4.14.1 4.14.2 4.14.3 4.14.4 4.14.5 4.14.6 4.2.0 4.2.1 4.3.0 4.3.1 4.3.2 4.4.0 4.5.0 4.6.1 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 trunk 1.9.0 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.10.0 2.10.1 2.10.2 2.10.3 2.10.4 2.11.0 2.11.1 2.11.2 2.11.3 2.12.0 2.12.1 2.12.2 2.12.3 2.13.0 2.13.1 2.13.2 2.13.3 2.13.4 2.14.0 2.15.0 2.16.0 2.16.1 2.17.0 2.17.1 2.17.3 2.18.0 2.18.1 2.19.1 2.19.2 2.19.3 2.19.4 2.19.5 2.19.6 2.19.7 2.19.8 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.20.0 2.20.1 2.20.2 2.21.0 2.21.1 2.21.2 2.21.3 2.21.4 2.22.0 2.22.1 2.22.2 2.22.3 2.23.0 2.23.1 2.23.2 2.24.0 2.24.1 2.24.2 2.25.0 2.25.1 2.25.2 2.25.3 2.26.0 2.27.0 2.27.1 2.27.2 2.27.3 2.28.0 2.29.0 2.29.1 2.29.2
give / includes / process-donation.php
give / includes Last commit date
admin 1 week ago api 3 years ago database 6 days ago deprecated 3 months ago donors 6 days ago emails 11 months ago forms 1 week ago frontend 6 years ago gateways 3 weeks ago libraries 11 months ago payments 6 days ago actions.php 3 weeks ago ajax-functions.php 2 months ago class-give-async-process.php 2 years ago class-give-background-updater.php 11 months ago class-give-cache-setting.php 1 year ago class-give-cache.php 11 months ago class-give-cli-commands.php 1 year ago class-give-comment.php 11 months ago class-give-cron.php 11 months ago class-give-donate-form.php 2 years ago class-give-donor.php 2 years ago class-give-email-access.php 1 week ago class-give-license-handler.php 3 months ago class-give-logging.php 11 months ago class-give-readme-parser.php 4 years ago class-give-roles.php 7 months ago class-give-scripts.php 1 month ago class-give-session.php 6 days ago class-give-stats.php 6 years ago class-give-template-loader.php 6 years ago class-give-tooltips.php 6 years ago class-give-translation.php 4 years ago class-notices.php 11 months ago country-functions.php 9 months ago currencies-list.php 9 months ago currency-functions.php 4 years ago error-tracking.php 6 years ago filters.php 11 months ago formatting.php 11 months ago install.php 11 months ago login-register.php 6 days ago misc-functions.php 1 week ago plugin-compatibility.php 6 years ago post-types.php 1 year ago price-functions.php 6 years ago process-donation.php 6 days ago setting-functions.php 7 years ago shortcodes.php 1 year ago template-functions.php 1 year ago user-functions.php 4 years ago
process-donation.php
1731 lines
1 <?php
2 /**
3 * Process Donation
4 *
5 * @package Give
6 * @subpackage Functions
7 * @copyright Copyright (c) 2016, GiveWP
8 * @license https://opensource.org/licenses/gpl-license GNU Public License
9 * @since 1.0
10 */
11
12 use Give\Helpers\Form\Utils as FormUtils;
13 use Give\Helpers\Frontend\Shortcode as ShortcodeUtils;
14 use Give\Helpers\Utils;
15
16 // Exit if accessed directly.
17 if ( ! defined( 'ABSPATH' ) ) {
18 exit;
19 }
20
21 /**
22 * Process Donation Form
23 *
24 * Handles the donation form process.
25 *
26 * @access private
27 * @since 4.16.7.2 Reject serialized data in name fields before storing donation data.
28 * @since 4.16.6 Bail early when the form ID is not a give_forms post or is a Visual Form Builder (v3) form.
29 * @since 3.16.1 Use give_maybe_safe_unserialize() on $user_info data
30 * @since 1.0
31 *
32 * @throws ReflectionException Exception Handling.
33 *
34 * @return mixed
35 */
36 function give_process_donation_form() {
37
38 // Sanitize Posted Data.
39 $post_data = give_clean( $_POST ); // WPCS: input var ok, CSRF ok.
40
41 // Check whether the form submitted via AJAX or not.
42 $is_ajax = isset( $post_data['give_ajax'] );
43
44 // Verify donation form nonce.
45 if ( ! give_verify_donation_form_nonce( $post_data['give-form-hash'], $post_data['give-form-id'] ) ) {
46 if ( $is_ajax ) {
47 /**
48 * Fires when AJAX sends back errors from the donation form.
49 *
50 * @since 1.0
51 */
52 do_action( 'give_ajax_donation_errors' );
53 give_die();
54 } else {
55 give_send_back_to_checkout();
56 }
57 }
58
59 $form_id = isset( $post_data['give-form-id'] ) ? absint( $post_data['give-form-id'] ) : 0;
60
61 if ( ! ShortcodeUtils::isValidForm( $form_id ) ) {
62 give_set_error(
63 'give_invalid_donation_form',
64 __( 'The donation form ID is invalid. Please reload the page and try again.', 'give' )
65 );
66
67 if ( $is_ajax ) {
68 /** This action is documented in this file (see give_ajax_donation_errors above). */
69 do_action( 'give_ajax_donation_errors' );
70 give_die();
71 return;
72 }
73
74 give_send_back_to_checkout();
75
76 return false;
77 }
78
79 // Visual Form Builder (v3) forms are processed through the givewp-donate route,
80 // so bail out when the legacy donation processor receives one.
81 if ( FormUtils::isV3Form( $form_id ) ) {
82 give_set_error(
83 'give_unsupported_form_version',
84 __( 'This donation form cannot be processed through this endpoint. Please reload the page and try again.', 'give' )
85 );
86
87 if ( $is_ajax ) {
88 /** This action is documented in this file (see give_ajax_donation_errors above). */
89 do_action( 'give_ajax_donation_errors' );
90 give_die();
91 return;
92 }
93
94 give_send_back_to_checkout();
95
96 return false;
97 }
98
99 /**
100 * Fires before processing the donation form.
101 *
102 * @since 1.0
103 */
104 do_action( 'give_pre_process_donation' );
105
106 // Validate the form $_POST data.
107 $valid_data = give_donation_form_validate_fields();
108
109 /**
110 * Fires after validating donation form fields.
111 *
112 * Allow you to hook to donation form errors.
113 *
114 * @since 1.0
115 *
116 * @param bool|array $valid_data Validate fields.
117 * @param array $deprecated Deprecated Since 2.0.2. Use $_POST instead.
118 */
119 $deprecated = $post_data;
120 do_action( 'give_checkout_error_checks', $valid_data, $deprecated );
121
122 // Process the login form.
123 if ( isset( $post_data['give_login_submit'] ) ) {
124 give_process_form_login();
125 }
126
127 // Validate the user.
128 $user = give_get_donation_form_user( $valid_data );
129
130 if ( false === $valid_data || ! $user || give_get_errors() ) {
131 if ( $is_ajax ) {
132 /**
133 * Fires when AJAX sends back errors from the donation form.
134 *
135 * @since 1.0
136 */
137 do_action( 'give_ajax_donation_errors' );
138 give_die();
139 } else {
140 return false;
141 }
142 }
143
144 // If AJAX send back success to proceed with form submission.
145 if ( $is_ajax ) {
146 echo 'success';
147 give_die();
148 }
149
150 /**
151 * Fires action after donation form field validated.
152 *
153 * @since 2.2.0
154 */
155 do_action( 'give_process_donation_after_validation' );
156
157 // Setup user information.
158 $user_info = [
159 'id' => $user['user_id'],
160 'title' => $user['user_title'],
161 'email' => $user['user_email'],
162 'first_name' => $user['user_first'],
163 'last_name' => $user['user_last'],
164 'address' => $user['address'],
165 ];
166
167 // Reject serialized data in name fields.
168 $serialized_keys = array_filter(
169 $user_info,
170 static function ( $value ) {
171 return is_string( $value ) && \Give\Helpers\Utils::isSerialized( $value );
172 }
173 );
174
175 if ( ! empty( $serialized_keys ) ) {
176 give_set_error( 'give_serialized_user_info', esc_html__( 'Name fields cannot contain serialized data.', 'give' ) );
177 return;
178 }
179
180 $auth_key = defined( 'AUTH_KEY' ) ? AUTH_KEY : '';
181
182 // Donation form ID.
183 $form_id = isset( $post_data['give-form-id'] ) ? absint( $post_data['give-form-id'] ) : 0;
184
185 $price = isset( $post_data['give-amount'] ) ?
186 (float) apply_filters( 'give_donation_total', give_maybe_sanitize_amount( $post_data['give-amount'], [ 'currency' => give_get_currency( $form_id ) ] ) ) :
187 '0.00';
188 $purchase_key = strtolower( md5( $user['user_email'] . date( 'Y-m-d H:i:s' ) . $auth_key . uniqid( 'give', true ) ) );
189
190 /**
191 * Update donation Purchase key.
192 *
193 * Use this filter to update default donation purchase key
194 * and add prefix in Invoice.
195 *
196 * @since 2.2.4
197 *
198 * @param string $purchase_key
199 * @param string $gateway
200 * @param string $purchase_key
201 *
202 * @return string $purchase_key
203 */
204 $purchase_key = apply_filters(
205 'give_donation_purchase_key',
206 $purchase_key,
207 $valid_data['gateway'],
208 // Use this purchase key value if you want to generate custom donation purchase key
209 // because donation purchase key editable by filters and you may get unedited donation purchase key.
210 $purchase_key
211 );
212
213 // Setup donation information.
214 $user_info = stripslashes_deep( $user_info );
215 $donation_data = [
216 'price' => $price,
217 'purchase_key' => $purchase_key,
218 'user_email' => $user['user_email'],
219 'date' => date( 'Y-m-d H:i:s', current_time( 'timestamp' ) ),
220 'user_info' => $user_info,
221 'post_data' => $post_data,
222 'gateway' => $valid_data['gateway'],
223 'card_info' => $valid_data['cc_info'],
224 ];
225
226 // Add the user data for hooks.
227 $valid_data['user'] = $user;
228
229 /**
230 * Fires before donation form gateway.
231 *
232 * Allow you to hook to donation form before the gateway.
233 *
234 * @since 1.0
235 *
236 * @param array $post_data Array of variables passed via the HTTP POST.
237 * @param array $user_info Array containing basic user information.
238 * @param bool|array $valid_data Validate fields.
239 */
240 do_action( 'give_checkout_before_gateway', $post_data, $user_info, $valid_data );
241
242 // Sanity check for price.
243 if ( ! $donation_data['price'] ) {
244 // Revert to manual.
245 $donation_data['gateway'] = 'manual';
246 $_POST['give-gateway'] = 'manual';
247 }
248
249 /**
250 * Allow the donation data to be modified before it is sent to the gateway.
251 *
252 * @since 1.7
253 */
254 $donation_data = apply_filters( 'give_donation_data_before_gateway', $donation_data, $valid_data );
255
256 // Setup the data we're storing in the donation session.
257 $session_data = $donation_data;
258
259 // Make sure credit card numbers are never stored in sessions.
260 unset( $session_data['card_info']['card_number'] );
261 unset( $session_data['post_data']['card_number'] );
262
263 // Used for showing data to non logged-in users after donation, and for other plugins needing donation data.
264 give_set_purchase_session( $session_data );
265
266 /**
267 * Prevent PHP notices from breaking receipt display.
268 * This is specifically an issue with the Stripe SDK.
269 *
270 * @link https://github.com/impress-org/givewp/issues/5199
271 */
272 ob_start();
273 // Send info to the gateway for payment processing.
274 give_send_to_gateway( $donation_data['gateway'], $donation_data );
275 ob_get_clean();
276 give_die();
277 }
278
279 add_action( 'give_purchase', 'give_process_donation_form' );
280 add_action( 'wp_ajax_give_process_donation', 'give_process_donation_form' );
281 add_action( 'wp_ajax_nopriv_give_process_donation', 'give_process_donation_form' );
282
283 /**
284 * Verify that when a logged in user makes a donation that the email address used doesn't belong to a different customer.
285 * Note: only for internal use
286 *
287 * @see https://github.com/impress-org/give/issues/4025
288 *
289 * @since 1.7
290 * @since 2.4.2 This function runs independently instead of give_checkout_error_checks hook and also edit donor email.
291 *
292 * @param array $valid_data Validated data submitted for the donation.
293 *
294 * @return void
295 */
296 function give_check_logged_in_user_for_existing_email( &$valid_data ) {
297
298 // Verify that the email address belongs to this donor.
299 if ( is_user_logged_in() ) {
300
301 $donor = new Give_Donor( get_current_user_id(), true );
302
303 // Bailout: check if wp user is existing donor or not.
304 if ( ! $donor->id ) {
305 return;
306 }
307
308 $submitted_email = strtolower( $valid_data['user_email'] );
309
310 $donor_emails = array_map( 'strtolower', $donor->emails );
311 $email_index = array_search( $submitted_email, $donor_emails, true );
312
313 // If donor matched with email then return set formatted email from database.
314 if ( false !== $email_index ) {
315 $valid_data['user_email'] = $donor->emails[ $email_index ];
316
317 return;
318 }
319
320 // If this email address is not registered with this customer, see if it belongs to any other customer.
321 $found_donor = new Give_Donor( $submitted_email );
322
323 if ( $found_donor->id > 0 ) {
324 give_set_error(
325 'give-customer-email-exists',
326 sprintf(
327 /* translators: 1. Donor Email, 2. Submitted Email */
328 __( 'You are logged in as %1$s, and are submitting a donation as %2$s, which is an existing donor. To ensure that the email address is tied to the correct donor, please submit this donation from a logged-out browser, or choose another email address.', 'give' ),
329 $donor->email,
330 $submitted_email
331 )
332 );
333 }
334 }
335 }
336
337 /**
338 * Process the checkout login form
339 *
340 * @access private
341 * @since 4.16.7 Require a valid nonce before processing the login form.
342 * @since 1.0
343 *
344 * @return void
345 */
346 function give_process_form_login() {
347
348 $is_ajax = ! empty( $_POST['give_ajax'] ) ? give_clean( $_POST['give_ajax'] ) : 0; // WPCS: input var ok, sanitization ok, CSRF ok.
349 $referrer = wp_get_referer();
350
351 // Default to no user until the login form is validated.
352 $user_data = [
353 'user_id' => - 1,
354 ];
355
356 // Require a valid nonce before processing the login form.
357 if ( empty( $_POST['give_login_nonce'] ) || ! wp_verify_nonce( $_POST['give_login_nonce'], 'give-login-nonce' ) ) {
358 give_set_error( 'invalid_nonce', __( 'Your session has expired. Please reload the page and try again.', 'give' ) );
359 } else {
360 $user_data = give_donation_form_validate_user_login();
361 }
362
363 if ( give_get_errors() || $user_data['user_id'] < 1 ) {
364 if ( $is_ajax ) {
365 /**
366 * Fires when AJAX sends back errors from the donation form.
367 *
368 * @since 1.0
369 */
370 ob_start();
371 do_action( 'give_ajax_donation_errors' );
372 $message = ob_get_contents();
373 ob_end_clean();
374 wp_send_json_error( $message );
375 return;
376 } else {
377 wp_safe_redirect( $referrer );
378 exit;
379 }
380 }
381
382 give_log_user_in( $user_data['user_id'], $user_data['user_login'], $user_data['user_pass'] );
383
384 if ( $is_ajax ) {
385 $message = Give_Notices::print_frontend_notice(
386 sprintf(
387 /* translators: %s: user first name */
388 esc_html__( 'Welcome %s! You have successfully logged into your account.', 'give' ),
389 ( ! empty( $user_data['user_first'] ) ) ? $user_data['user_first'] : $user_data['user_login']
390 ),
391 false,
392 'success'
393 );
394
395 wp_send_json_success( $message );
396 } else {
397 wp_safe_redirect( $referrer );
398 }
399 }
400
401 add_action( 'wp_ajax_give_process_donation_login', 'give_process_form_login' );
402 add_action( 'wp_ajax_nopriv_give_process_donation_login', 'give_process_form_login' );
403
404 /**
405 * Donation Form Validate Fields.
406 *
407 * @access private
408 * @since 3.5.0 validate serialized fields
409 * @since 1.0
410 *
411 * @return bool|array
412 */
413 function give_donation_form_validate_fields() {
414
415 $post_data = give_clean( $_POST ); // WPCS: input var ok, sanitization ok, CSRF ok.
416 give_donation_form_validate_name_fields($post_data);
417
418 // Validate Honeypot First.
419 if ( ! empty( $post_data['give-honeypot'] ) ) {
420 give_set_error( 'invalid_honeypot', esc_html__( 'Honeypot field detected. Go away bad bot!', 'give' ) );
421 }
422
423 // Validate serialized fields.
424 if (give_donation_form_has_serialized_fields($post_data)) {
425 give_set_error('invalid_serialized_fields', esc_html__('Serialized fields detected. Go away!', 'give'));
426 }
427
428 // Check spam detect.
429 if (
430 isset( $post_data['action'] )
431 && give_is_spam_donation()
432 ) {
433 give_set_error( 'spam_donation', __( 'The email you are using has been flagged as one used in SPAM comments or donations by our system. Please try using a different email address or contact the site administrator if you have any questions.', 'give' ) );
434 }
435
436 // Start an array to collect valid data.
437 $valid_data = [
438 'gateway' => give_donation_form_validate_gateway(), // Gateway fallback (amount is validated here).
439 'need_new_user' => false, // New user flag.
440 'need_user_login' => false, // Login user flag.
441 'logged_user_data' => [], // Logged user collected data.
442 'new_user_data' => [], // New user collected data.
443 'login_user_data' => [], // Login user collected data.
444 'guest_user_data' => [], // Guest user collected data.
445 'cc_info' => give_donation_form_validate_cc(), // Credit card info.
446 ];
447
448 $form_id = (int) $post_data['give-form-id'];
449
450 // Validate agree to terms.
451 if ( give_is_terms_enabled( $form_id ) ) {
452 give_donation_form_validate_agree_to_terms();
453 }
454
455 if ( is_user_logged_in() ) {
456
457 // Collect logged in user data.
458 $valid_data['logged_in_user'] = give_donation_form_validate_logged_in_user();
459 } elseif (
460 isset( $post_data['give-purchase-var'] )
461 && 'needs-to-register' === $post_data['give-purchase-var']
462 && ! empty( $post_data['give_create_account'] )
463 ) {
464
465 // Set new user registration as required.
466 $valid_data['need_new_user'] = true;
467
468 // Validate new user data.
469 $valid_data['new_user_data'] = give_donation_form_validate_new_user();
470 } elseif (
471 isset( $post_data['give-purchase-var'] )
472 && 'needs-to-login' === $post_data['give-purchase-var']
473 ) {
474
475 // Set user login as required.
476 $valid_data['need_user_login'] = true;
477
478 // Validate users login info.
479 $valid_data['login_user_data'] = give_donation_form_validate_user_login();
480 } else {
481
482 // Not registering or logging in, so setup guest user data.
483 $valid_data['guest_user_data'] = give_donation_form_validate_guest_user();
484 }
485
486 // Return collected data.
487 return $valid_data;
488 }
489
490 /**
491 * Detect serialized fields.
492 *
493 * @since 3.17.2 Use Utils::isSerialized() method which add supports to find hidden serialized data in the middle of a string
494 * @since 3.16.5 Make sure only string parameters are used with the ltrim() method to prevent PHP 8+ fatal errors
495 * @since 3.16.4 updated to check all values for serialized fields
496 * @since 3.16.2 added additional check for stripslashes_deep
497 * @since 3.14.2 add give-form-title, give_title
498 * @since 3.5.0
499 */
500 function give_donation_form_has_serialized_fields(array $post_data): bool
501 {
502 foreach ($post_data as $value) {
503
504 if (Utils::isSerialized($value)) {
505 return true;
506 }
507 }
508
509 return false;
510 }
511
512 /**
513 * Detect spam donation.
514 *
515 * @since 1.8.14
516 *
517 * @return bool|mixed
518 */
519 function give_is_spam_donation() {
520 $spam = false;
521
522 $user_agent = (string) isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : '';
523
524 if ( strlen( $user_agent ) < 2 ) {
525 $spam = true;
526 }
527
528 // Allow developer to customized Akismet spam detect API call and it's response.
529 return apply_filters( 'give_spam', $spam );
530 }
531
532 /**
533 * Donation Form Validate Gateway
534 *
535 * Validate the gateway and donation amount.
536 *
537 * @access private
538 * @since 1.0
539 *
540 * @return string
541 */
542 function give_donation_form_validate_gateway() {
543
544 $post_data = give_clean( $_POST ); // WPCS: input var ok, sanitization ok, CSRF ok.
545 $form_id = ! empty( $post_data['give-form-id'] ) ? $post_data['give-form-id'] : 0;
546 $amount = ! empty( $post_data['give-amount'] ) ? give_maybe_sanitize_amount( $post_data['give-amount'] ) : 0;
547 $gateway = ! empty( $post_data['give-gateway'] ) ? $post_data['give-gateway'] : 0;
548
549 // Bailout, if payment gateway is not submitted with donation form data.
550 if ( empty( $gateway ) ) {
551
552 give_set_error( 'empty_gateway', __( 'The donation form will process with a valid payment gateway.', 'give' ) );
553
554 } elseif ( ! give_is_gateway_active( $gateway ) ) {
555
556 give_set_error( 'invalid_gateway', __( 'The selected payment gateway is not enabled.', 'give' ) );
557
558 } elseif ( empty( $amount ) ) {
559
560 give_set_error( 'invalid_donation_amount', __( 'Please insert a valid donation amount.', 'give' ) );
561
562 } elseif ( ! give_verify_minimum_price( 'minimum' ) ) {
563
564 give_set_error(
565 'invalid_donation_minimum',
566 sprintf(
567 /* translators: %s: minimum donation amount */
568 __( 'This form has a minimum donation amount of %s.', 'give' ),
569 give_currency_filter(
570 give_format_amount(
571 give_get_form_minimum_price( $form_id ),
572 [
573 'sanitize' => false,
574 ]
575 )
576 )
577 )
578 );
579 } elseif ( ! give_verify_minimum_price( 'maximum' ) ) {
580
581 give_set_error(
582 'invalid_donation_maximum',
583 sprintf(
584 /* translators: %s: Maximum donation amount */
585 __( 'This form has a maximum donation amount of %s.', 'give' ),
586 give_currency_filter(
587 give_format_amount(
588 give_get_form_maximum_price( $form_id ),
589 [
590 'sanitize' => false,
591 ]
592 )
593 )
594 )
595 );
596 } // End if().
597
598 return $gateway;
599
600 }
601
602 /**
603 * Donation Form Validate Minimum or Maximum Donation Amount
604 *
605 * @access private
606 * @since 1.3.6
607 * @since 2.1 Added support for give maximum amount.
608 * @since 2.1.3 Added new filter to modify the return value.
609 *
610 * @param string $amount_range Which amount needs to verify? minimum or maximum.
611 *
612 * @return bool
613 */
614 function give_verify_minimum_price( $amount_range = 'minimum' ) {
615
616 $post_data = give_clean( $_POST ); // WPCS: input var ok, sanitization ok, CSRF ok.
617 $form_id = ! empty( $post_data['give-form-id'] ) ? $post_data['give-form-id'] : 0;
618 $amount = ! empty( $post_data['give-amount'] ) ? give_maybe_sanitize_amount( $post_data['give-amount'], [ 'currency' => give_get_currency( $form_id ) ] ) : 0;
619 $price_id = isset( $post_data['give-price-id'] ) ? absint( $post_data['give-price-id'] ) : '';
620
621 $variable_prices = give_has_variable_prices( $form_id );
622 $price_ids = array_map( 'absint', give_get_variable_price_ids( $form_id ) );
623 $verified_stat = false;
624
625 if ( $variable_prices && in_array( $price_id, $price_ids, true ) ) {
626
627 $price_level_amount = give_get_price_option_amount( $form_id, $price_id );
628
629 if ( $price_level_amount == $amount ) {
630 $verified_stat = true;
631 }
632 }
633
634 if ( ! $verified_stat ) {
635 switch ( $amount_range ) {
636 case 'minimum':
637 $verified_stat = ( give_get_form_minimum_price( $form_id ) > $amount ) ? false : true;
638 break;
639 case 'maximum':
640 $verified_stat = ( give_get_form_maximum_price( $form_id ) < $amount ) ? false : true;
641 break;
642 }
643 }
644
645 /**
646 * Filter the verify amount
647 *
648 * @since 2.1.3
649 *
650 * @param bool $verified_stat Was verification passed or not?
651 * @param string $amount_range Type of the amount.
652 * @param integer $form_id Give Donation Form ID.
653 */
654 return apply_filters( 'give_verify_minimum_maximum_price', $verified_stat, $amount_range, $form_id );
655 }
656
657 /**
658 * Donation form validate agree to "Terms and Conditions".
659 *
660 * @access private
661 * @since 1.0
662 *
663 * @return void
664 */
665 function give_donation_form_validate_agree_to_terms() {
666
667 $agree_to_terms = ! empty( $_POST['give_agree_to_terms'] ) ? give_clean( $_POST['give_agree_to_terms'] ) : 0; // WPCS: input var ok, sanitization ok, CSRF ok.
668
669 // Proceed only, if donor agreed to terms.
670 if ( ! $agree_to_terms ) {
671
672 // User did not agree.
673 give_set_error( 'agree_to_terms', apply_filters( 'give_agree_to_terms_text', __( 'You must agree to the terms and conditions.', 'give' ) ) );
674 }
675 }
676
677 /**
678 * Donation Form Required Fields.
679 *
680 * @access private
681 * @since 1.0
682 *
683 * @param int $form_id Donation Form ID.
684 *
685 * @return array
686 */
687 function give_get_required_fields( $form_id ) {
688
689 $posted_data = give_clean( filter_input_array( INPUT_POST ) );
690 $payment_mode = give_get_chosen_gateway( $form_id );
691
692 $required_fields = [
693 'give_email' => [
694 'error_id' => 'invalid_email',
695 'error_message' => __( 'Please enter a valid email address.', 'give' ),
696 ],
697 'give_first' => [
698 'error_id' => 'invalid_first_name',
699 'error_message' => __( 'Please enter your first name.', 'give' ),
700 ],
701 ];
702
703 $name_title_prefix = give_is_name_title_prefix_required( $form_id );
704 if ( $name_title_prefix ) {
705 $required_fields['give_title'] = [
706 'error_id' => 'invalid_title',
707 'error_message' => __( 'Please enter your title.', 'give' ),
708 ];
709 }
710
711 // If credit card fields related actions exists then check for the cc fields validations.
712 if (
713 has_action( "give_{$payment_mode}_cc_form", 'give_get_cc_form' ) ||
714 has_action( 'give_cc_form', 'give_get_cc_form' )
715 ) {
716
717 // Validate card number field for empty check.
718 if (
719 isset( $posted_data['card_number'] ) &&
720 empty( $posted_data['card_number'] )
721 ) {
722 $required_fields['card_number'] = [
723 'error_id' => 'empty_card_number',
724 'error_message' => __( 'Please enter a credit card number.', 'give' ),
725 ];
726 }
727
728 // Validate card cvc field for empty check.
729 if (
730 isset( $posted_data['card_cvc'] ) &&
731 empty( $posted_data['card_cvc'] )
732 ) {
733 $required_fields['card_cvc'] = [
734 'error_id' => 'empty_card_cvc',
735 'error_message' => __( 'Please enter a credit card CVC information.', 'give' ),
736 ];
737 }
738
739 // Validate card name field for empty check.
740 if (
741 (
742 isset( $posted_data['give_validate_stripe_payment_fields'] ) &&
743 '1' === $posted_data['give_validate_stripe_payment_fields'] &&
744 isset( $posted_data['card_name'] ) &&
745 empty( $posted_data['card_name'] )
746 ) ||
747 (
748 ! isset( $posted_data['give_validate_stripe_payment_fields'] ) &&
749 isset( $posted_data['card_name'] ) &&
750 empty( $posted_data['card_name'] )
751 )
752 ) {
753 $required_fields['card_name'] = [
754 'error_id' => 'empty_card_name',
755 'error_message' => __( 'Please enter a name of your credit card account holder.', 'give' ),
756 ];
757 }
758
759 // Validate card expiry field for empty check.
760 if (
761 isset( $posted_data['card_expiry'] ) &&
762 empty( $posted_data['card_expiry'] )
763 ) {
764 $required_fields['card_expiry'] = [
765 'error_id' => 'empty_card_expiry',
766 'error_message' => __( 'Please enter a credit card expiry date.', 'give' ),
767 ];
768 }
769 }
770
771 $require_address = give_require_billing_address( $payment_mode );
772
773 if ( $require_address ) {
774 $required_fields['card_address'] = [
775 'error_id' => 'invalid_card_address',
776 'error_message' => __( 'Please enter your primary billing address.', 'give' ),
777 ];
778 $required_fields['card_zip'] = [
779 'error_id' => 'invalid_zip_code',
780 'error_message' => __( 'Please enter your zip / postal code.', 'give' ),
781 ];
782 $required_fields['card_city'] = [
783 'error_id' => 'invalid_city',
784 'error_message' => __( 'Please enter your billing city.', 'give' ),
785 ];
786 $required_fields['billing_country'] = [
787 'error_id' => 'invalid_country',
788 'error_message' => __( 'Please select your billing country.', 'give' ),
789 ];
790
791 $required_fields['card_state'] = [
792 'error_id' => 'invalid_state',
793 'error_message' => __( 'Please enter billing state / province / County.', 'give' ),
794 ];
795
796 $country = ! empty( $_POST['billing_country'] ) ? give_clean( $_POST['billing_country'] ) : 0; // WPCS: input var ok, sanitization ok, CSRF ok.
797
798 // Check if billing country already exists.
799 if ( $country ) {
800
801 // Check if states is empty or not.
802 if ( array_key_exists( $country, give_states_not_required_country_list() ) ) {
803 // If states is empty remove the required fields of state in billing cart.
804 unset( $required_fields['card_state'] );
805 }
806
807 // Check if city is empty or not.
808 if ( array_key_exists( $country, give_city_not_required_country_list() ) ) {
809 // If states is empty remove the required fields of city in billing cart.
810 unset( $required_fields['card_city'] );
811 }
812
813 // Check if country is without post codes.
814 if ( array_key_exists( $country, give_get_country_list_without_postcodes() ) ) {
815 // If country is on the list, zip code is not required.
816 unset( $required_fields['card_zip'] );
817 }
818 }
819 } // End if().
820
821 if ( give_is_company_field_enabled( $form_id ) ) {
822 $form_option = give_get_meta( $form_id, '_give_company_field', true );
823 $global_setting = give_get_option( 'company_field' );
824
825 $is_company_field_required = false;
826
827 if ( ! empty( $form_option ) && give_is_setting_enabled( $form_option, [ 'required' ] ) ) {
828 $is_company_field_required = true;
829
830 } elseif ( 'global' === $form_option && give_is_setting_enabled( $global_setting, [ 'required' ] ) ) {
831 $is_company_field_required = true;
832
833 } elseif ( empty( $form_option ) && give_is_setting_enabled( $global_setting, [ 'required' ] ) ) {
834 $is_company_field_required = true;
835
836 }
837
838 if ( $is_company_field_required ) {
839 $required_fields['give_company_name'] = [
840 'error_id' => 'invalid_company',
841 'error_message' => __( 'Please enter Company Name.', 'give' ),
842 ];
843 }
844 }
845
846 if ( give_is_last_name_required( $form_id ) ) {
847 $required_fields['give_last'] = [
848 'error_id' => 'invalid_last_name',
849 'error_message' => __( 'Please enter your last name.', 'give' ),
850 ];
851 }
852
853 /**
854 * Filters the donation form required field.
855 *
856 * @since 1.7
857 */
858 $required_fields = apply_filters( 'give_donation_form_required_fields', $required_fields, $form_id );
859
860 return $required_fields;
861
862 }
863
864 /**
865 * Check if the Billing Address is required
866 *
867 * @since 1.0.1
868 *
869 * @param string $payment_mode Payment Mode.
870 *
871 * @return bool
872 */
873 function give_require_billing_address( $payment_mode ) {
874
875 $return = false;
876 $billing_country = ! empty( $_POST['billing_country'] ) ? give_clean( $_POST['billing_country'] ) : 0; // WPCS: input var ok, sanitization ok, CSRF ok.
877
878 if ( $billing_country || did_action( "give_{$payment_mode}_cc_form" ) || did_action( 'give_cc_form' ) ) {
879 $return = true;
880 }
881
882 // Let payment gateways and other extensions determine if address fields should be required.
883 return apply_filters( 'give_require_billing_address', $return );
884
885 }
886
887 /**
888 * Donation Form Validate Logged In User.
889 *
890 * @access private
891 * @since 4.16.7.2 Sanitize first and last name values when falling back to stored user data.
892 * @since 1.0
893 *
894 * @return array
895 */
896 function give_donation_form_validate_logged_in_user() {
897
898 $post_data = give_clean( $_POST ); // WPCS: input var ok, sanitization ok, CSRF ok.
899 $user_id = get_current_user_id();
900 $form_id = ! empty( $post_data['give-form-id'] ) ? $post_data['give-form-id'] : 0;
901
902 // Start empty array to collect valid user data.
903 $valid_user_data = [
904
905 // Assume there will be errors.
906 'user_id' => - 1,
907 ];
908
909 // Proceed only, if valid $user_id found.
910 if ( $user_id > 0 ) {
911
912 // Get the logged in user data.
913 $user_data = get_userdata( $user_id );
914
915 // Validate Required Form Fields.
916 give_validate_required_form_fields( $form_id );
917
918 // Verify data.
919 if ( is_object( $user_data ) && $user_data->ID > 0 ) {
920 // Collected logged in user data.
921 $valid_user_data = [
922 'user_id' => $user_id,
923 'user_email' => ! empty( $post_data['give_email'] )
924 ? sanitize_email( $post_data['give_email'] )
925 : $user_data->user_email,
926 'user_first' => ! empty( $post_data['give_first'] )
927 ? give_clean( $post_data['give_first'] )
928 : give_clean( $user_data->first_name ),
929 'user_last' => ! empty( $post_data['give_last'] )
930 ? give_clean( $post_data['give_last'] )
931 : give_clean( $user_data->last_name ),
932 ];
933
934 // Validate essential form fields.
935 give_donation_form_validate_name_fields( $post_data );
936
937 give_check_logged_in_user_for_existing_email( $valid_user_data );
938
939 if ( ! is_email( $valid_user_data['user_email'] ) ) {
940 give_set_error( 'email_invalid', esc_html__( 'Invalid email.', 'give' ) );
941 }
942 } else {
943
944 // Set invalid user information error.
945 give_set_error( 'invalid_user', esc_html__( 'The user information is invalid.', 'give' ) );
946 }
947 }
948
949 // Return user data.
950 return $valid_user_data;
951 }
952
953 /**
954 * Donate Form Validate New User
955 *
956 * @access private
957 * @since 4.16.6 Flag data as coming from the checkout registration flow.
958 * @since 1.0
959 *
960 * @return array
961 */
962 function give_donation_form_validate_new_user() {
963 // Default user data.
964 $auto_generated_password = wp_generate_password();
965 $default_user_data = [
966 'give-form-id' => '',
967 'user_id' => - 1, // Assume there will be errors.
968 'user_first' => '',
969 'user_last' => '',
970 'give_user_login' => false,
971 'give_email' => false,
972 'give_user_pass' => $auto_generated_password,
973 'give_user_pass_confirm' => $auto_generated_password,
974 ];
975
976 // Get data.
977 $post_data = give_clean( $_POST ); // WPCS: input var ok, sanitization ok, CSRF ok.
978 $user_data = wp_parse_args( $post_data, $default_user_data );
979
980 $form_id = absint( $user_data['give-form-id'] );
981 $nonce = ! empty( $post_data['give-form-user-register-hash'] ) ? $post_data['give-form-user-register-hash'] : '';
982
983 // Validate user creation nonce.
984 if ( ! wp_verify_nonce( $nonce, "give_form_create_user_nonce_{$form_id}" ) ) {
985 give_set_error( 'invalid_nonce', __( 'We\'re unable to recognize your session. Please refresh the screen to try again; otherwise contact your website administrator for assistance.', 'give' ) );
986 }
987
988 $registering_new_user = false;
989
990 give_donation_form_validate_name_fields( $user_data );
991
992 // Start an empty array to collect valid user data.
993 $valid_user_data = [
994
995 // Assume there will be errors.
996 'user_id' => - 1,
997
998 // Get first name.
999 'user_first' => $user_data['give_first'],
1000
1001 // Get last name.
1002 'user_last' => $user_data['give_last'],
1003
1004 // Get Password.
1005 'user_pass' => $user_data['give_user_pass'],
1006 ];
1007
1008 // Validate Required Form Fields.
1009 give_validate_required_form_fields( $form_id );
1010
1011 // Set Email as Username.
1012 $valid_user_data['user_login'] = $user_data['give_email'];
1013
1014 // Check if we have an email to verify.
1015 if ( give_validate_user_email( $user_data['give_email'], $registering_new_user ) ) {
1016 $valid_user_data['user_email'] = $user_data['give_email'];
1017 }
1018
1019 // Mark this data as coming from the nonce-verified checkout flow.
1020 $valid_user_data['give_donation_checkout_registration'] = true;
1021
1022 return $valid_user_data;
1023 }
1024
1025 /**
1026 * Donation Form Validate User Login
1027 *
1028 * @access private
1029 * @since 4.16.7 Authenticate via wp_authenticate() and return a single generic error.
1030 * @since 1.0
1031 *
1032 * @return array
1033 */
1034 function give_donation_form_validate_user_login() {
1035
1036 $post_data = give_clean( $_POST ); // WPCS: input var ok, sanitization ok, CSRF ok.
1037
1038 // Start an array to collect valid user data.
1039 $valid_user_data = [
1040
1041 // Assume there will be errors.
1042 'user_id' => - 1,
1043 ];
1044
1045 // Bailout, if Username is empty.
1046 if ( empty( $post_data['give_user_login'] ) ) {
1047 give_set_error( 'must_log_in', __( 'Please enter your username or email to log in.', 'give' ) );
1048
1049 return $valid_user_data;
1050 }
1051
1052 $give_user_login = strip_tags( $post_data['give_user_login'] );
1053
1054 // Bailout, if Password is empty.
1055 if ( empty( $post_data['give_user_pass'] ) ) {
1056 give_set_error( 'password_empty', __( 'Enter a password.', 'give' ) );
1057 return $valid_user_data;
1058 }
1059
1060 // Authenticate through WordPress's login machinery so its authentication
1061 // hooks, password checks, and failed-login actions all apply.
1062 $user_data = wp_authenticate( $give_user_login, $post_data['give_user_pass'] );
1063
1064 if ( is_wp_error( $user_data ) ) {
1065
1066 $core_auth_error_codes = [
1067 'incorrect_password',
1068 'invalid_username',
1069 'invalid_email',
1070 'empty_username',
1071 'empty_password',
1072 ];
1073
1074 if ( in_array( $user_data->get_error_code(), $core_auth_error_codes, true ) ) {
1075 // A single generic message for an unknown login and a wrong password.
1076 $error_message = __( 'The login/password does not match or is incorrect.', 'give' );
1077 } else {
1078 // Any other error comes from an authentication hook; surface its message.
1079 $error_message = wp_strip_all_tags( $user_data->get_error_message() );
1080
1081 if ( '' === $error_message ) {
1082 $error_message = __( 'The login/password does not match or is incorrect.', 'give' );
1083 }
1084 }
1085
1086 give_set_error( 'invalid_credentials', $error_message );
1087
1088 return $valid_user_data;
1089 }
1090
1091 // Repopulate the valid user data array.
1092 $valid_user_data = [
1093 'user_id' => $user_data->ID,
1094 'user_login' => $user_data->user_login,
1095 'user_email' => $user_data->user_email,
1096 'user_first' => $user_data->first_name,
1097 'user_last' => $user_data->last_name,
1098 'user_pass' => $post_data['give_user_pass'],
1099 ];
1100
1101 return $valid_user_data;
1102 }
1103
1104 /**
1105 * Donation Form Validate Guest User
1106 *
1107 * @access private
1108 * @since 1.0
1109 *
1110 * @return array
1111 */
1112 function give_donation_form_validate_guest_user() {
1113
1114 $post_data = give_clean( $_POST ); // WPCS: input var ok, sanitization ok, CSRF ok.
1115 $form_id = ! empty( $post_data['give-form-id'] ) ? $post_data['give-form-id'] : 0;
1116
1117 // Start an array to collect valid user data.
1118 $valid_user_data = [
1119 // Set a default id for guests.
1120 'user_id' => 0,
1121 ];
1122
1123 // Validate name fields.
1124 give_donation_form_validate_name_fields( $post_data );
1125
1126 // Validate Required Form Fields.
1127 give_validate_required_form_fields( $form_id );
1128
1129 // Get the guest email.
1130 $guest_email = ! empty( $post_data['give_email'] ) ? $post_data['give_email'] : false;
1131
1132 // Check email.
1133 if ( $guest_email && strlen( $guest_email ) > 0 ) {
1134
1135 // Validate email.
1136 if ( ! is_email( $guest_email ) ) {
1137
1138 // Invalid email.
1139 give_set_error( 'email_invalid', __( 'Invalid email.', 'give' ) );
1140
1141 } else {
1142
1143 // All is good to go.
1144 $valid_user_data['user_email'] = $guest_email;
1145
1146 // Get user_id from donor if exist.
1147 $donor = new Give_Donor( $guest_email );
1148
1149 if ( $donor->id ) {
1150 $donor_email_index = array_search(
1151 strtolower( $guest_email ),
1152 array_map( 'strtolower', $donor->emails ),
1153 true
1154 );
1155
1156 $valid_user_data['user_id'] = $donor->user_id;
1157
1158 // Set email to original format.
1159 // @see https://github.com/impress-org/give/issues/4025
1160 $valid_user_data['user_email'] = $donor->emails[ $donor_email_index ];
1161 }
1162 }
1163 } else {
1164 // No email.
1165 give_set_error( 'email_empty', __( 'Enter an email.', 'give' ) );
1166 }
1167
1168 return $valid_user_data;
1169 }
1170
1171 /**
1172 * Register And Login New User
1173 *
1174 * @param array $user_data User Data.
1175 *
1176 * @access private
1177 * @since 1.0
1178 *
1179 * @return integer
1180 */
1181 function give_register_and_login_new_user( $user_data = [] ) {
1182 // Verify the array.
1183 if ( empty( $user_data ) ) {
1184 return - 1;
1185 }
1186
1187 if ( give_get_errors() ) {
1188 return - 1;
1189 }
1190
1191 $user_args = apply_filters(
1192 'give_insert_user_args',
1193 [
1194 'user_login' => isset( $user_data['user_login'] ) ? $user_data['user_login'] : '',
1195 'user_pass' => isset( $user_data['user_pass'] ) ? $user_data['user_pass'] : '',
1196 'user_email' => isset( $user_data['user_email'] ) ? $user_data['user_email'] : '',
1197 'first_name' => isset( $user_data['user_first'] ) ? $user_data['user_first'] : '',
1198 'last_name' => isset( $user_data['user_last'] ) ? $user_data['user_last'] : '',
1199 'user_registered' => date( 'Y-m-d H:i:s' ),
1200 'role' => give_get_option( 'donor_default_user_role', 'give_donor' ),
1201 ],
1202 $user_data
1203 );
1204
1205 // Insert new user.
1206 $user_id = wp_insert_user( $user_args );
1207
1208 // Validate inserted user.
1209 if ( is_wp_error( $user_id ) ) {
1210 return - 1;
1211 }
1212
1213 // Allow themes and plugins to filter the user data.
1214 $user_data = apply_filters( 'give_insert_user_data', $user_data, $user_args );
1215
1216 /**
1217 * Fires after inserting user.
1218 *
1219 * @since 1.0
1220 *
1221 * @param int $user_id User id.
1222 * @param array $user_data Array containing user data.
1223 */
1224 do_action( 'give_insert_user', $user_id, $user_data );
1225
1226 /**
1227 * Filter allow user to alter if user when to login or not when user is register for the first time.
1228 *
1229 * @since 1.8.13
1230 *
1231 * return bool True if login with registration and False if only want to register.
1232 */
1233 if ( true === (bool) apply_filters( 'give_log_user_in_on_register', true ) ) {
1234 // Login new user.
1235 give_log_user_in( $user_id, $user_data['user_login'], $user_data['user_pass'] );
1236 }
1237
1238 // Return user id.
1239 return $user_id;
1240 }
1241
1242 /**
1243 * Get Donation Form User
1244 *
1245 * @since 1.0
1246 * @since 2.17.1 Do not run validation check for ajax request expect donation validation ajax request.
1247 *
1248 * @param array $valid_data Valid Data.
1249 *
1250 * @access private
1251 * @return array|bool
1252 */
1253 function give_get_donation_form_user( $valid_data = [] ) {
1254 // Initialize user.
1255 $user = false;
1256 $post_data = give_clean($_POST); // WPCS: input var ok, sanitization ok, CSRF ok.
1257 $is_validating_donation_form_on_ajax = ! empty($_POST['give_ajax']) ? $post_data['give_ajax'] : 0; // WPCS: input var ok, sanitization ok, CSRF ok.
1258
1259 if ( $is_validating_donation_form_on_ajax ) {
1260 // Do not create or login the user during the ajax submission (check for errors only).
1261 return true;
1262 } elseif ( is_user_logged_in() ) {
1263 // Set the valid user as the logged in collected data.
1264 $user = $valid_data['logged_in_user'];
1265 } elseif ( true === $valid_data['need_new_user'] || true === $valid_data['need_user_login'] ) {
1266 // New user registration.
1267 if ( true === $valid_data['need_new_user'] ) {
1268 // Set user.
1269 $user = $valid_data['new_user_data'];
1270
1271 // Register and login new user.
1272 $user['user_id'] = give_register_and_login_new_user($user);
1273 } elseif ( true === $valid_data['need_user_login'] ) {
1274 /**
1275 * The login form is now processed in the give_process_donation_login() function.
1276 * This is still here for backwards compatibility.
1277 * This also allows the old login process to still work if a user removes the checkout login submit button.
1278 *
1279 * This also ensures that the donor is logged in correctly if they click "Donation" instead of submitting the login form, meaning the donor is logged in during the donation process.
1280 */
1281 $user = $valid_data['login_user_data'];
1282
1283 // Login user.
1284 give_log_user_in( $user['user_id'], $user['user_login'], $user['user_pass'] );
1285 }
1286 } // End if().
1287
1288 // Check guest checkout.
1289 if ( false === $user && false === give_logged_in_only( $post_data['give-form-id'] ) ) {
1290
1291 // Set user.
1292 $user = $valid_data['guest_user_data'];
1293 }
1294
1295 // Verify we have an user.
1296 if ( false === $user || empty( $user ) ) {
1297 return false;
1298 }
1299
1300 // Get user first name.
1301 if ( ! isset( $user['user_first'] ) || strlen( trim( $user['user_first'] ) ) < 1 ) {
1302 $user['user_first'] = isset( $post_data['give_first'] ) ? strip_tags( trim( $post_data['give_first'] ) ) : '';
1303 }
1304
1305 // Get user last name.
1306 if ( ! isset( $user['user_last'] ) || strlen( trim( $user['user_last'] ) ) < 1 ) {
1307 $user['user_last'] = isset( $post_data['give_last'] ) ? strip_tags( trim( $post_data['give_last'] ) ) : '';
1308 }
1309
1310 // Add Title Prefix to user information.
1311 if ( empty( $user['user_title'] ) || strlen( trim( $user['user_title'] ) ) < 1 ) {
1312 $user['user_title'] = ! empty( $post_data['give_title'] ) ? strip_tags( trim( $post_data['give_title'] ) ) : '';
1313 }
1314
1315 // Get the user's billing address details.
1316 $user['address'] = [];
1317 $user['address']['line1'] = ! empty( $post_data['card_address'] ) ? $post_data['card_address'] : false;
1318 $user['address']['line2'] = ! empty( $post_data['card_address_2'] ) ? $post_data['card_address_2'] : false;
1319 $user['address']['city'] = ! empty( $post_data['card_city'] ) ? $post_data['card_city'] : false;
1320 $user['address']['state'] = ! empty( $post_data['card_state'] ) ? $post_data['card_state'] : false;
1321 $user['address']['zip'] = ! empty( $post_data['card_zip'] ) ? $post_data['card_zip'] : false;
1322 $user['address']['country'] = ! empty( $post_data['billing_country'] ) ? $post_data['billing_country'] : false;
1323
1324 if ( empty( $user['address']['country'] ) ) {
1325 $user['address'] = false;
1326 } // End if().
1327
1328 // Return valid user.
1329 return $user;
1330 }
1331
1332 /**
1333 * Validates the credit card info.
1334 *
1335 * @access private
1336 * @since 1.0
1337 *
1338 * @return array
1339 */
1340 function give_donation_form_validate_cc() {
1341
1342 $card_data = give_get_donation_cc_info();
1343
1344 // Validate the card zip.
1345 if ( ! empty( $card_data['card_zip'] ) ) {
1346 if ( ! give_donation_form_validate_cc_zip( $card_data['card_zip'], $card_data['card_country'] ) ) {
1347 give_set_error( 'invalid_cc_zip', __( 'The zip / postal code you entered for your billing address is invalid.', 'give' ) );
1348 }
1349 }
1350
1351 // Ensure no spaces.
1352 if ( ! empty( $card_data['card_number'] ) ) {
1353 $card_data['card_number'] = str_replace( '+', '', $card_data['card_number'] ); // no "+" signs.
1354 $card_data['card_number'] = str_replace( ' ', '', $card_data['card_number'] ); // No spaces.
1355 }
1356
1357 // This should validate card numbers at some point too.
1358 return $card_data;
1359 }
1360
1361 /**
1362 * Get credit card info.
1363 *
1364 * @access private
1365 * @since 1.0
1366 *
1367 * @return array
1368 */
1369 function give_get_donation_cc_info() {
1370
1371 // Sanitize the values submitted with donation form.
1372 $post_data = give_clean( $_POST ); // WPCS: input var ok, sanitization ok, CSRF ok.
1373
1374 $cc_info = [];
1375 $cc_info['card_name'] = ! empty( $post_data['card_name'] ) ? $post_data['card_name'] : '';
1376 $cc_info['card_number'] = ! empty( $post_data['card_number'] ) ? $post_data['card_number'] : '';
1377 $cc_info['card_cvc'] = ! empty( $post_data['card_cvc'] ) ? $post_data['card_cvc'] : '';
1378 $cc_info['card_exp_month'] = ! empty( $post_data['card_exp_month'] ) ? $post_data['card_exp_month'] : '';
1379 $cc_info['card_exp_year'] = ! empty( $post_data['card_exp_year'] ) ? $post_data['card_exp_year'] : '';
1380 $cc_info['card_address'] = ! empty( $post_data['card_address'] ) ? $post_data['card_address'] : '';
1381 $cc_info['card_address_2'] = ! empty( $post_data['card_address_2'] ) ? $post_data['card_address_2'] : '';
1382 $cc_info['card_city'] = ! empty( $post_data['card_city'] ) ? $post_data['card_city'] : '';
1383 $cc_info['card_state'] = ! empty( $post_data['card_state'] ) ? $post_data['card_state'] : '';
1384 $cc_info['card_country'] = ! empty( $post_data['billing_country'] ) ? $post_data['billing_country'] : '';
1385 $cc_info['card_zip'] = ! empty( $post_data['card_zip'] ) ? $post_data['card_zip'] : '';
1386
1387 // Return cc info.
1388 return $cc_info;
1389 }
1390
1391 /**
1392 * Validate zip code based on country code
1393 *
1394 * @since 1.0
1395 *
1396 * @param int $zip ZIP Code.
1397 * @param string $country_code Country Code.
1398 *
1399 * @return bool|mixed
1400 */
1401 function give_donation_form_validate_cc_zip( $zip = 0, $country_code = '' ) {
1402 $ret = false;
1403
1404 if ( empty( $zip ) || empty( $country_code ) ) {
1405 return $ret;
1406 }
1407
1408 $country_code = strtoupper( $country_code );
1409
1410 $zip_regex = [
1411 'AD' => 'AD\d{3}',
1412 'AM' => '(37)?\d{4}',
1413 'AR' => '^([A-Z]{1}\d{4}[A-Z]{3}|[A-Z]{1}\d{4}|\d{4})$',
1414 'AS' => '96799',
1415 'AT' => '\d{4}',
1416 'AU' => '^(0[289][0-9]{2})|([1345689][0-9]{3})|(2[0-8][0-9]{2})|(290[0-9])|(291[0-4])|(7[0-4][0-9]{2})|(7[8-9][0-9]{2})$',
1417 'AX' => '22\d{3}',
1418 'AZ' => '\d{4}',
1419 'BA' => '\d{5}',
1420 'BB' => '(BB\d{5})?',
1421 'BD' => '\d{4}',
1422 'BE' => '^[1-9]{1}[0-9]{3}$',
1423 'BG' => '\d{4}',
1424 'BH' => '((1[0-2]|[2-9])\d{2})?',
1425 'BM' => '[A-Z]{2}[ ]?[A-Z0-9]{2}',
1426 'BN' => '[A-Z]{2}[ ]?\d{4}',
1427 'BR' => '\d{5}[\-]?\d{3}',
1428 'BY' => '\d{6}',
1429 'CA' => '^[ABCEGHJKLMNPRSTVXY]{1}\d{1}[A-Z]{1} *\d{1}[A-Z]{1}\d{1}$',
1430 'CC' => '6799',
1431 'CH' => '^[1-9][0-9][0-9][0-9]$',
1432 'CK' => '\d{4}',
1433 'CL' => '\d{7}',
1434 'CN' => '\d{6}',
1435 'CR' => '\d{4,5}|\d{3}-\d{4}',
1436 'CS' => '\d{5}',
1437 'CV' => '\d{4}',
1438 'CX' => '6798',
1439 'CY' => '\d{4}',
1440 'CZ' => '\d{3}[ ]?\d{2}',
1441 'DE' => '\b((?:0[1-46-9]\d{3})|(?:[1-357-9]\d{4})|(?:[4][0-24-9]\d{3})|(?:[6][013-9]\d{3}))\b',
1442 'DK' => '^([D-d][K-k])?( |-)?[1-9]{1}[0-9]{3}$',
1443 'DO' => '\d{5}',
1444 'DZ' => '\d{5}',
1445 'EC' => '([A-Z]\d{4}[A-Z]|(?:[A-Z]{2})?\d{6})?',
1446 'EE' => '\d{5}',
1447 'EG' => '\d{5}',
1448 'ES' => '^([1-9]{2}|[0-9][1-9]|[1-9][0-9])[0-9]{3}$',
1449 'ET' => '\d{4}',
1450 'FI' => '\d{5}',
1451 'FK' => 'FIQQ 1ZZ',
1452 'FM' => '(9694[1-4])([ \-]\d{4})?',
1453 'FO' => '\d{3}',
1454 'FR' => '^(F-)?((2[A|B])|[0-9]{2})[0-9]{3}$',
1455 'GE' => '\d{4}',
1456 'GF' => '9[78]3\d{2}',
1457 'GL' => '39\d{2}',
1458 'GN' => '\d{3}',
1459 'GP' => '9[78][01]\d{2}',
1460 'GR' => '\d{3}[ ]?\d{2}',
1461 'GS' => 'SIQQ 1ZZ',
1462 'GT' => '\d{5}',
1463 'GU' => '969[123]\d([ \-]\d{4})?',
1464 'GW' => '\d{4}',
1465 'HM' => '\d{4}',
1466 'HN' => '(?:\d{5})?',
1467 'HR' => '\d{5}',
1468 'HT' => '\d{4}',
1469 'HU' => '\d{4}',
1470 'ID' => '\d{5}',
1471 'IE' => '((D|DUBLIN)?([1-9]|6[wW]|1[0-8]|2[024]))?',
1472 'IL' => '\d{5}',
1473 'IN' => '^[1-9][0-9][0-9][0-9][0-9][0-9]$', // India.
1474 'IO' => 'BBND 1ZZ',
1475 'IQ' => '\d{5}',
1476 'IS' => '\d{3}',
1477 'IT' => '^(V-|I-)?[0-9]{5}$',
1478 'JO' => '\d{5}',
1479 'JP' => '\d{3}-\d{4}',
1480 'KE' => '\d{5}',
1481 'KG' => '\d{6}',
1482 'KH' => '\d{5}',
1483 'KR' => '\d{5}',
1484 'KW' => '\d{5}',
1485 'KZ' => '\d{6}',
1486 'LA' => '\d{5}',
1487 'LB' => '(\d{4}([ ]?\d{4})?)?',
1488 'LI' => '(948[5-9])|(949[0-7])',
1489 'LK' => '\d{5}',
1490 'LR' => '\d{4}',
1491 'LS' => '\d{3}',
1492 'LT' => '\d{5}',
1493 'LU' => '\d{4}',
1494 'LV' => '\d{4}',
1495 'MA' => '\d{5}',
1496 'MC' => '980\d{2}',
1497 'MD' => '\d{4}',
1498 'ME' => '8\d{4}',
1499 'MG' => '\d{3}',
1500 'MH' => '969[67]\d([ \-]\d{4})?',
1501 'MK' => '\d{4}',
1502 'MN' => '\d{6}',
1503 'MP' => '9695[012]([ \-]\d{4})?',
1504 'MQ' => '9[78]2\d{2}',
1505 'MT' => '[A-Z]{3}[ ]?\d{2,4}',
1506 'MU' => '(\d{3}[A-Z]{2}\d{3})?',
1507 'MV' => '\d{5}',
1508 'MX' => '\d{5}',
1509 'MY' => '\d{5}',
1510 'NC' => '988\d{2}',
1511 'NE' => '\d{4}',
1512 'NF' => '2899',
1513 'NG' => '(\d{6})?',
1514 'NI' => '((\d{4}-)?\d{3}-\d{3}(-\d{1})?)?',
1515 'NL' => '^[1-9][0-9]{3}\s?([a-zA-Z]{2})?$',
1516 'NO' => '\d{4}',
1517 'NP' => '\d{5}',
1518 'NZ' => '\d{4}',
1519 'OM' => '(PC )?\d{3}',
1520 'PF' => '987\d{2}',
1521 'PG' => '\d{3}',
1522 'PH' => '\d{4}',
1523 'PK' => '\d{5}',
1524 'PL' => '\d{2}-\d{3}',
1525 'PM' => '9[78]5\d{2}',
1526 'PN' => 'PCRN 1ZZ',
1527 'PR' => '00[679]\d{2}([ \-]\d{4})?',
1528 'PT' => '\d{4}([\-]\d{3})?',
1529 'PW' => '96940',
1530 'PY' => '\d{4}',
1531 'RE' => '9[78]4\d{2}',
1532 'RO' => '\d{6}',
1533 'RS' => '\d{5}',
1534 'RU' => '\d{6}',
1535 'SA' => '\d{5}',
1536 'SE' => '^(s-|S-){0,1}[0-9]{3}\s?[0-9]{2}$',
1537 'SG' => '\d{6}',
1538 'SH' => '(ASCN|STHL) 1ZZ',
1539 'SI' => '\d{4}',
1540 'SJ' => '\d{4}',
1541 'SK' => '\d{3}[ ]?\d{2}',
1542 'SM' => '4789\d',
1543 'SN' => '\d{5}',
1544 'SO' => '\d{5}',
1545 'SZ' => '[HLMS]\d{3}',
1546 'TC' => 'TKCA 1ZZ',
1547 'TH' => '\d{5}',
1548 'TJ' => '\d{6}',
1549 'TM' => '\d{6}',
1550 'TN' => '\d{4}',
1551 'TR' => '\d{5}',
1552 'TW' => '\d{3}(\d{2})?',
1553 'UA' => '\d{5}',
1554 'UK' => '^(GIR|[A-Z]\d[A-Z\d]??|[A-Z]{2}\d[A-Z\d]??)[ ]??(\d[A-Z]{2})$',
1555 'US' => '^\d{5}([\-]?\d{4})?$',
1556 'UY' => '\d{5}',
1557 'UZ' => '\d{6}',
1558 'VA' => '00120',
1559 'VE' => '\d{4}',
1560 'VI' => '008(([0-4]\d)|(5[01]))([ \-]\d{4})?',
1561 'WF' => '986\d{2}',
1562 'YT' => '976\d{2}',
1563 'YU' => '\d{5}',
1564 'ZA' => '\d{4}',
1565 'ZM' => '\d{5}',
1566 ];
1567
1568 if ( ! isset( $zip_regex[ $country_code ] ) || preg_match( '/' . $zip_regex[ $country_code ] . '/i', $zip ) ) {
1569 $ret = true;
1570 }
1571
1572 return apply_filters( 'give_is_zip_valid', $ret, $zip, $country_code );
1573 }
1574
1575 /**
1576 * Validate donation amount and auto set correct donation level id on basis of amount.
1577 *
1578 * Note: If amount does not match to donation level amount then level id will be auto select to first match level id on basis of amount.
1579 *
1580 * @param array $valid_data List of Valid Data.
1581 *
1582 * @return bool
1583 */
1584 function give_validate_donation_amount( $valid_data ) {
1585
1586 $post_data = give_clean( $_POST ); // WPCS: input var ok, sanitization ok, CSRF ok.
1587
1588 /* @var Give_Donate_Form $form */
1589 $form = new Give_Donate_Form( $post_data['give-form-id'] );
1590
1591 // Get the form currency.
1592 $form_currency = give_get_currency( $post_data['give-form-id'] );
1593
1594 $donation_level_matched = false;
1595
1596 if ( $form->is_set_type_donation_form() ) {
1597
1598 // Sanitize donation amount.
1599 $post_data['give-amount'] = give_maybe_sanitize_amount( $post_data['give-amount'], [ 'currency' => $form_currency ] );
1600
1601 // Backward compatibility.
1602 if ( $form->is_custom_price( $post_data['give-amount'] ) ) {
1603 $post_data['give-price-id'] = 'custom';
1604 }
1605
1606 $donation_level_matched = true;
1607
1608 } elseif ( $form->is_multi_type_donation_form() ) {
1609
1610 $variable_prices = $form->get_prices();
1611
1612 // Bailout.
1613 if ( ! $variable_prices ) {
1614 return false;
1615 }
1616
1617 // Sanitize donation amount.
1618 $post_data['give-amount'] = give_maybe_sanitize_amount( $post_data['give-amount'], [ 'currency' => $form_currency ] );
1619 $variable_price_option_amount = give_maybe_sanitize_amount( give_get_price_option_amount( $post_data['give-form-id'], $post_data['give-price-id'] ), [ 'currency' => $form_currency ] );
1620 $new_price_id = '';
1621
1622 if ( $post_data['give-amount'] === $variable_price_option_amount ) {
1623 return true;
1624 }
1625
1626 if ( $form->is_custom_price( $post_data['give-amount'] ) ) {
1627 $new_price_id = 'custom';
1628 } else {
1629
1630 // Find correct donation level from all donation levels.
1631 foreach ( $variable_prices as $variable_price ) {
1632
1633 // Sanitize level amount.
1634 $variable_price['_give_amount'] = give_maybe_sanitize_amount( $variable_price['_give_amount'] );
1635
1636 // Set first match donation level ID.
1637 if ( $post_data['give-amount'] === $variable_price['_give_amount'] ) {
1638 $new_price_id = $variable_price['_give_id']['level_id'];
1639 break;
1640 }
1641 }
1642 }
1643
1644 // If donation amount is not find in donation levels then check if form has custom donation feature enable or not.
1645 // If yes then set price id to custom if amount is greater then custom minimum amount (if any).
1646 if ( $post_data['give-price-id'] === $new_price_id ) {
1647 $donation_level_matched = true;
1648 }
1649 } // End if().
1650
1651 if ( ! $donation_level_matched ) {
1652 give_set_error(
1653 'invalid_donation_amount',
1654 sprintf(
1655 /* translators: %s: invalid donation amount */
1656 __( 'Donation amount %s is invalid.', 'give' ),
1657 give_currency_filter(
1658 give_format_amount( $post_data['give-amount'], [ 'sanitize' => false ] )
1659 )
1660 )
1661 );
1662 }
1663 }
1664
1665 add_action( 'give_checkout_error_checks', 'give_validate_donation_amount', 10, 1 );
1666
1667 /**
1668 * Validate Required Form Fields.
1669 *
1670 * @param int $form_id Form ID.
1671 *
1672 * @since 2.0
1673 */
1674 function give_validate_required_form_fields( $form_id ) {
1675 // Sanitize values submitted with donation form.
1676 $post_data = give_clean( $_POST ); // WPCS: input var ok, sanitization ok, CSRF ok.
1677 $requiredFormFields = give_get_required_fields( $form_id );
1678
1679 // Loop through required fields and show error messages.
1680 foreach ( $requiredFormFields as $field_name => $value ) {
1681 if ( empty( $post_data[ $field_name ] ) ) {
1682 give_set_error( $value['error_id'], $value['error_message'] );
1683 }
1684 }
1685 }
1686
1687 /**
1688 * Validates and checks if name fields are valid or not.
1689 *
1690 * @param array $post_data List of post data.
1691 *
1692 * @since 4.16.7.2 Validate last name field even when omitted.
1693 * @since 3.16.5 Check if "give_title" is set to prevent PHP warnings
1694 * @since 3.16.4 Add additional validation for company name field
1695 * @since 3.16.3 Add additional validations for name title prefix field
1696 * @since 2.1
1697 *
1698 * @return void
1699 */
1700 function give_donation_form_validate_name_fields( $post_data ) {
1701
1702 $formId = absint( $post_data['give-form-id'] );
1703
1704 if (!give_is_name_title_prefix_enabled($formId) && isset($post_data['give_title'])) {
1705 give_set_error( 'disabled_name_title', esc_html__( 'The name title prefix field is not enabled.', 'give' ) );
1706 }
1707
1708 if (!give_is_company_field_enabled($formId) && isset($post_data['give_company_name'])) {
1709 give_set_error( 'disabled_company', esc_html__( 'The company field is not enabled.', 'give' ) );
1710 }
1711
1712 if (give_is_name_title_prefix_enabled($formId) && isset($post_data['give_title']) && !in_array($post_data['give_title'], array_values(give_get_name_title_prefixes($formId)))) {
1713 give_set_error( 'invalid_name_title', esc_html__( 'The name title prefix field is not valid.', 'give' ) );
1714 }
1715
1716 $is_alpha_first_name = ( ! is_email( $post_data['give_first'] ) && ! preg_match( '~[0-9]~', $post_data['give_first'] ) );
1717
1718 $lastName = isset( $post_data['give_last'] ) ? $post_data['give_last'] : '';
1719 $is_alpha_last_name = ( ! is_email( $lastName ) && ! preg_match( '~[0-9]~', $lastName ) );
1720
1721 $is_alpha_title = ( isset($post_data['give_title']) && ! is_email( $post_data['give_title'] ) && ! preg_match( '~[0-9]~', $post_data['give_title'] ) );
1722
1723 if ( ! $is_alpha_first_name || ( ! empty( $lastName ) && ! $is_alpha_last_name ) || ( ! empty( $post_data['give_title'] ) && ! $is_alpha_title ) ) {
1724 give_set_error( 'invalid_name', esc_html__( 'The First Name and Last Name fields cannot contain an email address or numbers.', 'give' ) );
1725 }
1726
1727 if ( give_is_last_name_required( $formId ) && empty( $lastName ) ) {
1728 give_set_error( 'invalid_last_name', esc_html__( 'Please enter your last name.', 'give' ) );
1729 }
1730 }
1731