Completed
Pull Request — master (#1856)
by Mehul
17:11
created

paypal-standard.php ➔ give_process_paypal_ipn()   F

Complexity

Conditions 18
Paths 243

Size

Total Lines 151
Code Lines 72

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 342

Importance

Changes 0
Metric Value
cc 18
eloc 72
nc 243
nop 0
dl 0
loc 151
ccs 0
cts 77
cp 0
crap 342
rs 3.7884
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 25 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     https://opensource.org/licenses/gpl-license GNU Public License
9
 * @since       1.0
10
 */
11
12
if ( ! defined( 'ABSPATH' ) ) {
13
	exit;
14
}
15
16
/**
17
 * Toggle PayPal CC Billing Detail Fieldset.
18
 *
19
 * @since  1.8.5
20
 *
21
 * @param $form_id
22
 *
23
 * @return bool
24
 */
25
function give_paypal_standard_billing_fields( $form_id ) {
26
27
	if ( give_is_setting_enabled( give_get_option( 'paypal_standard_billing_details' ) ) ) {
28
		give_default_cc_address_fields( $form_id );
29
30
		return true;
31
	}
32
33
	return false;
34
35
}
36
37
add_action( 'give_paypal_cc_form', 'give_paypal_standard_billing_fields' );
38
39
/**
40
 * Process PayPal Payment.
41
 *
42
 * @since 1.0
43
 *
44
 * @param array $payment_data Payment data.
45
 *
46
 * @return void
47
 */
48
function give_process_paypal_payment( $payment_data ) {
49
50
	// Validate nonce.
51
	give_validate_nonce( $payment_data['gateway_nonce'], 'give-gateway' );
52
	$payment_id = give_create_payment( $payment_data );
53
54
	// Check payment.
55
	if ( empty( $payment_id ) ) {
56
		// Record the error.
57
		give_record_gateway_error( __( 'Payment Error', 'give' ), sprintf( /* translators: %s: payment data */
58
		__( 'Payment creation failed before sending donor to PayPal. Payment data: %s', 'give' ), json_encode( $payment_data ) ), $payment_id );
59
		// Problems? Send back.
60
		give_send_back_to_checkout( '?payment-mode=' . $payment_data['post_data']['give-gateway'] );
61
	}
62
63
	// Redirect to PayPal.
64
	wp_redirect( give_build_paypal_url( $payment_id, $payment_data ) );
65
	exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The function give_process_paypal_payment() 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...
66
}
67
68
add_action( 'give_gateway_paypal', 'give_process_paypal_payment' );
69
70
/**
71
 * Listens for a PayPal IPN requests and then sends to the processing function.
72
 *
73
 * @since 1.0
74
 * @return void
75
 */
76
function give_listen_for_paypal_ipn() {
77
	// Regular PayPal IPN
78
	if ( isset( $_GET['give-listener'] ) && $_GET['give-listener'] == 'IPN' ) {
79
		/**
80
		 * Fires while verifying PayPal IPN
81
		 *
82
		 * @since 1.0
83
		 */
84
		do_action( 'give_verify_paypal_ipn' );
85
	}
86
}
87
88
add_action( 'init', 'give_listen_for_paypal_ipn' );
89
90
/**
91
 * Process PayPal IPN
92
 *
93
 * @since 1.0
94
 * @return void
95
 */
96
function give_process_paypal_ipn() {
97
98
	// Check the request method is POST.
99
	if ( isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' !== $_SERVER['REQUEST_METHOD'] ) {
100
		return;
101
	}
102
103
	// Set initial post data to empty string.
104
	$post_data = '';
105
106
	// Fallback just in case post_max_size is lower than needed.
107
	if ( ini_get( 'allow_url_fopen' ) ) {
108
		$post_data = file_get_contents( 'php://input' );
109
	} else {
110
		// If allow_url_fopen is not enabled, then make sure that post_max_size is large enough.
111
		ini_set( 'post_max_size', '12M' );
112
	}
113
	// Start the encoded data collection with notification command.
114
	$encoded_data = 'cmd=_notify-validate';
115
116
	// Get current arg separator.
117
	$arg_separator = give_get_php_arg_separator_output();
118
119
	// Verify there is a post_data.
120
	if ( $post_data || strlen( $post_data ) > 0 ) {
121
		// Append the data.
122
		$encoded_data .= $arg_separator . $post_data;
123
	} else {
124
		// Check if POST is empty.
125
		if ( empty( $_POST ) ) {
126
			// Nothing to do.
127
			return;
128
		} else {
129
			// Loop through each POST.
130
			foreach ( $_POST as $key => $value ) {
131
				// Encode the value and append the data.
132
				$encoded_data .= $arg_separator . "$key=" . urlencode( $value );
133
			}
134
		}
135
	}
136
137
	// Convert collected post data to an array.
138
	parse_str( $encoded_data, $encoded_data_array );
139
140
	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...
141
142
		if ( false !== strpos( $key, 'amp;' ) ) {
143
			$new_key = str_replace( '&amp;', '&', $key );
144
			$new_key = str_replace( 'amp;', '&', $new_key );
145
146
			unset( $encoded_data_array[ $key ] );
147
			$encoded_data_array[ $new_key ] = $value;
148
		}
149
	}
150
151
	// Validate IPN request w/ PayPal if user hasn't disabled this security measure.
152
	if ( give_is_setting_enabled( give_get_option( 'paypal_verification' ) ) ) {
153
154
		$remote_post_vars = array(
155
			'method'      => 'POST',
156
			'timeout'     => 45,
157
			'redirection' => 5,
158
			'httpversion' => '1.1',
159
			'blocking'    => true,
160
			'headers'     => array(
161
				'host'         => 'www.paypal.com',
162
				'connection'   => 'close',
163
				'content-type' => 'application/x-www-form-urlencoded',
164
				'post'         => '/cgi-bin/webscr HTTP/1.1',
165
166
			),
167
			'sslverify'   => false,
168
			'body'        => $encoded_data_array,
169
		);
170
171
		// Validate the IPN.
172
		$api_response = wp_remote_post( give_get_paypal_redirect(), $remote_post_vars );
173
174
		if ( is_wp_error( $api_response ) ) {
175
			give_record_gateway_error( __( 'IPN Error', 'give' ), sprintf( /* translators: %s: Paypal IPN response */
176
			__( 'Invalid IPN verification response. IPN data: %s', 'give' ), json_encode( $api_response ) ) );
177
178
			return; // Something went wrong
179
		}
180
181
		if ( 'VERIFIED' !== $api_response['body'] ) {
182
			give_record_gateway_error( __( 'IPN Error', 'give' ), sprintf( /* translators: %s: Paypal IPN response */
183
			__( 'Invalid IPN verification response. IPN data: %s', 'give' ), json_encode( $api_response ) ) );
184
185
			return; // Response not okay.
186
		}
187
	}// End if().
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
188
189
	// Check if $post_data_array has been populated.
190
	if ( ! is_array( $encoded_data_array ) && ! empty( $encoded_data_array ) ) {
191
		return;
192
	}
193
194
	$defaults = array(
195
		'txn_type'       => '',
196
		'payment_status' => '',
197
	);
198
199
	$encoded_data_array = wp_parse_args( $encoded_data_array, $defaults );
200
201
	$payment_id = isset( $encoded_data_array['custom'] ) ? absint( $encoded_data_array['custom'] ) : 0;
202
	$txn_type   = $encoded_data_array['txn_type'];
203
204
	// Check for PayPal IPN Notifications and update data based on it.
205
	$current_timestamp = current_time( 'timestamp' );
206
	$paypal_ipn_vars = array(
207
		'auth_status'    => ( $api_response['body'] ) ? $api_response['body'] : 'N/A',
0 ignored issues
show
Bug introduced by
The variable $api_response 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...
208
		'transaction_id' => $encoded_data_array['txn_id'],
209
		'payment_id'     => $payment_id,
210
	);
211
	update_option( 'give_last_paypal_ipn_received', $paypal_ipn_vars );
212
	give_insert_payment_note( $payment_id, sprintf(
213
				__( 'Last IPN received on %s at %s', 'give' ),
214
				date_i18n( 'm/d/Y', $current_timestamp ),
215
				date_i18n( 'H:i', $current_timestamp )
216
			)
217
	);
218
	give_update_meta( $payment_id, 'give_last_paypal_ipn_received', $current_timestamp );
219
220
	if ( has_action( 'give_paypal_' . $txn_type ) ) {
221
		/**
222
		 * Fires while processing PayPal IPN $txn_type.
223
		 *
224
		 * Allow PayPal IPN types to be processed separately.
225
		 *
226
		 * @since 1.0
227
		 *
228
		 * @param array $encoded_data_array Encoded data.
229
		 * @param int   $payment_id         Payment id.
230
		 */
231
		do_action( "give_paypal_{$txn_type}", $encoded_data_array, $payment_id );
232
	} else {
233
		/**
234
		 * Fires while process PayPal IPN.
235
		 *
236
		 * Fallback to web accept just in case the txn_type isn't present.
237
		 *
238
		 * @since 1.0
239
		 *
240
		 * @param array $encoded_data_array Encoded data.
241
		 * @param int   $payment_id         Payment id.
242
		 */
243
		do_action( 'give_paypal_web_accept', $encoded_data_array, $payment_id );
244
	}
245
	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...
246
}
247
248
add_action( 'give_verify_paypal_ipn', 'give_process_paypal_ipn' );
249
250
/**
251
 * Process web accept (one time) payment IPNs.
252
 *
253
 * @since 1.0
254
 *
255
 * @param array $data       The IPN Data.
256
 * @param int   $payment_id The payment ID from Give.
257
 *
258
 * @return void
259
 */
260
function give_process_paypal_web_accept( $data, $payment_id ) {
261
262
	// Only allow through these transaction types.
263
	if ( 'web_accept' !== $data['txn_type'] && 'cart' !== $data['txn_type'] && 'refunded' !== strtolower( $data['payment_status'] ) ) {
264
		return;
265
	}
266
267
	// Need $payment_id to continue.
268
	if ( empty( $payment_id ) ) {
269
		return;
270
	}
271
272
	// Collect donation payment details.
273
	$paypal_amount  = $data['mc_gross'];
274
	$payment_status = strtolower( $data['payment_status'] );
275
	$currency_code  = strtolower( $data['mc_currency'] );
276
	$business_email = isset( $data['business'] ) && is_email( $data['business'] ) ? trim( $data['business'] ) : trim( $data['receiver_email'] );
277
	$payment_meta   = give_get_payment_meta( $payment_id );
278
279
	// Must be a PayPal standard IPN.
280
	if ( 'paypal' !== give_get_payment_gateway( $payment_id ) ) {
281
		return;
282
	}
283
284
	// Verify payment recipient
285
	if ( strcasecmp( $business_email, trim( give_get_option( 'paypal_email' ) ) ) !== 0 ) {
286
287
		give_record_gateway_error( __( 'IPN Error', 'give' ), sprintf( /* translators: %s: Paypal IPN response */
288
		__( 'Invalid business email in IPN response. IPN data: %s', 'give' ), json_encode( $data ) ), $payment_id );
289
		give_update_payment_status( $payment_id, 'failed' );
290
		give_insert_payment_note( $payment_id, __( 'Payment failed due to invalid PayPal business email.', 'give' ) );
291
292
		return;
293
	}
294
295
	// Verify payment currency.
296
	if ( $currency_code !== strtolower( $payment_meta['currency'] ) ) {
297
298
		give_record_gateway_error( __( 'IPN Error', 'give' ), sprintf( /* translators: %s: Paypal IPN response */
299
		__( 'Invalid currency in IPN response. IPN data: %s', 'give' ), json_encode( $data ) ), $payment_id );
300
		give_update_payment_status( $payment_id, 'failed' );
301
		give_insert_payment_note( $payment_id, __( 'Payment failed due to invalid currency in PayPal IPN.', 'give' ) );
302
303
		return;
304
	}
305
306
	// Process refunds & reversed.
307
	if ( 'refunded' === $payment_status || 'reversed' === $payment_status ) {
308
		give_process_paypal_refund( $data, $payment_id );
309
310
		return;
311
	}
312
313
	// Only complete payments once.
314
	if ( 'publish' === get_post_status( $payment_id ) ) {
315
		return;
316
	}
317
318
	// Retrieve the total donation amount (before PayPal).
319
	$payment_amount = give_get_payment_amount( $payment_id );
320
321
	// Check that the donation PP and local db amounts match.
322
	if ( number_format( (float) $paypal_amount, 2 ) < number_format( (float) $payment_amount, 2 ) ) {
323
		// The prices don't match
324
		give_record_gateway_error( __( 'IPN Error', 'give' ), sprintf( /* translators: %s: Paypal IPN response */
325
		__( 'Invalid payment amount in IPN response. IPN data: %s', 'give' ), json_encode( $data ) ), $payment_id );
326
		give_update_payment_status( $payment_id, 'failed' );
327
		give_insert_payment_note( $payment_id, __( 'Payment failed due to invalid amount in PayPal IPN.', 'give' ) );
328
329
		return;
330
	}
331
332
	// Process completed donations.
333
	if ( 'completed' === $payment_status || give_is_test_mode() ) {
334
335
		give_insert_payment_note( $payment_id, sprintf( /* translators: %s: Paypal transaction ID */
336
		__( 'PayPal Transaction ID: %s', 'give' ), $data['txn_id'] ) );
337
		give_set_payment_transaction_id( $payment_id, $data['txn_id'] );
338
		give_update_payment_status( $payment_id, 'publish' );
339
340
	} elseif ( 'pending' === $payment_status && isset( $data['pending_reason'] ) ) {
341
342
		// Look for possible pending reasons, such as an echeck.
343
		$note = give_paypal_get_pending_donation_note( strtolower( $data['pending_reason'] ) );
344
345
		if ( ! empty( $note ) ) {
346
			give_insert_payment_note( $payment_id, $note );
347
		}
348
	}
349
350
}
351
352
add_action( 'give_paypal_web_accept', 'give_process_paypal_web_accept', 10, 2 );
353
354
/**
355
 * Process PayPal IPN Refunds
356
 *
357
 * @since 1.0
358
 *
359
 * @param array $data       IPN Data
360
 * @param int   $payment_id The payment ID.
361
 *
362
 * @return void
363
 */
364
function give_process_paypal_refund( $data, $payment_id = 0 ) {
365
366
	// Collect payment details.
367
	if ( empty( $payment_id ) ) {
368
		return;
369
	}
370
371
	// Only refund payments once.
372
	if ( 'refunded' === get_post_status( $payment_id ) ) {
373
		return;
374
	}
375
376
	$payment_amount = give_get_payment_amount( $payment_id );
377
	$refund_amount  = $data['payment_gross'] * - 1;
378
379
	if ( number_format( (float) $refund_amount, 2 ) < number_format( (float) $payment_amount, 2 ) ) {
380
381
		give_insert_payment_note( $payment_id, sprintf( /* translators: %s: Paypal parent transaction ID */
382
		__( 'Partial PayPal refund processed: %s', 'give' ), $data['parent_txn_id'] ) );
383
384
		return; // This is a partial refund
385
386
	}
387
388
	give_insert_payment_note( $payment_id, sprintf( /* translators: 1: Paypal parent transaction ID 2. Paypal reason code */
389
	__( 'PayPal Payment #%1$s Refunded for reason: %2$s', 'give' ), $data['parent_txn_id'], $data['reason_code'] ) );
390
	give_insert_payment_note( $payment_id, sprintf( /* translators: %s: Paypal transaction ID */
391
	__( 'PayPal Refund Transaction ID: %s', 'give' ), $data['txn_id'] ) );
392
	give_update_payment_status( $payment_id, 'refunded' );
393
}
394
395
/**
396
 * Get PayPal Redirect
397
 *
398
 * @since 1.0
399
 *
400
 * @param bool $ssl_check Is SSL?
401
 *
402
 * @return string
403
 */
404
function give_get_paypal_redirect( $ssl_check = false ) {
405
406
	if ( is_ssl() || ! $ssl_check ) {
407
		$protocol = 'https://';
408
	} else {
409
		$protocol = 'http://';
410
	}
411
412
	// Check the current payment mode
413
	if ( give_is_test_mode() ) {
414
		// Test mode
415
		$paypal_uri = $protocol . 'www.sandbox.paypal.com/cgi-bin/webscr';
416
	} else {
417
		// Live mode
418
		$paypal_uri = $protocol . 'www.paypal.com/cgi-bin/webscr';
419
	}
420
421
	return apply_filters( 'give_paypal_uri', $paypal_uri );
422
}
423
424
/**
425
 * Set the Page Style for offsite PayPal page.
426
 *
427
 * @since 1.0
428
 * @return string
429
 */
430
function give_get_paypal_page_style() {
431
	$page_style = trim( give_get_option( 'paypal_page_style', 'PayPal' ) );
432
433
	return apply_filters( 'give_paypal_page_style', $page_style );
434
}
435
436
/**
437
 * PayPal Success Page
438
 *
439
 * Shows "Donation Processing" message for PayPal payments that are still pending on site return
440
 *
441
 * @since      1.0
442
 *
443
 * @param $content
444
 *
445
 * @return string
446
 */
447
function give_paypal_success_page_content( $content ) {
448
449
	if ( ! isset( $_GET['payment-id'] ) && ! give_get_purchase_session() ) {
450
		return $content;
451
	}
452
453
	$payment_id = isset( $_GET['payment-id'] ) ? absint( $_GET['payment-id'] ) : false;
454
455
	if ( ! $payment_id ) {
456
		$session    = give_get_purchase_session();
457
		$payment_id = give_get_purchase_id_by_key( $session['purchase_key'] );
458
	}
459
460
	$payment = get_post( $payment_id );
461
	if ( $payment && 'pending' === $payment->post_status ) {
462
463
		// Payment is still pending so show processing indicator to fix the race condition.
464
		ob_start();
465
466
		give_get_template_part( 'payment', 'processing' );
467
468
		$content = ob_get_clean();
469
470
	}
471
472
	return $content;
473
474
}
475
476
add_filter( 'give_payment_confirm_paypal', 'give_paypal_success_page_content' );
477
478
/**
479
 * Given a Payment ID, extract the transaction ID
480
 *
481
 * @since  1.0
482
 *
483
 * @param  string $payment_id Payment ID
484
 *
485
 * @return string                   Transaction ID
486
 */
487
function give_paypal_get_payment_transaction_id( $payment_id ) {
488
489
	$transaction_id = '';
490
	$notes          = give_get_payment_notes( $payment_id );
491
492
	foreach ( $notes as $note ) {
493
		if ( preg_match( '/^PayPal Transaction ID: ([^\s]+)/', $note->comment_content, $match ) ) {
494
			$transaction_id = $match[1];
495
			continue;
496
		}
497
	}
498
499
	return apply_filters( 'give_paypal_set_payment_transaction_id', $transaction_id, $payment_id );
500
}
501
502
add_filter( 'give_get_payment_transaction_id-paypal', 'give_paypal_get_payment_transaction_id', 10, 1 );
503
504
/**
505
 * Given a transaction ID, generate a link to the PayPal transaction ID details
506
 *
507
 * @since  1.0
508
 *
509
 * @param  string $transaction_id The Transaction ID
510
 * @param  int    $payment_id     The payment ID for this transaction
511
 *
512
 * @return string                 A link to the PayPal transaction details
513
 */
514
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...
515
516
	$paypal_base_url = 'https://history.paypal.com/cgi-bin/webscr?cmd=_history-details-from-hub&id=';
517
	$transaction_url = '<a href="' . esc_url( $paypal_base_url . $transaction_id ) . '" target="_blank">' . $transaction_id . '</a>';
518
519
	return apply_filters( 'give_paypal_link_payment_details_transaction_id', $transaction_url );
520
521
}
522
523
add_filter( 'give_payment_details_transaction_id-paypal', 'give_paypal_link_transaction_id', 10, 2 );
524
525
526
/**
527
 * Get pending donation note.
528
 *
529
 * @since 1.6.3
530
 *
531
 * @param $pending_reason
532
 *
533
 * @return string
534
 */
535
function give_paypal_get_pending_donation_note( $pending_reason ) {
536
537
	$note = '';
538
539
	switch ( $pending_reason ) {
540
541
		case 'echeck' :
542
543
			$note = __( 'Payment made via eCheck and will clear automatically in 5-8 days.', 'give' );
544
545
			break;
546
547
		case 'address' :
548
549
			$note = __( 'Payment requires a confirmed donor address and must be accepted manually through PayPal.', 'give' );
550
551
			break;
552
553
		case 'intl' :
554
555
			$note = __( 'Payment must be accepted manually through PayPal due to international account regulations.', 'give' );
556
557
			break;
558
559
		case 'multi-currency' :
560
561
			$note = __( 'Payment received in non-shop currency and must be accepted manually through PayPal.', 'give' );
562
563
			break;
564
565
		case 'paymentreview' :
566
		case 'regulatory_review' :
567
568
			$note = __( 'Payment is being reviewed by PayPal staff as high-risk or in possible violation of government regulations.', 'give' );
569
570
			break;
571
572
		case 'unilateral' :
573
574
			$note = __( 'Payment was sent to non-confirmed or non-registered email address.', 'give' );
575
576
			break;
577
578
		case 'upgrade' :
579
580
			$note = __( 'PayPal account must be upgraded before this payment can be accepted.', 'give' );
581
582
			break;
583
584
		case 'verify' :
585
586
			$note = __( 'PayPal account is not verified. Verify account in order to accept this donation.', 'give' );
587
588
			break;
589
590
		case 'other' :
591
592
			$note = __( 'Payment is pending for unknown reasons. Contact PayPal support for assistance.', 'give' );
593
594
			break;
595
596
	}// End switch().
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
597
598
	return $note;
599
600
}
601
602
/**
603
 * Build paypal url
604
 *
605
 * @param int   $payment_id   Payment ID
606
 * @param array $payment_data Array of payment data.
607
 *
608
 * @return mixed|string
609
 */
610
function give_build_paypal_url( $payment_id, $payment_data ) {
611
	// Only send to PayPal if the pending payment is created successfully.
612
	$listener_url = add_query_arg( 'give-listener', 'IPN', home_url( 'index.php' ) );
613
614
	// Get the success url.
615
	$return_url = add_query_arg( array(
616
		'payment-confirmation' => 'paypal',
617
		'payment-id'           => $payment_id,
618
619
	), get_permalink( give_get_option( 'success_page' ) ) );
620
621
	// Get the PayPal redirect uri.
622
	$paypal_redirect = trailingslashit( give_get_paypal_redirect() ) . '?';
623
624
	// Item name.
625
	$item_name = give_build_paypal_item_title( $payment_data );
626
627
	// Setup PayPal API params.
628
	$paypal_args = array(
629
		'business'      => give_get_option( 'paypal_email', false ),
630
		'first_name'    => $payment_data['user_info']['first_name'],
631
		'last_name'     => $payment_data['user_info']['last_name'],
632
		'email'         => $payment_data['user_email'],
633
		'invoice'       => $payment_data['purchase_key'],
634
		'amount'        => $payment_data['price'],
635
		'item_name'     => stripslashes( $item_name ),
636
		'no_shipping'   => '1',
637
		'shipping'      => '0',
638
		'no_note'       => '1',
639
		'currency_code' => give_get_currency(),
640
		'charset'       => get_bloginfo( 'charset' ),
641
		'custom'        => $payment_id,
642
		'rm'            => '2',
643
		'return'        => $return_url,
644
		'cancel_return' => give_get_failed_transaction_uri( '?payment-id=' . $payment_id ),
645
		'notify_url'    => $listener_url,
646
		'page_style'    => give_get_paypal_page_style(),
647
		'cbt'           => get_bloginfo( 'name' ),
648
		'bn'            => 'givewp_SP',
649
	);
650
651
	// Add user address if present.
652
	if ( ! empty( $payment_data['user_info']['address'] ) ) {
653
		$default_address = array(
654
			'line1'   => '',
655
			'line2'   => '',
656
			'city'    => '',
657
			'state'   => '',
658
			'zip'     => '',
659
			'country' => '',
660
		);
661
662
		$address = wp_parse_args( $payment_data['user_info']['address'], $default_address );
663
664
		$paypal_args['address1'] = $address['line1'];
665
		$paypal_args['address2'] = $address['line2'];
666
		$paypal_args['city']     = $address['city'];
667
		$paypal_args['state']    = $address['state'];
668
		$paypal_args['zip']      = $address['zip'];
669
		$paypal_args['country']  = $address['country'];
670
	}
671
672
	// Donations or regular transactions?
673
	$paypal_args['cmd'] = give_get_paypal_button_type();
674
675
	/**
676
	 * Filter the paypal redirect args.
677
	 *
678
	 * @since 1.8
679
	 *
680
	 * @param array $paypal_args
681
	 * @param array $payment_data
682
	 */
683
	$paypal_args = apply_filters( 'give_paypal_redirect_args', $paypal_args, $payment_data );
684
685
	// Build query.
686
	$paypal_redirect .= http_build_query( $paypal_args );
687
688
	// Fix for some sites that encode the entities.
689
	$paypal_redirect = str_replace( '&amp;', '&', $paypal_redirect );
690
691
	return $paypal_redirect;
692
}
693
694
695
/**
696
 * Get paypal button type.
697
 *
698
 * @since 1.8
699
 * @return string
700
 */
701
function give_get_paypal_button_type() {
702
	// paypal_button_type can be donation or standard.
703
	$paypal_button_type = '_donations';
704
	if ( 'standard' === give_get_option( 'paypal_button_type' ) ) {
705
		$paypal_button_type = '_xclick';
706
	}
707
708
	return $paypal_button_type;
709
}
710
711
712
/**
713
 * Build item title for paypal.
714
 *
715
 * @since 1.8
716
 *
717
 * @param $payment_data
718
 *
719
 * @return string
720
 */
721
function give_build_paypal_item_title( $payment_data ) {
722
	$form_id   = intval( $payment_data['post_data']['give-form-id'] );
723
	$item_name = $payment_data['post_data']['give-form-title'];
724
725
	// Verify has variable prices.
726
	if ( give_has_variable_prices( $form_id ) && isset( $payment_data['post_data']['give-price-id'] ) ) {
727
728
		$item_price_level_text = give_get_price_option_name( $form_id, $payment_data['post_data']['give-price-id'] );
729
		$price_level_amount    = give_get_price_option_amount( $form_id, $payment_data['post_data']['give-price-id'] );
730
731
		// Donation given doesn't match selected level (must be a custom amount).
732
		if ( $price_level_amount != give_sanitize_amount( $payment_data['price'] ) ) {
733
			$custom_amount_text = give_get_meta( $form_id, '_give_custom_amount_text', true );
734
			// user custom amount text if any, fallback to default if not.
735
			$item_name .= ' - ' . give_check_variable( $custom_amount_text, 'empty', __( 'Custom Amount', 'give' ) );
736
737
		} elseif ( ! empty( $item_price_level_text ) ) {
738
			$item_name .= ' - ' . $item_price_level_text;
739
		}
740
	} // End if().
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
741
	elseif ( give_get_form_price( $form_id ) !== give_sanitize_amount( $payment_data['price'] ) ) {
742
		$custom_amount_text = give_get_meta( $form_id, '_give_custom_amount_text', true );
743
		// user custom amount text if any, fallback to default if not.
744
		$item_name .= ' - ' . give_check_variable( $custom_amount_text, 'empty', __( 'Custom Amount', 'give' ) );
745
	}
746
747
	return $item_name;
748
}
749