Completed
Pull Request — master (#1832)
by Devin
04:50
created

paypal-standard.php ➔ give_process_paypal_payment()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 19
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 9
nc 2
nop 1
dl 0
loc 19
ccs 0
cts 13
cp 0
crap 6
rs 9.4285
c 0
b 0
f 0
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
	if ( has_action( 'give_paypal_' . $txn_type ) ) {
205
		/**
206
		 * Fires while processing PayPal IPN $txn_type.
207
		 *
208
		 * Allow PayPal IPN types to be processed separately.
209
		 *
210
		 * @since 1.0
211
		 *
212
		 * @param array $encoded_data_array Encoded data.
213
		 * @param int   $payment_id         Payment id.
214
		 */
215
		do_action( "give_paypal_{$txn_type}", $encoded_data_array, $payment_id );
216
	} else {
217
		/**
218
		 * Fires while process PayPal IPN.
219
		 *
220
		 * Fallback to web accept just in case the txn_type isn't present.
221
		 *
222
		 * @since 1.0
223
		 *
224
		 * @param array $encoded_data_array Encoded data.
225
		 * @param int   $payment_id         Payment id.
226
		 */
227
		do_action( 'give_paypal_web_accept', $encoded_data_array, $payment_id );
228
	}
229
	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...
230
}
231
232
add_action( 'give_verify_paypal_ipn', 'give_process_paypal_ipn' );
233
234
/**
235
 * Process web accept (one time) payment IPNs.
236
 *
237
 * @since 1.0
238
 *
239
 * @param array $data       The IPN Data.
240
 * @param int   $payment_id The payment ID from Give.
241
 *
242
 * @return void
243
 */
244
function give_process_paypal_web_accept( $data, $payment_id ) {
245
246
	// Only allow through these transaction types.
247
	if ( 'web_accept' !== $data['txn_type'] && 'cart' !== $data['txn_type'] && 'refunded' !== strtolower( $data['payment_status'] ) ) {
248
		return;
249
	}
250
251
	// Need $payment_id to continue.
252
	if ( empty( $payment_id ) ) {
253
		return;
254
	}
255
256
	// Collect donation payment details.
257
	$paypal_amount  = $data['mc_gross'];
258
	$payment_status = strtolower( $data['payment_status'] );
259
	$currency_code  = strtolower( $data['mc_currency'] );
260
	$business_email = isset( $data['business'] ) && is_email( $data['business'] ) ? trim( $data['business'] ) : trim( $data['receiver_email'] );
261
	$payment_meta   = give_get_payment_meta( $payment_id );
262
263
	// Must be a PayPal standard IPN.
264
	if ( 'paypal' !== give_get_payment_gateway( $payment_id ) ) {
265
		return;
266
	}
267
268
	// Verify payment recipient
269
	if ( strcasecmp( $business_email, trim( give_get_option( 'paypal_email' ) ) ) !== 0 ) {
270
271
		give_record_gateway_error( __( 'IPN Error', 'give' ), sprintf( /* translators: %s: Paypal IPN response */
272
		__( 'Invalid business email in IPN response. IPN data: %s', 'give' ), json_encode( $data ) ), $payment_id );
273
		give_update_payment_status( $payment_id, 'failed' );
274
		give_insert_payment_note( $payment_id, __( 'Payment failed due to invalid PayPal business email.', 'give' ) );
275
276
		return;
277
	}
278
279
	// Verify payment currency.
280
	if ( $currency_code !== strtolower( $payment_meta['currency'] ) ) {
281
282
		give_record_gateway_error( __( 'IPN Error', 'give' ), sprintf( /* translators: %s: Paypal IPN response */
283
		__( 'Invalid currency in IPN response. IPN data: %s', 'give' ), json_encode( $data ) ), $payment_id );
284
		give_update_payment_status( $payment_id, 'failed' );
285
		give_insert_payment_note( $payment_id, __( 'Payment failed due to invalid currency in PayPal IPN.', 'give' ) );
286
287
		return;
288
	}
289
290
	// Process refunds & reversed.
291
	if ( 'refunded' === $payment_status || 'reversed' === $payment_status ) {
292
		give_process_paypal_refund( $data, $payment_id );
293
294
		return;
295
	}
296
297
	// Only complete payments once.
298
	if ( 'publish' === get_post_status( $payment_id ) ) {
299
		return;
300
	}
301
302
	// Retrieve the total donation amount (before PayPal).
303
	$payment_amount = give_get_payment_amount( $payment_id );
304
305
	// Check that the donation PP and local db amounts match.
306
	if ( number_format( (float) $paypal_amount, 2 ) < number_format( (float) $payment_amount, 2 ) ) {
307
		// The prices don't match
308
		give_record_gateway_error( __( 'IPN Error', 'give' ), sprintf( /* translators: %s: Paypal IPN response */
309
		__( 'Invalid payment amount in IPN response. IPN data: %s', 'give' ), json_encode( $data ) ), $payment_id );
310
		give_update_payment_status( $payment_id, 'failed' );
311
		give_insert_payment_note( $payment_id, __( 'Payment failed due to invalid amount in PayPal IPN.', 'give' ) );
312
313
		return;
314
	}
315
316
	// Process completed donations.
317
	if ( 'completed' === $payment_status || give_is_test_mode() ) {
318
319
		give_insert_payment_note( $payment_id, sprintf( /* translators: %s: Paypal transaction ID */
320
		__( 'PayPal Transaction ID: %s', 'give' ), $data['txn_id'] ) );
321
		give_set_payment_transaction_id( $payment_id, $data['txn_id'] );
322
		give_update_payment_status( $payment_id, 'publish' );
323
324
	} elseif ( 'pending' === $payment_status && isset( $data['pending_reason'] ) ) {
325
326
		// Look for possible pending reasons, such as an echeck.
327
		$note = give_paypal_get_pending_donation_note( strtolower( $data['pending_reason'] ) );
328
329
		if ( ! empty( $note ) ) {
330
			give_insert_payment_note( $payment_id, $note );
331
		}
332
	}
333
334
}
335
336
add_action( 'give_paypal_web_accept', 'give_process_paypal_web_accept', 10, 2 );
337
338
/**
339
 * Process PayPal IPN Refunds
340
 *
341
 * @since 1.0
342
 *
343
 * @param array $data       IPN Data
344
 * @param int   $payment_id The payment ID.
345
 *
346
 * @return void
347
 */
348
function give_process_paypal_refund( $data, $payment_id = 0 ) {
349
350
	// Collect payment details.
351
	if ( empty( $payment_id ) ) {
352
		return;
353
	}
354
355
	// Only refund payments once.
356
	if ( 'refunded' === get_post_status( $payment_id ) ) {
357
		return;
358
	}
359
360
	$payment_amount = give_get_payment_amount( $payment_id );
361
	$refund_amount  = $data['payment_gross'] * - 1;
362
363
	if ( number_format( (float) $refund_amount, 2 ) < number_format( (float) $payment_amount, 2 ) ) {
364
365
		give_insert_payment_note( $payment_id, sprintf( /* translators: %s: Paypal parent transaction ID */
366
		__( 'Partial PayPal refund processed: %s', 'give' ), $data['parent_txn_id'] ) );
367
368
		return; // This is a partial refund
369
370
	}
371
372
	give_insert_payment_note( $payment_id, sprintf( /* translators: 1: Paypal parent transaction ID 2. Paypal reason code */
373
	__( 'PayPal Payment #%1$s Refunded for reason: %2$s', 'give' ), $data['parent_txn_id'], $data['reason_code'] ) );
374
	give_insert_payment_note( $payment_id, sprintf( /* translators: %s: Paypal transaction ID */
375
	__( 'PayPal Refund Transaction ID: %s', 'give' ), $data['txn_id'] ) );
376
	give_update_payment_status( $payment_id, 'refunded' );
377
}
378
379
/**
380
 * Get PayPal Redirect
381
 *
382
 * @since 1.0
383
 *
384
 * @param bool $ssl_check Is SSL?
385
 *
386
 * @return string
387
 */
388
function give_get_paypal_redirect( $ssl_check = false ) {
389
390
	if ( is_ssl() || ! $ssl_check ) {
391
		$protocol = 'https://';
392
	} else {
393
		$protocol = 'http://';
394
	}
395
396
	// Check the current payment mode
397
	if ( give_is_test_mode() ) {
398
		// Test mode
399
		$paypal_uri = $protocol . 'www.sandbox.paypal.com/cgi-bin/webscr';
400
	} else {
401
		// Live mode
402
		$paypal_uri = $protocol . 'www.paypal.com/cgi-bin/webscr';
403
	}
404
405
	return apply_filters( 'give_paypal_uri', $paypal_uri );
406
}
407
408
/**
409
 * Set the Page Style for offsite PayPal page.
410
 *
411
 * @since 1.0
412
 * @return string
413
 */
414
function give_get_paypal_page_style() {
415
	$page_style = trim( give_get_option( 'paypal_page_style', 'PayPal' ) );
416
417
	return apply_filters( 'give_paypal_page_style', $page_style );
418
}
419
420
/**
421
 * PayPal Success Page
422
 *
423
 * Shows "Donation Processing" message for PayPal payments that are still pending on site return
424
 *
425
 * @since      1.0
426
 *
427
 * @param $content
428
 *
429
 * @return string
430
 */
431
function give_paypal_success_page_content( $content ) {
432
433
	if ( ! isset( $_GET['payment-id'] ) && ! give_get_purchase_session() ) {
434
		return $content;
435
	}
436
437
	$payment_id = isset( $_GET['payment-id'] ) ? absint( $_GET['payment-id'] ) : false;
438
439
	if ( ! $payment_id ) {
440
		$session    = give_get_purchase_session();
441
		$payment_id = give_get_purchase_id_by_key( $session['purchase_key'] );
442
	}
443
444
	$payment = get_post( $payment_id );
445
	if ( $payment && 'pending' === $payment->post_status ) {
446
447
		// Payment is still pending so show processing indicator to fix the race condition.
448
		ob_start();
449
450
		give_get_template_part( 'payment', 'processing' );
451
452
		$content = ob_get_clean();
453
454
	}
455
456
	return $content;
457
458
}
459
460
add_filter( 'give_payment_confirm_paypal', 'give_paypal_success_page_content' );
461
462
/**
463
 * Given a Payment ID, extract the transaction ID
464
 *
465
 * @since  1.0
466
 *
467
 * @param  string $payment_id Payment ID
468
 *
469
 * @return string                   Transaction ID
470
 */
471
function give_paypal_get_payment_transaction_id( $payment_id ) {
472
473
	$transaction_id = '';
474
	$notes          = give_get_payment_notes( $payment_id );
475
476
	foreach ( $notes as $note ) {
477
		if ( preg_match( '/^PayPal Transaction ID: ([^\s]+)/', $note->comment_content, $match ) ) {
478
			$transaction_id = $match[1];
479
			continue;
480
		}
481
	}
482
483
	return apply_filters( 'give_paypal_set_payment_transaction_id', $transaction_id, $payment_id );
484
}
485
486
add_filter( 'give_get_payment_transaction_id-paypal', 'give_paypal_get_payment_transaction_id', 10, 1 );
487
488
/**
489
 * Given a transaction ID, generate a link to the PayPal transaction ID details
490
 *
491
 * @since  1.0
492
 *
493
 * @param  string $transaction_id The Transaction ID
494
 * @param  int    $payment_id     The payment ID for this transaction
495
 *
496
 * @return string                 A link to the PayPal transaction details
497
 */
498
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...
499
500
	$paypal_base_url = 'https://history.paypal.com/cgi-bin/webscr?cmd=_history-details-from-hub&id=';
501
	$transaction_url = '<a href="' . esc_url( $paypal_base_url . $transaction_id ) . '" target="_blank">' . $transaction_id . '</a>';
502
503
	return apply_filters( 'give_paypal_link_payment_details_transaction_id', $transaction_url );
504
505
}
506
507
add_filter( 'give_payment_details_transaction_id-paypal', 'give_paypal_link_transaction_id', 10, 2 );
508
509
510
/**
511
 * Get pending donation note.
512
 *
513
 * @since 1.6.3
514
 *
515
 * @param $pending_reason
516
 *
517
 * @return string
518
 */
519
function give_paypal_get_pending_donation_note( $pending_reason ) {
520
521
	$note = '';
522
523
	switch ( $pending_reason ) {
524
525
		case 'echeck' :
526
527
			$note = __( 'Payment made via eCheck and will clear automatically in 5-8 days.', 'give' );
528
529
			break;
530
531
		case 'address' :
532
533
			$note = __( 'Payment requires a confirmed donor address and must be accepted manually through PayPal.', 'give' );
534
535
			break;
536
537
		case 'intl' :
538
539
			$note = __( 'Payment must be accepted manually through PayPal due to international account regulations.', 'give' );
540
541
			break;
542
543
		case 'multi-currency' :
544
545
			$note = __( 'Payment received in non-shop currency and must be accepted manually through PayPal.', 'give' );
546
547
			break;
548
549
		case 'paymentreview' :
550
		case 'regulatory_review' :
551
552
			$note = __( 'Payment is being reviewed by PayPal staff as high-risk or in possible violation of government regulations.', 'give' );
553
554
			break;
555
556
		case 'unilateral' :
557
558
			$note = __( 'Payment was sent to non-confirmed or non-registered email address.', 'give' );
559
560
			break;
561
562
		case 'upgrade' :
563
564
			$note = __( 'PayPal account must be upgraded before this payment can be accepted.', 'give' );
565
566
			break;
567
568
		case 'verify' :
569
570
			$note = __( 'PayPal account is not verified. Verify account in order to accept this donation.', 'give' );
571
572
			break;
573
574
		case 'other' :
575
576
			$note = __( 'Payment is pending for unknown reasons. Contact PayPal support for assistance.', 'give' );
577
578
			break;
579
580
	}// 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...
581
582
	return $note;
583
584
}
585
586
/**
587
 * Build paypal url
588
 *
589
 * @param int   $payment_id   Payment ID
590
 * @param array $payment_data Array of payment data.
591
 *
592
 * @return mixed|string
593
 */
594
function give_build_paypal_url( $payment_id, $payment_data ) {
595
	// Only send to PayPal if the pending payment is created successfully.
596
	$listener_url = add_query_arg( 'give-listener', 'IPN', home_url( 'index.php' ) );
597
598
	// Get the success url.
599
	$return_url = add_query_arg( array(
600
		'payment-confirmation' => 'paypal',
601
		'payment-id'           => $payment_id,
602
603
	), get_permalink( give_get_option( 'success_page' ) ) );
604
605
	// Get the PayPal redirect uri.
606
	$paypal_redirect = trailingslashit( give_get_paypal_redirect() ) . '?';
607
608
	// Item name.
609
	$item_name = give_build_paypal_item_title( $payment_data );
610
611
	// Setup PayPal API params.
612
	$paypal_args = array(
613
		'business'      => give_get_option( 'paypal_email', false ),
614
		'first_name'    => $payment_data['user_info']['first_name'],
615
		'last_name'     => $payment_data['user_info']['last_name'],
616
		'email'         => $payment_data['user_email'],
617
		'invoice'       => $payment_data['purchase_key'],
618
		'amount'        => $payment_data['price'],
619
		'item_name'     => stripslashes( $item_name ),
620
		'no_shipping'   => '1',
621
		'shipping'      => '0',
622
		'no_note'       => '1',
623
		'currency_code' => give_get_currency(),
624
		'charset'       => get_bloginfo( 'charset' ),
625
		'custom'        => $payment_id,
626
		'rm'            => '2',
627
		'return'        => $return_url,
628
		'cancel_return' => give_get_failed_transaction_uri( '?payment-id=' . $payment_id ),
629
		'notify_url'    => $listener_url,
630
		'page_style'    => give_get_paypal_page_style(),
631
		'cbt'           => get_bloginfo( 'name' ),
632
		'bn'            => 'givewp_SP',
633
	);
634
635
	// Add user address if present.
636
	if ( ! empty( $payment_data['user_info']['address'] ) ) {
637
		$default_address = array(
638
			'line1'   => '',
639
			'line2'   => '',
640
			'city'    => '',
641
			'state'   => '',
642
			'zip'     => '',
643
			'country' => '',
644
		);
645
646
		$address = wp_parse_args( $payment_data['user_info']['address'], $default_address );
647
648
		$paypal_args['address1'] = $address['line1'];
649
		$paypal_args['address2'] = $address['line2'];
650
		$paypal_args['city']     = $address['city'];
651
		$paypal_args['state']    = $address['state'];
652
		$paypal_args['zip']      = $address['zip'];
653
		$paypal_args['country']  = $address['country'];
654
	}
655
656
	// Donations or regular transactions?
657
	$paypal_args['cmd'] = give_get_paypal_button_type();
658
659
	/**
660
	 * Filter the paypal redirect args.
661
	 *
662
	 * @since 1.8
663
	 *
664
	 * @param array $paypal_args
665
	 * @param array $payment_data
666
	 */
667
	$paypal_args = apply_filters( 'give_paypal_redirect_args', $paypal_args, $payment_data );
668
669
	// Build query.
670
	$paypal_redirect .= http_build_query( $paypal_args );
671
672
	// Fix for some sites that encode the entities.
673
	$paypal_redirect = str_replace( '&amp;', '&', $paypal_redirect );
674
675
	return $paypal_redirect;
676
}
677
678
679
/**
680
 * Get paypal button type.
681
 *
682
 * @since 1.8
683
 * @return string
684
 */
685
function give_get_paypal_button_type() {
686
	// paypal_button_type can be donation or standard.
687
	$paypal_button_type = '_donations';
688
	if ( 'standard' === give_get_option( 'paypal_button_type' ) ) {
689
		$paypal_button_type = '_xclick';
690
	}
691
692
	return $paypal_button_type;
693
}
694
695
696
/**
697
 * Build item title for paypal.
698
 *
699
 * @since 1.8
700
 *
701
 * @param $payment_data
702
 *
703
 * @return string
704
 */
705
function give_build_paypal_item_title( $payment_data ) {
706
	$form_id   = intval( $payment_data['post_data']['give-form-id'] );
707
	$item_name = $payment_data['post_data']['give-form-title'];
708
709
	// Verify has variable prices.
710
	if ( give_has_variable_prices( $form_id ) && isset( $payment_data['post_data']['give-price-id'] ) ) {
711
712
		$item_price_level_text = give_get_price_option_name( $form_id, $payment_data['post_data']['give-price-id'] );
713
		$price_level_amount    = give_get_price_option_amount( $form_id, $payment_data['post_data']['give-price-id'] );
714
715
		// Donation given doesn't match selected level (must be a custom amount).
716
		if ( $price_level_amount != give_sanitize_amount( $payment_data['price'] ) ) {
717
			$custom_amount_text = give_get_meta( $form_id, '_give_custom_amount_text', true );
718
			// user custom amount text if any, fallback to default if not.
719
			$item_name .= ' - ' . give_check_variable( $custom_amount_text, 'empty', __( 'Custom Amount', 'give' ) );
720
721
		} elseif ( ! empty( $item_price_level_text ) ) {
722
			$item_name .= ' - ' . $item_price_level_text;
723
		}
724
	} // 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...
725
	elseif ( give_get_form_price( $form_id ) !== give_sanitize_amount( $payment_data['price'] ) ) {
726
		$custom_amount_text = give_get_meta( $form_id, '_give_custom_amount_text', true );
727
		// user custom amount text if any, fallback to default if not.
728
		$item_name .= ' - ' . give_check_variable( $custom_amount_text, 'empty', __( 'Custom Amount', 'give' ) );
729
	}
730
731
	return $item_name;
732
}
733