Passed
Push — master ( c58b71...f93832 )
by Brian
06:42 queued 01:38
created

GetPaid_Paypal_Gateway::get_connect_url()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 20
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 14
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 20
rs 9.7998
1
<?php
2
/**
3
 * Paypal payment gateway
4
 *
5
 */
6
7
defined( 'ABSPATH' ) || exit;
8
9
/**
10
 * Paypal Payment Gateway class.
11
 *
12
 */
13
class GetPaid_Paypal_Gateway extends GetPaid_Payment_Gateway {
14
15
    /**
16
	 * Payment method id.
17
	 *
18
	 * @var string
19
	 */
20
    public $id = 'paypal';
21
22
    /**
23
	 * An array of features that this gateway supports.
24
	 *
25
	 * @var array
26
	 */
27
    protected $supports = array( 'subscription', 'sandbox', 'single_subscription_group' );
28
29
    /**
30
	 * Payment method order.
31
	 *
32
	 * @var int
33
	 */
34
    public $order = 1;
35
36
    /**
37
	 * Stores line items to send to PayPal.
38
	 *
39
	 * @var array
40
	 */
41
    protected $line_items = array();
42
43
    /**
44
	 * Endpoint for requests from PayPal.
45
	 *
46
	 * @var string
47
	 */
48
	protected $notify_url;
49
50
	/**
51
	 * Endpoint for requests to PayPal.
52
	 *
53
	 * @var string
54
	 */
55
    protected $endpoint;
56
57
    /**
58
	 * Currencies this gateway is allowed for.
59
	 *
60
	 * @var array
61
	 */
62
	public $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' );
63
64
    /**
65
	 * URL to view a transaction.
66
	 *
67
	 * @var string
68
	 */
69
    public $view_transaction_url = 'https://www.{sandbox}paypal.com/activity/payment/%s';
70
71
    /**
72
	 * URL to view a subscription.
73
	 *
74
	 * @var string
75
	 */
76
	public $view_subscription_url = 'https://www.{sandbox}paypal.com/cgi-bin/webscr?cmd=_profile-recurring-payments&encrypted_profile_id=%s';
77
78
    /**
79
	 * Class constructor.
80
	 */
81
	public function __construct() {
82
83
        $this->title                = __( 'PayPal Standard', 'invoicing' );
84
        $this->method_title         = __( 'PayPal Standard', 'invoicing' );
85
        $this->checkout_button_text = __( 'Proceed to PayPal', 'invoicing' );
86
        $this->notify_url           = wpinv_get_ipn_url( $this->id );
87
88
		add_filter( 'getpaid_paypal_args', array( $this, 'process_subscription' ), 10, 2 );
89
        add_filter( 'getpaid_paypal_sandbox_notice', array( $this, 'sandbox_notice' ) );
90
		add_action( 'getpaid_authenticated_admin_action_connect_paypal', array( $this, 'connect_paypal' ) );
91
92
        parent::__construct();
93
    }
94
95
    /**
96
	 * Process Payment.
97
	 *
98
	 *
99
	 * @param WPInv_Invoice $invoice Invoice.
100
	 * @param array $submission_data Posted checkout fields.
101
	 * @param GetPaid_Payment_Form_Submission $submission Checkout submission.
102
	 * @return array
103
	 */
104
	public function process_payment( $invoice, $submission_data, $submission ) {
105
106
        // Get redirect url.
107
        $paypal_redirect = $this->get_request_url( $invoice );
108
109
        // Add a note about the request url.
110
        $invoice->add_note(
111
            sprintf(
112
                __( 'Redirecting to PayPal: %s', 'invoicing' ),
113
                esc_url( $paypal_redirect )
114
            ),
115
            false,
116
            false,
117
            true
118
        );
119
120
        // Redirect to PayPal
121
        wp_redirect( $paypal_redirect );
122
        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...
123
124
    }
125
126
    /**
127
	 * Get the PayPal request URL for an invoice.
128
	 *
129
	 * @param  WPInv_Invoice $invoice Invoice object.
130
	 * @return string
131
	 */
132
	public function get_request_url( $invoice ) {
133
134
        // Endpoint for this request
135
		$this->endpoint    = $this->is_sandbox( $invoice ) ? 'https://www.sandbox.paypal.com/cgi-bin/webscr?test_ipn=1&' : 'https://www.paypal.com/cgi-bin/webscr?';
136
137
        // Retrieve paypal args.
138
        $paypal_args       = map_deep( $this->get_paypal_args( $invoice ), 'urlencode' );
139
140
        if ( $invoice->is_recurring() ) {
141
            $paypal_args['bn'] = 'GetPaid_Subscribe_WPS_US';
142
        } else {
143
            $paypal_args['bn'] = 'GetPaid_ShoppingCart_WPS_US';
144
        }
145
146
        return add_query_arg( $paypal_args, $this->endpoint );
147
148
	}
149
150
    /**
151
	 * Get PayPal Args for passing to PP.
152
	 *
153
	 * @param  WPInv_Invoice $invoice Invoice object.
154
	 * @return array
155
	 */
156
	protected function get_paypal_args( $invoice ) {
157
158
        // Whether or not to send the line items as one item.
159
		$force_one_line_item = apply_filters( 'getpaid_paypal_force_one_line_item', true, $invoice );
160
161
		if ( $invoice->is_recurring() || ( wpinv_use_taxes() && wpinv_prices_include_tax() ) ) {
162
			$force_one_line_item = true;
163
		}
164
165
		$paypal_args = apply_filters(
166
			'getpaid_paypal_args',
167
			array_merge(
168
				$this->get_transaction_args( $invoice ),
169
				$this->get_line_item_args( $invoice, $force_one_line_item )
170
			),
171
			$invoice
172
		);
173
174
		return $this->fix_request_length( $invoice, $paypal_args );
175
    }
176
177
    /**
178
	 * Get transaction args for paypal request.
179
	 *
180
	 * @param WPInv_Invoice $invoice Invoice object.
181
	 * @return array
182
	 */
183
	protected function get_transaction_args( $invoice ) {
184
185
		return array(
186
            'cmd'           => '_cart',
187
            'business'      => wpinv_get_option( 'paypal_email', false ),
188
            'no_shipping'   => '1',
189
            'shipping'      => '0',
190
            'no_note'       => '1',
191
            'charset'       => 'utf-8',
192
            'rm'            => is_ssl() ? 2 : 1,
193
            'upload'        => 1,
194
            'currency_code' => $invoice->get_currency(), // https://developer.paypal.com/docs/nvp-soap-api/currency-codes/#paypal
195
            'return'        => esc_url_raw( $this->get_return_url( $invoice ) ),
196
            'cancel_return' => esc_url_raw( $invoice->get_checkout_payment_url() ),
197
            'notify_url'    => getpaid_limit_length( $this->notify_url, 255 ),
198
            'invoice'       => getpaid_limit_length( $invoice->get_number(), 127 ),
199
            'custom'        => $invoice->get_id(),
200
            'first_name'    => getpaid_limit_length( $invoice->get_first_name(), 32 ),
201
            'last_name'     => getpaid_limit_length( $invoice->get_last_name(), 64 ),
202
            'country'       => getpaid_limit_length( $invoice->get_country(), 2 ),
203
            'email'         => getpaid_limit_length( $invoice->get_email(), 127 ),
204
            'cbt'           => get_bloginfo( 'name' )
205
        );
206
207
    }
208
209
    /**
210
	 * Get line item args for paypal request.
211
	 *
212
	 * @param  WPInv_Invoice $invoice Invoice object.
213
	 * @param  bool     $force_one_line_item Create only one item for this invoice.
214
	 * @return array
215
	 */
216
	protected function get_line_item_args( $invoice, $force_one_line_item = false ) {
217
218
        // Maybe send invoice as a single item.
219
		if ( $force_one_line_item ) {
220
            return $this->get_line_item_args_single_item( $invoice );
221
        }
222
223
        // Send each line item individually.
224
        $line_item_args = array();
225
226
        // Prepare line items.
227
        $this->prepare_line_items( $invoice );
228
229
        // Add taxes to the cart
230
        if ( wpinv_use_taxes() && $invoice->is_taxable() ) {
231
            $line_item_args['tax_cart'] = wpinv_sanitize_amount( (float) $invoice->get_total_tax(), 2 );
0 ignored issues
show
Unused Code introduced by
The call to wpinv_sanitize_amount() has too many arguments starting with 2. ( Ignorable by Annotation )

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

231
            $line_item_args['tax_cart'] = /** @scrutinizer ignore-call */ wpinv_sanitize_amount( (float) $invoice->get_total_tax(), 2 );

This check compares calls to functions or methods with their respective definitions. If the call has more 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...
232
        }
233
234
        // Add discount.
235
        if ( $invoice->get_total_discount() > 0 ) {
236
            $line_item_args['discount_amount_cart'] = wpinv_sanitize_amount( (float) $invoice->get_total_discount(), 2 );
237
        }
238
239
		return array_merge( $line_item_args, $this->get_line_items() );
240
241
    }
242
243
    /**
244
	 * Get line item args for paypal request as a single line item.
245
	 *
246
	 * @param  WPInv_Invoice $invoice Invoice object.
247
	 * @return array
248
	 */
249
	protected function get_line_item_args_single_item( $invoice ) {
250
		$this->delete_line_items();
251
252
        $item_name = sprintf( __( 'Invoice #%s', 'invoicing' ), $invoice->get_number() );
253
		$this->add_line_item( $item_name, 1, wpinv_round_amount( (float) $invoice->get_total(), 2, true ), $invoice->get_id() );
0 ignored issues
show
Bug introduced by
It seems like wpinv_round_amount((doub...->get_total(), 2, true) can also be of type string; however, parameter $amount of GetPaid_Paypal_Gateway::add_line_item() does only seem to accept double, 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

253
		$this->add_line_item( $item_name, 1, /** @scrutinizer ignore-type */ wpinv_round_amount( (float) $invoice->get_total(), 2, true ), $invoice->get_id() );
Loading history...
254
255
		return $this->get_line_items();
256
    }
257
258
    /**
259
	 * Return all line items.
260
	 */
261
	protected function get_line_items() {
262
		return $this->line_items;
263
	}
264
265
    /**
266
	 * Remove all line items.
267
	 */
268
	protected function delete_line_items() {
269
		$this->line_items = array();
270
    }
271
272
    /**
273
	 * Prepare line items to send to paypal.
274
	 *
275
	 * @param  WPInv_Invoice $invoice Invoice object.
276
	 */
277
	protected function prepare_line_items( $invoice ) {
278
		$this->delete_line_items();
279
280
		// Items.
281
		foreach ( $invoice->get_items() as $item ) {
282
			$amount   = $item->get_price();
283
			$quantity = $invoice->get_template() == 'amount' ? 1 : $item->get_quantity();
284
			$this->add_line_item( $item->get_raw_name(), $quantity, $amount, $item->get_id() );
285
        }
286
287
        // Fees.
288
		foreach ( $invoice->get_fees() as $fee => $data ) {
289
            $this->add_line_item( $fee, 1, wpinv_sanitize_amount( $data['initial_fee'] ) );
290
        }
291
292
    }
293
294
    /**
295
	 * Add PayPal Line Item.
296
	 *
297
	 * @param  string $item_name Item name.
298
	 * @param  float    $quantity Item quantity.
299
	 * @param  float  $amount Amount.
300
	 * @param  string $item_number Item number.
301
	 */
302
	protected function add_line_item( $item_name, $quantity = 1, $amount = 0.0, $item_number = '' ) {
303
		$index = ( count( $this->line_items ) / 4 ) + 1;
304
305
		$item = apply_filters(
306
			'getpaid_paypal_line_item',
307
			array(
308
				'item_name'   => html_entity_decode( getpaid_limit_length( $item_name ? wp_strip_all_tags( $item_name ) : __( 'Item', 'invoicing' ), 127 ), ENT_NOQUOTES, 'UTF-8' ),
309
				'quantity'    => (float) $quantity,
310
				'amount'      => wpinv_sanitize_amount( (float) $amount, 2 ),
0 ignored issues
show
Unused Code introduced by
The call to wpinv_sanitize_amount() has too many arguments starting with 2. ( Ignorable by Annotation )

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

310
				'amount'      => /** @scrutinizer ignore-call */ wpinv_sanitize_amount( (float) $amount, 2 ),

This check compares calls to functions or methods with their respective definitions. If the call has more 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...
311
				'item_number' => $item_number,
312
			),
313
			$item_name,
314
			$quantity,
315
			$amount,
316
			$item_number
317
		);
318
319
		$this->line_items[ 'item_name_' . $index ]   = getpaid_limit_length( $item['item_name'], 127 );
320
        $this->line_items[ 'quantity_' . $index ]    = $item['quantity'];
321
322
        // The price or amount of the product, service, or contribution, not including shipping, handling, or tax.
323
		$this->line_items[ 'amount_' . $index ]      = $item['amount'] * $item['quantity'];
324
		$this->line_items[ 'item_number_' . $index ] = getpaid_limit_length( $item['item_number'], 127 );
325
    }
326
327
    /**
328
	 * If the default request with line items is too long, generate a new one with only one line item.
329
	 *
330
	 * https://support.microsoft.com/en-us/help/208427/maximum-url-length-is-2-083-characters-in-internet-explorer.
331
	 *
332
	 * @param WPInv_Invoice $invoice Invoice to be sent to Paypal.
333
	 * @param array    $paypal_args Arguments sent to Paypal in the request.
334
	 * @return array
335
	 */
336
	protected function fix_request_length( $invoice, $paypal_args ) {
337
		$max_paypal_length = 2083;
338
		$query_candidate   = http_build_query( $paypal_args, '', '&' );
339
340
		if ( strlen( $this->endpoint . $query_candidate ) <= $max_paypal_length ) {
341
			return $paypal_args;
342
		}
343
344
		return apply_filters(
345
			'getpaid_paypal_args',
346
			array_merge(
347
				$this->get_transaction_args( $invoice ),
348
				$this->get_line_item_args( $invoice, true )
349
			),
350
			$invoice
351
		);
352
353
    }
354
355
    /**
356
	 * Processes recurring invoices.
357
	 *
358
	 * @param  array $paypal_args PayPal args.
359
	 * @param  WPInv_Invoice    $invoice Invoice object.
360
	 */
361
	public function process_subscription( $paypal_args, $invoice ) {
362
363
        // Make sure this is a subscription.
364
        if ( ! $invoice->is_recurring() || ! $subscription = getpaid_get_invoice_subscription( $invoice ) ) {
365
            return $paypal_args;
366
        }
367
368
        // It's a subscription
369
        $paypal_args['cmd'] = '_xclick-subscriptions';
370
371
        // Subscription name.
372
        $paypal_args['item_name'] = sprintf( __( 'Invoice #%s', 'invoicing' ), $invoice->get_number() );
373
374
        // Get subscription args.
375
        $period                 = strtoupper( substr( $subscription->get_period(), 0, 1) );
376
        $interval               = (int) $subscription->get_frequency();
377
        $bill_times             = (int) $subscription->get_bill_times();
378
        $initial_amount         = (float) wpinv_sanitize_amount( $invoice->get_initial_total(), 2 );
0 ignored issues
show
Unused Code introduced by
The call to wpinv_sanitize_amount() has too many arguments starting with 2. ( Ignorable by Annotation )

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

378
        $initial_amount         = (float) /** @scrutinizer ignore-call */ wpinv_sanitize_amount( $invoice->get_initial_total(), 2 );

This check compares calls to functions or methods with their respective definitions. If the call has more 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...
379
        $recurring_amount       = (float) wpinv_sanitize_amount( $invoice->get_recurring_total(), 2 );
380
        $subscription_item      = $invoice->get_recurring( true );
381
382
        if ( $subscription_item->has_free_trial() ) {
383
384
            $paypal_args['a1'] = 0 == $initial_amount ? 0 : $initial_amount;
385
386
			// Trial period length.
387
			$paypal_args['p1'] = $subscription_item->get_trial_interval();
388
389
			// Trial period.
390
			$paypal_args['t1'] = $subscription_item->get_trial_period();
391
392
        } else if ( $initial_amount != $recurring_amount ) {
393
394
            // No trial period, but initial amount includes a sign-up fee and/or other items, so charge it as a separate period.
395
396
            if ( 1 == $bill_times ) {
397
                $param_number = 3;
398
            } else {
399
                $param_number = 1;
400
            }
401
402
            $paypal_args[ 'a' . $param_number ] = $initial_amount ? $initial_amount : 0;
403
404
            // Sign Up interval
405
            $paypal_args[ 'p' . $param_number ] = $interval;
406
407
            // Sign Up unit of duration
408
            $paypal_args[ 't' . $param_number ] = $period;
409
410
        }
411
412
        // We have a recurring payment
413
		if ( ! isset( $param_number ) || 1 == $param_number ) {
414
415
			// Subscription price
416
			$paypal_args['a3'] = $recurring_amount;
417
418
			// Subscription duration
419
			$paypal_args['p3'] = $interval;
420
421
			// Subscription period
422
			$paypal_args['t3'] = $period;
423
424
        }
425
426
        // Recurring payments
427
		if ( 1 == $bill_times || ( $initial_amount != $recurring_amount && ! $subscription_item->has_free_trial() && 2 == $bill_times ) ) {
428
429
			// Non-recurring payments
430
			$paypal_args['src'] = 0;
431
432
		} else {
433
434
			$paypal_args['src'] = 1;
435
436
			if ( $bill_times > 0 ) {
437
438
				// An initial period is being used to charge a sign-up fee
439
				if ( $initial_amount != $recurring_amount && ! $subscription_item->has_free_trial() ) {
440
					$bill_times--;
441
				}
442
443
                // Make sure it's not over the max of 52
444
                $paypal_args['srt'] = ( $bill_times <= 52 ? absint( $bill_times ) : 52 );
445
446
			}
447
        }
448
449
        // Force return URL so that order description & instructions display
450
        $paypal_args['rm'] = 2;
451
452
        // Get rid of redudant items.
453
        foreach ( array( 'item_name_1', 'quantity_1', 'amount_1', 'item_number_1' ) as $arg ) {
454
455
            if ( isset( $paypal_args[ $arg ] ) ) {
456
                unset( $paypal_args[ $arg ] );
457
            }
458
459
        }
460
461
        return apply_filters(
462
			'getpaid_paypal_subscription_args',
463
			$paypal_args,
464
			$invoice
465
        );
466
467
    }
468
469
    /**
470
	 * Processes ipns and marks payments as complete.
471
	 *
472
	 * @return void
473
	 */
474
	public function verify_ipn() {
475
        new GetPaid_Paypal_Gateway_IPN_Handler( $this );
476
    }
477
478
    /**
479
     * Returns a sandbox notice.
480
     */
481
    public function sandbox_notice() {
482
483
        return sprintf(
484
			__( 'SANDBOX ENABLED. You can use sandbox testing accounts only. See the %sPayPal Sandbox Testing Guide%s for more details.', 'invoicing' ),
485
			'<a href="https://developer.paypal.com/docs/classic/lifecycle/ug_sandbox/">',
486
			'</a>'
487
		);
488
489
    }
490
491
	/**
492
	 * Filters the gateway settings.
493
	 *
494
	 * @param array $admin_settings
495
	 */
496
	public function admin_settings( $admin_settings ) {
497
498
        $currencies = sprintf(
499
            __( 'Supported Currencies: %s', 'invoicing' ),
500
            implode( ', ', $this->currencies )
501
        );
502
503
        $admin_settings['paypal_active']['desc'] .= " ($currencies)";
504
        $admin_settings['paypal_desc']['std']     = __( 'Pay via PayPal: you can pay with your credit card if you don\'t have a PayPal account.', 'invoicing' );
505
506
		// Access tokens.
507
        $live_account    = wpinv_get_option( 'paypal_live_access_token' );
0 ignored issues
show
Unused Code introduced by
The assignment to $live_account is dead and can be removed.
Loading history...
508
        $sandbox_account = wpinv_get_option( 'paypal_test_access_token' );
0 ignored issues
show
Unused Code introduced by
The assignment to $sandbox_account is dead and can be removed.
Loading history...
509
510
		$admin_settings['paypal_connect'] = array(
511
			'type'       => 'raw_html',
512
			'id'         => 'paypal_connect',
513
			'name'       => __( 'Connect to PayPal', 'invoicing' ),
514
			'desc'       => sprintf(
515
				'<div class="wpinv-paypal-connect-live"><a class="button button-primary" href="%s">%s</a><br><strong style="color: green">%s</strong></div><div class="wpinv-paypal-connect-sandbox"><a class="button button-primary" href="%s">%s</a><br><strong style="color: green">%s</strong></div>%s',
516
				esc_url( self::get_connect_url( false ) ),
517
				__( 'Connect to PayPal', 'invoicing' ),
518
				__( 'Connected', 'invoicing' ),
519
				esc_url( self::get_connect_url( true ) ),
520
				__( 'Connect to PayPal Sandox', 'invoicing' ),
521
				__( 'Connected', 'invoicing' ),
522
				$this->get_js()
0 ignored issues
show
Bug introduced by
$this->get_js() of type void is incompatible with the type double|integer|string expected by parameter $values of sprintf(). ( Ignorable by Annotation )

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

522
				/** @scrutinizer ignore-type */ $this->get_js()
Loading history...
Bug introduced by
Are you sure the usage of $this->get_js() targeting GetPaid_Paypal_Gateway::get_js() 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...
523
			),
524
		);
525
526
		$admin_settings['disable_paypal_connect'] = array(
527
			'type'       => 'checkbox',
528
			'id'         => 'disable_paypal_connect',
529
			'name'       => __( 'Manual Mode', 'invoicing' ),
530
			'desc'       => __( 'Manually enter your credentials', 'invoicing' ),
531
			'std'        => false,
532
		);
533
534
        $admin_settings['paypal_email'] = array(
535
            'type'  => 'text',
536
			'class' => 'live-auth-data',
537
            'id'    => 'paypal_email',
538
            'name'  => __( 'Live Email Address', 'invoicing' ),
539
            'desc'  => __( 'The email address of your PayPal account.', 'invoicing' ),
540
        );
541
542
		$admin_settings['paypal_merchant_id'] = array(
543
            'type'  => 'text',
544
			'class' => 'live-auth-data',
545
            'id'    => 'paypal_merchant_id',
546
            'name'  => __( 'Live Merchant ID', 'invoicing' ),
547
        );
548
549
		$admin_settings['paypal_client_id'] = array(
550
            'type'  => 'text',
551
			'class' => 'live-auth-data',
552
            'id'    => 'paypal_client_id',
553
            'name'  => __( 'Live Client ID', 'invoicing' ),
554
        );
555
556
		$admin_settings['paypal_client_secret'] = array(
557
            'type'  => 'text',
558
			'class' => 'live-auth-data',
559
            'id'    => 'paypal_client_secret',
560
            'name'  => __( 'Live Client Secret', 'invoicing' ),
561
        );
562
563
		$admin_settings['paypal_sandbox_email'] = array(
564
            'type'  => 'text',
565
			'class' => 'sandbox-auth-data',
566
            'id'    => 'paypal_sandbox_email',
567
            'name'  => __( 'Sandbox Email Address', 'invoicing' ),
568
            'desc'  => __( 'The email address of your sandbox PayPal account.', 'invoicing' ),
569
			'std'   => wpinv_get_option( 'paypal_email', '' ),
570
        );
571
572
		$admin_settings['paypal_sandbox_merchant_id'] = array(
573
            'type'  => 'text',
574
			'class' => 'sandbox-auth-data',
575
            'id'    => 'paypal_sandbox_merchant_id',
576
            'name'  => __( 'Sandbox Merchant ID', 'invoicing' ),
577
        );
578
579
		$admin_settings['paypal_sandbox_client_id'] = array(
580
            'type'  => 'text',
581
			'class' => 'sandbox-auth-data',
582
            'id'    => 'paypal_sandbox_client_id',
583
            'name'  => __( 'Sandbox Client ID', 'invoicing' ),
584
        );
585
586
		$admin_settings['paypal_sandbox_client_secret'] = array(
587
            'type'  => 'text',
588
			'class' => 'sandbox-auth-data',
589
            'id'    => 'paypal_sandbox_client_secret',
590
            'name'  => __( 'Sandbox Client Secret', 'invoicing' ),
591
        );
592
593
        $admin_settings['paypal_ipn_url'] = array(
594
            'type'     => 'ipn_url',
595
            'id'       => 'paypal_ipn_url',
596
            'name'     => __( 'IPN Url', 'invoicing' ),
597
            'std'      => $this->notify_url,
598
            'desc'     => __( "If you've not enabled IPNs in your paypal account, use the above URL to enable them.", 'invoicing' ) . ' <a href="https://developer.paypal.com/docs/api-basics/notifications/ipn/"><em>' . __( 'Learn more.', 'invoicing' ) . '</em></a>',
599
            'readonly' => true,
600
        );
601
602
		return $admin_settings;
603
	}
604
605
	/**
606
	 * Retrieves the PayPal connect URL.
607
	 *
608
	 *
609
     * @param bool $is_sandbox
610
     * @return string
611
	 */
612
	public static function get_connect_url( $is_sandbox ) {
613
614
        $redirect_url = add_query_arg(
615
            array(
616
                'getpaid-admin-action' => 'connect_paypal',
617
                'page'                 => 'wpinv-settings',
618
                'live_mode'            => (int) empty( $is_sandbox ),
619
                'tab'                  => 'gateways',
620
                'section'              => 'paypal',
621
                'getpaid-nonce'        => wp_create_nonce( 'getpaid-nonce' ),
622
            ),
623
            admin_url( 'admin.php' )
624
        );
625
626
        return add_query_arg(
627
            array(
628
                'live_mode'    => (int) empty( $is_sandbox ),
629
                'redirect_url' => urlencode( str_replace( '&amp;', '&', $redirect_url ) )
630
            ),
631
            'https://ayecode.io/oauth/paypal'
632
        );
633
634
    }
635
636
	/**
637
	 * Generates settings page js.
638
	 *
639
     * @return void
640
	 */
641
	public static function get_js() {
642
        ob_start();
643
        ?>
644
            <script>
645
                jQuery(document).ready(function() {
646
647
					var areAllInputsFilled = function ( el ) {
648
						return jQuery(el).filter(function() {
649
							return jQuery.trim( jQuery(this).val() ).length == 0
650
						}).length == 0;
651
					}
652
653
                    jQuery( '#wpinv-settings-paypal_sandbox' ).on ( 'change', function( e ) {
654
655
						var showing_manual = jQuery( '#wpinv-settings-disable_paypal_connect' ).is(':checked');
656
657
						if ( showing_manual ) {
658
							jQuery ( '.wpinv-paypal-connect-live' ).closest( 'tr' ).hide()
659
							jQuery ( 'tr.sandbox-auth-data' ).toggle( this.checked )
660
							jQuery ( 'tr.live-auth-data' ).toggle( ! this.checked )
661
						} else {
662
							jQuery ( '.wpinv-paypal-connect-live' ).closest( 'tr' ).show()
663
							jQuery ( 'tr.sandbox-auth-data, tr.live-auth-data' ).hide()
664
						}
665
666
						jQuery( '.wpinv-paypal-connect-live' ).toggle( ! this.checked )
667
						jQuery( '.wpinv-paypal-connect-sandbox' ).toggle( this.checked )
668
669
						jQuery( '.wpinv-paypal-connect-live strong' ).toggle( areAllInputsFilled( 'input.live-auth-data' ) )
670
						jQuery( '.wpinv-paypal-connect-sandbox strong' ).toggle( areAllInputsFilled( 'input.sandbox-auth-data' ) )
671
672
                    })
673
674
                    jQuery( '#wpinv-settings-disable_paypal_connect' ).on ( 'change', function( e ) {
675
                        jQuery( '#wpinv-settings-paypal_sandbox' ).trigger( 'change' )
676
                    });
677
678
                    // Set initial state.
679
                    jQuery( '#wpinv-settings-disable_paypal_connect' ).trigger( 'change' )
680
681
                });
682
            </script>
683
        <?php
684
        return ob_get_clean();
0 ignored issues
show
Bug Best Practice introduced by
The expression return ob_get_clean() returns the type string which is incompatible with the documented return type void.
Loading history...
685
    }
686
687
	/**
688
	 * Connects to PayPal.
689
	 *
690
	 * @param array $data Connection data.
691
	 * @return void
692
	 */
693
	public function connect_paypal( $data ) {
694
695
		$sandbox      = $this->is_sandbox();
696
		$data         = wp_unslash( $data );
697
		$access_token = empty( $data['access_token'] ) ? '' : sanitize_text_field( $data['access_token'] );
698
699
		if ( isset( $data['live_mode'] ) ) {
700
			$sandbox = empty( $data['live_mode'] );
701
		}
702
703
		wpinv_update_option( 'stripe_sandbox', (int) $sandbox );
704
		wpinv_update_option( 'stripe_active', 1 );
705
706
		if ( ! empty( $data['error_description'] ) ) {
707
			getpaid_admin()->show_error( wp_kses_post( urldecode( $data['error_description'] ) ) );
708
		} else {
709
710
			// Retrieve the user info.
711
			$user_info = wp_remote_get(
712
				! $sandbox ? 'https://api-m.paypal.com/v1/identity/oauth2/userinfo?schema=paypalv1.1' : 'https://api-m.sandbox.paypal.com/v1/identity/oauth2/userinfo?schema=paypalv1.1',
713
				array(
714
715
					'headers' => array(
716
						'Authorization' => 'Bearer ' . $access_token,
717
						'Content-type'  => 'application/json',
718
					)
719
720
				)
721
			);
722
723
			if ( is_wp_error( $user_info ) ) {
724
				getpaid_admin()->show_error( wp_kses_post( $user_info->get_error_message() ) );
725
			} else {
726
727
				// Create application.
728
				$user_info = json_decode( wp_remote_retrieve_body( $user_info ) );
729
				$app       = wp_remote_post(
730
					! $sandbox ? 'https://api-m.paypal.com/v1/identity/applications' : 'https://api-m.sandbox.paypal.com/v1/identity/applications',
731
					array(
732
733
						'body'    => array(
734
							'application_type' => 'web',
735
							'redirect_uris'    => array(
736
								add_query_arg( 'getpaid_oauth', 'paypal', home_url() ),
737
							),
738
							'client_name'      => 'GetPaid',
739
							'contacts'         => array( $user_info->emails[0]->value ),
740
							'payer_id'         => $user_info->payer_id,
741
							'migrated_app'     => '',
742
						),
743
						'headers' => array(
744
							'Authorization' => 'Bearer ' . $access_token,
745
							'Content-type'  => 'application/json',
746
						)
747
748
					)
749
				);
750
751
				if ( is_wp_error( $app ) ) {
752
					getpaid_admin()->show_error( wp_kses_post( $app->get_error_message() ) );
753
				} else {
754
755
					$app = json_decode( $app );
0 ignored issues
show
Bug introduced by
$app of type WP_Error|array is incompatible with the type string expected by parameter $json of json_decode(). ( Ignorable by Annotation )

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

755
					$app = json_decode( /** @scrutinizer ignore-type */ $app );
Loading history...
756
					if ( $sandbox ) {
757
						wpinv_update_option( 'paypal_sandbox_email', sanitize_email( $user_info->emails[0]->value ) );
758
						wpinv_update_option( 'paypal_sandbox_merchant_id', '' );
759
						wpinv_update_option( 'paypal_sandbox_client_id', sanitize_text_field( $app->client_id ) );
760
						wpinv_update_option( 'paypal_sandbox_client_secret', sanitize_text_field( $app->client_secret ) );
761
						wpinv_update_option( 'paypal_sandbox_client_secret_expires_at', sanitize_text_field( $app->client_secret_expires_at ) );
762
						wpinv_update_option( 'paypal_sandbox_refresh_token', sanitize_text_field( urldecode( $data['refresh_token'] ) ) );
763
						set_transient( 'getpaid_paypal_sandbox_access_token', sanitize_text_field( urldecode( $data['access_token'] ) ), (int) $data['expires_in'] );
764
						getpaid_admin()->show_success( __( 'Successfully connected your PayPal sandbox account', 'wpinv-stripe' ) );
765
					} else {
766
						wpinv_update_option( 'paypal_email', sanitize_email( $user_info->emails[0]->value ) );
767
						wpinv_update_option( 'paypal_merchant_id', '' );
768
						wpinv_update_option( 'paypal_client_id', sanitize_text_field( $app->client_id ) );
769
						wpinv_update_option( 'paypal_client_secret', sanitize_text_field( $app->client_secret ) );
770
						wpinv_update_option( 'paypal_client_secret_expires_at', sanitize_text_field( $app->client_secret_expires_at ) );
771
						wpinv_update_option( 'paypal_refresh_token', sanitize_text_field( urldecode( $data['refresh_token'] ) ) );
772
						set_transient( 'getpaid_paypal_access_token', sanitize_text_field( urldecode( $data['access_token'] ) ), (int) $data['expires_in'] );
773
						getpaid_admin()->show_success( __( 'Successfully connected your PayPal account', 'wpinv-stripe' ) );
774
					}
775
776
				}
777
778
			}
779
780
		}
781
782
		wp_redirect( admin_url( 'admin.php?page=wpinv-settings&tab=gateways&section=paypal' ) );
783
		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...
784
	}
785
786
}
787