Issues (4296)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

includes/gateways/paypal-standard.php (33 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
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
 * @param int $form_id Form ID.
20
 *
21
 * @since 1.8.5
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
 * @param array $payment_data Payment data.
43
 *
44
 * @since 1.0
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
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( __( 'Payment Error', 'give' ), sprintf( /* translators: %s: payment data */
59
			__( 'Payment creation failed before sending donor to PayPal. Payment data: %s', 'give' ), json_encode( $payment_data ) ), $payment_id );
0 ignored issues
show
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...
This line of the multi-line function call does not seem to be indented correctly. Expected 8 spaces, but found 12.
Loading history...
60
		// Problems? Send back.
61
		give_send_back_to_checkout( '?payment-mode=' . $payment_data['post_data']['give-gateway'] );
62
	}
63
64
	// Redirect to PayPal.
65
	wp_redirect( give_build_paypal_url( $payment_id, $payment_data ) );
0 ignored issues
show
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...
66
	exit;
67
}
68
69
add_action( 'give_gateway_paypal', 'give_process_paypal_payment' );
70
71
/**
72
 * Listens for a PayPal IPN requests and then sends to the processing function.
73
 *
74
 * @since 1.0
75
 * @return void
76
 */
77
function give_listen_for_paypal_ipn() {
78
79
	// Regular PayPal IPN.
80
	if ( isset( $_GET['give-listener'] ) && 'IPN' === $_GET['give-listener'] ) {
0 ignored issues
show
Detected access of super global var $_GET, probably need manual inspection.
Loading history...
Detected usage of a non-sanitized input variable: $_GET
Loading history...
81
		/**
82
		 * Fires while verifying PayPal IPN
83
		 *
84
		 * @since 1.0
85
		 */
86
		do_action( 'give_verify_paypal_ipn' );
87
	}
88
}
89
90
add_action( 'init', 'give_listen_for_paypal_ipn' );
91
92
/**
93
 * Process PayPal IPN
94
 *
95
 * @since 1.0
96
 * @return void
97
 */
98
function give_process_paypal_ipn() {
99
100
	// Check the request method is POST.
101
	if ( isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' !== $_SERVER['REQUEST_METHOD'] ) {
0 ignored issues
show
Detected usage of a non-sanitized input variable: $_SERVER
Loading history...
102
		return;
103
	}
104
105
	// Set initial post data to empty string.
106
	$post_data = '';
107
108
	// Fallback just in case post_max_size is lower than needed.
109
	if ( ini_get( 'allow_url_fopen' ) ) {
110
		$post_data = file_get_contents( 'php://input' );
0 ignored issues
show
file_get_contents is highly discouraged, please use wpcom_vip_file_get_contents() instead.
Loading history...
111
	} else {
112
		// If allow_url_fopen is not enabled, then make sure that post_max_size is large enough.
113
		ini_set( 'post_max_size', '12M' );
114
	}
115
	// Start the encoded data collection with notification command.
116
	$encoded_data = 'cmd=_notify-validate';
117
118
	// Get current arg separator.
119
	$arg_separator = give_get_php_arg_separator_output();
120
121
	// Verify there is a post_data.
122
	if ( $post_data || strlen( $post_data ) > 0 ) {
123
		// Append the data.
124
		$encoded_data .= $arg_separator . $post_data;
125
	} else {
126
		// Check if POST is empty.
127
		if ( empty( $_POST ) ) {
0 ignored issues
show
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
128
			// Nothing to do.
129
			return;
130
		} else {
131
			// Loop through each POST.
132
			foreach ( $_POST as $key => $value ) {
0 ignored issues
show
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
133
				// Encode the value and append the data.
134
				$encoded_data .= $arg_separator . "$key=" . urlencode( $value );
135
			}
136
		}
137
	}
138
139
	// Convert collected post data to an array.
140
	parse_str( $encoded_data, $encoded_data_array );
141
142
	foreach ( $encoded_data_array as $key => $value ) {
0 ignored issues
show
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...
143
144
		if ( false !== strpos( $key, 'amp;' ) ) {
145
			$new_key = str_replace( '&amp;', '&', $key );
146
			$new_key = str_replace( 'amp;', '&', $new_key );
147
148
			unset( $encoded_data_array[ $key ] );
149
			$encoded_data_array[ $new_key ] = $value;
150
		}
151
	}
152
153
	$api_response = false;
154
155
	// Validate IPN request w/ PayPal if user hasn't disabled this security measure.
156
	if ( give_is_setting_enabled( give_get_option( 'paypal_verification' ) ) ) {
157
158
		$remote_post_vars = array(
159
			'method'      => 'POST',
160
			'timeout'     => 45,
161
			'redirection' => 5,
162
			'httpversion' => '1.1',
163
			'blocking'    => true,
164
			'headers'     => array(
165
				'host'         => 'www.paypal.com',
166
				'connection'   => 'close',
167
				'content-type' => 'application/x-www-form-urlencoded',
168
				'post'         => '/cgi-bin/webscr HTTP/1.1',
169
170
			),
171
			'sslverify'   => false,
172
			'body'        => $encoded_data_array,
173
		);
174
175
		// Validate the IPN.
176
		$api_response = wp_remote_post( give_get_paypal_redirect(), $remote_post_vars );
177
178 View Code Duplication
		if ( is_wp_error( $api_response ) ) {
0 ignored issues
show
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...
179
			give_record_gateway_error( __( 'IPN Error', 'give' ), sprintf( /* translators: %s: Paypal IPN response */
180
				__( 'Invalid IPN verification response. IPN data: %s', 'give' ), json_encode( $api_response ) ) );
0 ignored issues
show
This line of the multi-line function call does not seem to be indented correctly. Expected 12 spaces, but found 16.
Loading history...
181
182
			return; // Something went wrong.
183
		}
184
185 View Code Duplication
		if ( 'VERIFIED' !== $api_response['body'] ) {
0 ignored issues
show
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...
186
			give_record_gateway_error( __( 'IPN Error', 'give' ), sprintf( /* translators: %s: Paypal IPN response */
187
				__( 'Invalid IPN verification response. IPN data: %s', 'give' ), json_encode( $api_response ) ) );
0 ignored issues
show
This line of the multi-line function call does not seem to be indented correctly. Expected 12 spaces, but found 16.
Loading history...
188
189
			return; // Response not okay.
190
		}
191
	}// End if().
192
193
	// Check if $post_data_array has been populated.
194
	if ( ! is_array( $encoded_data_array ) && ! empty( $encoded_data_array ) ) {
195
		return;
196
	}
197
198
	$defaults = array(
199
		'txn_type'       => '',
200
		'payment_status' => '',
201
	);
202
203
	$encoded_data_array = wp_parse_args( $encoded_data_array, $defaults );
204
205
	$payment_id = isset( $encoded_data_array['custom'] ) ? absint( $encoded_data_array['custom'] ) : 0;
206
	$txn_type   = $encoded_data_array['txn_type'];
207
208
	// Check for PayPal IPN Notifications and update data based on it.
209
	$current_timestamp = current_time( 'timestamp' );
210
	$paypal_ipn_vars   = array(
211
		'auth_status'    => isset( $api_response['body'] ) ? $api_response['body'] : 'N/A',
212
		'transaction_id' => isset( $encoded_data_array['txn_id'] ) ? $encoded_data_array['txn_id'] : 'N/A',
213
		'payment_id'     => $payment_id,
214
	);
215
	update_option( 'give_last_paypal_ipn_received', $paypal_ipn_vars, false );
216
	give_insert_payment_note( $payment_id, sprintf(
217
			__( 'IPN received on %s at %s', 'give' ),
0 ignored issues
show
This line of the multi-line function call does not seem to be indented correctly. Expected 8 spaces, but found 12.
Loading history...
218
			date_i18n( 'm/d/Y', $current_timestamp ),
0 ignored issues
show
This line of the multi-line function call does not seem to be indented correctly. Expected 8 spaces, but found 12.
Loading history...
219
			date_i18n( 'H:i', $current_timestamp )
0 ignored issues
show
This line of the multi-line function call does not seem to be indented correctly. Expected 8 spaces, but found 12.
Loading history...
220
		)
0 ignored issues
show
This line of the multi-line function call does not seem to be indented correctly. Expected 4 spaces, but found 8.
Loading history...
221
	);
222
	give_update_meta( $payment_id, 'give_last_paypal_ipn_received', $current_timestamp );
223
224
	if ( has_action( 'give_paypal_' . $txn_type ) ) {
225
		/**
226
		 * Fires while processing PayPal IPN $txn_type.
227
		 *
228
		 * Allow PayPal IPN types to be processed separately.
229
		 *
230
		 * @since 1.0
231
		 *
232
		 * @param array $encoded_data_array Encoded data.
233
		 * @param int   $payment_id         Payment id.
234
		 */
235
		do_action( "give_paypal_{$txn_type}", $encoded_data_array, $payment_id );
236
	} else {
237
		/**
238
		 * Fires while process PayPal IPN.
239
		 *
240
		 * Fallback to web accept just in case the txn_type isn't present.
241
		 *
242
		 * @since 1.0
243
		 *
244
		 * @param array $encoded_data_array Encoded data.
245
		 * @param int   $payment_id         Payment id.
246
		 */
247
		do_action( 'give_paypal_web_accept', $encoded_data_array, $payment_id );
248
	}
249
	exit;
250
}
251
252
add_action( 'give_verify_paypal_ipn', 'give_process_paypal_ipn' );
253
254
/**
255
 * Process web accept (one time) payment IPNs.
256
 *
257
 * @since 1.0
258
 *
259
 * @param array $data       The IPN Data.
260
 * @param int   $payment_id The payment ID from Give.
261
 *
262
 * @return void
263
 */
264
function give_process_paypal_web_accept( $data, $payment_id ) {
265
266
	// Only allow through these transaction types.
267
	if ( 'web_accept' !== $data['txn_type'] && 'cart' !== $data['txn_type'] && 'refunded' !== strtolower( $data['payment_status'] ) ) {
268
		return;
269
	}
270
271
	// Need $payment_id to continue.
272
	if ( empty( $payment_id ) ) {
273
		return;
274
	}
275
276
	// Collect donation payment details.
277
	$paypal_amount  = $data['mc_gross'];
278
	$payment_status = strtolower( $data['payment_status'] );
279
	$currency_code  = strtolower( $data['mc_currency'] );
280
	$business_email = isset( $data['business'] ) && is_email( $data['business'] ) ? trim( $data['business'] ) : trim( $data['receiver_email'] );
281
	$payment_meta   = give_get_payment_meta( $payment_id );
282
283
	// Must be a PayPal standard IPN.
284
	if ( 'paypal' !== give_get_payment_gateway( $payment_id ) ) {
285
		return;
286
	}
287
288
	// Verify payment recipient.
289 View Code Duplication
	if ( strcasecmp( $business_email, trim( give_get_option( 'paypal_email' ) ) ) !== 0 ) {
0 ignored issues
show
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...
Found "!== 0". Use Yoda Condition checks, you must
Loading history...
290
291
		give_record_gateway_error( __( 'IPN Error', 'give' ), sprintf( /* translators: %s: Paypal IPN response */
292
			__( 'Invalid business email in IPN response. IPN data: %s', 'give' ), json_encode( $data ) ), $payment_id );
0 ignored issues
show
This line of the multi-line function call does not seem to be indented correctly. Expected 8 spaces, but found 12.
Loading history...
293
		give_update_payment_status( $payment_id, 'failed' );
294
		give_insert_payment_note( $payment_id, __( 'Payment failed due to invalid PayPal business email.', 'give' ) );
295
296
		return;
297
	}
298
299
	// Verify payment currency.
300 View Code Duplication
	if ( $currency_code !== strtolower( $payment_meta['currency'] ) ) {
0 ignored issues
show
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...
301
302
		give_record_gateway_error( __( 'IPN Error', 'give' ), sprintf( /* translators: %s: Paypal IPN response */
303
			__( 'Invalid currency in IPN response. IPN data: %s', 'give' ), json_encode( $data ) ), $payment_id );
0 ignored issues
show
This line of the multi-line function call does not seem to be indented correctly. Expected 8 spaces, but found 12.
Loading history...
304
		give_update_payment_status( $payment_id, 'failed' );
305
		give_insert_payment_note( $payment_id, __( 'Payment failed due to invalid currency in PayPal IPN.', 'give' ) );
306
307
		return;
308
	}
309
310
	// Process refunds & reversed.
311
	if ( 'refunded' === $payment_status || 'reversed' === $payment_status ) {
312
		give_process_paypal_refund( $data, $payment_id );
313
314
		return;
315
	}
316
317
	// Only complete payments once.
318
	if ( 'publish' === get_post_status( $payment_id ) ) {
319
		return;
320
	}
321
322
	// Retrieve the total donation amount (before PayPal).
323
	$payment_amount = give_donation_amount( $payment_id );
324
325
	// Check that the donation PP and local db amounts match.
326 View Code Duplication
	if ( number_format( (float) $paypal_amount, 2 ) < number_format( (float) $payment_amount, 2 ) ) {
0 ignored issues
show
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...
327
		// The prices don't match
328
		give_record_gateway_error( __( 'IPN Error', 'give' ), sprintf( /* translators: %s: Paypal IPN response */
329
			__( 'Invalid payment amount in IPN response. IPN data: %s', 'give' ), json_encode( $data ) ), $payment_id );
0 ignored issues
show
This line of the multi-line function call does not seem to be indented correctly. Expected 8 spaces, but found 12.
Loading history...
330
		give_update_payment_status( $payment_id, 'failed' );
331
		give_insert_payment_note( $payment_id, __( 'Payment failed due to invalid amount in PayPal IPN.', 'give' ) );
332
333
		return;
334
	}
335
336
	// Process completed donations.
337
	if ( 'completed' === $payment_status || give_is_test_mode() ) {
338
339
		give_insert_payment_note( $payment_id, sprintf( /* translators: %s: Paypal transaction ID */
340
			__( 'PayPal Transaction ID: %s', 'give' ), $data['txn_id'] ) );
0 ignored issues
show
This line of the multi-line function call does not seem to be indented correctly. Expected 8 spaces, but found 12.
Loading history...
341
		give_set_payment_transaction_id( $payment_id, $data['txn_id'] );
342
		give_update_payment_status( $payment_id, 'publish' );
343
344
	} elseif ( 'pending' === $payment_status && isset( $data['pending_reason'] ) ) {
345
346
		// Look for possible pending reasons, such as an echeck.
347
		$note = give_paypal_get_pending_donation_note( strtolower( $data['pending_reason'] ) );
348
349
		if ( ! empty( $note ) ) {
350
			give_insert_payment_note( $payment_id, $note );
351
		}
352
	}
353
354
}
355
356
add_action( 'give_paypal_web_accept', 'give_process_paypal_web_accept', 10, 2 );
357
358
/**
359
 * Process PayPal IPN Refunds
360
 *
361
 * @since 1.0
362
 *
363
 * @param array $data       IPN Data
364
 * @param int   $payment_id The payment ID.
365
 *
366
 * @return void
367
 */
368
function give_process_paypal_refund( $data, $payment_id = 0 ) {
369
370
	// Collect payment details.
371
	if ( empty( $payment_id ) ) {
372
		return;
373
	}
374
375
	// Only refund payments once.
376
	if ( 'refunded' === get_post_status( $payment_id ) ) {
377
		return;
378
	}
379
380
	$payment_amount = give_donation_amount( $payment_id );
381
	$refund_amount  = $data['payment_gross'] * - 1;
382
383
	if ( number_format( (float) $refund_amount, 2 ) < number_format( (float) $payment_amount, 2 ) ) {
384
385
		give_insert_payment_note( $payment_id, sprintf( /* translators: %s: Paypal parent transaction ID */
386
			__( 'Partial PayPal refund processed: %s', 'give' ), $data['parent_txn_id'] ) );
0 ignored issues
show
This line of the multi-line function call does not seem to be indented correctly. Expected 8 spaces, but found 12.
Loading history...
387
388
		return; // This is a partial refund
389
390
	}
391
392
	give_insert_payment_note( $payment_id, sprintf( /* translators: 1: Paypal parent transaction ID 2. Paypal reason code */
393
		__( 'PayPal Payment #%1$s Refunded for reason: %2$s', 'give' ), $data['parent_txn_id'], $data['reason_code'] ) );
0 ignored issues
show
This line of the multi-line function call does not seem to be indented correctly. Expected 4 spaces, but found 8.
Loading history...
394
	give_insert_payment_note( $payment_id, sprintf( /* translators: %s: Paypal transaction ID */
395
		__( 'PayPal Refund Transaction ID: %s', 'give' ), $data['txn_id'] ) );
0 ignored issues
show
This line of the multi-line function call does not seem to be indented correctly. Expected 4 spaces, but found 8.
Loading history...
396
	give_update_payment_status( $payment_id, 'refunded' );
397
}
398
399
/**
400
 * Get PayPal Redirect
401
 *
402
 * @since 1.0
403
 *
404
 * @param bool $ssl_check Is SSL?
405
 *
406
 * @return string
407
 */
408
function give_get_paypal_redirect( $ssl_check = false ) {
409
410
	if ( is_ssl() || ! $ssl_check ) {
411
		$protocol = 'https://';
412
	} else {
413
		$protocol = 'http://';
414
	}
415
416
	// Check the current payment mode
417
	if ( give_is_test_mode() ) {
418
		// Test mode
419
		$paypal_uri = $protocol . 'www.sandbox.paypal.com/cgi-bin/webscr';
420
	} else {
421
		// Live mode
422
		$paypal_uri = $protocol . 'www.paypal.com/cgi-bin/webscr';
423
	}
424
425
	return apply_filters( 'give_paypal_uri', $paypal_uri );
426
}
427
428
/**
429
 * Set the Page Style for offsite PayPal page.
430
 *
431
 * @since 1.0
432
 * @return string
433
 */
434
function give_get_paypal_page_style() {
435
	$page_style = trim( give_get_option( 'paypal_page_style', 'PayPal' ) );
436
437
	return apply_filters( 'give_paypal_page_style', $page_style );
438
}
439
440
/**
441
 * PayPal Success Page
442
 *
443
 * Shows "Donation Processing" message for PayPal payments that are still pending on site return
444
 *
445
 * @since      1.0
446
 *
447
 * @param $content
448
 *
449
 * @return string
450
 */
451
function give_paypal_success_page_content( $content ) {
452
453
	if ( ! isset( $_GET['payment-id'] ) && ! give_get_purchase_session() ) {
0 ignored issues
show
Detected access of super global var $_GET, probably need manual inspection.
Loading history...
454
		return $content;
455
	}
456
457
	$payment_id = isset( $_GET['payment-id'] ) ? absint( $_GET['payment-id'] ) : false;
0 ignored issues
show
Detected access of super global var $_GET, probably need manual inspection.
Loading history...
458
459
	if ( ! $payment_id ) {
460
		$session    = give_get_purchase_session();
461
		$payment_id = give_get_donation_id_by_key( $session['purchase_key'] );
462
	}
463
464
	$payment = get_post( $payment_id );
465
	if ( $payment && 'pending' === $payment->post_status ) {
466
467
		// Payment is still pending so show processing indicator to fix the race condition.
468
		ob_start();
469
470
		give_get_template_part( 'payment', 'processing' );
471
472
		$content = ob_get_clean();
473
474
	}
475
476
	return $content;
477
478
}
479
480
add_filter( 'give_payment_confirm_paypal', 'give_paypal_success_page_content' );
481
482
/**
483
 * Given a transaction ID, generate a link to the PayPal transaction ID details
484
 *
485
 * @since  1.0
486
 *
487
 * @param  string $transaction_id The Transaction ID
488
 * @param  int    $payment_id     The payment ID for this transaction
489
 *
490
 * @return string                 A link to the PayPal transaction details
491
 */
492
function give_paypal_link_transaction_id( $transaction_id, $payment_id ) {
0 ignored issues
show
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...
493
494
	$paypal_base_url = 'https://history.paypal.com/cgi-bin/webscr?cmd=_history-details-from-hub&id=';
495
	$transaction_url = '<a href="' . esc_url( $paypal_base_url . $transaction_id ) . '" target="_blank">' . $transaction_id . '</a>';
496
497
	return apply_filters( 'give_paypal_link_payment_details_transaction_id', $transaction_url );
498
499
}
500
501
add_filter( 'give_payment_details_transaction_id-paypal', 'give_paypal_link_transaction_id', 10, 2 );
502
503
504
/**
505
 * Get pending donation note.
506
 *
507
 * @since 1.6.3
508
 *
509
 * @param $pending_reason
510
 *
511
 * @return string
512
 */
513
function give_paypal_get_pending_donation_note( $pending_reason ) {
514
515
	$note = '';
516
517
	switch ( $pending_reason ) {
518
519
		case 'echeck' :
520
521
			$note = __( 'Payment made via eCheck and will clear automatically in 5-8 days.', 'give' );
522
523
			break;
524
525
		case 'address' :
526
527
			$note = __( 'Payment requires a confirmed donor address and must be accepted manually through PayPal.', 'give' );
528
529
			break;
530
531
		case 'intl' :
532
533
			$note = __( 'Payment must be accepted manually through PayPal due to international account regulations.', 'give' );
534
535
			break;
536
537
		case 'multi-currency' :
538
539
			$note = __( 'Payment received in non-shop currency and must be accepted manually through PayPal.', 'give' );
540
541
			break;
542
543
		case 'paymentreview' :
544
		case 'regulatory_review' :
545
546
			$note = __( 'Payment is being reviewed by PayPal staff as high-risk or in possible violation of government regulations.', 'give' );
547
548
			break;
549
550
		case 'unilateral' :
551
552
			$note = __( 'Payment was sent to non-confirmed or non-registered email address.', 'give' );
553
554
			break;
555
556
		case 'upgrade' :
557
558
			$note = __( 'PayPal account must be upgraded before this payment can be accepted.', 'give' );
559
560
			break;
561
562
		case 'verify' :
563
564
			$note = __( 'PayPal account is not verified. Verify account in order to accept this donation.', 'give' );
565
566
			break;
567
568
		case 'other' :
569
570
			$note = __( 'Payment is pending for unknown reasons. Contact PayPal support for assistance.', 'give' );
571
572
			break;
573
574
	}// End switch().
575
576
	return $note;
577
578
}
579
580
/**
581
 * Build paypal url
582
 *
583
 * @param int   $payment_id   Payment ID
584
 * @param array $payment_data Array of payment data.
585
 *
586
 * @return mixed|string
587
 */
588
function give_build_paypal_url( $payment_id, $payment_data ) {
589
	// Only send to PayPal if the pending payment is created successfully.
590
	$listener_url = add_query_arg( 'give-listener', 'IPN', home_url( 'index.php' ) );
591
592
	// Get the success url.
593
	$return_url = add_query_arg( array(
594
		'payment-confirmation' => 'paypal',
595
		'payment-id'           => $payment_id,
596
597
	), get_permalink( give_get_option( 'success_page' ) ) );
598
599
	// Get the PayPal redirect uri.
600
	$paypal_redirect = trailingslashit( give_get_paypal_redirect() ) . '?';
601
602
	// Item name.
603
	$item_name = give_payment_gateway_item_title( $payment_data );
604
605
	// Setup PayPal API params.
606
	$paypal_args = array(
607
		'business'      => give_get_option( 'paypal_email', false ),
608
		'first_name'    => $payment_data['user_info']['first_name'],
609
		'last_name'     => $payment_data['user_info']['last_name'],
610
		'email'         => $payment_data['user_email'],
611
		'invoice'       => $payment_data['purchase_key'],
612
		'amount'        => $payment_data['price'],
613
		'item_name'     => stripslashes( $item_name ),
614
		'no_shipping'   => '1',
615
		'shipping'      => '0',
616
		'no_note'       => '1',
617
		'currency_code' => give_get_currency( $payment_id, $payment_data ),
618
		'charset'       => get_bloginfo( 'charset' ),
619
		'custom'        => $payment_id,
620
		'rm'            => '2',
621
		'return'        => $return_url,
622
		'cancel_return' => give_get_failed_transaction_uri( '?payment-id=' . $payment_id ),
0 ignored issues
show
'?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...
623
		'notify_url'    => $listener_url,
624
		'page_style'    => give_get_paypal_page_style(),
625
		'cbt'           => get_bloginfo( 'name' ),
626
		'bn'            => 'givewp_SP',
627
	);
628
629
	// Add user address if present.
630
	if ( ! empty( $payment_data['user_info']['address'] ) ) {
631
		$default_address = array(
632
			'line1'   => '',
633
			'line2'   => '',
634
			'city'    => '',
635
			'state'   => '',
636
			'zip'     => '',
637
			'country' => '',
638
		);
639
640
		$address = wp_parse_args( $payment_data['user_info']['address'], $default_address );
641
642
		$paypal_args['address1'] = $address['line1'];
643
		$paypal_args['address2'] = $address['line2'];
644
		$paypal_args['city']     = $address['city'];
645
		$paypal_args['state']    = $address['state'];
646
		$paypal_args['zip']      = $address['zip'];
647
		$paypal_args['country']  = $address['country'];
648
	}
649
650
	// Donations or regular transactions?
651
	$paypal_args['cmd'] = give_get_paypal_button_type();
652
653
	/**
654
	 * Filter the paypal redirect args.
655
	 *
656
	 * @param array $paypal_args  PayPal Arguments.
657
	 * @param array $payment_data Payment Data.
658
	 *
659
	 * @since 1.8
660
	 */
661
	$paypal_args = apply_filters( 'give_paypal_redirect_args', $paypal_args, $payment_data );
662
663
	// Build query.
664
	$paypal_redirect .= http_build_query( $paypal_args );
665
666
	// Fix for some sites that encode the entities.
667
	$paypal_redirect = str_replace( '&amp;', '&', $paypal_redirect );
668
669
	return $paypal_redirect;
670
}
671
672
673
/**
674
 * Get paypal button type.
675
 *
676
 * @since 1.8
677
 * @return string
678
 */
679
function give_get_paypal_button_type() {
680
	// paypal_button_type can be donation or standard.
681
	$paypal_button_type = '_donations';
682
	if ( 'standard' === give_get_option( 'paypal_button_type' ) ) {
683
		$paypal_button_type = '_xclick';
684
	}
685
686
	return $paypal_button_type;
687
}
688
689
/**
690
 * Update Purchase key for specific gateway.
691
 *
692
 * @since 2.2.4
693
 *
694
 * @param string $custom_purchase_key
695
 * @param string $gateway
696
 * @param string $purchase_key
697
 *
698
 * @return string
699
 */
700
function give_paypal_purchase_key( $custom_purchase_key, $gateway, $purchase_key ) {
701
702
	if ( 'paypal' === $gateway ) {
703
		$invoice_id_prefix   = give_get_option( 'paypal_invoice_prefix', 'GIVE-' );
704
		$custom_purchase_key = $invoice_id_prefix . $purchase_key;
705
	}
706
707
	return $custom_purchase_key;
708
}
709
710
add_filter( 'give_donation_purchase_key', 'give_paypal_purchase_key', 10, 3 );
711