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