paypal-standard.php ➔ give_process_paypal_web_accept()   F
last analyzed

Complexity

Conditions 19
Paths 42

Size

Total Lines 103

Duplication

Lines 27
Ratio 26.21 %

Importance

Changes 0
Metric Value
cc 19
nc 42
nop 2
dl 27
loc 103
rs 3.6133
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
2
/**
3
 * PayPal Standard Gateway
4
 *
5
 * @package     Give
6
 * @subpackage  Gateways
7
 * @copyright   Copyright (c) 2016, GiveWP
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
 * @param int $form_id Form ID.
20
 *
21
 * @return bool
22
 * @since 1.8.5
23
 *
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
 * @param array $payment_data Payment data.
43
 *
44
 * @return void
45
 * @since 1.0
46
 *
47
 */
48
function give_process_paypal_payment( $payment_data ) {
49
50
	// Validate nonce.
51
	give_validate_nonce( $payment_data['gateway_nonce'], 'give-gateway' );
52
53
	$payment_id = give_create_payment( $payment_data );
54
55
	// Check payment.
56
	if ( empty( $payment_id ) ) {
57
		// Record the error.
58
		give_record_gateway_error(
59
			__( 'Payment Error', 'give' ), sprintf( /* translators: %s: payment data */
60
			__( 'Payment creation failed before sending donor to PayPal. Payment data: %s', 'give' ), json_encode( $payment_data )
61
		), $payment_id
0 ignored issues
show
Security Bug introduced by
It seems like $payment_id defined by give_create_payment($payment_data) on line 53 can also be of type false; however, give_record_gateway_error() does only seem to accept integer, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
62
		);
63
		// Problems? Send back.
64
		give_send_back_to_checkout( '?payment-mode=' . $payment_data['post_data']['give-gateway'] );
65
	}
66
67
	// Redirect to PayPal.
68
	wp_redirect( give_build_paypal_url( $payment_id, $payment_data ) );
0 ignored issues
show
Security Bug introduced by
It seems like $payment_id defined by give_create_payment($payment_data) on line 53 can also be of type false; however, give_build_paypal_url() does only seem to accept integer, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
69
	exit;
70
}
71
72
add_action( 'give_gateway_paypal', 'give_process_paypal_payment' );
73
74
/**
75
 * Listens for a PayPal IPN requests and then sends to the processing function.
76
 *
77
 * @return void
78
 * @since 1.0
79
 */
80
function give_listen_for_paypal_ipn() {
81
82
	// Regular PayPal IPN.
83
	if ( isset( $_GET['give-listener'] ) && 'IPN' === $_GET['give-listener'] ) {
84
		/**
85
		 * Fires while verifying PayPal IPN
86
		 *
87
		 * @since 1.0
88
		 */
89
		do_action( 'give_verify_paypal_ipn' );
90
	}
91
}
92
93
add_action( 'init', 'give_listen_for_paypal_ipn' );
94
95
/**
96
 * Process PayPal IPN
97
 *
98
 * @return void
99
 * @since 1.0
100
 */
101
function give_process_paypal_ipn() {
102
103
	// Check the request method is POST.
104
	if ( isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' !== $_SERVER['REQUEST_METHOD'] ) {
105
		return;
106
	}
107
108
	// Set initial post data to empty string.
109
	$post_data = '';
110
111
	// Fallback just in case post_max_size is lower than needed.
112
	if ( ini_get( 'allow_url_fopen' ) ) {
113
		$post_data = file_get_contents( 'php://input' );
114
	} else {
115
		// If allow_url_fopen is not enabled, then make sure that post_max_size is large enough.
116
		ini_set( 'post_max_size', '12M' );
117
	}
118
	// Start the encoded data collection with notification command.
119
	$encoded_data = 'cmd=_notify-validate';
120
121
	// Get current arg separator.
122
	$arg_separator = give_get_php_arg_separator_output();
123
124
	// Verify there is a post_data.
125
	if ( $post_data || strlen( $post_data ) > 0 ) {
126
		// Append the data.
127
		$encoded_data .= $arg_separator . $post_data;
128
	} else {
129
		// Check if POST is empty.
130
		if ( empty( $_POST ) ) {
131
			// Nothing to do.
132
			return;
133
		} else {
134
			// Loop through each POST.
135
			foreach ( $_POST as $key => $value ) {
136
				// Encode the value and append the data.
137
				$encoded_data .= $arg_separator . "$key=" . urlencode( $value );
138
			}
139
		}
140
	}
141
142
	// Convert collected post data to an array.
143
	parse_str( $encoded_data, $encoded_data_array );
144
145
	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...
146
147
		if ( false !== strpos( $key, 'amp;' ) ) {
148
			$new_key = str_replace( '&amp;', '&', $key );
149
			$new_key = str_replace( 'amp;', '&', $new_key );
150
151
			unset( $encoded_data_array[ $key ] );
152
			$encoded_data_array[ $new_key ] = $value;
153
		}
154
	}
155
156
	$api_response = false;
157
158
	// Validate IPN request w/ PayPal if user hasn't disabled this security measure.
159
	if ( give_is_setting_enabled( give_get_option( 'paypal_verification' ) ) ) {
160
161
		$remote_post_vars = array(
162
			'method'      => 'POST',
163
			'timeout'     => 45,
164
			'redirection' => 5,
165
			'httpversion' => '1.1',
166
			'blocking'    => true,
167
			'headers'     => array(
168
				'host'         => 'www.paypal.com',
169
				'connection'   => 'close',
170
				'content-type' => 'application/x-www-form-urlencoded',
171
				'post'         => '/cgi-bin/webscr HTTP/1.1',
172
173
			),
174
			'sslverify'   => false,
175
			'body'        => $encoded_data_array,
176
		);
177
178
		// Validate the IPN.
179
		$api_response = wp_remote_post( give_get_paypal_redirect(), $remote_post_vars );
180
181 View Code Duplication
		if ( is_wp_error( $api_response ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
182
			give_record_gateway_error(
183
				__( 'IPN Error', 'give' ), sprintf( /* translators: %s: Paypal IPN response */
184
					__( 'Invalid IPN verification response. IPN data: %s', 'give' ), json_encode( $api_response )
185
				)
186
			);
187
188
			return; // Something went wrong.
189
		}
190
191 View Code Duplication
		if ( 'VERIFIED' !== $api_response['body'] ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
192
			give_record_gateway_error(
193
				__( 'IPN Error', 'give' ), sprintf( /* translators: %s: Paypal IPN response */
194
					__( 'Invalid IPN verification response. IPN data: %s', 'give' ), json_encode( $api_response )
195
				)
196
			);
197
198
			return; // Response not okay.
199
		}
200
	}// End if().
201
202
	// Check if $post_data_array has been populated.
203
	if ( ! is_array( $encoded_data_array ) && ! empty( $encoded_data_array ) ) {
204
		return;
205
	}
206
207
	$defaults = array(
208
		'txn_type'       => '',
209
		'payment_status' => '',
210
	);
211
212
	$encoded_data_array = wp_parse_args( $encoded_data_array, $defaults );
213
214
	$payment_id = isset( $encoded_data_array['custom'] ) ? absint( $encoded_data_array['custom'] ) : 0;
215
	$txn_type   = $encoded_data_array['txn_type'];
216
217
	// Check for PayPal IPN Notifications and update data based on it.
218
	$current_timestamp = current_time( 'timestamp' );
219
	$paypal_ipn_vars   = array(
220
		'auth_status'    => isset( $api_response['body'] ) ? $api_response['body'] : 'N/A',
221
		'transaction_id' => isset( $encoded_data_array['txn_id'] ) ? $encoded_data_array['txn_id'] : 'N/A',
222
		'payment_id'     => $payment_id,
223
	);
224
	update_option( 'give_last_paypal_ipn_received', $paypal_ipn_vars, false );
225
	give_insert_payment_note(
226
		$payment_id, sprintf(
227
			__( 'IPN received on %1$s at %2$s', 'give' ),
228
			date_i18n( 'm/d/Y', $current_timestamp ),
229
			date_i18n( 'H:i', $current_timestamp )
230
		)
231
	);
232
	give_update_meta( $payment_id, 'give_last_paypal_ipn_received', $current_timestamp );
233
234
	if ( has_action( 'give_paypal_' . $txn_type ) ) {
235
		/**
236
		 * Fires while processing PayPal IPN $txn_type.
237
		 *
238
		 * Allow PayPal IPN types to be processed separately.
239
		 *
240
		 * @param array $encoded_data_array Encoded data.
241
		 * @param int   $payment_id         Payment id.
242
		 *
243
		 * @since 1.0
244
		 *
245
		 */
246
		do_action( "give_paypal_{$txn_type}", $encoded_data_array, $payment_id );
247
	} else {
248
		/**
249
		 * Fires while process PayPal IPN.
250
		 *
251
		 * Fallback to web accept just in case the txn_type isn't present.
252
		 *
253
		 * @param array $encoded_data_array Encoded data.
254
		 * @param int   $payment_id         Payment id.
255
		 *
256
		 * @since 1.0
257
		 *
258
		 */
259
		do_action( 'give_paypal_web_accept', $encoded_data_array, $payment_id );
260
	}
261
	exit;
262
}
263
264
add_action( 'give_verify_paypal_ipn', 'give_process_paypal_ipn' );
265
266
/**
267
 * Process web accept (one time) payment IPNs.
268
 *
269
 * @param array $data       The IPN Data.
270
 * @param int   $payment_id The payment ID from Give.
271
 *
272
 * @return void
273
 * @since 1.0
274
 *
275
 */
276
function give_process_paypal_web_accept( $data, $payment_id ) {
277
278
	// Only allow through these transaction types.
279
	if ( 'web_accept' !== $data['txn_type'] && 'cart' !== $data['txn_type'] && 'refunded' !== strtolower( $data['payment_status'] ) ) {
280
		return;
281
	}
282
283
	// Need $payment_id to continue.
284
	if ( empty( $payment_id ) ) {
285
		return;
286
	}
287
288
	// Collect donation payment details.
289
	$paypal_amount  = $data['mc_gross'];
290
	$payment_status = strtolower( $data['payment_status'] );
291
	$currency_code  = strtolower( $data['mc_currency'] );
292
	$business_email = isset( $data['business'] ) && is_email( $data['business'] ) ? trim( $data['business'] ) : trim( $data['receiver_email'] );
293
	$payment_meta   = give_get_payment_meta( $payment_id );
294
295
	// Must be a PayPal standard IPN.
296
	if ( 'paypal' !== give_get_payment_gateway( $payment_id ) ) {
297
		return;
298
	}
299
300
	// Verify payment recipient.
301 View Code Duplication
	if ( strcasecmp( $business_email, trim( give_get_option( 'paypal_email' ) ) ) !== 0 ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
302
303
		give_record_gateway_error(
304
			__( 'IPN Error', 'give' ), sprintf( /* translators: %s: Paypal IPN response */
305
			__( 'Invalid business email in IPN response. IPN data: %s', 'give' ), json_encode( $data )
306
		), $payment_id
307
		);
308
		give_update_payment_status( $payment_id, 'failed' );
309
		give_insert_payment_note( $payment_id, __( 'Payment failed due to invalid PayPal business email.', 'give' ) );
310
311
		return;
312
	}
313
314
	// Verify payment currency.
315 View Code Duplication
	if ( $currency_code !== strtolower( $payment_meta['currency'] ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
316
317
		give_record_gateway_error(
318
			__( 'IPN Error', 'give' ), sprintf( /* translators: %s: Paypal IPN response */
319
			__( 'Invalid currency in IPN response. IPN data: %s', 'give' ), json_encode( $data )
320
		), $payment_id
321
		);
322
		give_update_payment_status( $payment_id, 'failed' );
323
		give_insert_payment_note( $payment_id, __( 'Payment failed due to invalid currency in PayPal IPN.', 'give' ) );
324
325
		return;
326
	}
327
328
	// Process refunds & reversed.
329
	if ( 'refunded' === $payment_status || 'reversed' === $payment_status ) {
330
		give_process_paypal_refund( $data, $payment_id );
331
332
		return;
333
	}
334
335
	// Only complete payments once.
336
	if ( 'publish' === get_post_status( $payment_id ) ) {
337
		return;
338
	}
339
340
	// Retrieve the total donation amount (before PayPal).
341
	$payment_amount = give_donation_amount( $payment_id );
342
343
	// Check that the donation PP and local db amounts match.
344 View Code Duplication
	if ( number_format( (float) $paypal_amount, 2 ) < number_format( (float) $payment_amount, 2 ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
345
		// The prices don't match
346
		give_record_gateway_error(
347
			__( 'IPN Error', 'give' ), sprintf( /* translators: %s: Paypal IPN response */
348
			__( 'Invalid payment amount in IPN response. IPN data: %s', 'give' ), json_encode( $data )
349
		), $payment_id
350
		);
351
		give_update_payment_status( $payment_id, 'failed' );
352
		give_insert_payment_note( $payment_id, __( 'Payment failed due to invalid amount in PayPal IPN.', 'give' ) );
353
354
		return;
355
	}
356
357
	// Process completed donations.
358
	if ( 'completed' === $payment_status || give_is_test_mode() ) {
359
360
		give_insert_payment_note(
361
			$payment_id, sprintf( /* translators: %s: Paypal transaction ID */
362
				__( 'PayPal Transaction ID: %s', 'give' ), $data['txn_id']
363
			)
364
		);
365
		give_set_payment_transaction_id( $payment_id, $data['txn_id'] );
366
		give_update_payment_status( $payment_id, 'publish' );
367
368
	} elseif ( 'pending' === $payment_status && isset( $data['pending_reason'] ) ) {
369
370
		// Look for possible pending reasons, such as an eCheck.
371
		$note = give_paypal_get_pending_donation_note( $data['pending_reason'] );
372
373
		if ( ! empty( $note ) ) {
374
			give_insert_payment_note( $payment_id, $note );
375
		}
376
	}
377
378
}
379
380
add_action( 'give_paypal_web_accept', 'give_process_paypal_web_accept', 10, 2 );
381
382
/**
383
 * Process PayPal IPN Refunds
384
 *
385
 * @param array $data       IPN Data
386
 * @param int   $payment_id The payment ID.
387
 *
388
 * @return void
389
 * @since 1.0
390
 *
391
 */
392
function give_process_paypal_refund( $data, $payment_id = 0 ) {
393
394
	// Collect payment details.
395
	if ( empty( $payment_id ) ) {
396
		return;
397
	}
398
399
	// Only refund payments once.
400
	if ( 'refunded' === get_post_status( $payment_id ) ) {
401
		return;
402
	}
403
404
	$payment_amount = give_donation_amount( $payment_id );
405
	$refund_amount  = $data['payment_gross'] * - 1;
406
407
	if ( number_format( (float) $refund_amount, 2 ) < number_format( (float) $payment_amount, 2 ) ) {
408
409
		give_insert_payment_note(
410
			$payment_id, sprintf( /* translators: %s: Paypal parent transaction ID */
411
				__( 'Partial PayPal refund processed: %s', 'give' ), $data['parent_txn_id']
412
			)
413
		);
414
415
		return; // This is a partial refund
416
417
	}
418
419
	give_insert_payment_note(
420
		$payment_id, sprintf( /* translators: 1: Paypal parent transaction ID 2. Paypal reason code */
421
			__( 'PayPal Payment #%1$s Refunded for reason: %2$s', 'give' ), $data['parent_txn_id'], $data['reason_code']
422
		)
423
	);
424
	give_insert_payment_note(
425
		$payment_id, sprintf( /* translators: %s: Paypal transaction ID */
426
			__( 'PayPal Refund Transaction ID: %s', 'give' ), $data['txn_id']
427
		)
428
	);
429
	give_update_payment_status( $payment_id, 'refunded' );
430
}
431
432
/**
433
 * Get PayPal Redirect
434
 *
435
 * @param bool $ssl_check Is SSL?
436
 *
437
 * @return string
438
 * @since 1.0
439
 *
440
 */
441
function give_get_paypal_redirect( $ssl_check = false ) {
442
443
	if ( is_ssl() || ! $ssl_check ) {
444
		$protocol = 'https://';
445
	} else {
446
		$protocol = 'http://';
447
	}
448
449
	// Check the current payment mode
450
	if ( give_is_test_mode() ) {
451
		// Test mode
452
		$paypal_uri = $protocol . 'www.sandbox.paypal.com/cgi-bin/webscr';
453
	} else {
454
		// Live mode
455
		$paypal_uri = $protocol . 'www.paypal.com/cgi-bin/webscr';
456
	}
457
458
	return apply_filters( 'give_paypal_uri', $paypal_uri );
459
}
460
461
/**
462
 * Set the Page Style for offsite PayPal page.
463
 *
464
 * @return string
465
 * @since 1.0
466
 */
467
function give_get_paypal_page_style() {
468
	$page_style = trim( give_get_option( 'paypal_page_style', 'PayPal' ) );
469
470
	return apply_filters( 'give_paypal_page_style', $page_style );
471
}
472
473
/**
474
 * PayPal Success Page
475
 *
476
 * Shows "Donation Processing" message for PayPal payments that are still pending on site return
477
 *
478
 * @param $content
479
 *
480
 * @return string
481
 * @since      1.0
482
 *
483
 */
484
function give_paypal_success_page_content( $content ) {
485
486
	if ( ! isset( $_GET['payment-id'] ) && ! give_get_purchase_session() ) {
487
		return $content;
488
	}
489
490
	$payment_id = isset( $_GET['payment-id'] ) ? absint( $_GET['payment-id'] ) : false;
491
492
	if ( ! $payment_id ) {
493
		$session    = give_get_purchase_session();
494
		$payment_id = give_get_donation_id_by_key( $session['purchase_key'] );
495
	}
496
497
	$payment = get_post( $payment_id );
498
	if ( $payment && 'pending' === $payment->post_status ) {
499
500
		// Payment is still pending so show processing indicator to fix the race condition.
501
		ob_start();
502
503
		give_get_template_part( 'payment', 'processing' );
504
505
		$content = ob_get_clean();
506
507
	}
508
509
	return $content;
510
511
}
512
513
add_filter( 'give_payment_confirm_paypal', 'give_paypal_success_page_content' );
514
515
/**
516
 * Given a transaction ID, generate a link to the PayPal transaction ID details
517
 *
518
 * @param string $transaction_id The Transaction ID
519
 * @param int    $payment_id     The payment ID for this transaction
520
 *
521
 * @return string                 A link to the PayPal transaction details
522
 * @since  1.0
523
 *
524
 */
525
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...
526
527
	$paypal_base_url = 'https://history.paypal.com/cgi-bin/webscr?cmd=_history-details-from-hub&id=';
528
	$transaction_url = '<a href="' . esc_url( $paypal_base_url . $transaction_id ) . '" target="_blank">' . $transaction_id . '</a>';
529
530
	return apply_filters( 'give_paypal_link_payment_details_transaction_id', $transaction_url );
531
532
}
533
534
add_filter( 'give_payment_details_transaction_id-paypal', 'give_paypal_link_transaction_id', 10, 2 );
535
536
537
/**
538
 * Get pending donation note.
539
 *
540
 * @param $pending_reason
541
 *
542
 * @return string
543
 * @since 1.6.3
544
 *
545
 */
546
function give_paypal_get_pending_donation_note( $pending_reason ) {
547
548
	$note = '';
549
550
	switch ( $pending_reason ) {
551
552
		case 'echeck':
553
			$note = __( 'Payment made via eCheck and will clear automatically in 5-8 days.', 'give' );
554
			break;
555
556
		case 'address':
557
			$note = __( 'Payment requires a confirmed donor address and must be accepted manually through PayPal.', 'give' );
558
			break;
559
560
		case 'intl':
561
			$note = __( 'Payment must be accepted manually through PayPal due to international account regulations.', 'give' );
562
			break;
563
564
		case 'multi-currency':
565
			$note = __( 'Payment received in non-shop currency and must be accepted manually through PayPal.', 'give' );
566
			break;
567
568
		case 'paymentreview':
569
		case 'regulatory_review':
570
			$note = __( 'Payment is being reviewed by PayPal staff as high-risk or in possible violation of government regulations.', 'give' );
571
			break;
572
573
		case 'unilateral':
574
			$note = __( 'Payment was sent to non-confirmed or non-registered email address.', 'give' );
575
			break;
576
577
		case 'upgrade':
578
			$note = __( 'PayPal account must be upgraded before this payment can be accepted.', 'give' );
579
			break;
580
581
		case 'verify':
582
			$note = __( 'PayPal account is not verified. Verify account in order to accept this donation.', 'give' );
583
			break;
584
585
		case 'other':
586
			$note = __( 'Payment is pending for unknown reasons. Contact PayPal support for assistance.', 'give' );
587
			break;
588
589
	} // End switch().
590
591
	return apply_filters('give_paypal_get_pending_donation_note', $note);
592
593
}
594
595
/**
596
 * Build paypal url
597
 *
598
 * @param int   $payment_id   Payment ID
599
 * @param array $payment_data Array of payment data.
600
 *
601
 * @return mixed|string
602
 */
603
function give_build_paypal_url( $payment_id, $payment_data ) {
604
	// Only send to PayPal if the pending payment is created successfully.
605
	$listener_url = add_query_arg( 'give-listener', 'IPN', home_url( 'index.php' ) );
606
607
	// Get the success url.
608
	$return_url = add_query_arg(
609
		array(
610
			'payment-confirmation' => 'paypal',
611
			'payment-id'           => $payment_id,
612
613
		), get_permalink( give_get_option( 'success_page' ) )
614
	);
615
616
	// Get the PayPal redirect uri.
617
	$paypal_redirect = trailingslashit( give_get_paypal_redirect() ) . '?';
618
619
	// Item name.
620
	$item_name = give_payment_gateway_item_title( $payment_data );
621
622
	// Setup PayPal API params.
623
	$paypal_args = array(
624
		'business'      => give_get_option( 'paypal_email', false ),
625
		'first_name'    => $payment_data['user_info']['first_name'],
626
		'last_name'     => $payment_data['user_info']['last_name'],
627
		'email'         => $payment_data['user_email'],
628
		'invoice'       => $payment_data['purchase_key'],
629
		'amount'        => $payment_data['price'],
630
		'item_name'     => stripslashes( $item_name ),
631
		'no_shipping'   => '1',
632
		'shipping'      => '0',
633
		'no_note'       => '1',
634
		'currency_code' => give_get_currency( $payment_id, $payment_data ),
635
		'charset'       => get_bloginfo( 'charset' ),
636
		'custom'        => $payment_id,
637
		'rm'            => '2',
638
		'return'        => $return_url,
639
		'cancel_return' => give_get_failed_transaction_uri( '?payment-id=' . $payment_id ),
0 ignored issues
show
Documentation introduced by
'?payment-id=' . $payment_id is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
640
		'notify_url'    => $listener_url,
641
		'page_style'    => give_get_paypal_page_style(),
642
		'cbt'           => get_bloginfo( 'name' ),
643
		'bn'            => 'givewp_SP',
644
	);
645
646
	// Add user address if present.
647
	if ( ! empty( $payment_data['user_info']['address'] ) ) {
648
		$default_address = array(
649
			'line1'   => '',
650
			'line2'   => '',
651
			'city'    => '',
652
			'state'   => '',
653
			'zip'     => '',
654
			'country' => '',
655
		);
656
657
		$address = wp_parse_args( $payment_data['user_info']['address'], $default_address );
658
659
		$paypal_args['address1'] = $address['line1'];
660
		$paypal_args['address2'] = $address['line2'];
661
		$paypal_args['city']     = $address['city'];
662
		$paypal_args['state']    = $address['state'];
663
		$paypal_args['zip']      = $address['zip'];
664
		$paypal_args['country']  = $address['country'];
665
	}
666
667
	// Donations or regular transactions?
668
	$paypal_args['cmd'] = give_get_paypal_button_type();
669
670
	/**
671
	 * Filter the paypal redirect args.
672
	 *
673
	 * @param array $paypal_args  PayPal Arguments.
674
	 * @param array $payment_data Payment Data.
675
	 *
676
	 * @since 1.8
677
	 */
678
	$paypal_args = apply_filters( 'give_paypal_redirect_args', $paypal_args, $payment_data );
679
680
	// Build query.
681
	$paypal_redirect .= http_build_query( $paypal_args );
682
683
	// Fix for some sites that encode the entities.
684
	$paypal_redirect = str_replace( '&amp;', '&', $paypal_redirect );
685
686
	return $paypal_redirect;
687
}
688
689
690
/**
691
 * Get paypal button type.
692
 *
693
 * @return string
694
 * @since 1.8
695
 */
696
function give_get_paypal_button_type() {
697
	// paypal_button_type can be donation or standard.
698
	$paypal_button_type = '_donations';
699
	if ( 'standard' === give_get_option( 'paypal_button_type' ) ) {
700
		$paypal_button_type = '_xclick';
701
	}
702
703
	return $paypal_button_type;
704
}
705
706
/**
707
 * Update Purchase key for specific gateway.
708
 *
709
 * @param string $custom_purchase_key
710
 * @param string $gateway
711
 * @param string $purchase_key
712
 *
713
 * @return string
714
 * @since 2.2.4
715
 *
716
 */
717
function give_paypal_purchase_key( $custom_purchase_key, $gateway, $purchase_key ) {
718
719
	if ( 'paypal' === $gateway ) {
720
		$invoice_id_prefix   = give_get_option( 'paypal_invoice_prefix', 'GIVE-' );
721
		$custom_purchase_key = $invoice_id_prefix . $purchase_key;
722
	}
723
724
	return $custom_purchase_key;
725
}
726
727
add_filter( 'give_donation_purchase_key', 'give_paypal_purchase_key', 10, 3 );
728
729
730
/**
731
 * PayPal Standard Connect button.
732
 *
733
 * This uses Stripe's Connect button but swaps the link and logo with PayPal's.
734
 *
735
 * @return string
736
 * @since 2.5.0
737
 *
738
 */
739
function give_paypal_connect_button() {
740
741
	// Prepare Stripe Connect URL.
742
	$link = admin_url( 'edit.php?post_type=give_forms&page=give-settings&tab=gateways&section=paypal-standard' );
743
744
	return sprintf(
745
		'<a href="%1$s" id="give-paypal-connect"><span>%2$s</span></a>',
746
		esc_url( $link ),
747
		esc_html__( 'Connect to PayPal', 'give' )
748
	);
749
}
750