Completed
Push — master ( 9a5cf9...f11a89 )
by Devin
37:27 queued 17:25
created

paypal-standard.php ➔ give_get_pending_donation_note()   C

Complexity

Conditions 11
Paths 11

Size

Total Lines 62
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 132

Importance

Changes 0
Metric Value
cc 11
eloc 31
nc 11
nop 1
dl 0
loc 62
ccs 0
cts 5
cp 0
crap 132
rs 6.1722
c 0
b 0
f 0

How to fix   Long Method    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 35 and the first side effect is on line 13.

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
 * PayPal Standard Gateway
4
 *
5
 * @package     Give
6
 * @subpackage  Gateways
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
if ( ! defined( 'ABSPATH' ) ) {
13
	exit;
14
}
15
16
/**
17
 * PayPal Remove CC Form.
18
 *
19
 * PayPal Standard does not need a CC form, so remove it.
20
 *
21
 * @access private
22
 * @since  1.0
23
 */
24
add_action( 'give_paypal_cc_form', '__return_false' );
25
26
/**
27
 * Process PayPal Purchase.
28
 *
29
 * @since 1.0
30
 *
31
 * @param array $purchase_data Purchase Data
32
 *
33
 * @return void
34
 */
35
function give_process_paypal_purchase( $purchase_data ) {
36
37
	if ( ! wp_verify_nonce( $purchase_data['gateway_nonce'], 'give-gateway' ) ) {
38
		wp_die( esc_html__( 'Nonce verification has failed.', 'give' ), esc_html__( 'Error', 'give' ), array( 'response' => 403 ) );
39
	}
40
41
	$form_id  = intval( $purchase_data['post_data']['give-form-id'] );
42
	$price_id = isset( $purchase_data['post_data']['give-price-id'] ) ? $purchase_data['post_data']['give-price-id'] : '';
43
44
	// Collect payment data.
45
	$payment_data = array(
46
		'price'           => $purchase_data['price'],
47
		'give_form_title' => $purchase_data['post_data']['give-form-title'],
48
		'give_form_id'    => $form_id,
49
		'give_price_id'   => $price_id,
50
		'date'            => $purchase_data['date'],
51
		'user_email'      => $purchase_data['user_email'],
52
		'purchase_key'    => $purchase_data['purchase_key'],
53
		'currency'        => give_get_currency(),
54
		'user_info'       => $purchase_data['user_info'],
55
		'status'          => 'pending',
56
		'gateway'         => 'paypal'
57
	);
58
59
	// Record the pending payment.
60
	$payment_id = give_insert_payment( $payment_data );
61
62
	// Check payment.
63
	if ( ! $payment_id ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $payment_id of type false|integer is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

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

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
64
		// Record the error.
65
		give_record_gateway_error(
66
			esc_html__( 'Payment Error', 'give' ),
67
			sprintf(
68
			/* translators: %s: payment data */
69
				esc_html__( 'Payment creation failed before sending donor to PayPal. Payment data: %s', 'give' ),
70
				json_encode( $payment_data )
71
			),
72
			$payment_id
73
		);
74
		// Problems? Send back.
75
		give_send_back_to_checkout( '?payment-mode=' . $purchase_data['post_data']['give-gateway'] );
76
77
	} else {
78
79
		// Only send to PayPal if the pending payment is created successfully.
80
		$listener_url = add_query_arg( 'give-listener', 'IPN', home_url( 'index.php' ) );
81
82
		// Get the success url.
83
		$return_url = add_query_arg( array(
84
			'payment-confirmation' => 'paypal',
85
			'payment-id'           => $payment_id
86
87
		), get_permalink( give_get_option( 'success_page' ) ) );
88
89
		// Get the PayPal redirect uri.
90
		$paypal_redirect = trailingslashit( give_get_paypal_redirect() ) . '?';
91
92
		//Item name - pass level name if variable priced.
93
		$item_name = $purchase_data['post_data']['give-form-title'];
94
95
		//Verify has variable prices.
96
		if ( give_has_variable_prices( $form_id ) && isset( $purchase_data['post_data']['give-price-id'] ) ) {
97
98
			$item_price_level_text = give_get_price_option_name( $form_id, $purchase_data['post_data']['give-price-id'] );
99
100
			$price_level_amount = give_get_price_option_amount( $form_id, $purchase_data['post_data']['give-price-id'] );
101
102
			//Donation given doesn't match selected level (must be a custom amount).
103
			if ( $price_level_amount != give_sanitize_amount( $purchase_data['price'] ) ) {
104
				$custom_amount_text = get_post_meta( $form_id, '_give_custom_amount_text', true );
105
				//user custom amount text if any, fallback to default if not.
106
				$item_name .= ' - ' . ( ! empty( $custom_amount_text ) ? $custom_amount_text : esc_html__( 'Custom Amount', 'give' ) );
107
108
			} //Is there any donation level text?
109
			elseif ( ! empty( $item_price_level_text ) ) {
110
				$item_name .= ' - ' . $item_price_level_text;
111
			}
112
113
		} //Single donation: Custom Amount.
114
		elseif ( give_get_form_price( $form_id ) !== give_sanitize_amount( $purchase_data['price'] ) ) {
115
			$custom_amount_text = get_post_meta( $form_id, '_give_custom_amount_text', true );
116
			//user custom amount text if any, fallback to default if not.
117
			$item_name .= ' - ' . ( ! empty( $custom_amount_text ) ? $custom_amount_text : esc_html__( 'Custom Amount', 'give' ) );
118
		}
119
120
		// Setup PayPal API params.
121
		$paypal_args = array(
122
			'business'      => give_get_option( 'paypal_email', false ),
123
			'first_name'    => $purchase_data['user_info']['first_name'],
124
			'last_name'     => $purchase_data['user_info']['last_name'],
125
			'email'         => $purchase_data['user_email'],
126
			'invoice'       => $purchase_data['purchase_key'],
127
			'amount'        => $purchase_data['price'],
128
			'item_name'     => stripslashes( $item_name ),
129
			'no_shipping'   => '1',
130
			'shipping'      => '0',
131
			'no_note'       => '1',
132
			'currency_code' => give_get_currency(),
133
			'charset'       => get_bloginfo( 'charset' ),
134
			'custom'        => $payment_id,
135
			'rm'            => '2',
136
			'return'        => $return_url,
137
			'cancel_return' => give_get_failed_transaction_uri( '?payment-id=' . $payment_id ),
138
			'notify_url'    => $listener_url,
139
			'page_style'    => give_get_paypal_page_style(),
140
			'cbt'           => get_bloginfo( 'name' ),
141
			'bn'            => 'givewp_SP'
142
		);
143
144
		//Add user address if present.
145
		if ( ! empty( $purchase_data['user_info']['address'] ) ) {
146
			$paypal_args['address1'] = isset( $purchase_data['user_info']['address']['line1'] ) ? $purchase_data['user_info']['address']['line1'] : '';
147
			$paypal_args['address2'] = isset( $purchase_data['user_info']['address']['line2'] ) ? $purchase_data['user_info']['address']['line2'] : '';
148
			$paypal_args['city']     = isset( $purchase_data['user_info']['address']['city'] ) ? $purchase_data['user_info']['address']['city'] : '';
149
			$paypal_args['state']    = isset( $purchase_data['user_info']['address']['state'] ) ? $purchase_data['user_info']['address']['state'] : '';
150
			$paypal_args['country']  = isset( $purchase_data['user_info']['address']['country'] ) ? $purchase_data['user_info']['address']['country'] : '';
151
		}
152
153
		//Donations or regular transactions?
154
		if ( give_get_option( 'paypal_button_type' ) === 'standard' ) {
155
			$paypal_extra_args = array(
156
				'cmd' => '_xclick',
157
			);
158
		} else {
159
			$paypal_extra_args = array(
160
				'cmd' => '_donations',
161
			);
162
		}
163
164
		$paypal_args = array_merge( $paypal_extra_args, $paypal_args );
165
		$paypal_args = apply_filters( 'give_paypal_redirect_args', $paypal_args, $purchase_data );
166
167
		// Build query.
168
		$paypal_redirect .= http_build_query( $paypal_args );
169
170
		// Fix for some sites that encode the entities.
171
		$paypal_redirect = str_replace( '&amp;', '&', $paypal_redirect );
172
173
		// Redirect to PayPal.
174
		wp_redirect( $paypal_redirect );
175
		exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The function give_process_paypal_purchase() 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...
176
	}
177
178
}
179
180
add_action( 'give_gateway_paypal', 'give_process_paypal_purchase' );
181
182
/**
183
 * Listens for a PayPal IPN requests and then sends to the processing function
184
 *
185
 * @since 1.0
186
 * @return void
187
 */
188
function give_listen_for_paypal_ipn() {
189
	// Regular PayPal IPN
190
	if ( isset( $_GET['give-listener'] ) && $_GET['give-listener'] == 'IPN' ) {
191
		do_action( 'give_verify_paypal_ipn' );
192
	}
193
}
194
195
add_action( 'init', 'give_listen_for_paypal_ipn' );
196
197
/**
198
 * Process PayPal IPN
199
 *
200
 * @since 1.0
201
 * @return void
202
 */
203
function give_process_paypal_ipn() {
204
205
	// Check the request method is POST
206
	if ( isset( $_SERVER['REQUEST_METHOD'] ) && $_SERVER['REQUEST_METHOD'] != 'POST' ) {
207
		return;
208
	}
209
210
	// Set initial post data to empty string
211
	$post_data = '';
212
213
	// Fallback just in case post_max_size is lower than needed
214
	if ( ini_get( 'allow_url_fopen' ) ) {
215
		$post_data = file_get_contents( 'php://input' );
216
	} else {
217
		// If allow_url_fopen is not enabled, then make sure that post_max_size is large enough
218
		ini_set( 'post_max_size', '12M' );
219
	}
220
	// Start the encoded data collection with notification command
221
	$encoded_data = 'cmd=_notify-validate';
222
223
	// Get current arg separator
224
	$arg_separator = give_get_php_arg_separator_output();
225
226
	// Verify there is a post_data
227
	if ( $post_data || strlen( $post_data ) > 0 ) {
228
		// Append the data
229
		$encoded_data .= $arg_separator . $post_data;
230
	} else {
231
		// Check if POST is empty
232
		if ( empty( $_POST ) ) {
233
			// Nothing to do
234
			return;
235
		} else {
236
			// Loop through each POST
237
			foreach ( $_POST as $key => $value ) {
238
				// Encode the value and append the data.
239
				$encoded_data .= $arg_separator . "$key=" . urlencode( $value );
240
			}
241
		}
242
	}
243
244
	// Convert collected post data to an array.
245
	parse_str( $encoded_data, $encoded_data_array );
246
247
	foreach ( $encoded_data_array as $key => $value ) {
0 ignored issues
show
Bug introduced by
The expression $encoded_data_array of type null|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
248
249
		if ( false !== strpos( $key, 'amp;' ) ) {
250
			$new_key = str_replace( '&amp;', '&', $key );
251
			$new_key = str_replace( 'amp;', '&', $new_key );
252
253
			unset( $encoded_data_array[ $key ] );
254
			$encoded_data_array[ $new_key ] = $value;
255
		}
256
257
	}
258
259
	//Validate IPN request w/ PayPal if user hasn't disabled this security measure.
260
	if ( ! give_get_option( 'disable_paypal_verification' ) ) {
261
262
		$remote_post_vars = array(
263
			'method'      => 'POST',
264
			'timeout'     => 45,
265
			'redirection' => 5,
266
			'httpversion' => '1.1',
267
			'blocking'    => true,
268
			'headers'     => array(
269
				'host'         => 'www.paypal.com',
270
				'connection'   => 'close',
271
				'content-type' => 'application/x-www-form-urlencoded',
272
				'post'         => '/cgi-bin/webscr HTTP/1.1',
273
274
			),
275
			'sslverify'   => false,
276
			'body'        => $encoded_data_array
277
		);
278
279
		// Validate the IPN.
280
		$api_response = wp_remote_post( give_get_paypal_redirect(), $remote_post_vars );
281
282
		if ( is_wp_error( $api_response ) ) {
283
			give_record_gateway_error(
284
				esc_html__( 'IPN Error', 'give' ),
285
				sprintf(
286
				/* translators: %s: Paypal IPN response */
287
					esc_html__( 'Invalid IPN verification response. IPN data: %s', 'give' ),
288
					json_encode( $api_response )
289
				)
290
			);
291
292
			return; // Something went wrong
293
		}
294
295
		if ( $api_response['body'] !== 'VERIFIED' && give_get_option( 'disable_paypal_verification', false ) ) {
296
			give_record_gateway_error(
297
				esc_html__( 'IPN Error', 'give' ),
298
				sprintf(
299
				/* translators: %s: Paypal IPN response */
300
					esc_html__( 'Invalid IPN verification response. IPN data: %s', 'give' ),
301
					json_encode( $api_response )
302
				)
303
			);
304
305
			return; // Response not okay
306
		}
307
308
	}
309
310
	// Check if $post_data_array has been populated
311
	if ( ! is_array( $encoded_data_array ) && ! empty( $encoded_data_array ) ) {
312
		return;
313
	}
314
315
	$defaults = array(
316
		'txn_type'       => '',
317
		'payment_status' => ''
318
	);
319
320
	$encoded_data_array = wp_parse_args( $encoded_data_array, $defaults );
321
322
	$payment_id = isset( $encoded_data_array['custom'] ) ? absint( $encoded_data_array['custom'] ) : 0;
323
324
	if ( has_action( 'give_paypal_' . $encoded_data_array['txn_type'] ) ) {
325
		// Allow PayPal IPN types to be processed separately.
326
		do_action( 'give_paypal_' . $encoded_data_array['txn_type'], $encoded_data_array, $payment_id );
327
	} else {
328
		// Fallback to web accept just in case the txn_type isn't present.
329
		do_action( 'give_paypal_web_accept', $encoded_data_array, $payment_id );
330
	}
331
	exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The function give_process_paypal_ipn() 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...
332
}
333
334
add_action( 'give_verify_paypal_ipn', 'give_process_paypal_ipn' );
335
336
/**
337
 * Process web accept (one time) payment IPNs.
338
 *
339
 * @since 1.0
340
 *
341
 * @param array $data       IPN Data
342
 * @param int   $payment_id The payment ID from Give.
343
 *
344
 * @return void
345
 */
346
function give_process_paypal_web_accept_and_cart( $data, $payment_id ) {
347
348
	//Only allow through these transaction types.
349
	if ( $data['txn_type'] != 'web_accept' && $data['txn_type'] != 'cart' && strtolower( $data['payment_status'] ) != 'refunded' ) {
350
		return;
351
	}
352
353
	//Need $payment_id to continue.
354
	if ( empty( $payment_id ) ) {
355
		return;
356
	}
357
358
	// Collect payment details
359
	$purchase_key   = isset( $data['invoice'] ) ? $data['invoice'] : $data['item_number'];
360
	$paypal_amount  = $data['mc_gross'];
361
	$payment_status = strtolower( $data['payment_status'] );
362
	$currency_code  = strtolower( $data['mc_currency'] );
363
	$business_email = isset( $data['business'] ) && is_email( $data['business'] ) ? trim( $data['business'] ) : trim( $data['receiver_email'] );
364
	$payment_meta   = give_get_payment_meta( $payment_id );
365
	$pp_button_type = give_get_option( 'paypal_button_type' );
366
367
	// Must be a PayPal standard IPN.
368
	if ( give_get_payment_gateway( $payment_id ) != 'paypal' ) {
369
		return;
370
	}
371
372
	// Verify payment recipient
373
	if ( strcasecmp( $business_email, trim( give_get_option( 'paypal_email' ) ) ) != 0 ) {
374
375
		give_record_gateway_error(
376
			esc_html__( 'IPN Error', 'give' ),
377
			sprintf(
378
			/* translators: %s: Paypal IPN response */
379
				esc_html__( 'Invalid business email in IPN response. IPN data: %s', 'give' ),
380
				json_encode( $data )
381
			),
382
			$payment_id
383
		);
384
		give_update_payment_status( $payment_id, 'failed' );
385
		give_insert_payment_note( $payment_id, esc_html__( 'Payment failed due to invalid PayPal business email.', 'give' ) );
386
387
		return;
388
	}
389
390
	// Verify payment currency.
391
	if ( $currency_code != strtolower( $payment_meta['currency'] ) ) {
392
393
		give_record_gateway_error(
394
			esc_html__( 'IPN Error', 'give' ),
395
			sprintf(
396
			/* translators: %s: Paypal IPN response */
397
				esc_html__( 'Invalid currency in IPN response. IPN data: %s', 'give' ),
398
				json_encode( $data )
399
			),
400
			$payment_id
401
		);
402
		give_update_payment_status( $payment_id, 'failed' );
403
		give_insert_payment_note( $payment_id, esc_html__( 'Payment failed due to invalid currency in PayPal IPN.', 'give' ) );
404
405
		return;
406
	}
407
408
	if ( ! give_get_payment_user_email( $payment_id ) ) {
409
410
		// No email associated with donation, so store email from PayPal.
411
		give_update_payment_meta( $payment_id, '_give_payment_user_email', $data['payer_email'] );
412
413
		// Setup and store the donors's details.
414
		$address            = array();
415
		$address['line1']   = ! empty( $data['address_street'] ) ? sanitize_text_field( $data['address_street'] ) : false;
416
		$address['city']    = ! empty( $data['address_city'] ) ? sanitize_text_field( $data['address_city'] ) : false;
417
		$address['state']   = ! empty( $data['address_state'] ) ? sanitize_text_field( $data['address_state'] ) : false;
418
		$address['country'] = ! empty( $data['address_country_code'] ) ? sanitize_text_field( $data['address_country_code'] ) : false;
419
		$address['zip']     = ! empty( $data['address_zip'] ) ? sanitize_text_field( $data['address_zip'] ) : false;
420
421
		$user_info = array(
422
			'id'         => '-1',
423
			'email'      => sanitize_text_field( $data['payer_email'] ),
424
			'first_name' => sanitize_text_field( $data['first_name'] ),
425
			'last_name'  => sanitize_text_field( $data['last_name'] ),
426
			'discount'   => '',
427
			'address'    => $address
428
		);
429
430
		$payment_meta['user_info'] = $user_info;
431
		give_update_payment_meta( $payment_id, '_give_payment_meta', $payment_meta );
432
	}
433
434
	//Process refunds & reversed.
435
	if ( $payment_status == 'refunded' || $payment_status == 'reversed' ) {
436
437
		// Process a refund
438
		give_process_paypal_refund( $data, $payment_id );
439
440
		return;
441
	}
442
443
	if ( get_post_status( $payment_id ) == 'publish' ) {
444
		return; // Only complete payments once
445
	}
446
447
	// Retrieve the total donation amount (before PayPal).
448
	$payment_amount = give_get_payment_amount( $payment_id );
449
450
	//Check that the donation PP and local db amounts match.
451
	if ( number_format( (float) $paypal_amount, 2 ) < number_format( (float) $payment_amount, 2 ) ) {
452
		// The prices don't match
453
		give_record_gateway_error(
454
			esc_html__( 'IPN Error', 'give' ),
455
			sprintf(
456
			/* translators: %s: Paypal IPN response */
457
				esc_html__( 'Invalid payment amount in IPN response. IPN data: %s', 'give' ),
458
				json_encode( $data )
459
			),
460
			$payment_id
461
		);
462
		give_update_payment_status( $payment_id, 'failed' );
463
		give_insert_payment_note( $payment_id, esc_html__( 'Payment failed due to invalid amount in PayPal IPN.', 'give' ) );
464
465
		return;
466
	}
467
468
	//Verify the payment ID matches local db's if "standard" transaction is set.
469
	//@see
470
471
	if ( $pp_button_type == 'standard' && $purchase_key != give_get_payment_key( $payment_id ) ) {
472
		// Purchase keys don't match
473
		give_record_gateway_error(
474
			esc_html__( 'IPN Error', 'give' ),
475
			sprintf(
476
			/* translators: %s: Paypal IPN response */
477
				esc_html__( 'Invalid purchase key in IPN response. IPN data: %s', 'give' ),
478
				json_encode( $data )
479
			),
480
			$payment_id
481
		);
482
		give_update_payment_status( $payment_id, 'failed' );
483
		give_insert_payment_note( $payment_id, esc_html__( 'Payment failed due to invalid purchase key in PayPal IPN.', 'give' ) );
484
485
		return;
486
	}
487
488
	//Process completed donations.
489
	if ( $payment_status == 'completed' || give_is_test_mode() ) {
490
491
		give_insert_payment_note(
492
			$payment_id,
493
			sprintf(
494
			/* translators: %s: Paypal transaction ID */
495
				esc_html__( 'PayPal Transaction ID: %s', 'give' ),
496
				$data['txn_id']
497
			)
498
		);
499
		give_set_payment_transaction_id( $payment_id, $data['txn_id'] );
500
		give_update_payment_status( $payment_id, 'publish' );
501
502
	} elseif ( 'pending' == $payment_status && isset( $data['pending_reason'] ) ) {
503
504
		// Look for possible pending reasons, such as an echeck.
505
		$note = give_paypal_get_pending_donation_note( strtolower( $data['pending_reason'] ) );
506
507
		if ( ! empty( $note ) ) {
508
509
			give_insert_payment_note( $payment_id, $note );
510
511
		}
512
	}
513
514
}
515
516
add_action( 'give_paypal_web_accept', 'give_process_paypal_web_accept_and_cart', 10, 2 );
517
518
/**
519
 * Process PayPal IPN Refunds
520
 *
521
 * @since 1.0
522
 *
523
 * @param array $data       IPN Data
524
 * @param int   $payment_id The payment ID.
525
 *
526
 * @return void
527
 */
528
function give_process_paypal_refund( $data, $payment_id = 0 ) {
529
530
	// Collect payment details
531
532
	if ( empty( $payment_id ) ) {
533
		return;
534
	}
535
536
	if ( get_post_status( $payment_id ) == 'refunded' ) {
537
		return; // Only refund payments once
538
	}
539
540
	$payment_amount = give_get_payment_amount( $payment_id );
541
	$refund_amount  = $data['payment_gross'] * - 1;
542
543
	if ( number_format( (float) $refund_amount, 2 ) < number_format( (float) $payment_amount, 2 ) ) {
544
545
		give_insert_payment_note(
546
			$payment_id,
547
			sprintf(
548
			/* translators: %s: Paypal parent transaction ID */
549
				esc_html__( 'Partial PayPal refund processed: %s', 'give' ),
550
				$data['parent_txn_id']
551
			)
552
		);
553
554
		return; // This is a partial refund
555
556
	}
557
558
	give_insert_payment_note(
559
		$payment_id,
560
		sprintf(
561
		/* translators: %s: Paypal parent transaction ID */
562
			esc_html__( 'PayPal Payment #%s Refunded for reason: %s', 'give' ),
563
			$data['parent_txn_id'], $data['reason_code']
564
		)
565
	);
566
	give_insert_payment_note(
567
		$payment_id,
568
		sprintf(
569
		/* translators: %s: Paypal transaction ID */
570
			esc_html__( 'PayPal Refund Transaction ID: %s', 'give' ),
571
			$data['txn_id']
572
		)
573
	);
574
	give_update_payment_status( $payment_id, 'refunded' );
575
}
576
577
/**
578
 * Get PayPal Redirect
579
 *
580
 * @since 1.0
581
 *
582
 * @param bool $ssl_check Is SSL?
583
 *
584
 * @return string
585
 */
586
function give_get_paypal_redirect( $ssl_check = false ) {
587
588
	if ( is_ssl() || ! $ssl_check ) {
589
		$protocal = 'https://';
590
	} else {
591
		$protocal = 'http://';
592
	}
593
594
	// Check the current payment mode
595
	if ( give_is_test_mode() ) {
596
		// Test mode
597
		$paypal_uri = $protocal . 'www.sandbox.paypal.com/cgi-bin/webscr';
598
	} else {
599
		// Live mode
600
		$paypal_uri = $protocal . 'www.paypal.com/cgi-bin/webscr';
601
	}
602
603
	return apply_filters( 'give_paypal_uri', $paypal_uri );
604
}
605
606
/**
607
 * Set the Page Style for PayPal Purchase page
608
 *
609
 * @since 1.0
610
 * @return string
611
 */
612
function give_get_paypal_page_style() {
613
	$page_style = trim( give_get_option( 'paypal_page_style', 'PayPal' ) );
614
615
	return apply_filters( 'give_paypal_page_style', $page_style );
616
}
617
618
/**
619
 * PayPal Success Page
620
 *
621
 * Shows "Donation Processing" message for PayPal payments that are still pending on site return
622
 *
623
 * @since      1.0
624
 *
625
 * @param $content
626
 *
627
 * @return string
628
 *
629
 */
630
function give_paypal_success_page_content( $content ) {
631
632
	if ( ! isset( $_GET['payment-id'] ) && ! give_get_purchase_session() ) {
633
		return $content;
634
	}
635
636
	$payment_id = isset( $_GET['payment-id'] ) ? absint( $_GET['payment-id'] ) : false;
637
638
	if ( ! $payment_id ) {
639
		$session    = give_get_purchase_session();
640
		$payment_id = give_get_purchase_id_by_key( $session['purchase_key'] );
641
	}
642
643
	$payment = get_post( $payment_id );
644
	if ( $payment && 'pending' == $payment->post_status ) {
645
646
		// Payment is still pending so show processing indicator to fix the race condition.
647
		ob_start();
648
649
		give_get_template_part( 'payment', 'processing' );
650
651
		$content = ob_get_clean();
652
653
	}
654
655
	return $content;
656
657
}
658
659
add_filter( 'give_payment_confirm_paypal', 'give_paypal_success_page_content' );
660
661
/**
662
 * Given a Payment ID, extract the transaction ID
663
 *
664
 * @since  1.0
665
 *
666
 * @param  string $payment_id Payment ID
667
 *
668
 * @return string                   Transaction ID
669
 */
670
function give_paypal_get_payment_transaction_id( $payment_id ) {
671
672
	$transaction_id = '';
673
	$notes          = give_get_payment_notes( $payment_id );
674
675
	foreach ( $notes as $note ) {
676
		if ( preg_match( '/^PayPal Transaction ID: ([^\s]+)/', $note->comment_content, $match ) ) {
677
			$transaction_id = $match[1];
678
			continue;
679
		}
680
	}
681
682
	return apply_filters( 'give_paypal_set_payment_transaction_id', $transaction_id, $payment_id );
683
}
684
685
add_filter( 'give_get_payment_transaction_id-paypal', 'give_paypal_get_payment_transaction_id', 10, 1 );
686
687
/**
688
 * Given a transaction ID, generate a link to the PayPal transaction ID details
689
 *
690
 * @since  1.0
691
 *
692
 * @param  string $transaction_id The Transaction ID
693
 * @param  int    $payment_id     The payment ID for this transaction
694
 *
695
 * @return string                 A link to the PayPal transaction details
696
 */
697
function give_paypal_link_transaction_id( $transaction_id, $payment_id ) {
0 ignored issues
show
Unused Code introduced by
The parameter $payment_id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
698
699
	$paypal_base_url = 'https://history.paypal.com/cgi-bin/webscr?cmd=_history-details-from-hub&id=';
700
	$transaction_url = '<a href="' . esc_url( $paypal_base_url . $transaction_id ) . '" target="_blank">' . $transaction_id . '</a>';
701
702
	return apply_filters( 'give_paypal_link_payment_details_transaction_id', $transaction_url );
703
704
}
705
706
add_filter( 'give_payment_details_transaction_id-paypal', 'give_paypal_link_transaction_id', 10, 2 );
707
708
709
/**
710
 * Get pending donation note.
711
 *
712
 * @since 1.6.3
713
 *
714
 * @param $pending_reason
715
 *
716
 * @return string
717
 */
718
function give_get_pending_donation_note( $pending_reason ) {
719
	switch ( $pending_reason ) {
720
721
		case 'echeck' :
722
723
			$note = esc_html__( 'Payment made via eCheck and will clear automatically in 5-8 days.', 'give' );
724
725
			break;
726
727
		case 'address' :
728
729
			$note = esc_html__( 'Payment requires a confirmed donor address and must be accepted manually through PayPal.', 'give' );
730
731
			break;
732
733
		case 'intl' :
734
735
			$note = esc_html__( 'Payment must be accepted manually through PayPal due to international account regulations.', 'give' );
736
737
			break;
738
739
		case 'multi-currency' :
740
741
			$note = esc_html__( 'Payment received in non-shop currency and must be accepted manually through PayPal.', 'give' );
742
743
			break;
744
745
		case 'paymentreview' :
746
		case 'regulatory_review' :
747
748
			$note = esc_html__( 'Payment is being reviewed by PayPal staff as high-risk or in possible violation of government regulations.', 'give' );
749
750
			break;
751
752
		case 'unilateral' :
753
754
			$note = esc_html__( 'Payment was sent to non-confirmed or non-registered email address.', 'give' );
755
756
			break;
757
758
		case 'upgrade' :
759
760
			$note = esc_html__( 'PayPal account must be upgraded before this payment can be accepted.', 'give' );
761
762
			break;
763
764
		case 'verify' :
765
766
			$note = esc_html__( 'PayPal account is not verified. Verify account in order to accept this donation.', 'give' );
767
768
			break;
769
770
		case 'other' :
771
772
			$note = esc_html__( 'Payment is pending for unknown reasons. Contact PayPal support for assistance.', 'give' );
773
774
			break;
775
776
	}
777
778
	return $note;
0 ignored issues
show
Bug introduced by
The variable $note does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
779
}