Completed
Pull Request — master (#664)
by Devin
19:01
created

paypal-standard.php ➔ give_process_paypal_purchase()   D

Complexity

Conditions 13
Paths 116

Size

Total Lines 131
Code Lines 76

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 182

Importance

Changes 2
Bugs 0 Features 1
Metric Value
cc 13
eloc 76
c 2
b 0
f 1
nc 116
nop 1
dl 0
loc 131
rs 4.7208
ccs 0
cts 77
cp 0
crap 182

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 35 and the first side effect is on line 13.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

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

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

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

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

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
64
		// Record the error
65
		give_record_gateway_error( __( 'Payment Error', 'give' ), sprintf( __( 'Payment creation failed before sending buyer to PayPal. Payment data: %s', 'give' ), json_encode( $payment_data ) ), $payment );
66
		// Problems? send back
67
		give_send_back_to_checkout( '?payment-mode=' . $purchase_data['post_data']['give-gateway'] );
68
	} else {
69
		// Only send to PayPal if the pending payment is created successfully
70
		$listener_url = add_query_arg( 'give-listener', 'IPN', home_url( 'index.php' ) );
71
72
		// Get the success url
73
		$return_url = add_query_arg( array(
74
			'payment-confirmation' => 'paypal',
75
			'payment-id'           => $payment
76
77
		), get_permalink( give_get_option( 'success_page' ) ) );
78
79
		// Get the PayPal redirect uri
80
		$paypal_redirect = trailingslashit( give_get_paypal_redirect() ) . '?';
81
82
		//Item name - pass level name if variable priced
83
		$item_name = $purchase_data['post_data']['give-form-title'];
84
85
		//Verify has variable prices
86
		if ( give_has_variable_prices( $form_id ) && isset( $purchase_data['post_data']['give-price-id'] ) ) {
87
88
			$item_price_level_text = give_get_price_option_name( $form_id, $purchase_data['post_data']['give-price-id'] );
89
90
			$price_level_amount = give_get_price_option_amount( $form_id, $purchase_data['post_data']['give-price-id'] );
91
92
			//Donation given doesn't match selected level (must be a custom amount)
93
			if ( $price_level_amount != give_sanitize_amount( $purchase_data['price'] ) ) {
94
				$custom_amount_text = get_post_meta( $form_id, '_give_custom_amount_text', true );
95
				//user custom amount text if any, fallback to default if not
96
				$item_name .= ' - ' . ( ! empty( $custom_amount_text ) ? $custom_amount_text : __( 'Custom Amount', 'give' ) );
97
98
			} //Is there any donation level text?
99
			elseif ( ! empty( $item_price_level_text ) ) {
100
				$item_name .= ' - ' . $item_price_level_text;
101
			}
102
103
		} //Single donation: Custom Amount
104
		elseif ( give_get_form_price( $form_id ) !== give_sanitize_amount( $purchase_data['price'] ) ) {
105
			$custom_amount_text = get_post_meta( $form_id, '_give_custom_amount_text', true );
106
			//user custom amount text if any, fallback to default if not
107
			$item_name .= ' - ' . ( ! empty( $custom_amount_text ) ? $custom_amount_text : __( 'Custom Amount', 'give' ) );
108
		}
109
110
		// Setup PayPal arguments
111
		$paypal_args = array(
112
			'business'      => give_get_option( 'paypal_email', false ),
113
			'email'         => $purchase_data['user_email'],
114
			'invoice'       => $purchase_data['purchase_key'],
115
			'amount'        => $purchase_data['price'],
116
			// The all important donation amount
117
			'item_name'     => $item_name,
118
			// "Purpose" field pre-populated with Form Title
119
			'no_shipping'   => '1',
120
			'shipping'      => '0',
121
			'no_note'       => '1',
122
			'currency_code' => give_get_currency(),
123
			'charset'       => get_bloginfo( 'charset' ),
124
			'custom'        => $payment,
125
			'rm'            => '2',
126
			'return'        => $return_url,
127
			'cancel_return' => give_get_failed_transaction_uri( '?payment-id=' . $payment ),
128
			'notify_url'    => $listener_url,
129
			'page_style'    => give_get_paypal_page_style(),
130
			'cbt'           => get_bloginfo( 'name' ),
131
			'bn'            => 'givewp_SP'
132
		);
133
134
		if ( ! empty( $purchase_data['user_info']['address'] ) ) {
135
			$paypal_args['address1'] = $purchase_data['user_info']['address']['line1'];
136
			$paypal_args['address2'] = $purchase_data['user_info']['address']['line2'];
137
			$paypal_args['city']     = $purchase_data['user_info']['address']['city'];
138
			$paypal_args['country']  = $purchase_data['user_info']['address']['country'];
139
		}
140
141
		if ( give_get_option( 'paypal_button_type' ) === 'standard' ) {
142
			$paypal_extra_args = array(
143
				'cmd' => '_xclick',
144
			);
145
		} else {
146
			$paypal_extra_args = array(
147
				'cmd' => '_donations',
148
			);
149
		}
150
151
		$paypal_args = array_merge( $paypal_extra_args, $paypal_args );
152
		$paypal_args = apply_filters( 'give_paypal_redirect_args', $paypal_args, $purchase_data );
153
154
		// Build query
155
		$paypal_redirect .= http_build_query( $paypal_args );
156
157
		// Fix for some sites that encode the entities
158
		$paypal_redirect = str_replace( '&amp;', '&', $paypal_redirect );
159
160
		// Redirect to PayPal
161
		wp_redirect( $paypal_redirect );
162
		exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The function give_process_paypal_purchase() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
163
	}
164
165
}
166
167
add_action( 'give_gateway_paypal', 'give_process_paypal_purchase' );
168
169
/**
170
 * Listens for a PayPal IPN requests and then sends to the processing function
171
 *
172
 * @since 1.0
173
 * @return void
174
 */
175
function give_listen_for_paypal_ipn() {
176
	// Regular PayPal IPN
177
	if ( isset( $_GET['give-listener'] ) && $_GET['give-listener'] == 'IPN' ) {
178
		do_action( 'give_verify_paypal_ipn' );
179
	}
180
}
181
182
add_action( 'init', 'give_listen_for_paypal_ipn' );
183
184
/**
185
 * Process PayPal IPN
186
 *
187
 * @since 1.0
188
 * @return void
189
 */
190
function give_process_paypal_ipn() {
191
192
	// Check the request method is POST
193
	if ( isset( $_SERVER['REQUEST_METHOD'] ) && $_SERVER['REQUEST_METHOD'] != 'POST' ) {
194
		return;
195
	}
196
197
	// Set initial post data to empty string
198
	$post_data = '';
199
200
	// Fallback just in case post_max_size is lower than needed
201
	if ( ini_get( 'allow_url_fopen' ) ) {
202
		$post_data = file_get_contents( 'php://input' );
203
	} else {
204
		// If allow_url_fopen is not enabled, then make sure that post_max_size is large enough
205
		ini_set( 'post_max_size', '12M' );
206
	}
207
	// Start the encoded data collection with notification command
208
	$encoded_data = 'cmd=_notify-validate';
209
210
	// Get current arg separator
211
	$arg_separator = give_get_php_arg_separator_output();
212
213
	// Verify there is a post_data
214
	if ( $post_data || strlen( $post_data ) > 0 ) {
215
		// Append the data
216
		$encoded_data .= $arg_separator . $post_data;
217
	} else {
218
		// Check if POST is empty
219
		if ( empty( $_POST ) ) {
220
			// Nothing to do
221
			return;
222
		} else {
223
			// Loop through each POST
224
			foreach ( $_POST as $key => $value ) {
225
				// Encode the value and append the data
226
				$encoded_data .= $arg_separator . "$key=" . urlencode( $value );
227
			}
228
		}
229
	}
230
231
	// Convert collected post data to an array
232
	parse_str( $encoded_data, $encoded_data_array );
233
234
	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...
235
236
		if ( false !== strpos( $key, 'amp;' ) ) {
237
			$new_key = str_replace( '&amp;', '&', $key );
238
			$new_key = str_replace( 'amp;', '&' , $new_key );
239
240
			unset( $encoded_data_array[ $key ] );
241
			$encoded_data_array[ $new_key ] = $value;
242
		}
243
244
	}
245
	
246
	//Validate IPN request w/ PayPal if user hasn't disabled this security measure
247
	if ( ! give_get_option( 'disable_paypal_verification' ) ) {
248
249
		$remote_post_vars = array(
250
			'method'      => 'POST',
251
			'timeout'     => 45,
252
			'redirection' => 5,
253
			'httpversion' => '1.1',
254
			'blocking'    => true,
255
			'headers'     => array(
256
				'host'         => 'www.paypal.com',
257
				'connection'   => 'close',
258
				'content-type' => 'application/x-www-form-urlencoded',
259
				'post'         => '/cgi-bin/webscr HTTP/1.1',
260
261
			),
262
			'sslverify'   => false,
263
			'body'        => $encoded_data_array
264
		);
265
266
		// Validate the IPN
267
		$api_response = wp_remote_post( give_get_paypal_redirect(), $remote_post_vars );
268
269
		if ( is_wp_error( $api_response ) ) {
270
			give_record_gateway_error( __( 'IPN Error', 'give' ), sprintf( __( 'Invalid IPN verification response. IPN data: %s', 'give' ), json_encode( $api_response ) ) );
271
			return; // Something went wrong
272
		}
273
274
		if ( $api_response['body'] !== 'VERIFIED' && give_get_option( 'disable_paypal_verification', false ) ) {
275
			give_record_gateway_error( __( 'IPN Error', 'give' ), sprintf( __( 'Invalid IPN verification response. IPN data: %s', 'give' ), json_encode( $api_response ) ) );
276
			return; // Response not okay
277
		}
278
279
	}
280
281
	// Check if $post_data_array has been populated
282
	if ( ! is_array( $encoded_data_array ) && ! empty( $encoded_data_array ) ) {
283
		return;
284
	}
285
286
	$defaults = array(
287
		'txn_type'       => '',
288
		'payment_status' => ''
289
	);
290
291
	$encoded_data_array = wp_parse_args( $encoded_data_array, $defaults );
292
293
	$payment_id = isset( $encoded_data_array['custom'] ) ? absint( $encoded_data_array['custom'] ) : 0;
294
295
	if ( has_action( 'give_paypal_' . $encoded_data_array['txn_type'] ) ) {
296
		// Allow PayPal IPN types to be processed separately
297
		do_action( 'give_paypal_' . $encoded_data_array['txn_type'], $encoded_data_array, $payment_id );
298
	} else {
299
		// Fallback to web accept just in case the txn_type isn't present
300
		do_action( 'give_paypal_web_accept', $encoded_data_array, $payment_id );
301
	}
302
	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...
303
}
304
305
add_action( 'give_verify_paypal_ipn', 'give_process_paypal_ipn' );
306
307
/**
308
 * Process web accept (one time) payment IPNs
309
 *
310
 * @since 1.0
311
 *
312
 * @param array $data IPN Data
313
 *
314
 * @return void
315
 */
316
function give_process_paypal_web_accept_and_cart( $data, $payment_id ) {
317
318
	if ( $data['txn_type'] != 'web_accept' && $data['txn_type'] != 'cart' && $data['payment_status'] != 'Refunded' ) {
319
		return;
320
	}
321
322
	if ( empty( $payment_id ) ) {
323
		return;
324
	}
325
326
	// Collect payment details
327
	$purchase_key   = isset( $data['invoice'] ) ? $data['invoice'] : $data['item_number'];
328
	$paypal_amount  = $data['mc_gross'];
329
	$payment_status = strtolower( $data['payment_status'] );
330
	$currency_code  = strtolower( $data['mc_currency'] );
331
	$business_email = isset( $data['business'] ) && is_email( $data['business'] ) ? trim( $data['business'] ) : trim( $data['receiver_email'] );
332
	$payment_meta   = give_get_payment_meta( $payment_id );
333
334
335
	if ( give_get_payment_gateway( $payment_id ) != 'paypal' ) {
336
		return; // this isn't a PayPal standard IPN
337
	}
338
339
	// Verify payment recipient
340
	if ( strcasecmp( $business_email, trim( give_get_option( 'paypal_email' ) ) ) != 0 ) {
341
342
		give_record_gateway_error( __( 'IPN Error', 'give' ), sprintf( __( 'Invalid business email in IPN response. IPN data: %s', 'give' ), json_encode( $data ) ), $payment_id );
343
		give_update_payment_status( $payment_id, 'failed' );
344
		give_insert_payment_note( $payment_id, __( 'Payment failed due to invalid PayPal business email.', 'give' ) );
345
346
		return;
347
	}
348
349
	// Verify payment currency
350
	if ( $currency_code != strtolower( $payment_meta['currency'] ) ) {
351
352
		give_record_gateway_error( __( 'IPN Error', 'give' ), sprintf( __( 'Invalid currency in IPN response. IPN data: %s', 'give' ), json_encode( $data ) ), $payment_id );
353
		give_update_payment_status( $payment_id, 'failed' );
354
		give_insert_payment_note( $payment_id, __( 'Payment failed due to invalid currency in PayPal IPN.', 'give' ) );
355
356
		return;
357
	}
358
359
	if ( ! give_get_payment_user_email( $payment_id ) ) {
360
361
		// No email associated with purchase, so store from PayPal
362
		give_update_payment_meta( $payment_id, '_give_payment_user_email', $data['payer_email'] );
363
364
		// Setup and store the donors's details
365
		$address            = array();
366
		$address['line1']   = ! empty( $data['address_street'] ) ? sanitize_text_field( $data['address_street'] ) : false;
367
		$address['city']    = ! empty( $data['address_city'] ) ? sanitize_text_field( $data['address_city'] ) : false;
368
		$address['state']   = ! empty( $data['address_state'] ) ? sanitize_text_field( $data['address_state'] ) : false;
369
		$address['country'] = ! empty( $data['address_country_code'] ) ? sanitize_text_field( $data['address_country_code'] ) : false;
370
		$address['zip']     = ! empty( $data['address_zip'] ) ? sanitize_text_field( $data['address_zip'] ) : false;
371
372
		$user_info = array(
373
			'id'         => '-1',
374
			'email'      => sanitize_text_field( $data['payer_email'] ),
375
			'first_name' => sanitize_text_field( $data['first_name'] ),
376
			'last_name'  => sanitize_text_field( $data['last_name'] ),
377
			'discount'   => '',
378
			'address'    => $address
379
		);
380
381
		$payment_meta['user_info'] = $user_info;
382
		give_update_payment_meta( $payment_id, '_give_payment_meta', $payment_meta );
383
	}
384
385
	if ( $payment_status == 'refunded' || $payment_status == 'reversed' ) {
386
387
		// Process a refund
388
		give_process_paypal_refund( $data, $payment_id );
389
390
	} else {
391
392
		if ( get_post_status( $payment_id ) == 'publish' ) {
393
			return; // Only complete payments once
394
		}
395
396
		// Retrieve the total purchase amount (before PayPal)
397
		$payment_amount = give_get_payment_amount( $payment_id );
398
399
		if ( number_format( (float) $paypal_amount, 2 ) < number_format( (float) $payment_amount, 2 ) ) {
400
			// The prices don't match
401
			give_record_gateway_error( __( 'IPN Error', 'give' ), sprintf( __( 'Invalid payment amount in IPN response. IPN data: %s', 'give' ), json_encode( $data ) ), $payment_id );
402
			give_update_payment_status( $payment_id, 'failed' );
403
			give_insert_payment_note( $payment_id, __( 'Payment failed due to invalid amount in PayPal IPN.', 'give' ) );
404
405
			return;
406
		}
407
		if ( $purchase_key != give_get_payment_key( $payment_id ) ) {
408
			// Purchase keys don't match
409
			give_record_gateway_error( __( 'IPN Error', 'give' ), sprintf( __( 'Invalid purchase key in IPN response. IPN data: %s', 'give' ), json_encode( $data ) ), $payment_id );
410
			give_update_payment_status( $payment_id, 'failed' );
411
			give_insert_payment_note( $payment_id, __( 'Payment failed due to invalid purchase key in PayPal IPN.', 'give' ) );
412
413
			return;
414
		}
415
416
		if ( $payment_status == 'completed' || give_is_test_mode() ) {
417
			give_insert_payment_note( $payment_id, sprintf( __( 'PayPal Transaction ID: %s', 'give' ), $data['txn_id'] ) );
418
			give_set_payment_transaction_id( $payment_id, $data['txn_id'] );
419
			give_update_payment_status( $payment_id, 'publish' );
420
		} else if ( 'pending' == $payment_status && isset( $data['pending_reason'] ) ) {
421
422
			// Look for possible pending reasons, such as an echeck
423
424
			$note = '';
425
426
			switch ( strtolower( $data['pending_reason'] ) ) {
427
428
				case 'echeck' :
429
430
					$note = __( 'Payment made via eCheck and will clear automatically in 5-8 days', 'give' );
431
432
					break;
433
434
				case 'address' :
435
436
					$note = __( 'Payment requires a confirmed donor address and must be accepted manually through PayPal', 'give' );
437
438
					break;
439
440
				case 'intl' :
441
442
					$note = __( 'Payment must be accepted manually through PayPal due to international account regulations', 'give' );
443
444
					break;
445
446
				case 'multi-currency' :
447
448
					$note = __( 'Payment received in non-shop currency and must be accepted manually through PayPal', 'give' );
449
450
					break;
451
452
				case 'paymentreview' :
453
				case 'regulatory_review' :
454
455
					$note = __( 'Payment is being reviewed by PayPal staff as high-risk or in possible violation of government regulations', 'give' );
456
457
					break;
458
459
				case 'unilateral' :
460
461
					$note = __( 'Payment was sent to non-confirmed or non-registered email address.', 'give' );
462
463
					break;
464
465
				case 'upgrade' :
466
467
					$note = __( 'PayPal account must be upgraded before this payment can be accepted', 'give' );
468
469
					break;
470
471
				case 'verify' :
472
473
					$note = __( 'PayPal account is not verified. Verify account in order to accept this payment', 'give' );
474
475
					break;
476
477
				case 'other' :
478
479
					$note = __( 'Payment is pending for unknown reasons. Contact PayPal support for assistance', 'give' );
480
481
					break;
482
483
			}
484
485
			if ( ! empty( $note ) ) {
486
487
				give_insert_payment_note( $payment_id, $note );
488
489
			}
490
		}
491
	}
492
}
493
494
add_action( 'give_paypal_web_accept', 'give_process_paypal_web_accept_and_cart', 10, 2 );
495
496
/**
497
 * Process PayPal IPN Refunds
498
 *
499
 * @since 1.0
500
 *
501
 * @param array $data IPN Data
502
 *
503
 * @return void
504
 */
505
function give_process_paypal_refund( $data, $payment_id = 0 ) {
506
507
	// Collect payment details
508
509
	if ( empty( $payment_id ) ) {
510
		return;
511
	}
512
513
	if ( get_post_status( $payment_id ) == 'refunded' ) {
514
		return; // Only refund payments once
515
	}
516
517
	$payment_amount = give_get_payment_amount( $payment_id );
518
	$refund_amount  = $data['payment_gross'] * - 1;
519
520
	if ( number_format( (float) $refund_amount, 2 ) < number_format( (float) $payment_amount, 2 ) ) {
521
522
		give_insert_payment_note( $payment_id, sprintf( __( 'Partial PayPal refund processed: %s', 'give' ), $data['parent_txn_id'] ) );
523
524
		return; // This is a partial refund
525
526
	}
527
528
	give_insert_payment_note( $payment_id, sprintf( __( 'PayPal Payment #%s Refunded for reason: %s', 'give' ), $data['parent_txn_id'], $data['reason_code'] ) );
529
	give_insert_payment_note( $payment_id, sprintf( __( 'PayPal Refund Transaction ID: %s', 'give' ), $data['txn_id'] ) );
530
	give_update_payment_status( $payment_id, 'refunded' );
531
}
532
533
/**
534
 * Get PayPal Redirect
535
 *
536
 * @since 1.0
537
 *
538
 * @param bool $ssl_check Is SSL?
539
 *
540
 * @return string
541
 */
542
function give_get_paypal_redirect( $ssl_check = false ) {
543
544
	if ( is_ssl() || ! $ssl_check ) {
545
		$protocal = 'https://';
546
	} else {
547
		$protocal = 'http://';
548
	}
549
550
	// Check the current payment mode
551
	if ( give_is_test_mode() ) {
552
		// Test mode
553
		$paypal_uri = $protocal . 'www.sandbox.paypal.com/cgi-bin/webscr';
554
	} else {
555
		// Live mode
556
		$paypal_uri = $protocal . 'www.paypal.com/cgi-bin/webscr';
557
	}
558
559
	return apply_filters( 'give_paypal_uri', $paypal_uri );
560
}
561
562
/**
563
 * Set the Page Style for PayPal Purchase page
564
 *
565
 * @since 1.0
566
 * @return string
567
 */
568
function give_get_paypal_page_style() {
569
	$page_style = trim( give_get_option( 'paypal_page_style', 'PayPal' ) );
570
	return apply_filters( 'give_paypal_page_style', $page_style );
571
}
572
573
/**
574
 * PayPal Success Page
575
 *
576
 * @description: Shows "Donation Processing" message for PayPal payments that are still pending on site return
577
 *
578
 * @since      1.0
579
 *
580
 * @param $content
581
 *
582
 * @return string
583
 *
584
 */
585
function give_paypal_success_page_content( $content ) {
586
587
	if ( ! isset( $_GET['payment-id'] ) && ! give_get_purchase_session() ) {
588
		return $content;
589
	}
590
591
	$payment_id = isset( $_GET['payment-id'] ) ? absint( $_GET['payment-id'] ) : false;
592
593
	if ( ! $payment_id ) {
594
		$session    = give_get_purchase_session();
595
		$payment_id = give_get_purchase_id_by_key( $session['purchase_key'] );
596
	}
597
598
	$payment = get_post( $payment_id );
599
600
	if ( $payment && 'pending' == $payment->post_status ) {
601
602
		// Payment is still pending so show processing indicator to fix the Race Condition
603
		ob_start();
604
605
		give_get_template_part( 'payment', 'processing' );
606
607
		$content = ob_get_clean();
608
609
	}
610
611
	return $content;
612
613
}
614
615
add_filter( 'give_payment_confirm_paypal', 'give_paypal_success_page_content' );
616
617
/**
618
 * Given a Payment ID, extract the transaction ID
619
 *
620
 * @since  1.0
621
 *
622
 * @param  string $payment_id Payment ID
623
 *
624
 * @return string                   Transaction ID
625
 */
626
function give_paypal_get_payment_transaction_id( $payment_id ) {
627
628
	$transaction_id = '';
629
	$notes          = give_get_payment_notes( $payment_id );
630
631
	foreach ( $notes as $note ) {
632
		if ( preg_match( '/^PayPal Transaction ID: ([^\s]+)/', $note->comment_content, $match ) ) {
633
			$transaction_id = $match[1];
634
			continue;
635
		}
636
	}
637
638
	return apply_filters( 'give_paypal_set_payment_transaction_id', $transaction_id, $payment_id );
639
}
640
641
add_filter( 'give_get_payment_transaction_id-paypal', 'give_paypal_get_payment_transaction_id', 10, 1 );
642
643
/**
644
 * Given a transaction ID, generate a link to the PayPal transaction ID details
645
 *
646
 * @since  1.0
647
 *
648
 * @param  string $transaction_id The Transaction ID
649
 * @param  int    $payment_id     The payment ID for this transaction
650
 *
651
 * @return string                 A link to the PayPal transaction details
652
 */
653
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...
654
655
	$paypal_base_url = 'https://history.paypal.com/cgi-bin/webscr?cmd=_history-details-from-hub&id=';
656
	$transaction_url = '<a href="' . esc_url( $paypal_base_url . $transaction_id ) . '" target="_blank">' . $transaction_id . '</a>';
657
658
	return apply_filters( 'give_paypal_link_payment_details_transaction_id', $transaction_url );
659
660
}
661
662
add_filter( 'give_payment_details_transaction_id-paypal', 'give_paypal_link_transaction_id', 10, 2 );
663