Passed
Push — master ( cb75a2...6929e7 )
by Brian
05:54 queued 11s
created

wpinv_paypal_success_page_content()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 18
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 7
c 0
b 0
f 0
dl 0
loc 18
rs 10
cc 4
nc 3
nop 1
1
<?php
2
// Exit if accessed directly
3
if ( ! defined( 'ABSPATH' ) ) exit;
4
5
add_action( 'wpinv_paypal_cc_form', '__return_false' );
6
add_filter( 'wpinv_paypal_support_subscription', '__return_true' );
7
8
9
function wpinv_paypal_gateway_description_sandbox_notice( $description, $gateway ) {
10
    if ( 'paypal' == $gateway && wpinv_is_test_mode( 'paypal' ) ) {
11
        $description .= '<br>' . sprintf(
12
            __( 'SANDBOX ENABLED. You can use sandbox testing accounts only. See the %sPayPal Sandbox Testing Guide%s for more details.', 'invoicing' ),
13
            '<a href="https://developer.paypal.com/docs/classic/lifecycle/ug_sandbox/">',
14
            '</a>'
15
        );
16
    }
17
    return $description;
18
19
}
20
add_filter( 'wpinv_gateway_description', 'wpinv_paypal_gateway_description_sandbox_notice', 10, 2 );
21
22
function wpinv_process_paypal_payment( $purchase_data ) {
23
    if( ! wp_verify_nonce( $purchase_data['gateway_nonce'], 'wpi-gateway' ) ) {
24
        wp_die( __( 'Nonce verification has failed', 'invoicing' ), __( 'Error', 'invoicing' ), array( 'response' => 403 ) );
25
    }
26
27
    // Collect payment data
28
    $payment_data = array(
29
        'price'         => $purchase_data['price'],
30
        'date'          => $purchase_data['date'],
31
        'user_email'    => $purchase_data['user_email'],
32
        'invoice_key'   => $purchase_data['invoice_key'],
33
        'currency'      => wpinv_get_currency(),
34
        'items'         => $purchase_data['items'],
35
        'user_info'     => $purchase_data['user_info'],
36
        'cart_details'  => $purchase_data['cart_details'],
37
        'gateway'       => 'paypal',
38
        'status'        => 'wpi-pending'
39
    );
40
41
    // Record the pending payment
42
    $invoice = wpinv_get_invoice( $purchase_data['invoice_id'] );
43
44
    // Check payment
45
    if ( ! $invoice ) {
46
        // Record the error
47
        wpinv_record_gateway_error( __( 'Payment Error', 'invoicing' ), sprintf( __( 'Payment creation failed before sending buyer to PayPal. Payment data: %s', 'invoicing' ), json_encode( $payment_data ) ), $invoice );
48
        // Problems? send back
49
        wpinv_send_back_to_checkout( '?payment-mode=' . $purchase_data['post_data']['wpi-gateway'] );
50
    } else {
51
        // Only send to PayPal if the pending payment is created successfully
52
        $listener_url = wpinv_get_ipn_url( 'paypal' );
53
54
        // Get the success url
55
        $return_url = add_query_arg( array(
56
                'payment-confirm' => 'paypal',
57
                'invoice-id'      => $invoice->ID,
58
                'utm_nooverride'  => 1
59
            ), get_permalink( wpinv_get_option( 'success_page', false ) ) );
0 ignored issues
show
Bug introduced by
It seems like wpinv_get_option('success_page', false) can also be of type false; however, parameter $post of get_permalink() does only seem to accept WP_Post|integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

59
            ), get_permalink( /** @scrutinizer ignore-type */ wpinv_get_option( 'success_page', false ) ) );
Loading history...
60
61
        // Get the PayPal redirect uri
62
        $paypal_redirect = wpinv_get_paypal_redirect();
63
64
        // Setup PayPal arguments
65
        $paypal_args = array(
66
            'cmd'           => '_cart',
67
            'upload'        => '1',
68
            'business'      => wpinv_get_option( 'paypal_email', false ),
69
            'email'         => $invoice->get_email(),
70
            'first_name'    => $invoice->get_first_name(),
71
            'last_name'     => $invoice->get_last_name(),
72
            'invoice'       => $invoice->get_key(),
73
            'no_shipping'   => '1',
74
            'shipping'      => '0',
75
            'no_note'       => '1',
76
            'currency_code' => $invoice->get_currency(),
77
            'charset'       => get_bloginfo( 'charset' ),
78
            'custom'        => $invoice->ID,
79
            'rm'            => is_ssl() ? 2 : 1,
80
            'return'        => esc_url_raw( $return_url ),
81
            'cancel_return' => $invoice->get_checkout_payment_url(),
82
            'notify_url'    => $listener_url,
83
            'cbt'           => get_bloginfo( 'name' ),
84
            'bn'            => 'WPInvoicing_SP',
85
        );
86
87
        // Add cart items
88
        $i = 1;
89
        if( is_array( $purchase_data['cart_details'] ) && ! empty( $purchase_data['cart_details'] ) ) {
90
            foreach ( $purchase_data['cart_details'] as $item ) {
91
                $item['quantity'] = $item['quantity'] > 0 ? $item['quantity'] : 1;
92
                $item_amount = wpinv_sanitize_amount( $item['subtotal'] / $item['quantity'], 2 );
93
94
                if ( $item_amount <= 0 ) {
95
                    $item_amount = 0;
96
                }
97
98
                $paypal_args['item_number_' . $i ]      = $item['id'];
99
                $paypal_args['item_name_' . $i ]        = stripslashes_deep( html_entity_decode( wpinv_get_cart_item_name( $item ), ENT_COMPAT, 'UTF-8' ) );
100
                $paypal_args['quantity_' . $i ]         = $item['quantity'];
101
                $paypal_args['amount_' . $i ]           = $item_amount;
102
                $paypal_args['discount_amount_' . $i ]  = wpinv_sanitize_amount( $item['discount'], 2 );
103
104
                $i++;
105
            }
106
        }
107
108
        // Add taxes to the cart
109
        if ( wpinv_use_taxes() && $invoice->is_taxable() ) {
110
            $paypal_args['tax_cart'] = wpinv_sanitize_amount( (float) $invoice->get_tax(), 2 );
0 ignored issues
show
Bug introduced by
The call to WPInv_Invoice::get_tax() has too few arguments starting with tax. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

110
            $paypal_args['tax_cart'] = wpinv_sanitize_amount( (float) $invoice->/** @scrutinizer ignore-call */ get_tax(), 2 );

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
111
        }
112
113
        $paypal_args = apply_filters( 'wpinv_paypal_args', $paypal_args, $purchase_data, $invoice );
114
115
        // Build query
116
        $paypal_redirect .= http_build_query( $paypal_args );
117
118
        // Fix for some sites that encode the entities
119
        $paypal_redirect = str_replace( '&amp;', '&', $paypal_redirect );
120
121
        // Get rid of cart contents
122
        wpinv_empty_cart();
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_empty_cart() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

122
        /** @scrutinizer ignore-deprecated */ wpinv_empty_cart();
Loading history...
123
        
124
        // Redirect to PayPal
125
        wp_redirect( $paypal_redirect );
126
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
127
    }
128
}
129
add_action( 'wpinv_gateway_paypal', 'wpinv_process_paypal_payment' );
130
131
function wpinv_get_paypal_recurring_args( $paypal_args, $purchase_data, $invoice ) {
132
    if ( $invoice->is_recurring() && $subscription = wpinv_get_subscription( $invoice->ID ) ) {
133
134
        $period             = strtoupper( substr( $subscription->period, 0, 1) );
135
        $interval           = $subscription->frequency;
0 ignored issues
show
Bug Best Practice introduced by
The property frequency does not exist on WPInv_Subscription. Since you implemented __get, consider adding a @property annotation.
Loading history...
136
        $bill_times         = (int) $subscription->bill_times;
137
        
138
        $initial_amount     = wpinv_sanitize_amount( $invoice->get_total(), 2 );
139
        $recurring_amount   = wpinv_sanitize_amount( $invoice->get_recurring_details( 'total' ), 2 );
140
        
141
        $paypal_args['cmd'] = '_xclick-subscriptions';
142
        $paypal_args['sra'] = '1';
143
        $paypal_args['src'] = '1';
144
        
145
        // Set item description
146
        $item_name                  = wpinv_get_cart_item_name( array( 'id' => $subscription->product_id ) );
147
        $paypal_args['item_name']   = stripslashes_deep( html_entity_decode( $item_name, ENT_COMPAT, 'UTF-8' ) );
148
        $paypal_args['item_number'] = $subscription->product_id;
149
        $item   = new WPInv_Item( $subscription->period );
150
151
        if ( $invoice->is_free_trial() && $item->has_free_trial() ) {
152
            $paypal_args['a1']  = $initial_amount;
153
            $paypal_args['p1']  = $item->get_trial_interval();
154
            $paypal_args['t1']  = $item->get_trial_period();
155
            
156
            // Set the recurring amount
157
            $paypal_args['a3']  = $recurring_amount;
158
        } else if ( $initial_amount != $recurring_amount && $bill_times != 1 ) {
159
            $paypal_args['a1']  = $initial_amount;
160
            $paypal_args['p1']  = $interval;
161
            $paypal_args['t1']  = $period;
162
163
            // Set the recurring amount
164
            $paypal_args['a3']  = $recurring_amount;
165
            
166
            if ( $bill_times > 1 ) {
167
                $bill_times--;
168
            }
169
        } else {
170
            $paypal_args['a3']  = $initial_amount;
171
        }
172
        
173
        $paypal_args['p3']  = $interval;
174
        $paypal_args['t3']  = $period;
175
        
176
        if ( $bill_times > 1 ) {
177
            // Make sure it's not over the max of 52
178
            $paypal_args['srt'] = ( $bill_times <= 52 ? absint( $bill_times ) : 52 );
179
        }
180
                
181
        // Remove cart items
182
        $i = 1;
183
        if( is_array( $purchase_data['cart_details'] ) && ! empty( $purchase_data['cart_details'] ) ) {
184
            foreach ( $purchase_data['cart_details'] as $item ) {                
185
                if ( isset( $paypal_args['item_number_' . $i] ) ) {
186
                    unset( $paypal_args['item_number_' . $i] );
187
                }
188
                if ( isset( $paypal_args['item_name_' . $i] ) ) {
189
                    unset( $paypal_args['item_name_' . $i] );
190
                }
191
                if ( isset( $paypal_args['quantity_' . $i] ) ) {
192
                    unset( $paypal_args['quantity_' . $i] );
193
                }
194
                if ( isset( $paypal_args['amount_' . $i] ) ) {
195
                    unset( $paypal_args['amount_' . $i] );
196
                }
197
                if ( isset( $paypal_args['discount_amount_' . $i] ) ) {
198
                    unset( $paypal_args['discount_amount_' . $i] );
199
                }
200
201
                $i++;
202
            }
203
        }
204
        
205
        if ( isset( $paypal_args['tax_cart'] ) ) {
206
            unset( $paypal_args['tax_cart'] );
207
        }
208
                
209
        if ( isset( $paypal_args['upload'] ) ) {
210
            unset( $paypal_args['upload'] );
211
        }
212
        
213
        $paypal_args = apply_filters( 'wpinv_paypal_recurring_args', $paypal_args, $purchase_data, $invoice );
214
    }
215
    
216
    return $paypal_args;
217
}
218
add_filter( 'wpinv_paypal_args', 'wpinv_get_paypal_recurring_args', 10, 3 );
219
220
function wpinv_process_paypal_ipn() {
221
	// Check the request method is POST
222
	if ( isset( $_SERVER['REQUEST_METHOD'] ) && $_SERVER['REQUEST_METHOD'] != 'POST' ) {
223
		return;
224
	}
225
226
	// Set initial post data to empty string
227
	$post_data = '';
228
229
	// Fallback just in case post_max_size is lower than needed
230
	if ( ini_get( 'allow_url_fopen' ) ) {
231
		$post_data = file_get_contents( 'php://input' );
232
	} else {
233
		// If allow_url_fopen is not enabled, then make sure that post_max_size is large enough
234
		ini_set( 'post_max_size', '12M' );
235
	}
236
	// Start the encoded data collection with notification command
237
	$encoded_data = 'cmd=_notify-validate';
238
239
	// Get current arg separator
240
	$arg_separator = wpinv_get_php_arg_separator_output();
241
242
	// Verify there is a post_data
243
	if ( $post_data || strlen( $post_data ) > 0 ) {
244
		// Append the data
245
		$encoded_data .= $arg_separator.$post_data;
246
	} else {
247
		// Check if POST is empty
248
		if ( empty( $_POST ) ) {
249
			// Nothing to do
250
			return;
251
		} else {
252
			// Loop through each POST
253
			foreach ( $_POST as $key => $value ) {
254
				// Encode the value and append the data
255
				$encoded_data .= $arg_separator."$key=" . urlencode( $value );
256
			}
257
		}
258
	}
259
260
	// Convert collected post data to an array
261
	wp_parse_str( $encoded_data, $encoded_data_array );
0 ignored issues
show
Security Variable Injection introduced by
$encoded_data can contain request data and is used in variable name context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_POST, and $_POST is assigned to $value
    in includes/gateways/paypal.php on line 253
  2. Data is passed through urlencode(), and $arg_separator . $key.'=' . urlencode($value) is assigned to $encoded_data
    in includes/gateways/paypal.php on line 255

Used in variable context

  1. wp_parse_str() is called
    in includes/gateways/paypal.php on line 262
  2. Enters via parameter $string
    in wordpress/wp-includes/formatting.php on line 4865
  3. parse_str() is called
    in wordpress/wp-includes/formatting.php on line 4866

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
262
263
	foreach ( $encoded_data_array as $key => $value ) {
264
		if ( false !== strpos( $key, 'amp;' ) ) {
265
			$new_key = str_replace( '&amp;', '&', $key );
266
			$new_key = str_replace( 'amp;', '&' , $new_key );
267
268
			unset( $encoded_data_array[ $key ] );
269
			$encoded_data_array[ $new_key ] = $value;
270
		}
271
	}
272
273
	// Get the PayPal redirect uri
274
	$paypal_redirect = wpinv_get_paypal_redirect( true );
0 ignored issues
show
Unused Code introduced by
The assignment to $paypal_redirect is dead and can be removed.
Loading history...
275
276
	if ( !wpinv_get_option( 'disable_paypal_verification', false ) ) {
277
		// Validate the IPN
278
279
		$remote_post_vars      = array(
280
			'method'           => 'POST',
281
			'timeout'          => 45,
282
			'redirection'      => 5,
283
			'httpversion'      => '1.1',
284
			'blocking'         => true,
285
			'headers'          => array(
286
				'host'         => 'www.paypal.com',
287
				'connection'   => 'close',
288
				'content-type' => 'application/x-www-form-urlencoded',
289
				'post'         => '/cgi-bin/webscr HTTP/1.1',
290
291
			),
292
			'sslverify'        => false,
293
			'body'             => $encoded_data_array
294
		);
295
296
		// Get response
297
		$api_response = wp_remote_post( wpinv_get_paypal_redirect(), $remote_post_vars );
298
299
		if ( is_wp_error( $api_response ) ) {
300
			wpinv_record_gateway_error( __( 'IPN Error', 'invoicing' ), sprintf( __( 'Invalid IPN verification response. IPN data: %s', 'invoicing' ), json_encode( $api_response ) ) );
301
			return; // Something went wrong
302
		}
303
304
		if ( $api_response['body'] !== 'VERIFIED' && wpinv_get_option( 'disable_paypal_verification', false ) ) {
305
			wpinv_record_gateway_error( __( 'IPN Error', 'invoicing' ), sprintf( __( 'Invalid IPN verification response. IPN data: %s', 'invoicing' ), json_encode( $api_response ) ) );
306
			return; // Response not okay
307
		}
308
	}
309
310
	// Check if $post_data_array has been populated
311
	if ( !is_array( $encoded_data_array ) && !empty( $encoded_data_array ) )
312
		return;
313
314
	$defaults = array(
315
		'txn_type'       => '',
316
		'payment_status' => ''
317
	);
318
319
	$encoded_data_array = wp_parse_args( $encoded_data_array, $defaults );
320
321
	$invoice_id = isset( $encoded_data_array['custom'] ) ? absint( $encoded_data_array['custom'] ) : 0;
322
    
323
	wpinv_error_log( $encoded_data_array['txn_type'], 'PayPal txn_type', __FILE__, __LINE__ );
324
	wpinv_error_log( $encoded_data_array, 'PayPal IPN response', __FILE__, __LINE__ );
325
326
	if ( has_action( 'wpinv_paypal_' . $encoded_data_array['txn_type'] ) ) {
327
		// Allow PayPal IPN types to be processed separately
328
		do_action( 'wpinv_paypal_' . $encoded_data_array['txn_type'], $encoded_data_array, $invoice_id );
329
	} else {
330
		// Fallback to web accept just in case the txn_type isn't present
331
		do_action( 'wpinv_paypal_web_accept', $encoded_data_array, $invoice_id );
332
	}
333
	exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
334
}
335
add_action( 'wpinv_verify_paypal_ipn', 'wpinv_process_paypal_ipn' );
336
337
function wpinv_process_paypal_web_accept_and_cart( $data, $invoice_id ) {
338
	if ( $data['txn_type'] != 'web_accept' && $data['txn_type'] != 'cart' && $data['payment_status'] != 'Refunded' ) {
339
		return;
340
	}
341
342
	if( empty( $invoice_id ) ) {
343
		return;
344
	}
345
346
	// Collect payment details
347
	$purchase_key   = isset( $data['invoice'] ) ? $data['invoice'] : $data['item_number'];
348
	$paypal_amount  = $data['mc_gross'];
349
	$payment_status = strtolower( $data['payment_status'] );
350
	$currency_code  = strtolower( $data['mc_currency'] );
351
	$business_email = isset( $data['business'] ) && is_email( $data['business'] ) ? trim( $data['business'] ) : trim( $data['receiver_email'] );
352
	$payment_meta   = wpinv_get_invoice_meta( $invoice_id );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_get_invoice_meta() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

352
	$payment_meta   = /** @scrutinizer ignore-deprecated */ wpinv_get_invoice_meta( $invoice_id );
Loading history...
353
354
	if ( wpinv_get_payment_gateway( $invoice_id ) != 'paypal' ) {
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_get_payment_gateway() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

354
	if ( /** @scrutinizer ignore-deprecated */ wpinv_get_payment_gateway( $invoice_id ) != 'paypal' ) {
Loading history...
355
		return; // this isn't a PayPal standard IPN
356
	}
357
358
	// Verify payment recipient
359
	if ( strcasecmp( $business_email, trim( wpinv_get_option( 'paypal_email', false ) ) ) != 0 ) {
0 ignored issues
show
Bug introduced by
It seems like wpinv_get_option('paypal_email', false) can also be of type false; however, parameter $str of trim() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

359
	if ( strcasecmp( $business_email, trim( /** @scrutinizer ignore-type */ wpinv_get_option( 'paypal_email', false ) ) ) != 0 ) {
Loading history...
360
		wpinv_record_gateway_error( __( 'IPN Error', 'invoicing' ), sprintf( __( 'Invalid business email in IPN response. IPN data: %s', 'invoicing' ), json_encode( $data ) ), $invoice_id );
361
		wpinv_update_payment_status( $invoice_id, 'wpi-failed' );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_update_payment_status() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

361
		/** @scrutinizer ignore-deprecated */ wpinv_update_payment_status( $invoice_id, 'wpi-failed' );
Loading history...
362
		wpinv_insert_payment_note( $invoice_id, __( 'Payment failed due to invalid PayPal business email.', 'invoicing' ), '', '', true );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_insert_payment_note() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

362
		/** @scrutinizer ignore-deprecated */ wpinv_insert_payment_note( $invoice_id, __( 'Payment failed due to invalid PayPal business email.', 'invoicing' ), '', '', true );
Loading history...
363
		return;
364
	}
365
366
	// Verify payment currency
367
	if ( $currency_code != strtolower( $payment_meta['currency'] ) ) {
368
		wpinv_record_gateway_error( __( 'IPN Error', 'invoicing' ), sprintf( __( 'Invalid currency in IPN response. IPN data: %s', 'invoicing' ), json_encode( $data ) ), $invoice_id );
369
		wpinv_update_payment_status( $invoice_id, 'wpi-failed' );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_update_payment_status() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

369
		/** @scrutinizer ignore-deprecated */ wpinv_update_payment_status( $invoice_id, 'wpi-failed' );
Loading history...
370
		wpinv_insert_payment_note( $invoice_id, __( 'Payment failed due to invalid currency in PayPal IPN.', 'invoicing' ), '', '', true );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_insert_payment_note() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

370
		/** @scrutinizer ignore-deprecated */ wpinv_insert_payment_note( $invoice_id, __( 'Payment failed due to invalid currency in PayPal IPN.', 'invoicing' ), '', '', true );
Loading history...
371
		return;
372
	}
373
374
	if ( !wpinv_get_payment_user_email( $invoice_id ) ) {
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_get_payment_user_email() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

374
	if ( !/** @scrutinizer ignore-deprecated */ wpinv_get_payment_user_email( $invoice_id ) ) {
Loading history...
375
		// This runs when a Buy Now purchase was made. It bypasses checkout so no personal info is collected until PayPal
376
		// No email associated with purchase, so store from PayPal
377
		wpinv_update_invoice_meta( $invoice_id, '_wpinv_email', $data['payer_email'] );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_update_invoice_meta() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

377
		/** @scrutinizer ignore-deprecated */ wpinv_update_invoice_meta( $invoice_id, '_wpinv_email', $data['payer_email'] );
Loading history...
378
379
		// Setup and store the customer's details
380
		$user_info = array(
381
			'user_id'    => '-1',
382
			'email'      => sanitize_text_field( $data['payer_email'] ),
383
			'first_name' => sanitize_text_field( $data['first_name'] ),
384
			'last_name'  => sanitize_text_field( $data['last_name'] ),
385
			'discount'   => '',
386
		);
387
		$user_info['address'] = ! empty( $data['address_street']       ) ? sanitize_text_field( $data['address_street'] )       : false;
388
		$user_info['city']    = ! empty( $data['address_city']         ) ? sanitize_text_field( $data['address_city'] )         : false;
389
		$user_info['state']   = ! empty( $data['address_state']        ) ? sanitize_text_field( $data['address_state'] )        : false;
390
		$user_info['country'] = ! empty( $data['address_country_code'] ) ? sanitize_text_field( $data['address_country_code'] ) : false;
391
		$user_info['zip']     = ! empty( $data['address_zip']          ) ? sanitize_text_field( $data['address_zip'] )          : false;
392
393
		$payment_meta['user_info'] = $user_info;
394
		wpinv_update_invoice_meta( $invoice_id, '_wpinv_payment_meta', $payment_meta );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_update_invoice_meta() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

394
		/** @scrutinizer ignore-deprecated */ wpinv_update_invoice_meta( $invoice_id, '_wpinv_payment_meta', $payment_meta );
Loading history...
395
	}
396
397
	if ( $payment_status == 'refunded' || $payment_status == 'reversed' ) {
398
		// Process a refund
399
		wpinv_process_paypal_refund( $data, $invoice_id );
400
	} else {
401
		if ( get_post_status( $invoice_id ) == 'publish' ) {
402
			return; // Only paid payments once
403
		}
404
405
		// Retrieve the total purchase amount (before PayPal)
406
		$payment_amount = wpinv_payment_total( $invoice_id );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_payment_total() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

406
		$payment_amount = /** @scrutinizer ignore-deprecated */ wpinv_payment_total( $invoice_id );
Loading history...
407
408
		if ( number_format( (float) $paypal_amount, 2 ) < number_format( (float) $payment_amount, 2 ) ) {
409
			// The prices don't match
410
			wpinv_record_gateway_error( __( 'IPN Error', 'invoicing' ), sprintf( __( 'Invalid payment amount in IPN response. IPN data: %s', 'invoicing' ), json_encode( $data ) ), $invoice_id );
411
			wpinv_update_payment_status( $invoice_id, 'wpi-failed' );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_update_payment_status() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

411
			/** @scrutinizer ignore-deprecated */ wpinv_update_payment_status( $invoice_id, 'wpi-failed' );
Loading history...
412
			wpinv_insert_payment_note( $invoice_id, __( 'Payment failed due to invalid amount in PayPal IPN.', 'invoicing' ), '', '', true );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_insert_payment_note() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

412
			/** @scrutinizer ignore-deprecated */ wpinv_insert_payment_note( $invoice_id, __( 'Payment failed due to invalid amount in PayPal IPN.', 'invoicing' ), '', '', true );
Loading history...
413
			return;
414
		}
415
		if ( $purchase_key != wpinv_get_payment_key( $invoice_id ) ) {
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_get_payment_key() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

415
		if ( $purchase_key != /** @scrutinizer ignore-deprecated */ wpinv_get_payment_key( $invoice_id ) ) {
Loading history...
416
			// Purchase keys don't match
417
			wpinv_record_gateway_error( __( 'IPN Error', 'invoicing' ), sprintf( __( 'Invalid purchase key in IPN response. IPN data: %s', 'invoicing' ), json_encode( $data ) ), $invoice_id );
418
			wpinv_update_payment_status( $invoice_id, 'wpi-failed' );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_update_payment_status() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

418
			/** @scrutinizer ignore-deprecated */ wpinv_update_payment_status( $invoice_id, 'wpi-failed' );
Loading history...
419
			wpinv_insert_payment_note( $invoice_id, __( 'Payment failed due to invalid purchase key in PayPal IPN.', 'invoicing' ), '', '', true );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_insert_payment_note() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

419
			/** @scrutinizer ignore-deprecated */ wpinv_insert_payment_note( $invoice_id, __( 'Payment failed due to invalid purchase key in PayPal IPN.', 'invoicing' ), '', '', true );
Loading history...
420
			return;
421
		}
422
423
		if ( 'complete' == $payment_status || 'completed' == $payment_status || 'processed' == $payment_status || wpinv_is_test_mode( 'paypal' ) ) {
424
			wpinv_insert_payment_note( $invoice_id, sprintf( __( 'PayPal Transaction ID: %s', 'invoicing' ) , $data['txn_id'] ), '', '', true );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_insert_payment_note() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

424
			/** @scrutinizer ignore-deprecated */ wpinv_insert_payment_note( $invoice_id, sprintf( __( 'PayPal Transaction ID: %s', 'invoicing' ) , $data['txn_id'] ), '', '', true );
Loading history...
425
			wpinv_set_payment_transaction_id( $invoice_id, $data['txn_id'] );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_set_payment_transaction_id() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

425
			/** @scrutinizer ignore-deprecated */ wpinv_set_payment_transaction_id( $invoice_id, $data['txn_id'] );
Loading history...
426
			wpinv_update_payment_status( $invoice_id, 'publish' );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_update_payment_status() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

426
			/** @scrutinizer ignore-deprecated */ wpinv_update_payment_status( $invoice_id, 'publish' );
Loading history...
427
		} else if ( 'pending' == $payment_status && isset( $data['pending_reason'] ) ) {
428
			// Look for possible pending reasons, such as an echeck
429
			$note = '';
430
431
			switch( strtolower( $data['pending_reason'] ) ) {
432
				case 'echeck' :
433
					$note = __( 'Payment made via eCheck and will clear automatically in 5-8 days', 'invoicing' );
434
					break;
435
				
436
                case 'address' :
437
					$note = __( 'Payment requires a confirmed customer address and must be accepted manually through PayPal', 'invoicing' );
438
					break;
439
				
440
                case 'intl' :
441
					$note = __( 'Payment must be accepted manually through PayPal due to international account regulations', 'invoicing' );
442
					break;
443
				
444
                case 'multi-currency' :
445
					$note = __( 'Payment received in non-shop currency and must be accepted manually through PayPal', 'invoicing' );
446
					break;
447
				
448
                case 'paymentreview' :
449
                case 'regulatory_review' :
450
					$note = __( 'Payment is being reviewed by PayPal staff as high-risk or in possible violation of government regulations', 'invoicing' );
451
					break;
452
				
453
                case 'unilateral' :
454
					$note = __( 'Payment was sent to non-confirmed or non-registered email address.', 'invoicing' );
455
					break;
456
				
457
                case 'upgrade' :
458
					$note = __( 'PayPal account must be upgraded before this payment can be accepted', 'invoicing' );
459
					break;
460
				
461
                case 'verify' :
462
					$note = __( 'PayPal account is not verified. Verify account in order to accept this payment', 'invoicing' );
463
					break;
464
465
				case 'other' :
466
					$note = __( 'Payment is pending for unknown reasons. Contact PayPal support for assistance', 'invoicing' );
467
					break;
468
			}
469
470
			if ( ! empty( $note ) ) {
471
				wpinv_insert_payment_note( $invoice_id, $note, '', '', true );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_insert_payment_note() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

471
				/** @scrutinizer ignore-deprecated */ wpinv_insert_payment_note( $invoice_id, $note, '', '', true );
Loading history...
472
			}
473
		} else {
474
			wpinv_insert_payment_note( $invoice_id, wp_sprintf( __( 'PayPal IPN has been received with invalid payment status: %s', 'invoicing' ), $payment_status ), '', '', true );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_insert_payment_note() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

474
			/** @scrutinizer ignore-deprecated */ wpinv_insert_payment_note( $invoice_id, wp_sprintf( __( 'PayPal IPN has been received with invalid payment status: %s', 'invoicing' ), $payment_status ), '', '', true );
Loading history...
475
		}
476
	}
477
}
478
add_action( 'wpinv_paypal_web_accept', 'wpinv_process_paypal_web_accept_and_cart', 10, 2 );
479
480
// Process PayPal subscription sign ups
481
add_action( 'wpinv_paypal_subscr_signup', 'wpinv_process_paypal_subscr_signup' );
482
483
// Process PayPal subscription payments
484
add_action( 'wpinv_paypal_subscr_payment', 'wpinv_process_paypal_subscr_payment' );
485
486
// Process PayPal subscription cancellations
487
add_action( 'wpinv_paypal_subscr_cancel', 'wpinv_process_paypal_subscr_cancel' );
488
489
// Process PayPal subscription end of term notices
490
add_action( 'wpinv_paypal_subscr_eot', 'wpinv_process_paypal_subscr_eot' );
491
492
// Process PayPal payment failed
493
add_action( 'wpinv_paypal_subscr_failed', 'wpinv_process_paypal_subscr_failed' );
494
495
496
/**
497
 * Process the subscription started IPN.
498
 */
499
function wpinv_process_paypal_subscr_signup( $ipn_data ) {
500
    $parent_invoice_id = absint( $ipn_data['custom'] );
501
    if( empty( $parent_invoice_id ) ) {
502
        return;
503
    }
504
505
    $invoice = wpinv_get_invoice( $parent_invoice_id );
506
    if ( empty( $invoice ) ) {
507
        return;
508
    }
509
510
    if ( $invoice->is_free_trial() && !empty( $ipn_data['invoice'] ) ) {
0 ignored issues
show
Bug introduced by
Are you sure the usage of $invoice->is_free_trial() targeting WPInv_Invoice::is_free_trial() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Deprecated Code introduced by
The function WPInv_Invoice::is_free_trial() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

510
    if ( /** @scrutinizer ignore-deprecated */ $invoice->is_free_trial() && !empty( $ipn_data['invoice'] ) ) {
Loading history...
511
        wpinv_insert_payment_note( $parent_invoice_id, sprintf( __( 'PayPal Invoice ID: %s', 'invoicing' ) , $ipn_data['invoice'] ), '', '', true);
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_insert_payment_note() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

511
        /** @scrutinizer ignore-deprecated */ wpinv_insert_payment_note( $parent_invoice_id, sprintf( __( 'PayPal Invoice ID: %s', 'invoicing' ) , $ipn_data['invoice'] ), '', '', true);
Loading history...
512
        if ( !empty( $ipn_data['txn_id'] ) ) {
513
            wpinv_set_payment_transaction_id( $parent_invoice_id, $ipn_data['txn_id'] );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_set_payment_transaction_id() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

513
            /** @scrutinizer ignore-deprecated */ wpinv_set_payment_transaction_id( $parent_invoice_id, $ipn_data['txn_id'] );
Loading history...
514
        }
515
    }
516
517
    $subscription = wpinv_get_paypal_subscription( $ipn_data );
518
    if ( false === $subscription ) {
519
        return;
520
    }
521
    
522
    wpinv_update_payment_status( $parent_invoice_id, 'publish' );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_update_payment_status() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

522
    /** @scrutinizer ignore-deprecated */ wpinv_update_payment_status( $parent_invoice_id, 'publish' );
Loading history...
523
    sleep(1);
524
    wpinv_insert_payment_note( $parent_invoice_id, sprintf( __( 'PayPal Subscription ID: %s', 'invoicing' ) , $ipn_data['subscr_id'] ), '', '', true );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_insert_payment_note() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

524
    /** @scrutinizer ignore-deprecated */ wpinv_insert_payment_note( $parent_invoice_id, sprintf( __( 'PayPal Subscription ID: %s', 'invoicing' ) , $ipn_data['subscr_id'] ), '', '', true );
Loading history...
525
    update_post_meta($parent_invoice_id,'_wpinv_subscr_profile_id', $ipn_data['subscr_id']);
526
527
    $status     = 'trialling' == $subscription->status ? 'trialling' : 'active';
528
    $diff_days  = absint( ( ( strtotime( $subscription->expiration ) - strtotime( $subscription->created ) ) / DAY_IN_SECONDS ) );
529
    $created    = date_i18n( 'Y-m-d H:i:s' );
530
    $expiration = date_i18n( 'Y-m-d 23:59:59', ( strtotime( $created ) + ( $diff_days * DAY_IN_SECONDS ) ) );
531
532
    // Retrieve pending subscription from database and update it's status to active and set proper profile ID
533
    $subscription->update( array( 'profile_id' => $ipn_data['subscr_id'], 'status' => $status, 'created' => $created, 'expiration' => $expiration ) );
534
}
535
536
/**
537
 * Process the subscription payment received IPN.
538
 */
539
function wpinv_process_paypal_subscr_payment( $ipn_data ) {
540
    $parent_invoice_id = absint( $ipn_data['custom'] );
541
542
    $parent_invoice = wpinv_get_invoice( $parent_invoice_id );
543
    if ( empty( $parent_invoice ) ) {
544
        return;
545
    }
546
547
    $subscription = wpinv_get_paypal_subscription( $ipn_data );
548
    if ( false === $subscription ) {
549
        return;
550
    }
551
552
    $transaction_id = wpinv_get_payment_transaction_id( $parent_invoice_id );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_get_payment_transaction_id() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

552
    $transaction_id = /** @scrutinizer ignore-deprecated */ wpinv_get_payment_transaction_id( $parent_invoice_id );
Loading history...
553
    $times_billed   = $subscription->get_times_billed();
554
    $signup_date    = strtotime( $subscription->created );
555
    $today          = date( 'Ynd', $signup_date ) == date( 'Ynd', strtotime( $ipn_data['payment_date'] ) );
556
557
    // Look to see if payment is same day as signup and we have set the transaction ID on the parent payment yet.
558
    if ( (empty($times_billed) || $today) && ( !$transaction_id || $transaction_id == $parent_invoice_id ) ) {
559
        wpinv_update_payment_status( $parent_invoice_id, 'publish' );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_update_payment_status() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

559
        /** @scrutinizer ignore-deprecated */ wpinv_update_payment_status( $parent_invoice_id, 'publish' );
Loading history...
560
        sleep(1);
561
        
562
        // This is the very first payment
563
        wpinv_set_payment_transaction_id( $parent_invoice_id, $ipn_data['txn_id'] );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_set_payment_transaction_id() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

563
        /** @scrutinizer ignore-deprecated */ wpinv_set_payment_transaction_id( $parent_invoice_id, $ipn_data['txn_id'] );
Loading history...
564
        wpinv_insert_payment_note( $parent_invoice_id, sprintf( __( 'PayPal Transaction ID: %s', 'invoicing' ) , $ipn_data['txn_id'] ), '', '', true );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_insert_payment_note() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

564
        /** @scrutinizer ignore-deprecated */ wpinv_insert_payment_note( $parent_invoice_id, sprintf( __( 'PayPal Transaction ID: %s', 'invoicing' ) , $ipn_data['txn_id'] ), '', '', true );
Loading history...
565
        return;
566
    }
567
568
    if ( wpinv_get_id_by_transaction_id( $ipn_data['txn_id'] ) ) {
569
        return; // Payment already recorded
570
    }
571
572
    $currency_code = strtolower( $ipn_data['mc_currency'] );
573
574
    // verify details
575
    if ( $currency_code != strtolower( wpinv_get_currency() ) ) {
576
        // the currency code is invalid
577
        wpinv_record_gateway_error( __( 'IPN Error', 'invoicing' ), sprintf( __( 'Invalid currency in IPN response. IPN data: ', 'invoicing' ), json_encode( $ipn_data ) ) );
578
        return;
579
    }
580
581
    $args = array(
582
        'amount'         => $ipn_data['mc_gross'],
583
        'transaction_id' => $ipn_data['txn_id'],
584
        'gateway'        => 'paypal'
585
    );
586
    
587
    $invoice_id = $subscription->add_payment( $args );
588
589
    if ( $invoice_id > 0 ) {
590
        wpinv_insert_payment_note( $invoice_id, wp_sprintf( __( 'PayPal Transaction ID: %s', 'invoicing' ) , $ipn_data['txn_id'] ), '', '', true );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_insert_payment_note() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

590
        /** @scrutinizer ignore-deprecated */ wpinv_insert_payment_note( $invoice_id, wp_sprintf( __( 'PayPal Transaction ID: %s', 'invoicing' ) , $ipn_data['txn_id'] ), '', '', true );
Loading history...
591
        wpinv_insert_payment_note( $invoice_id, wp_sprintf( __( 'PayPal Subscription ID: %s', 'invoicing' ) , $ipn_data['subscr_id'] ), '', '', true );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_insert_payment_note() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

591
        /** @scrutinizer ignore-deprecated */ wpinv_insert_payment_note( $invoice_id, wp_sprintf( __( 'PayPal Subscription ID: %s', 'invoicing' ) , $ipn_data['subscr_id'] ), '', '', true );
Loading history...
592
593
        $subscription->renew();
594
    }
595
}
596
597
/**
598
 * Process the subscription canceled IPN.
599
 */
600
function wpinv_process_paypal_subscr_cancel( $ipn_data ) {
601
    $subscription = wpinv_get_paypal_subscription( $ipn_data );
602
603
    if( false === $subscription ) {
604
        return;
605
    }
606
607
    $subscription->cancel();
608
}
609
610
/**
611
 * Process the subscription expired IPN.
612
 */
613
function wpinv_process_paypal_subscr_eot( $ipn_data ) {
614
    $subscription = wpinv_get_paypal_subscription( $ipn_data );
615
616
    if( false === $subscription ) {
617
        return;
618
    }
619
620
    $subscription->complete();
621
}
622
623
/**
624
 * Process the subscription payment failed IPN.
625
 */
626
function wpinv_process_paypal_subscr_failed( $ipn_data ) {
627
    $subscription = wpinv_get_paypal_subscription( $ipn_data );
628
629
    if( false === $subscription ) {
630
        return;
631
    }
632
633
    $subscription->failing();
634
635
    do_action( 'wpinv_recurring_payment_failed', $subscription );
636
}
637
638
/**
639
 * Retrieve the subscription this IPN notice is for.
640
 */
641
function wpinv_get_paypal_subscription( $ipn_data = array() ) {
642
    $parent_invoice_id = absint( $ipn_data['custom'] );
643
644
    if( empty( $parent_invoice_id ) ) {
645
        return false;
646
    }
647
648
    $invoice = wpinv_get_invoice( $parent_invoice_id );
649
    if ( empty( $invoice ) ) {
650
        return false;
651
    }
652
653
    $subscription = new WPInv_Subscription( $ipn_data['subscr_id'], true );
654
655
    if ( ! ( ! empty( $subscription ) && $subscription->id > 0 ) ) {
656
        $subscription = wpinv_get_subscription( $parent_invoice_id );
657
658
        if ( ! empty( $subscription ) && $subscription->id > 0 ) {
659
            $subscription->update( array( 'profile_id' => sanitize_text_field( $ipn_data['subscr_id'] ) ) );
660
        } else {
661
            return false;
662
        }
663
    }
664
665
    return $subscription;
666
667
}
668
669
function wpinv_process_paypal_refund( $data, $invoice_id = 0 ) {
670
	// Collect payment details
671
672
	if( empty( $invoice_id ) ) {
673
		return;
674
	}
675
676
	if ( get_post_status( $invoice_id ) == 'wpi-refunded' ) {
677
		return; // Only refund payments once
678
	}
679
680
	$payment_amount = wpinv_payment_total( $invoice_id );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_payment_total() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

680
	$payment_amount = /** @scrutinizer ignore-deprecated */ wpinv_payment_total( $invoice_id );
Loading history...
681
	$refund_amount  = $data['mc_gross'] * -1;
682
683
	do_action( 'wpinv_paypal_refund_request', $data, $invoice_id );
684
685
	if ( number_format( (float) $refund_amount, 2 ) < number_format( (float) $payment_amount, 2 ) ) {
686
		wpinv_insert_payment_note( $invoice_id, wp_sprintf( __( 'PayPal partial refund of %s processed for transaction #%s for reason: %s', 'invoicing' ), (float)$refund_amount . ' '. $data['mc_currency'], $data['parent_txn_id'], $data['reason_code'] ), '', '', true );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_insert_payment_note() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

686
		/** @scrutinizer ignore-deprecated */ wpinv_insert_payment_note( $invoice_id, wp_sprintf( __( 'PayPal partial refund of %s processed for transaction #%s for reason: %s', 'invoicing' ), (float)$refund_amount . ' '. $data['mc_currency'], $data['parent_txn_id'], $data['reason_code'] ), '', '', true );
Loading history...
687
688
		do_action( 'wpinv_paypal_invoice_partially_refunded', $data, $invoice_id, $refund_amount );
689
690
		return; // This is a partial refund
691
	}
692
693
	wpinv_insert_payment_note( $invoice_id, sprintf( __( 'PayPal Payment #%s Refunded for reason: %s', 'invoicing' ), $data['parent_txn_id'], $data['reason_code'] ), '', '', true );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_insert_payment_note() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

693
	/** @scrutinizer ignore-deprecated */ wpinv_insert_payment_note( $invoice_id, sprintf( __( 'PayPal Payment #%s Refunded for reason: %s', 'invoicing' ), $data['parent_txn_id'], $data['reason_code'] ), '', '', true );
Loading history...
694
	wpinv_insert_payment_note( $invoice_id, sprintf( __( 'PayPal Refund Transaction ID: %s', 'invoicing' ), $data['txn_id'] ), '', '', true );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_insert_payment_note() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

694
	/** @scrutinizer ignore-deprecated */ wpinv_insert_payment_note( $invoice_id, sprintf( __( 'PayPal Refund Transaction ID: %s', 'invoicing' ), $data['txn_id'] ), '', '', true );
Loading history...
695
	wpinv_update_payment_status( $invoice_id, 'wpi-refunded' );
0 ignored issues
show
Deprecated Code introduced by
The function wpinv_update_payment_status() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

695
	/** @scrutinizer ignore-deprecated */ wpinv_update_payment_status( $invoice_id, 'wpi-refunded' );
Loading history...
696
697
	do_action( 'wpinv_paypal_invoice_fully_refunded', $data, $invoice_id );
698
}
699
700
function wpinv_get_paypal_redirect( $ssl_check = false ) {
0 ignored issues
show
Unused Code introduced by
The parameter $ssl_check is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

700
function wpinv_get_paypal_redirect( /** @scrutinizer ignore-unused */ $ssl_check = false ) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
701
    return apply_filters( 'wpinv_paypal_uri', wpinv_is_test_mode( 'paypal' ) ? 'https://www.sandbox.paypal.com/cgi-bin/webscr?test_ipn=1&' : 'https://www.paypal.com/cgi-bin/webscr?' );
702
}
703
704
/**
705
 * Confirms PayPal payments.
706
 * 
707
 * @param string $content Success page content.
708
 */
709
function wpinv_paypal_success_page_content( $content ) {
710
711
    // Retrieve the invoice.
712
    $invoice_id = getpaid_get_current_invoice_id();
713
    $invoice    = wpinv_get_invoice( $invoice_id );
714
715
    // Ensure that it exists and that it is pending payment.
716
    if ( empty( $invoice_id ) || ! $invoice->has_status( 'wpi-pending' ) ) {
717
        return $content;
718
    }
719
720
    // Can the user view this invoice??
721
    if ( ! wpinv_user_can_view_invoice( $invoice ) ) {
722
        return $content;
723
    }
724
725
    // Show payment processing indicator.
726
    return wpinv_get_template_html( 'wpinv-payment-processing.php', compact( 'invoice' ) );
727
728
}
729
add_filter( 'wpinv_payment_confirm_paypal', 'wpinv_paypal_success_page_content' );
730
731
function wpinv_paypal_get_transaction_id( $invoice_id ) {
732
    $transaction_id = '';
733
    $notes = wpinv_get_invoice_notes( $invoice_id );
734
735
    foreach ( $notes as $note ) {
736
        if ( preg_match( '/^PayPal Transaction ID: ([^\s]+)/', $note->comment_content, $match ) ) {
737
            $transaction_id = $match[1];
738
            continue;
739
        }
740
    }
741
742
    return apply_filters( 'wpinv_paypal_set_transaction_id', $transaction_id, $invoice_id );
743
}
744
add_filter( 'wpinv_payment_get_transaction_id-paypal', 'wpinv_paypal_get_transaction_id', 10, 1 );
745
746
function wpinv_paypal_link_transaction_id( $transaction_id, $invoice_id, $invoice ) {
747
    if ( $transaction_id == $invoice_id ) {
748
        $transaction_link = $transaction_id;
749
    } else {
750
        if ( ! empty( $invoice ) && ! empty( $invoice->mode ) ) {
751
            $mode = $invoice->mode;
752
        } else {
753
            $mode = wpinv_is_test_mode( 'paypal' ) ? 'test' : 'live';
754
        }
755
756
        $sandbox = $mode == 'test' ? '.sandbox' : '';
757
        $transaction_url = 'https://www' . $sandbox . '.paypal.com/webscr?cmd=_history-details-from-hub&id=' . $transaction_id;
758
759
        $transaction_link = '<a href="' . esc_url( $transaction_url ) . '" target="_blank">' . $transaction_id . '</a>';
760
    }
761
762
    return apply_filters( 'wpinv_paypal_link_payment_details_transaction_id', $transaction_link, $transaction_id, $invoice );
763
}
764
add_filter( 'wpinv_payment_details_transaction_id-paypal', 'wpinv_paypal_link_transaction_id', 10, 3 );
765
766
function wpinv_paypal_profile_id_link( $profile_id, $subscription ) {
767
    $link = $profile_id;
768
769
    if ( ! empty( $profile_id ) && ! empty( $subscription ) && ( $invoice_id = $subscription->get_original_payment_id() ) ) {
770
        $invoice = wpinv_get_invoice( $invoice_id );
771
772
        if ( ! empty( $invoice ) && ! empty( $invoice->mode ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The property mode does not exist on WPInv_Invoice. Since you implemented __get, consider adding a @property annotation.
Loading history...
773
            $mode = $invoice->mode;
774
        } else {
775
            $mode = wpinv_is_test_mode( 'paypal' ) ? 'test' : 'live';
776
        }
777
778
        $sandbox = $mode == 'test' ? '.sandbox' : '';
779
        $url = 'https://www' . $sandbox . '.paypal.com/cgi-bin/webscr?cmd=_profile-recurring-payments&encrypted_profile_id=' . $profile_id;
780
781
        $link = '<a href="' . esc_url( $url ) . '" target="_blank">' . $profile_id . '</a>';
782
    }
783
    
784
    return apply_filters( 'wpinv_paypal_profile_id_link', $link, $profile_id, $subscription );
785
}
786
add_filter( 'wpinv_subscription_profile_link_paypal', 'wpinv_paypal_profile_id_link', 10, 2 );
787
788
function wpinv_paypal_transaction_id_link( $transaction_id, $subscription ) {
789
    if ( ! empty( $transaction_id ) && ! empty( $subscription ) && ( $invoice_id = $subscription->get_original_payment_id() ) ) {
790
        $invoice = wpinv_get_invoice( $invoice_id );
791
792
        if ( ! empty( $invoice ) ) {
793
            return wpinv_paypal_link_transaction_id( $transaction_id, $invoice_id, $invoice );
794
        }        
795
    }
796
    
797
    return $transaction_id;
798
}
799
add_filter( 'wpinv_subscription_transaction_link_paypal', 'wpinv_paypal_transaction_id_link', 10, 2 );
800
801
function wpinv_is_paypal_valid_for_use() {
802
    return in_array( wpinv_get_currency(), apply_filters( 'wpinv_paypal_supported_currencies', array( 'AUD', 'BRL', 'CAD', 'MXN', 'NZD', 'HKD', 'SGD', 'USD', 'EUR', 'JPY', 'TRY', 'NOK', 'CZK', 'DKK', 'HUF', 'ILS', 'MYR', 'PHP', 'PLN', 'SEK', 'CHF', 'TWD', 'THB', 'GBP', 'RMB', 'RUB', 'INR' ) ) );
803
}
804
805
function wpinv_check_paypal_currency_support( $gateway_list ) {
806
    if ( isset( $gateway_list['paypal'] ) && ! wpinv_is_paypal_valid_for_use() ) {
807
        unset( $gateway_list['paypal'] );
808
    }
809
    return $gateway_list;
810
}
811
add_filter( 'wpinv_enabled_payment_gateways', 'wpinv_check_paypal_currency_support', 10, 1 );
812
813
function wpinv_gateway_paypal_button_label( $label ) {
0 ignored issues
show
Unused Code introduced by
The parameter $label is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

813
function wpinv_gateway_paypal_button_label( /** @scrutinizer ignore-unused */ $label ) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
814
    return __( 'Proceed to PayPal', 'invoicing' );
815
}
816
add_filter( 'wpinv_gateway_paypal_button_label', 'wpinv_gateway_paypal_button_label', 10, 1 );