Completed
Pull Request — master (#664)
by Devin
19:01
created

process-purchase.php ➔ give_check_purchase_email()   C

Complexity

Conditions 11
Paths 17

Size

Total Lines 43
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 132

Importance

Changes 0
Metric Value
cc 11
eloc 20
nc 17
nop 2
dl 0
loc 43
ccs 0
cts 25
cp 0
crap 132
rs 5.2653
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 26 and the first side effect is on line 14.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/**
3
 * Process Purchase
4
 *
5
 * @package     Give
6
 * @subpackage  Functions
7
 * @copyright   Copyright (c) 2016, WordImpress
8
 * @license     http://opensource.org/licenses/gpl-2.0.php GNU Public License
9
 * @since       1.0
10
 */
11
12
// Exit if accessed directly
13
if ( ! defined( 'ABSPATH' ) ) {
14
	exit;
15
}
16
17
/**
18
 * Process Purchase Form
19
 *
20
 * Handles the purchase form process.
21
 *
22
 * @access      private
23
 * @since       1.0
24
 * @return      void
25
 */
26
function give_process_purchase_form() {
27
28
	do_action( 'give_pre_process_purchase' );
29
30
	// Validate the form $_POST data
31
	$valid_data = give_purchase_form_validate_fields();
32
33
	// Allow themes and plugins to hook to errors
34
	do_action( 'give_checkout_error_checks', $valid_data, $_POST );
35
36
	$is_ajax = isset( $_POST['give_ajax'] );
37
38
	// Process the login form
39
	if ( isset( $_POST['give_login_submit'] ) ) {
40
		give_process_form_login();
41
	}
42
43
	// Validate the user
44
	$user = give_get_purchase_form_user( $valid_data );
45
46
	if ( false === $valid_data || give_get_errors() || ! $user ) {
47
		if ( $is_ajax ) {
48
			do_action( 'give_ajax_checkout_errors' );
49
			give_die();
50
		} else {
51
			return false;
52
		}
53
	}
54
55
	//If AJAX send back success to proceed with form submission
56
	if ( $is_ajax ) {
57
		echo 'success';
58
		give_die();
59
	}
60
61
	//After AJAX: Setup session if not using php_sessions
62
	if ( ! Give()->session->use_php_sessions() ) {
63
		//Double-check that set_cookie is publicly accessible;
64
		// we're using a slightly modified class-wp-sessions.php
65
		$session_reflection = new ReflectionMethod( 'WP_Session', 'set_cookie' );
66
		if ( $session_reflection->isPublic() ) {
67
			// Manually set the cookie.
68
			Give()->session->init()->set_cookie();
0 ignored issues
show
Bug introduced by
The method set_cookie cannot be called on Give()->session->init() (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
69
		}
70
	}
71
72
	// Setup user information
73
	$user_info = array(
74
		'id'         => $user['user_id'],
75
		'email'      => $user['user_email'],
76
		'first_name' => $user['user_first'],
77
		'last_name'  => $user['user_last'],
78
		'address'    => $user['address']
79
	);
80
81
	$auth_key = defined( 'AUTH_KEY' ) ? AUTH_KEY : '';
82
83
	$price = isset( $_POST['give-amount'] ) ? (float) apply_filters( 'give_donation_total', give_sanitize_amount( give_format_amount( $_POST['give-amount'] ) ) ) : '0.00';
84
	$purchase_key = strtolower( md5( $user['user_email'] . date( 'Y-m-d H:i:s' ) . $auth_key . uniqid( 'give', true ) ) );
85
	
86
	// Setup purchase information
87
	$purchase_data = array(
88
		'price'        => $price,
89
		'purchase_key' => $purchase_key,
90
		'user_email'   => $user['user_email'],
91
		'date'         => date( 'Y-m-d H:i:s', current_time( 'timestamp' ) ),
92
		'user_info'    => stripslashes_deep( $user_info ),
93
		'post_data'    => $_POST,
94
		'gateway'      => $valid_data['gateway'],
95
		'card_info'    => $valid_data['cc_info']
96
	);
97
98
	// Add the user data for hooks
99
	$valid_data['user'] = $user;
100
101
	// Allow themes and plugins to hook before the gateway
102
	do_action( 'give_checkout_before_gateway', $_POST, $user_info, $valid_data );
103
104
	//Sanity check for price
105
	if ( ! $purchase_data['price'] ) {
106
		// Revert to manual
107
		$purchase_data['gateway'] = 'manual';
108
		$_POST['give-gateway']    = 'manual';
109
	}
110
111
	// Allow the purchase data to be modified before it is sent to the gateway
112
	$purchase_data = apply_filters( 'give_purchase_data_before_gateway', $purchase_data, $valid_data );
113
114
	// Setup the data we're storing in the purchase session
115
	$session_data = $purchase_data;
116
117
	// Make sure credit card numbers are never stored in sessions
118
	unset( $session_data['card_info']['card_number'] );
119
	unset( $session_data['post_data']['card_number'] );
120
121
	// Used for showing data to non logged-in users after purchase, and for other plugins needing purchase data.
122
	give_set_purchase_session( $session_data );
123
124
	// Send info to the gateway for payment processing
125
	give_send_to_gateway( $purchase_data['gateway'], $purchase_data );
126
	give_die();
127
128
}
129
130
add_action( 'give_purchase', 'give_process_purchase_form' );
131
add_action( 'wp_ajax_give_process_checkout', 'give_process_purchase_form' );
132
add_action( 'wp_ajax_nopriv_give_process_checkout', 'give_process_purchase_form' );
133
134
/**
135
 * Process the checkout login form
136
 *
137
 * @access      private
138
 * @since       1.0
139
 * @return      void
140
 */
141
function give_process_form_login() {
142
143
	$is_ajax = isset( $_POST['give_ajax'] );
144
145
	$user_data = give_purchase_form_validate_user_login();
146
147
	if ( give_get_errors() || $user_data['user_id'] < 1 ) {
148
		if ( $is_ajax ) {
149
			do_action( 'give_ajax_checkout_errors' );
150
			give_die();
151
		} else {
152
			wp_redirect( $_SERVER['HTTP_REFERER'] );
153
			exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The function give_process_form_login() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
154
		}
155
	}
156
157
	give_log_user_in( $user_data['user_id'], $user_data['user_login'], $user_data['user_pass'] );
158
159
	if ( $is_ajax ) {
160
		echo 'success';
161
		give_die();
162
	} else {
163
		wp_redirect( $_SERVER['HTTP_REFERER'] );
164
	}
165
}
166
167
add_action( 'wp_ajax_give_process_checkout_login', 'give_process_form_login' );
168
add_action( 'wp_ajax_nopriv_give_process_checkout_login', 'give_process_form_login' );
169
170
/**
171
 * Purchase Form Validate Fields
172
 *
173
 * @access      private
174
 * @since       1.0
175
 * @return      bool|array
176
 */
177
function give_purchase_form_validate_fields() {
178
179
	// Check if there is $_POST
180
	if ( empty( $_POST ) ) {
181
		return false;
182
	}
183
184
	$form_id = isset( $_POST['give-form-id'] ) ? $_POST['give-form-id'] : '';
185
186
	// Start an array to collect valid data
187
	$valid_data = array(
188
		'gateway'          => give_purchase_form_validate_gateway(), // Gateway fallback (amount is validated here)
189
		'need_new_user'    => false,     // New user flag
190
		'need_user_login'  => false,     // Login user flag
191
		'logged_user_data' => array(),   // Logged user collected data
192
		'new_user_data'    => array(),   // New user collected data
193
		'login_user_data'  => array(),   // Login user collected data
194
		'guest_user_data'  => array(),   // Guest user collected data
195
		'cc_info'          => give_purchase_form_validate_cc()    // Credit card info
196
	);
197
198
	// Validate agree to terms
199
	$terms_option = get_post_meta( $form_id, '_give_terms_option', true );
200
	if ( isset( $terms_option ) && $terms_option === 'yes' ) {
201
		give_purchase_form_validate_agree_to_terms();
202
	}
203
204
	if ( is_user_logged_in() ) {
205
		// Collect logged in user data
206
		$valid_data['logged_in_user'] = give_purchase_form_validate_logged_in_user();
207
	} else if ( isset( $_POST['give-purchase-var'] ) && $_POST['give-purchase-var'] == 'needs-to-register' ) {
208
		// Set new user registration as required
209
		$valid_data['need_new_user'] = true;
210
		// Validate new user data
211
		$valid_data['new_user_data'] = give_purchase_form_validate_new_user();
212
		// Check if login validation is needed
213
	} else if ( isset( $_POST['give-purchase-var'] ) && $_POST['give-purchase-var'] == 'needs-to-login' ) {
214
		// Set user login as required
215
		$valid_data['need_user_login'] = true;
216
		// Validate users login info
217
		$valid_data['login_user_data'] = give_purchase_form_validate_user_login();
218
	} else {
219
		// Not registering or logging in, so setup guest user data
220
		$valid_data['guest_user_data'] = give_purchase_form_validate_guest_user();
221
	}
222
223
	// Return collected data
224
	return $valid_data;
225
}
226
227
/**
228
 * Purchase Form Validate Gateway
229
 *
230
 * @description: Validate the gateway and donation amount
231
 *
232
 * @access      private
233
 * @since       1.0
234
 * @return      string
235
 */
236
function give_purchase_form_validate_gateway() {
237
238
	$form_id = isset( $_REQUEST['give-form-id'] ) ? $_REQUEST['give-form-id'] : 0;
239
	$amount  = isset( $_REQUEST['give-amount'] ) ? give_sanitize_amount( $_REQUEST['give-amount'] ) : 0;
240
	$gateway = give_get_default_gateway( $form_id );
241
242
	// Check if a gateway value is present
243
	if ( ! empty( $_REQUEST['give-gateway'] ) ) {
244
245
		$gateway = sanitize_text_field( $_REQUEST['give-gateway'] );
246
247
		//Is amount being donated in LIVE mode 0.00? If so, error:
248
		if ( $amount == 0 && ! give_is_test_mode() ) {
249
250
			give_set_error( 'invalid_donation_amount', __( 'Please insert a valid donation amount.', 'give' ) );
251
252
		} //Check for a minimum custom amount
253
		elseif ( ! give_verify_minimum_price() ) {
254
255
			$minimum       = give_currency_filter( give_format_amount( give_get_form_minimum_price( $form_id ) ) );
256
			$error_message = __( 'This form has a minimum donation amount of %s', 'give' );
257
258
			give_set_error( 'invalid_donation_minimum', sprintf( $error_message, $minimum ) );
259
260
		} //Is this test mode zero donation? Let it through but set to manual gateway
261
		elseif ( $amount == 0 && give_is_test_mode() ) {
262
263
			$gateway = 'manual';
264
265
		} //Check if this gateway is active
266
		elseif ( ! give_is_gateway_active( $gateway ) ) {
267
268
			give_set_error( 'invalid_gateway', __( 'The selected payment gateway is not enabled', 'give' ) );
269
270
		}
271
272
	}
273
274
	return $gateway;
275
276
}
277
278
/**
279
 * Donation Form Validate Minimum Donation Amount
280
 *
281
 * @access      private
282
 * @since       1.3.6
283
 * @return      bool
284
 */
285
function give_verify_minimum_price() {
286
287
	$amount          = give_sanitize_amount( $_REQUEST['give-amount'] );
288
	$form_id         = isset( $_REQUEST['give-form-id'] ) ? $_REQUEST['give-form-id'] : 0;
289
	$price_id        = isset( $_REQUEST['give-price-id'] ) ? $_REQUEST['give-price-id'] : 0;
290
	$variable_prices = give_has_variable_prices( $form_id );
291
292
	if ( $variable_prices && ! empty( $price_id ) ) {
293
294
		$price_level_amount = give_get_price_option_amount( $form_id, $price_id );
295
296
		if ( $price_level_amount == $amount ) {
297
			return true;
298
		}
299
	}
300
301
	$minimum = give_get_form_minimum_price( $form_id );
302
303
	if ( $minimum > $amount ) {
304
		return false;
305
	}
306
307
	return true;
308
}
309
310
/**
311
 * Purchase Form Validate Agree To Terms
312
 *
313
 * @access      private
314
 * @since       1.0
315
 * @return      void
316
 */
317
function give_purchase_form_validate_agree_to_terms() {
318
	// Validate agree to terms
319
	if ( ! isset( $_POST['give_agree_to_terms'] ) || $_POST['give_agree_to_terms'] != 1 ) {
320
		// User did not agree
321
		give_set_error( 'agree_to_terms', apply_filters( 'give_agree_to_terms_text', __( 'You must agree to the terms of use', 'give' ) ) );
322
	}
323
}
324
325
/**
326
 * Purchase Form Required Fields
327
 *
328
 * @access      private
329
 * @since       1.0
330
 *
331
 * @param       $form_id
332
 *
333
 * @return      array
334
 */
335
function give_purchase_form_required_fields( $form_id ) {
336
337
	$payment_mode = give_get_chosen_gateway( $form_id );
338
339
	$required_fields = array(
340
		'give_email' => array(
341
			'error_id'      => 'invalid_email',
342
			'error_message' => __( 'Please enter a valid email address', 'give' )
343
		),
344
		'give_first' => array(
345
			'error_id'      => 'invalid_first_name',
346
			'error_message' => __( 'Please enter your first name', 'give' )
347
		)
348
	);
349
350
	$require_address = give_require_billing_address( $payment_mode );
351
352
	if ( $require_address ) {
353
		$required_fields['card_address']    = array(
354
			'error_id'      => 'invalid_card_address',
355
			'error_message' => __( 'Please enter your primary billing address', 'give' )
356
		);
357
		$required_fields['card_zip']        = array(
358
			'error_id'      => 'invalid_zip_code',
359
			'error_message' => __( 'Please enter your zip / postal code', 'give' )
360
		);
361
		$required_fields['card_city']       = array(
362
			'error_id'      => 'invalid_city',
363
			'error_message' => __( 'Please enter your billing city', 'give' )
364
		);
365
		$required_fields['billing_country'] = array(
366
			'error_id'      => 'invalid_country',
367
			'error_message' => __( 'Please select your billing country', 'give' )
368
		);
369
		$required_fields['card_state']      = array(
370
			'error_id'      => 'invalid_state',
371
			'error_message' => __( 'Please enter billing state / province', 'give' )
372
		);
373
	}
374
375
	return apply_filters( 'give_purchase_form_required_fields', $required_fields, $form_id );
376
377
}
378
379
/**
380
 * Check if the Billing Address is required
381
 *
382
 * @since  1.0.1
383
 *
384
 * @param $payment_mode
385
 *
386
 * @return mixed|void
387
 */
388
function give_require_billing_address( $payment_mode ) {
389
390
	$return = false;
391
392
	if ( isset( $_POST['billing_country'] ) || did_action( "give_{$payment_mode}_cc_form" ) || did_action( 'give_cc_form' ) ) {
393
		$return = true;
394
	}
395
396
	// Let payment gateways and other extensions determine if address fields should be required
397
	return apply_filters( 'give_require_billing_address', $return );
398
399
}
400
401
/**
402
 * Purchase Form Validate Logged In User
403
 *
404
 * @access      private
405
 * @since       1.0
406
 * @return      array
407
 */
408
function give_purchase_form_validate_logged_in_user() {
409
	global $user_ID;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
410
411
	$form_id = isset( $_POST['give-form-id'] ) ? $_POST['give-form-id'] : '';
412
413
	// Start empty array to collect valid user data
414
	$valid_user_data = array(
415
		// Assume there will be errors
416
		'user_id' => - 1
417
	);
418
419
	// Verify there is a user_ID
420
	if ( $user_ID > 0 ) {
421
		// Get the logged in user data
422
		$user_data = get_userdata( $user_ID );
423
424
		// Loop through required fields and show error messages
425
		foreach ( give_purchase_form_required_fields( $form_id ) as $field_name => $value ) {
426
			if ( in_array( $value, give_purchase_form_required_fields( $form_id ) ) && empty( $_POST[ $field_name ] ) ) {
427
				give_set_error( $value['error_id'], $value['error_message'] );
428
			}
429
		}
430
431
		// Verify data
432
		if ( $user_data ) {
433
			// Collected logged in user data
434
			$valid_user_data = array(
435
				'user_id'    => $user_ID,
436
				'user_email' => isset( $_POST['give_email'] ) ? sanitize_email( $_POST['give_email'] ) : $user_data->user_email,
437
				'user_first' => isset( $_POST['give_first'] ) && ! empty( $_POST['give_first'] ) ? sanitize_text_field( $_POST['give_first'] ) : $user_data->first_name,
438
				'user_last'  => isset( $_POST['give_last'] ) && ! empty( $_POST['give_last'] ) ? sanitize_text_field( $_POST['give_last'] ) : $user_data->last_name,
439
			);
440
441
			if ( ! is_email( $valid_user_data['user_email'] ) ) {
442
				give_set_error( 'email_invalid', __( 'Invalid email', 'give' ) );
443
			}
444
445
		} else {
446
			// Set invalid user error
447
			give_set_error( 'invalid_user', __( 'The user information is invalid', 'give' ) );
448
		}
449
	}
450
451
	// Return user data
452
	return $valid_user_data;
453
}
454
455
/**
456
 * Donate Form Validate New User
457
 *
458
 * @access      private
459
 * @since       1.0
460
 * @return      array
461
 */
462
function give_purchase_form_validate_new_user() {
463
464
	$registering_new_user = false;
465
	$form_id              = isset( $_POST['give-form-id'] ) ? $_POST['give-form-id'] : '';
466
467
	// Start an empty array to collect valid user data
468
	$valid_user_data = array(
469
		// Assume there will be errors
470
		'user_id'    => - 1,
471
		// Get first name
472
		'user_first' => isset( $_POST['give_first'] ) ? sanitize_text_field( $_POST['give_first'] ) : '',
473
		// Get last name
474
		'user_last'  => isset( $_POST['give_last'] ) ? sanitize_text_field( $_POST['give_last'] ) : '',
475
	);
476
477
	// Check the new user's credentials against existing ones
478
	$user_login   = isset( $_POST['give_user_login'] ) ? trim( $_POST['give_user_login'] ) : false;
479
	$user_email   = isset( $_POST['give_email'] ) ? trim( $_POST['give_email'] ) : false;
480
	$user_pass    = isset( $_POST['give_user_pass'] ) ? trim( $_POST['give_user_pass'] ) : false;
481
	$pass_confirm = isset( $_POST['give_user_pass_confirm'] ) ? trim( $_POST['give_user_pass_confirm'] ) : false;
482
483
	// Loop through required fields and show error messages
484
	foreach ( give_purchase_form_required_fields( $form_id ) as $field_name => $value ) {
485
		if ( in_array( $value, give_purchase_form_required_fields( $form_id ) ) && empty( $_POST[ $field_name ] ) ) {
486
			give_set_error( $value['error_id'], $value['error_message'] );
487
		}
488
	}
489
490
	// Check if we have an username to register
491
	if ( $user_login && strlen( $user_login ) > 0 ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $user_login of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
492
		$registering_new_user = true;
493
494
		// We have an user name, check if it already exists
495
		if ( username_exists( $user_login ) ) {
496
			// Username already registered
497
			give_set_error( 'username_unavailable', __( 'Username already taken', 'give' ) );
498
			// Check if it's valid
499
		} else if ( ! give_validate_username( $user_login ) ) {
500
			// Invalid username
501
			if ( is_multisite() ) {
502
				give_set_error( 'username_invalid', __( 'Invalid username. Only lowercase letters (a-z) and numbers are allowed', 'give' ) );
503
			} else {
504
				give_set_error( 'username_invalid', __( 'Invalid username', 'give' ) );
505
			}
506
		} else {
507
			// All the checks have run and it's good to go
508
			$valid_user_data['user_login'] = $user_login;
509
		}
510
	} elseif ( give_logged_in_only( $form_id ) ) {
511
		give_set_error( 'registration_required', esc_html__( 'You must register or login to complete your donation', 'give' ) );
512
	}
513
514
	// Check if we have an email to verify
515
	if ( $user_email && strlen( $user_email ) > 0 ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $user_email of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
516
		// Validate email
517
		if ( ! is_email( $user_email ) ) {
518
			give_set_error( 'email_invalid', __( 'Sorry, that email is invalid', 'give' ) );
519
			// Check if email exists
520
		} else if ( email_exists( $user_email ) && $registering_new_user ) {
521
			give_set_error( 'email_used', __( 'Sorry, that email already active for another user', 'give' ) );
522
		} else {
523
			// All the checks have run and it's good to go
524
			$valid_user_data['user_email'] = $user_email;
525
		}
526
	} else {
527
		// No email
528
		give_set_error( 'email_empty', __( 'Enter an email', 'give' ) );
529
	}
530
531
	// Check password
532
	if ( $user_pass && $pass_confirm ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $user_pass of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
Bug Best Practice introduced by
The expression $pass_confirm of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
533
		// Verify confirmation matches
534
		if ( $user_pass != $pass_confirm ) {
535
			// Passwords do not match
536
			give_set_error( 'password_mismatch', __( 'Passwords don\'t match', 'give' ) );
537
		} else {
538
			// All is good to go
539
			$valid_user_data['user_pass'] = $user_pass;
540
		}
541
	} else {
542
		// Password or confirmation missing
543
		if ( ! $user_pass && $registering_new_user ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $user_pass of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
544
			// The password is invalid
545
			give_set_error( 'password_empty', __( 'Enter a password', 'give' ) );
546
		} else if ( ! $pass_confirm && $registering_new_user ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $pass_confirm of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
547
			// Confirmation password is invalid
548
			give_set_error( 'confirmation_empty', __( 'Enter the password confirmation', 'give' ) );
549
		}
550
	}
551
552
	return $valid_user_data;
553
}
554
555
/**
556
 * Donation Form Validate User Login
557
 *
558
 * @access      private
559
 * @since       1.0
560
 * @return      array
561
 */
562
function give_purchase_form_validate_user_login() {
563
564
	// Start an array to collect valid user data
565
	$valid_user_data = array(
566
		// Assume there will be errors
567
		'user_id' => - 1
568
	);
569
570
	// Username
571
	if ( ! isset( $_POST['give_user_login'] ) || $_POST['give_user_login'] == '' ) {
572
		give_set_error( 'must_log_in', __( 'You must login or register to complete your donation', 'give' ) );
573
574
		return $valid_user_data;
575
	}
576
577
	// Get the user by login
578
	$user_data = get_user_by( 'login', strip_tags( $_POST['give_user_login'] ) );
579
580
	// Check if user exists
581
	if ( $user_data ) {
582
		// Get password
583
		$user_pass = isset( $_POST['give_user_pass'] ) ? $_POST['give_user_pass'] : false;
584
585
		// Check user_pass
586
		if ( $user_pass ) {
587
			// Check if password is valid
588
			if ( ! wp_check_password( $user_pass, $user_data->user_pass, $user_data->ID ) ) {
589
				// Incorrect password
590
				give_set_error(
591
					'password_incorrect',
592
					sprintf(
593
						__( 'The password you entered is incorrect. %sReset Password%s', 'give' ),
594
						'<a href="' . wp_lostpassword_url( "http://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]" ) . '" title="' . __( 'Lost Password', 'give' ) . '">',
595
						'</a>'
596
					)
597
				);
598
				// All is correct
599
			} else {
600
				// Repopulate the valid user data array
601
				$valid_user_data = array(
602
					'user_id'    => $user_data->ID,
603
					'user_login' => $user_data->user_login,
604
					'user_email' => $user_data->user_email,
605
					'user_first' => $user_data->first_name,
606
					'user_last'  => $user_data->last_name,
607
					'user_pass'  => $user_pass,
608
				);
609
			}
610
		} else {
611
			// Empty password
612
			give_set_error( 'password_empty', __( 'Enter a password', 'give' ) );
613
		}
614
	} else {
615
		// no username
616
		give_set_error( 'username_incorrect', __( 'The username you entered does not exist', 'give' ) );
617
	}
618
619
	return $valid_user_data;
620
}
621
622
/**
623
 * Purchase Form Validate Guest User
624
 *
625
 * @access  private
626
 * @since   1.0
627
 * @return  array
628
 */
629
function give_purchase_form_validate_guest_user() {
630
631
	$form_id = isset( $_POST['give-form-id'] ) ? $_POST['give-form-id'] : '';
632
633
	// Start an array to collect valid user data
634
	$valid_user_data = array(
635
		// Set a default id for guests
636
		'user_id' => 0,
637
	);
638
639
	// Show error message if user must be logged in
640
	if ( give_logged_in_only( $form_id ) ) {
641
		give_set_error( 'logged_in_only', __( 'You must be logged into to donate', 'give' ) );
642
	}
643
644
	// Get the guest email
645
	$guest_email = isset( $_POST['give_email'] ) ? $_POST['give_email'] : false;
646
647
	// Check email
648
	if ( $guest_email && strlen( $guest_email ) > 0 ) {
649
		// Validate email
650
		if ( ! is_email( $guest_email ) ) {
651
			// Invalid email
652
			give_set_error( 'email_invalid', __( 'Invalid email', 'give' ) );
653
		} else {
654
			// All is good to go
655
			$valid_user_data['user_email'] = $guest_email;
656
		}
657
	} else {
658
		// No email
659
		give_set_error( 'email_empty', __( 'Enter an email', 'give' ) );
660
	}
661
662
	// Loop through required fields and show error messages
663
	foreach ( give_purchase_form_required_fields( $form_id ) as $field_name => $value ) {
664
		if ( in_array( $value, give_purchase_form_required_fields( $form_id ) ) && empty( $_POST[ $field_name ] ) ) {
665
			give_set_error( $value['error_id'], $value['error_message'] );
666
		}
667
	}
668
669
	return $valid_user_data;
670
}
671
672
/**
673
 * Register And Login New User
674
 *
675
 * @param array $user_data
676
 *
677
 * @access  private
678
 * @since   1.0
679
 * @return  integer
680
 */
681
function give_register_and_login_new_user( $user_data = array() ) {
682
	// Verify the array
683
	if ( empty( $user_data ) ) {
684
		return - 1;
685
	}
686
687
	if ( give_get_errors() ) {
688
		return - 1;
689
	}
690
691
	$user_args = apply_filters( 'give_insert_user_args', array(
692
		'user_login'      => isset( $user_data['user_login'] ) ? $user_data['user_login'] : '',
693
		'user_pass'       => isset( $user_data['user_pass'] ) ? $user_data['user_pass'] : '',
694
		'user_email'      => isset( $user_data['user_email'] ) ? $user_data['user_email'] : '',
695
		'first_name'      => isset( $user_data['user_first'] ) ? $user_data['user_first'] : '',
696
		'last_name'       => isset( $user_data['user_last'] ) ? $user_data['user_last'] : '',
697
		'user_registered' => date( 'Y-m-d H:i:s' ),
698
		'role'            => get_option( 'default_role' )
699
	), $user_data );
700
701
	// Insert new user
702
	$user_id = wp_insert_user( $user_args );
703
704
	// Validate inserted user
705
	if ( is_wp_error( $user_id ) ) {
706
		return - 1;
707
	}
708
709
	// Allow themes and plugins to filter the user data
710
	$user_data = apply_filters( 'give_insert_user_data', $user_data, $user_args );
711
712
	// Allow themes and plugins to hook
713
	do_action( 'give_insert_user', $user_id, $user_data );
714
715
	// Login new user
716
	give_log_user_in( $user_id, $user_data['user_login'], $user_data['user_pass'] );
717
718
	// Return user id
719
	return $user_id;
720
}
721
722
/**
723
 * Get Purchase Form User
724
 *
725
 * @param array $valid_data
726
 *
727
 * @access  private
728
 * @since   1.0
729
 * @return  array
730
 */
731
function give_get_purchase_form_user( $valid_data = array() ) {
732
733
	// Initialize user
734
	$user    = false;
735
	$is_ajax = defined( 'DOING_AJAX' ) && DOING_AJAX;
736
737
	if ( $is_ajax ) {
738
		// Do not create or login the user during the ajax submission (check for errors only)
739
		return true;
740
	} else if ( is_user_logged_in() ) {
741
		// Set the valid user as the logged in collected data
742
		$user = $valid_data['logged_in_user'];
743
	} else if ( $valid_data['need_new_user'] === true || $valid_data['need_user_login'] === true ) {
744
		// New user registration
745
		if ( $valid_data['need_new_user'] === true ) {
746
			// Set user
747
			$user = $valid_data['new_user_data'];
748
			// Register and login new user
749
			$user['user_id'] = give_register_and_login_new_user( $user );
750
			// User login
751
		} else if ( $valid_data['need_user_login'] === true && ! $is_ajax ) {
752
753
			/*
754
			 * The login form is now processed in the give_process_purchase_login() function.
755
			 * This is still here for backwards compatibility.
756
			 * This also allows the old login process to still work if a user removes the
757
			 * checkout login submit button.
758
			 *
759
			 * This also ensures that the donor is logged in correctly if they click "Purchase"
760
			 * instead of submitting the login form, meaning the donor is logged in during the purchase process.
761
			 */
762
763
			// Set user
764
			$user = $valid_data['login_user_data'];
765
			// Login user
766
			give_log_user_in( $user['user_id'], $user['user_login'], $user['user_pass'] );
767
		}
768
	}
769
770
	// Check guest checkout
771
	if ( false === $user && false === give_logged_in_only( $_POST['give-form-id'] ) ) {
772
		// Set user
773
		$user = $valid_data['guest_user_data'];
774
	}
775
776
	// Verify we have an user
777
	if ( false === $user || empty( $user ) ) {
778
		// Return false
779
		return false;
780
	}
781
782
	// Get user first name
783
	if ( ! isset( $user['user_first'] ) || strlen( trim( $user['user_first'] ) ) < 1 ) {
784
		$user['user_first'] = isset( $_POST['give_first'] ) ? strip_tags( trim( $_POST['give_first'] ) ) : '';
785
	}
786
787
	// Get user last name
788
	if ( ! isset( $user['user_last'] ) || strlen( trim( $user['user_last'] ) ) < 1 ) {
789
		$user['user_last'] = isset( $_POST['give_last'] ) ? strip_tags( trim( $_POST['give_last'] ) ) : '';
790
	}
791
792
	// Get the user's billing address details
793
	$user['address']            = array();
794
	$user['address']['line1']   = ! empty( $_POST['card_address'] ) ? sanitize_text_field( $_POST['card_address'] ) : false;
795
	$user['address']['line2']   = ! empty( $_POST['card_address_2'] ) ? sanitize_text_field( $_POST['card_address_2'] ) : false;
796
	$user['address']['city']    = ! empty( $_POST['card_city'] ) ? sanitize_text_field( $_POST['card_city'] ) : false;
797
	$user['address']['state']   = ! empty( $_POST['card_state'] ) ? sanitize_text_field( $_POST['card_state'] ) : false;
798
	$user['address']['country'] = ! empty( $_POST['billing_country'] ) ? sanitize_text_field( $_POST['billing_country'] ) : false;
799
	$user['address']['zip']     = ! empty( $_POST['card_zip'] ) ? sanitize_text_field( $_POST['card_zip'] ) : false;
800
801
	if ( empty( $user['address']['country'] ) ) {
802
		$user['address'] = false;
803
	} // Country will always be set if address fields are present
804
805
	if ( ! empty( $user['user_id'] ) && $user['user_id'] > 0 && ! empty( $user['address'] ) ) {
806
		// Store the address in the user's meta so the donation form can be pre-populated with it on return purchases
807
		update_user_meta( $user['user_id'], '_give_user_address', $user['address'] );
808
	}
809
810
	// Return valid user
811
	return $user;
812
}
813
814
/**
815
 * Validates the credit card info
816
 *
817
 * @access  private
818
 * @since   1.0
819
 * @return  array
820
 */
821
function give_purchase_form_validate_cc() {
822
823
	$card_data = give_get_purchase_cc_info();
824
825
	// Validate the card zip
826
	if ( ! empty( $card_data['card_zip'] ) ) {
827
		if ( ! give_purchase_form_validate_cc_zip( $card_data['card_zip'], $card_data['card_country'] ) ) {
828
			give_set_error( 'invalid_cc_zip', __( 'The zip / postal code you entered for your billing address is invalid', 'give' ) );
829
		}
830
	}
831
832
	//Ensure no spaces
833
	if ( ! empty( $card_data['card_number'] ) ) {
834
		$card_data['card_number'] = str_replace( '+', '', $card_data['card_number'] ); //no "+" signs
835
		$card_data['card_number'] = str_replace( ' ', '', $card_data['card_number'] ); // No spaces
836
	}
837
838
	// This should validate card numbers at some point too
839
	return $card_data;
840
}
841
842
/**
843
 * Get Credit Card Info
844
 *
845
 * @access  private
846
 * @since   1.0
847
 * @return  array
848
 */
849
function give_get_purchase_cc_info() {
850
	$cc_info                   = array();
851
	$cc_info['card_name']      = isset( $_POST['card_name'] ) ? sanitize_text_field( $_POST['card_name'] ) : '';
852
	$cc_info['card_number']    = isset( $_POST['card_number'] ) ? sanitize_text_field( $_POST['card_number'] ) : '';
853
	$cc_info['card_cvc']       = isset( $_POST['card_cvc'] ) ? sanitize_text_field( $_POST['card_cvc'] ) : '';
854
	$cc_info['card_exp_month'] = isset( $_POST['card_exp_month'] ) ? sanitize_text_field( $_POST['card_exp_month'] ) : '';
855
	$cc_info['card_exp_year']  = isset( $_POST['card_exp_year'] ) ? sanitize_text_field( $_POST['card_exp_year'] ) : '';
856
	$cc_info['card_address']   = isset( $_POST['card_address'] ) ? sanitize_text_field( $_POST['card_address'] ) : '';
857
	$cc_info['card_address_2'] = isset( $_POST['card_address_2'] ) ? sanitize_text_field( $_POST['card_address_2'] ) : '';
858
	$cc_info['card_city']      = isset( $_POST['card_city'] ) ? sanitize_text_field( $_POST['card_city'] ) : '';
859
	$cc_info['card_state']     = isset( $_POST['card_state'] ) ? sanitize_text_field( $_POST['card_state'] ) : '';
860
	$cc_info['card_country']   = isset( $_POST['billing_country'] ) ? sanitize_text_field( $_POST['billing_country'] ) : '';
861
	$cc_info['card_zip']       = isset( $_POST['card_zip'] ) ? sanitize_text_field( $_POST['card_zip'] ) : '';
862
863
	// Return cc info
864
	return $cc_info;
865
}
866
867
/**
868
 * Validate zip code based on country code
869
 *
870
 * @since  1.0
871
 *
872
 * @param int $zip
873
 * @param string $country_code
874
 *
875
 * @return bool|mixed|void
876
 */
877
function give_purchase_form_validate_cc_zip( $zip = 0, $country_code = '' ) {
878
	$ret = false;
879
880
	if ( empty( $zip ) || empty( $country_code ) ) {
881
		return $ret;
882
	}
883
884
	$country_code = strtoupper( $country_code );
885
886
	$zip_regex = array(
887
		"AD" => "AD\d{3}",
888
		"AM" => "(37)?\d{4}",
889
		"AR" => "^([A-Z]{1}\d{4}[A-Z]{3}|[A-Z]{1}\d{4}|\d{4})$",
890
		"AS" => "96799",
891
		"AT" => "\d{4}",
892
		"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})$",
893
		"AX" => "22\d{3}",
894
		"AZ" => "\d{4}",
895
		"BA" => "\d{5}",
896
		"BB" => "(BB\d{5})?",
897
		"BD" => "\d{4}",
898
		"BE" => "^[1-9]{1}[0-9]{3}$",
899
		"BG" => "\d{4}",
900
		"BH" => "((1[0-2]|[2-9])\d{2})?",
901
		"BM" => "[A-Z]{2}[ ]?[A-Z0-9]{2}",
902
		"BN" => "[A-Z]{2}[ ]?\d{4}",
903
		"BR" => "\d{5}[\-]?\d{3}",
904
		"BY" => "\d{6}",
905
		"CA" => "^[ABCEGHJKLMNPRSTVXY]{1}\d{1}[A-Z]{1} *\d{1}[A-Z]{1}\d{1}$",
906
		"CC" => "6799",
907
		"CH" => "^[1-9][0-9][0-9][0-9]$",
908
		"CK" => "\d{4}",
909
		"CL" => "\d{7}",
910
		"CN" => "\d{6}",
911
		"CR" => "\d{4,5}|\d{3}-\d{4}",
912
		"CS" => "\d{5}",
913
		"CV" => "\d{4}",
914
		"CX" => "6798",
915
		"CY" => "\d{4}",
916
		"CZ" => "\d{3}[ ]?\d{2}",
917
		"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",
918
		"DK" => "^([D-d][K-k])?( |-)?[1-9]{1}[0-9]{3}$",
919
		"DO" => "\d{5}",
920
		"DZ" => "\d{5}",
921
		"EC" => "([A-Z]\d{4}[A-Z]|(?:[A-Z]{2})?\d{6})?",
922
		"EE" => "\d{5}",
923
		"EG" => "\d{5}",
924
		"ES" => "^([1-9]{2}|[0-9][1-9]|[1-9][0-9])[0-9]{3}$",
925
		"ET" => "\d{4}",
926
		"FI" => "\d{5}",
927
		"FK" => "FIQQ 1ZZ",
928
		"FM" => "(9694[1-4])([ \-]\d{4})?",
929
		"FO" => "\d{3}",
930
		"FR" => "^(F-)?((2[A|B])|[0-9]{2})[0-9]{3}$",
931
		"GE" => "\d{4}",
932
		"GF" => "9[78]3\d{2}",
933
		"GL" => "39\d{2}",
934
		"GN" => "\d{3}",
935
		"GP" => "9[78][01]\d{2}",
936
		"GR" => "\d{3}[ ]?\d{2}",
937
		"GS" => "SIQQ 1ZZ",
938
		"GT" => "\d{5}",
939
		"GU" => "969[123]\d([ \-]\d{4})?",
940
		"GW" => "\d{4}",
941
		"HM" => "\d{4}",
942
		"HN" => "(?:\d{5})?",
943
		"HR" => "\d{5}",
944
		"HT" => "\d{4}",
945
		"HU" => "\d{4}",
946
		"ID" => "\d{5}",
947
		"IE" => "((D|DUBLIN)?([1-9]|6[wW]|1[0-8]|2[024]))?",
948
		"IL" => "\d{5}",
949
		"IN" => "^[1-9][0-9][0-9][0-9][0-9][0-9]$", //india
950
		"IO" => "BBND 1ZZ",
951
		"IQ" => "\d{5}",
952
		"IS" => "\d{3}",
953
		"IT" => "^(V-|I-)?[0-9]{5}$",
954
		"JO" => "\d{5}",
955
		"JP" => "\d{3}-\d{4}",
956
		"KE" => "\d{5}",
957
		"KG" => "\d{6}",
958
		"KH" => "\d{5}",
959
		"KR" => "\d{3}[\-]\d{3}",
960
		"KW" => "\d{5}",
961
		"KZ" => "\d{6}",
962
		"LA" => "\d{5}",
963
		"LB" => "(\d{4}([ ]?\d{4})?)?",
964
		"LI" => "(948[5-9])|(949[0-7])",
965
		"LK" => "\d{5}",
966
		"LR" => "\d{4}",
967
		"LS" => "\d{3}",
968
		"LT" => "\d{5}",
969
		"LU" => "\d{4}",
970
		"LV" => "\d{4}",
971
		"MA" => "\d{5}",
972
		"MC" => "980\d{2}",
973
		"MD" => "\d{4}",
974
		"ME" => "8\d{4}",
975
		"MG" => "\d{3}",
976
		"MH" => "969[67]\d([ \-]\d{4})?",
977
		"MK" => "\d{4}",
978
		"MN" => "\d{6}",
979
		"MP" => "9695[012]([ \-]\d{4})?",
980
		"MQ" => "9[78]2\d{2}",
981
		"MT" => "[A-Z]{3}[ ]?\d{2,4}",
982
		"MU" => "(\d{3}[A-Z]{2}\d{3})?",
983
		"MV" => "\d{5}",
984
		"MX" => "\d{5}",
985
		"MY" => "\d{5}",
986
		"NC" => "988\d{2}",
987
		"NE" => "\d{4}",
988
		"NF" => "2899",
989
		"NG" => "(\d{6})?",
990
		"NI" => "((\d{4}-)?\d{3}-\d{3}(-\d{1})?)?",
991
		"NL" => "^[1-9][0-9]{3}\s?([a-zA-Z]{2})?$",
992
		"NO" => "\d{4}",
993
		"NP" => "\d{5}",
994
		"NZ" => "\d{4}",
995
		"OM" => "(PC )?\d{3}",
996
		"PF" => "987\d{2}",
997
		"PG" => "\d{3}",
998
		"PH" => "\d{4}",
999
		"PK" => "\d{5}",
1000
		"PL" => "\d{2}-\d{3}",
1001
		"PM" => "9[78]5\d{2}",
1002
		"PN" => "PCRN 1ZZ",
1003
		"PR" => "00[679]\d{2}([ \-]\d{4})?",
1004
		"PT" => "\d{4}([\-]\d{3})?",
1005
		"PW" => "96940",
1006
		"PY" => "\d{4}",
1007
		"RE" => "9[78]4\d{2}",
1008
		"RO" => "\d{6}",
1009
		"RS" => "\d{5}",
1010
		"RU" => "\d{6}",
1011
		"SA" => "\d{5}",
1012
		"SE" => "^(s-|S-){0,1}[0-9]{3}\s?[0-9]{2}$",
1013
		"SG" => "\d{6}",
1014
		"SH" => "(ASCN|STHL) 1ZZ",
1015
		"SI" => "\d{4}",
1016
		"SJ" => "\d{4}",
1017
		"SK" => "\d{3}[ ]?\d{2}",
1018
		"SM" => "4789\d",
1019
		"SN" => "\d{5}",
1020
		"SO" => "\d{5}",
1021
		"SZ" => "[HLMS]\d{3}",
1022
		"TC" => "TKCA 1ZZ",
1023
		"TH" => "\d{5}",
1024
		"TJ" => "\d{6}",
1025
		"TM" => "\d{6}",
1026
		"TN" => "\d{4}",
1027
		"TR" => "\d{5}",
1028
		"TW" => "\d{3}(\d{2})?",
1029
		"UA" => "\d{5}",
1030
		"UK" => "^(GIR|[A-Z]\d[A-Z\d]??|[A-Z]{2}\d[A-Z\d]??)[ ]??(\d[A-Z]{2})$",
1031
		"US" => "^\d{5}([\-]?\d{4})?$",
1032
		"UY" => "\d{5}",
1033
		"UZ" => "\d{6}",
1034
		"VA" => "00120",
1035
		"VE" => "\d{4}",
1036
		"VI" => "008(([0-4]\d)|(5[01]))([ \-]\d{4})?",
1037
		"WF" => "986\d{2}",
1038
		"YT" => "976\d{2}",
1039
		"YU" => "\d{5}",
1040
		"ZA" => "\d{4}",
1041
		"ZM" => "\d{5}"
1042
	);
1043
1044
	if ( ! isset ( $zip_regex[ $country_code ] ) || preg_match( "/" . $zip_regex[ $country_code ] . "/i", $zip ) ) {
1045
		$ret = true;
1046
	}
1047
1048
	return apply_filters( 'give_is_zip_valid', $ret, $zip, $country_code );
1049
}