Completed
Push — master ( 2e9b05...544bdc )
by
unknown
05:52
created

init()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 0
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * The PayPal Express Checkout Gateway class
4
 *
5
 */
6
7
class WPSC_Payment_Gateway_Paypal_Express_Checkout extends WPSC_Payment_Gateway {
8
	public $sandbox_url = 'https://www.sandbox.paypal.com/webscr';
9
	public $live_url    = 'https://www.paypal.com/cgi-bin/webscr';
10
	private $paypal_data;
11
	protected $gateway;
12
13
	/**
14
	 * Constructor of PayPal Express Checkout Gateway.
15
	 * The $child parameter should be set to true, if the constructor
16
	 * is called from a child class.
17
	 *
18
	 * @param array $options
19
	 * @param bool $child
20
	 *
21
	 * @since 3.9
22
	 */
23
	public function __construct( $options, $child = false ) {
24
		parent::__construct();
25
26
		require_once( 'php-merchant/gateways/paypal-express-checkout.php' );
27
		$this->gateway = new PHP_Merchant_Paypal_Express_Checkout( $options );
28
29
		if ( ! $child ) {
30
			$this->title = __( 'PayPal Express Checkout 3.0', 'wp-e-commerce' );
31
			$this->gateway->set_options( array(
32
				'api_username'     => $this->setting->get( 'api_username' ),
33
				'api_password'     => $this->setting->get( 'api_password' ),
34
				'api_signature'    => $this->setting->get( 'api_signature' ),
35
				'cancel_url'       => $this->get_shopping_cart_payment_url(),
36
				'currency'         => $this->get_currency_code(),
37
				'test'             => (bool) $this->setting->get( 'sandbox_mode' ),
38
				'address_override' => 1,
39
				'solution_type'    => 'mark',
40
				'cart_logo'        => $this->setting->get( 'cart_logo' ),
41
				'cart_border'      => $this->setting->get( 'cart_border' ),
42
				'incontext'        => (bool) $this->setting->get( 'incontext' ),
43
			) );
44
45
			// Express Checkout Button
46
			if ( (bool) $this->setting->get( 'shortcut' ) && ! (bool) $this->setting->get( 'incontext' ) ) {
47
				add_action( 'wpsc_cart_item_table_form_actions_left', array( $this, 'add_ecs_button' ), 2, 2 );
48
			}
49
			// Incontext Checkout Scripts
50
			if ( (bool) $this->setting->get( 'incontext' ) && ! (bool) $this->setting->get( 'shortcut' ) ) {
51
				add_action( 'wp_enqueue_scripts', array( $this, 'incontext_load_scripts' ) );
52
			}
53
		}
54
	}
55
56
	public function incontext_load_scripts() {
57
		if( wpsc_is_checkout() ) {
58
			wp_register_script( 'ec-incontext', WPSC_URL . '/wpsc-components/merchant-core-v3/gateways/ec-incontext.js', '', null, true );
59
			wp_localize_script( 'ec-incontext', 'wpec_ppic', array(
60
				'mid' => esc_attr( $this->setting->get( 'api_merchantid' ) ),
61
				'env' => (bool) $this->setting->get( 'sandbox_mode' ) === true ? 'sandbox' : 'production',
62
				)
63
			);
64
			wp_enqueue_script( 'ec-incontext' );
65
			wp_enqueue_script( 'ppincontext', 'https://www.paypalobjects.com/api/checkout.js', array(), null, true );
66
		}
67
	}
68
69
	/**
70
	 * Insert the ExpessCheckout Shortcut Button
71
	 *
72
	 * @return void
73
	 */
74
	public function add_ecs_button( $cart_table, $context ) {
75
76
		if ( ! wpsc_uses_shipping() && wpsc_is_gateway_active( 'paypal-digital-goods' ) || ! wpsc_is_gateway_active( 'paypal-express-checkout' ) ) {
77
			return;
78
		}
79
80
		if ( 'bottom' == $context ) {
81
			return;
82
		}
83
84
		if ( _wpsc_get_current_controller_name() === 'cart' ) {
85
			$url = $this->get_shortcut_url();
86
			echo '<a class="express-checkout-button" href="'. esc_url( $url ) .'"><img src="https://www.paypalobjects.com/webstatic/en_US/i/buttons/checkout-logo-large.png" alt="' . __( 'Check out with PayPal', 'wp-e-commerce' ) . '" /></a>';
87
		}
88
	}
89
90
	/**
91
	 * Return the ExpressCheckout Shortcut redirection URL
92
	 *
93
	 * @return void
94
	 */
95
	public function get_shortcut_url() {
96
		$location = add_query_arg( array(
97
			'payment_gateway'          => 'paypal-express-checkout',
98
			'payment_gateway_callback' => 'shortcut_process',
99
		), home_url( 'index.php' ) );
100
101
		return apply_filters( 'wpsc_paypal_express_checkout_shortcut_url', $location );
102
	}
103
104
	/**
105
	 * ExpressCheckout Shortcut Callback
106
	 *
107
	 * @return int
108
	 */
109
	public function callback_shortcut_process() {
110
		if ( ! isset( $_GET['payment_gateway'] ) ) {
111
			return;
112
		}
113
		$payment_gateway = $_GET['payment_gateway'];
114
115
		global $wpsc_cart;
116
		//	Create a new PurchaseLog Object
117
		$purchase_log = new WPSC_Purchase_Log();
118
119
		// Create a Sessionid
120
		$sessionid = ( mt_rand( 100, 999 ) . time() );
121
		wpsc_update_customer_meta( 'checkout_session_id', $sessionid );
122
		$purchase_log->set( array(
123
			'user_ID'        => get_current_user_id(),
124
			'date'           => time(),
125
			'plugin_version' => WPSC_VERSION,
126
			'statusno'       => '0',
127
			'sessionid'      => $sessionid,
128
		) );
129
130
		if ( wpsc_is_tax_included() ) {
131
			$tax            = $wpsc_cart->calculate_total_tax();
132
			$tax_percentage = $wpsc_cart->tax_percentage;
133
		} else {
134
			$tax            = 0;
135
			$tax_percentage = 0;
136
		}
137
		$purchase_log->set( array(
138
			'wpec_taxes_total' => $tax,
139
			'wpec_taxes_rate'  => $tax_percentage,
140
		) );
141
142
		// Save the purchase_log object to generate it's id
143
		$purchase_log->save();
144
		$purchase_log_id = $purchase_log->get( 'id' );
145
146
		$wpsc_cart->log_id = $purchase_log_id;
147
		wpsc_update_customer_meta( 'current_purchase_log_id', $purchase_log_id );
148
149
		$purchase_log->set( array(
150
			'gateway'       => $payment_gateway,
151
			'base_shipping' => $wpsc_cart->calculate_base_shipping(),
152
			'totalprice'    => $wpsc_cart->calculate_total_price(),
153
		) );
154
155
		$purchase_log->save();
156
157
		$wpsc_cart->empty_db( $purchase_log_id );
158
		$wpsc_cart->save_to_db( $purchase_log_id );
159
		$wpsc_cart->submit_stock_claims( $purchase_log_id );
160
161
		// Save an empty Form
162
		$form   = WPSC_Checkout_Form::get();
163
		$fields = $form->get_fields();
164
		WPSC_Checkout_Form_Data::save_form( $purchase_log, $fields );
165
166
		// Return Customer to Review Order Page if there is Shipping
167
		add_filter( 'wpsc_paypal_express_checkout_transact_url', array( &$this, 'review_order_url' ) );
168
		add_filter( 'wpsc_paypal_express_checkout_return_url', array( &$this, 'review_order_callback' ) );
169
170
		// Set a Temporary Option for EC Shortcut
171
		wpsc_update_customer_meta( 'esc-' . $sessionid, true );
172
173
		// Apply Checkout Actions
174
		do_action( 'wpsc_submit_checkout', array(
175
			'purchase_log_id' => $purchase_log_id,
176
			'our_user_id'     => get_current_user_id(),
177
		) );
178
		do_action( 'wpsc_submit_checkout_gateway', $payment_gateway, $purchase_log );
179
180
		return $sessionid;
181
	}
182
183
	/**
184
	 * Return Customer to Review Order Page if there are Shipping Costs.
185
	 *
186
	 * @param string $url
187
	 * @return string
188
	 */
189
	public function review_order_url( $url ) {
190
		if ( wpsc_uses_shipping() ) {
191
		   $url = wpsc_get_checkout_url( 'review-order' );
192
		}
193
194
		return $url;
195
	}
196
197
	/**
198
	 * Sets the Review Callback for Review Order page.
199
	 *
200
	 * @param string $url
201
	 * @return string
202
	 */
203
	public function review_order_callback( $url ) {
204
		$args = array(
205
			'payment_gateway_callback' => 'review_transaction',
206
			'payment_gateway'          => 'paypal-express-checkout',
207
		);
208
		$url = add_query_arg( $args, $url );
209
210
		return esc_url( $url );
211
	}
212
213
	/**
214
	 * Run the gateway hooks
215
	 *
216
	 * @access public
217
	 * @since 4.0
218
	 *
219
	 * @return void
220
	 */
221
	public function init() {
222
		add_filter(
223
			'wpsc_payment_method_form_fields',
224
			array( 'WPSC_Payment_Gateway_Paypal_Express_Checkout', 'filter_unselect_default' ), 100 , 1
225
		);
226
	}
227
228
	/**
229
	 * No payment gateway is selected by default
230
	 *
231
	 * @access public
232
	 * @param array $fields
233
	 * @return array
234
	 *
235
	 * @since 3.9
236
	 */
237
	public static function filter_unselect_default( $fields ) {
238
		foreach ( $fields as $i => $field ) {
239
			$fields[ $i ][ 'checked' ] = false;
240
		}
241
242
		return $fields;
243
	}
244
245
	/**
246
	 * Returns the HTML of the logo of the payment gateway.
247
	 *
248
	 * @access public
249
	 * @return string
250
	 *
251
	 * @since 3.9
252
	 */
253
	public function get_mark_html() {
254
		$html = '<img src="https://www.paypalobjects.com/webstatic/mktg/logo/pp_cc_mark_37x23.jpg" border="0" alt="PayPal Logo">';
255
256
		return apply_filters( 'wpsc_paypal-ec_mark_html', $html );
257
	}
258
259
	/**
260
	 * Returns the PayPal redirect URL
261
	 *
262
	 * @param array $data Arguments to encode with the URL
263
	 * @return string
264
	 *
265
	 * @since 3.9
266
	 */
267
	public function get_redirect_url( $data = array() ) {
268
269
		// Select either the Sandbox or the Live URL
270
		if ( $this->setting->get( 'sandbox_mode' ) ) {
271
			$url = $this->sandbox_url;
272
		} else {
273
			$url = $this->live_url;
274
		}
275
276
		// Common Vars
277
		$common = array(
278
			'cmd'        => '_express-checkout',
279
			'useraction' => 'commit',
280
		);
281
282
		if ( wp_is_mobile() ) {
283
			$common['cmd'] = '_express-checkout-mobile';
284
		}
285
286
		// Merge the two arrays
287
		$data = array_merge( $data, $common );
288
289
		// Build the URL
290
		$url = add_query_arg( $data, $url );
291
292
		return $url;
293
	}
294
295
	/**
296
	 * Returns the URL of the Return Page after the PayPal Checkout
297
	 *
298
	 * @return string
299
	 */
300
	protected function get_return_url() {
301
		$transact_url = get_option( 'transact_url' );
302
		$transact_url = apply_filters( 'wpsc_paypal_express_checkout_transact_url', $transact_url );
303
304
		$location = add_query_arg( array(
305
			'sessionid'                => $this->purchase_log->get( 'sessionid' ),
306
			'payment_gateway'          => 'paypal-express-checkout',
307
			'payment_gateway_callback' => 'confirm_transaction',
308
		),
309
		$transact_url
310
	);
311
		return apply_filters( 'wpsc_paypal_express_checkout_return_url', $location, $this );
312
	}
313
314
	/**
315
	 * Returns the URL of the IPN Page
316
	 *
317
	 * @return string
318
	 */
319
	protected function get_notify_url() {
320
		$location = add_query_arg( array(
321
			'payment_gateway'          => 'paypal-express-checkout',
322
			'payment_gateway_callback' => 'ipn',
323
		), home_url( 'index.php' ) );
324
325
		return apply_filters( 'wpsc_paypal_express_checkout_notify_url', $location );
326
	}
327
328
	/**
329
	 * Creates a new Purchase Log entry and set it to the current object
330
	 *
331
	 * @return null
332
	 */
333
	protected function set_purchase_log_for_callbacks( $sessionid = false ) {
334
		// Define the sessionid if it's not passed
335
		if ( $sessionid === false ) {
336
			$sessionid = $_REQUEST['sessionid'];
337
		}
338
339
		// Create a new Purchase Log entry
340
		$purchase_log = new WPSC_Purchase_Log( $sessionid, 'sessionid' );
341
342
		if ( ! $purchase_log->exists() ) {
343
			return null;
344
		}
345
346
		// Set the Purchase Log for the gateway object
347
		$this->set_purchase_log( $purchase_log );
348
	}
349
350
	/**
351
	 * IPN Callback function
352
	 *
353
	 * @return void
354
	 */
355
	public function callback_ipn() {
356
		$ipn = new PHP_Merchant_Paypal_IPN( false, (bool) $this->setting->get( 'sandbox_mode', false ) );
357
358
		if ( $ipn->is_verified() ) {
359
			$sessionid = $ipn->get( 'invoice' );
360
			$this->set_purchase_log_for_callbacks( $sessionid );
361
362
			if ( $ipn->is_payment_denied() ) {
363
				$this->purchase_log->set( 'processed', WPSC_Purchase_Log::PAYMENT_DECLINED );
364
			} elseif ( $ipn->is_payment_refunded() ) {
365
				$this->purchase_log->set( 'processed', WPSC_Purchase_Log::REFUNDED );
366
			} elseif ( $ipn->is_payment_completed() ) {
367
				$this->purchase_log->set( 'processed', WPSC_Purchase_Log::ACCEPTED_PAYMENT );
368
			} elseif ( $ipn->is_payment_pending() ) {
369
				if ( $ipn->is_payment_refund_pending() ) {
370
					$this->purchase_log->set( 'processed', WPSC_Purchase_Log::REFUND_PENDING );
371
				} else {
372
					$this->purchase_log->set( 'processed', WPSC_Purchase_Log::ORDER_RECEIVED );
373
				}
374
			}
375
376
			$this->purchase_log->save();
377
			transaction_results( $sessionid, false );
378
		}
379
380
		exit;
381
	}
382
383
	/**
384
	 * Pull and Record PayPal Details
385
	 *
386
	 * @return void
387
	 */
388
	public function pull_paypal_details() {
389
		$this->set_purchase_log_for_callbacks();
390
391
		// Pull the User Details from PayPal
392
		$this->paypal_data = $paypal = $this->gateway->get_details_for( $_GET['token'] );
393
		$payer = $paypal->get( 'payer' );
394
		$address = $paypal->get( 'shipping_address' );
395
396
		// PurchaseLog Update
397
		if ( isset( $address['country_code'] ) ) {
398
			$this->purchase_log->set( 'billing_country', $address['country_code'] );
399
			$this->purchase_log->set( 'shipping_country', $address['country_code'] );
400
		}
401
		if ( isset( $address['state'] ) ) {
402
			$this->purchase_log->set( 'billing_region', $address['state'] );
403
			$this->purchase_log->set( 'shipping_region', $address['state'] );
404
		}
405
406
		// Save Checkout Form Fields
407
		$form   = WPSC_Checkout_Form::get();
408
		$fields = $form->get_fields();
409
		$_POST['wpsc_checkout_details'] = array();
410
		foreach( $fields as $field ) {
411
			$this->set_post_var( $field, $payer, $address );
412
		}
413
414
		// Save details to the Forms Table
415
		WPSC_Checkout_Form_Data::save_form( $this->purchase_log, $fields );
416
	}
417
418
	/**
419
	 * To insert Data to the Form Table, we need to pass it
420
	 * to the global $_POST variable first
421
	 *
422
	 * @param object $payer
423
	 * @param object $field
424
	 * @param array $address
425
	 *
426
	 * @return void
427
	 */
428
	private function set_post_var( $field, $payer, $address ) {
429
		switch( $field->unique_name ) {
430
			// Shipping Details
431
		case 'shippingfirstname':
432
			$_POST['wpsc_checkout_details'][$field->id] = $this->validate_var( $address['name'] );
433
			break;
434
		case 'shippinglastname':
435
			$_POST['wpsc_checkout_details'][$field->id] = '';
436
			break;
437
		case 'shippingaddress':
438
				$_POST['wpsc_checkout_details'][$field->id] = $this->validate_var( $address['street'] );
439
			break;
440
		case 'shippingcity':
441
				$_POST['wpsc_checkout_details'][$field->id] = $this->validate_var( $address['city'] );
442
			break;
443
		case 'shippingstate':
444
				$_POST['wpsc_checkout_details'][$field->id] = $this->validate_var( $address['state'] );
445
			break;
446
		case 'shippingcountry':
447
				$_POST['wpsc_checkout_details'][$field->id] = $this->validate_var( $address['country_code'] );
448
			break;
449
		case 'shippingpostcode':
450
				$_POST['wpsc_checkout_details'][$field->id] = $this->validate_var( $address['zip'] );
451
			break;
452
			// Billing Details
453
		case 'billingfirstname':
454
				$_POST['wpsc_checkout_details'][$field->id] = $this->validate_var( $payer->first_name );
455
			break;
456
		case 'billinglastname':
457
				$_POST['wpsc_checkout_details'][$field->id] = $this->validate_var( $payer->last_name );
458
			break;
459
		case 'billingaddress':
460
		case 'billingcity':
461
		case 'billingstate':
462
		case 'billingpostcode':
463
		case 'billingphone':
464
			$_POST['wpsc_checkout_details'][$field->id] = '';
465
			break;
466
		case 'billingcountry':
467
				$_POST['wpsc_checkout_details'][$field->id] = $this->validate_var( $payer->country );
468
			break;
469
		case 'billingemail':
470
				$_POST['wpsc_checkout_details'][$field->id] = $this->validate_var( $payer->email );
471
			break;
472
		}
473
	}
474
475
	/**
476
	 * Verify that the variable isset and return it, otherwise return an empty
477
	 * string
478
	 *
479
	 * @param string $var
480
	 *
481
	 * @return string
482
	 */
483
	private function validate_var( $var ) {
484
		if ( isset( $var ) ) {
485
			return $var;
486
		}
487
		return '';
488
	}
489
490
	/**
491
	 * Review Transaction Callback
492
	 *
493
	 * @return void
494
	 */
495
	public function callback_review_transaction() {
496
		// Pull Customer Details from PayPal
497
		$this->pull_paypal_details();
498
499
		// If no Shipping is required, confirm the Transaction
500
		if ( !wpsc_uses_shipping() ) {
501
			$this->callback_confirm_transaction();
502
		}
503
504
		// Display Customer Details
505
		add_filter( 'wpsc_review_order_buyers_details', array( &$this, 'review_order_buyer_details' ) );
506
		add_filter( 'wpsc_review_order_shipping_details', array( &$this, 'review_order_shipping_details' ) );
507
	}
508
509
	/**
510
	 * Display Customer Details from PayPal
511
	 *
512
	 * @param string $output
513
	 * @return string
514
	 */
515
	public function review_order_buyer_details( $output ) {
516
		$payer = $this->paypal_data->get( 'payer' );
517
		$output .= '<ul>';
518
		$output .= '<li><strong>' . __( 'Email:', 'wp-e-commerce' ) . ' </strong>' . $payer->email . '</li>';
519
		$output .= '<li><strong>' . __( 'First Name:', 'wp-e-commerce' ) . ' </strong>' . $payer->first_name . '</li>';
520
		$output .= '<li><strong>' . __( 'Last Name:', 'wp-e-commerce' ) . ' </strong>' . $payer->last_name . '</li>';
521
		$output .= '</ul>';
522
		return $output;
523
	}
524
525
	/**
526
	 * Display Shipping Details from PayPal
527
	 *
528
	 * @param string $output
529
	 * @return string
530
	 */
531
	public function review_order_shipping_details( $output ) {
532
		$address = $this->paypal_data->get( 'shipping_address' );
533
		$output .= '<ul>';
534
		$output .= '<li>' . $address[ 'name' ] . '</li>';
535
		$output .= '<li>' . $address[ 'street' ] . '</li>';
536
		$output .= '<li>' . $address[ 'city' ] . '</li>';
537
		$output .= '<li>' . $address[ 'state' ] . '</li>';
538
		$output .= '<li>' . $address[ 'zip' ] . '</li>';
539
		$output .= '<li>' . $address[ 'country_code' ] . '</li>';
540
		$output .= '</ul>';
541
		return $output;
542
	}
543
544
	/**
545
	 * Confirm Transaction Callback
546
	 *
547
	 * @return bool
548
	 *
549
	 * @since 3.9
550
	 */
551
	public function callback_confirm_transaction() {
552
		if ( ! isset( $_REQUEST['sessionid'] ) || ! isset( $_REQUEST['token'] ) || ! isset( $_REQUEST['PayerID'] ) ) {
553
			return false;
554
		}
555
556
		// Set the Purchase Log
557
		$this->set_purchase_log_for_callbacks();
558
559
		// Display the Confirmation Page
560
		$this->do_transaction();
561
562
		// Remove Shortcut option if it exists
563
		$sessionid = $_REQUEST['sessionid'];
564
		wpsc_delete_customer_meta( 'esc-' . $sessionid );
565
	}
566
567
	/**
568
	 * Process the transaction through the PayPal APIs
569
	 *
570
	 * @since 3.9
571
	 */
572
	public function do_transaction() {
573
		$args = array_map( 'urldecode', $_GET );
574
		extract( $args, EXTR_SKIP );
0 ignored issues
show
introduced by
extract() usage is highly discouraged, due to the complexity and unintended issues it might cause.
Loading history...
575
576
		if ( ! isset( $sessionid ) || ! isset( $token ) || ! isset( $PayerID ) ) {
577
			return;
578
		}
579
580
		$this->set_purchase_log_for_callbacks();
581
582
		$total = $this->convert( $this->purchase_log->get( 'totalprice' ) );
583
		$options = array(
584
			'token'         => $token,
585
			'payer_id'      => $PayerID,
586
			'message_id'    => $this->purchase_log->get( 'id' ),
587
			'invoice'		=> $this->purchase_log->get( 'sessionid' ),
588
		);
589
		$options += $this->checkout_data->get_gateway_data();
590
		$options += $this->purchase_log->get_gateway_data( parent::get_currency_code(), $this->get_currency_code() );
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (get_currency_code() instead of do_transaction()). Are you sure this is correct? If so, you might want to change this to $this->get_currency_code().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
591
592
		if ( $this->setting->get( 'ipn', false ) ) {
593
			$options['notify_url'] = $this->get_notify_url();
594
		}
595
596
		// GetExpressCheckoutDetails
597
		$details = $this->gateway->get_details_for( $token );
598
		$this->log_payer_details( $details );
599
600
		$response = $this->gateway->purchase( $options );
601
		$this->log_protection_status( $response );
602
		$location = remove_query_arg( 'payment_gateway_callback' );
603
604
		if ( $response->has_errors() ) {
605
			$errors = $response->get_params();
606
607
			if ( isset( $errors['L_ERRORCODE0'] ) && '10486' == $errors['L_ERRORCODE0'] ) {
608
				wp_redirect( $this->get_redirect_url( array( 'token' => $token ) ) );
609
				exit;
610
			}
611
612
			wpsc_update_customer_meta( 'paypal_express_checkout_errors', $response->get_errors() );
613
			$location = add_query_arg( array( 'payment_gateway_callback' => 'display_paypal_error' ) );
614
615
		} elseif ( $response->is_payment_completed() || $response->is_payment_pending() ) {
616
			$location = remove_query_arg( 'payment_gateway' );
617
618
			if ( $response->is_payment_completed() ) {
619
				$this->purchase_log->set( 'processed', WPSC_Purchase_Log::ACCEPTED_PAYMENT );
620
			} else {
621
				$this->purchase_log->set( 'processed', WPSC_Purchase_Log::ORDER_RECEIVED );
622
			}
623
624
			$this->purchase_log->set( 'transactid', $response->get( 'transaction_id' ) )
625
				->set( 'date', time() )
626
				->save();
627
		} else {
628
			$location = add_query_arg( array( 'payment_gateway_callback' => 'display_generic_error' ) );
629
		}
630
631
		wp_redirect( esc_url_raw( $location ) );
632
		exit;
633
	}
634
635
	public function callback_display_paypal_error() {
636
		add_filter( 'wpsc_get_transaction_html_output', array( $this, 'filter_paypal_error_page' ) );
637
	}
638
639
	public function callback_display_generic_error() {
640
		add_filter( 'wpsc_get_transaction_html_output', array( $this, 'filter_generic_error_page' ) );
641
	}
642
643
	/**
644
	 * Records the Payer ID, Payer Status and Shipping Status to the Purchase
645
	 * Log on GetExpressCheckout Call
646
	 *
647
	 * @return void
648
	 */
649
	public function log_payer_details( $details ) {
650
		if ( isset( $details->get( 'payer' )->id ) && !empty( $details->get( 'payer' )->id ) ) {
651
			$payer_id = $details->get( 'payer' )->id;
652
		} else {
653
			$payer_id = 'not set';
654
		}
655
		if ( isset( $details->get( 'payer' )->status ) && !empty( $details->get( 'payer' )->status ) ) {
656
			$payer_status = $details->get( 'payer' )->status;
657
		} else {
658
			$payer_status = 'not set';
659
		}
660
		if ( isset( $details->get( 'payer' )->shipping_status ) && !empty( $details->get( 'payer' )->shipping_status ) ) {
661
			$payer_shipping_status = $details->get( 'payer' )->shipping_status;
662
		} else {
663
			$payer_shipping_status = 'not set';
664
		}
665
		$paypal_log = array(
666
			'payer_id'        => $payer_id,
667
			'payer_status'    => $payer_status,
668
			'shipping_status' => $payer_shipping_status,
669
			'protection'      => null,
670
		);
671
672
		wpsc_update_purchase_meta( $this->purchase_log->get( 'id' ), 'paypal_ec_details' , $paypal_log );
673
	}
674
675
	/**
676
	 * Records the Protection Eligibility status to the Purchase Log on
677
	 * DoExpressCheckout Call
678
	 *
679
	 * @return void
680
	 */
681
	public function log_protection_status( $response ) {
682
		$params = $response->get_params();
683
684
		if ( isset( $params['PAYMENTINFO_0_PROTECTIONELIGIBILITY'] ) ) {
685
			$elg                      = $params['PAYMENTINFO_0_PROTECTIONELIGIBILITY'];
686
		} else {
687
			$elg = false;
688
		}
689
		$paypal_log               = wpsc_get_purchase_meta( $this->purchase_log->get( 'id' ), 'paypal_ec_details', true );
690
		$paypal_log['protection'] = $elg;
691
		wpsc_update_purchase_meta( $this->purchase_log->get( 'id' ), 'paypal_ec_details' , $paypal_log );
692
	}
693
694
	public function callback_process_confirmed_payment() {
695
		$args = array_map( 'urldecode', $_GET );
696
		extract( $args, EXTR_SKIP );
0 ignored issues
show
introduced by
extract() usage is highly discouraged, due to the complexity and unintended issues it might cause.
Loading history...
697
698
		if ( ! isset( $sessionid ) || ! isset( $token ) || ! isset( $PayerID ) ) {
699
			return;
700
		}
701
702
		$this->set_purchase_log_for_callbacks();
703
704
		$total = $this->convert( $this->purchase_log->get( 'totalprice' ) );
705
		$options = array(
706
			'token'         => $token,
707
			'payer_id'      => $PayerID,
708
			'message_id'    => $this->purchase_log->get( 'id' ),
709
			'invoice'       => $this->purchase_log->get( 'sessionid' ),
710
		);
711
		$options += $this->checkout_data->get_gateway_data();
712
		$options += $this->purchase_log->get_gateway_data( parent::get_currency_code(), $this->get_currency_code() );
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (get_currency_code() instead of callback_process_confirmed_payment()). Are you sure this is correct? If so, you might want to change this to $this->get_currency_code().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
713
714
		if ( $this->setting->get( 'ipn', false ) ) {
715
			$options['notify_url'] = $this->get_notify_url();
716
		}
717
718
		// GetExpressCheckoutDetails
719
		$details = $this->gateway->get_details_for( $token );
720
		$this->log_payer_details( $details );
721
722
		$response = $this->gateway->purchase( $options );
723
		$this->log_protection_status( $response );
724
		$location = remove_query_arg( 'payment_gateway_callback' );
725
726
		if ( $response->has_errors() ) {
727
			wpsc_update_customer_meta( 'paypal_express_checkout_errors', $response->get_errors() );
728
			$location = add_query_arg( array( 'payment_gateway_callback' => 'display_paypal_error' ) );
729
		} elseif ( $response->is_payment_completed() || $response->is_payment_pending() ) {
730
			$location = remove_query_arg( 'payment_gateway' );
731
732
			if ( $response->is_payment_completed() ) {
733
				$this->purchase_log->set( 'processed', WPSC_Purchase_Log::ACCEPTED_PAYMENT );
734
			} else {
735
				$this->purchase_log->set( 'processed', WPSC_Purchase_Log::ORDER_RECEIVED );
736
			}
737
738
			$this->purchase_log->set( 'transactid', $response->get( 'transaction_id' ) )
739
				->set( 'date', time() )
740
				->save();
741
		} else {
742
			$location = add_query_arg( array( 'payment_gateway_callback' => 'display_generic_error' ) );
743
		}
744
745
		wp_redirect( esc_url_raw( $location ) );
746
		exit;
747
	}
748
749
	/**
750
	 * Error Page Template
751
	 *
752
	 * @since 3.9
753
	 */
754
	public function filter_paypal_error_page() {
755
		$errors = wpsc_get_customer_meta( 'paypal_express_checkout_errors' );
756
		ob_start();
757
?>
758
	<p>
759
	<?php _e( 'Sorry, your transaction could not be processed by PayPal. Please contact the site administrator. The following errors are returned:' , 'wp-e-commerce' ); ?>
760
		</p>
761
			<ul>
762
			<?php foreach ( $errors as $error ): ?>
763
			<li><?php echo esc_html( $error['details'] ) ?> (<?php echo esc_html( $error['code'] ); ?>)</li>
764
			<?php endforeach; ?>
765
		</ul>
766
			<p><a href="<?php echo esc_url( $this->get_shopping_cart_payment_url() ); ?>"><?php ( 'Click here to go back to the checkout page.') ?></a></p>
767
<?php
768
		$output = apply_filters( 'wpsc_paypal_express_checkout_gateway_error_message', ob_get_clean(), $errors );
769
		return $output;
770
	}
771
772
	/**
773
	 * Generic Error Page Template
774
	 *
775
	 * @since 3.9
776
	 */
777
	public function filter_generic_error_page() {
778
		ob_start();
779
?>
780
<p><?php _e( 'Sorry, but your transaction could not be processed by PayPal for some reason. Please contact the site administrator.' , 'wp-e-commerce' ); ?></p>
781
<p><a href="<?php echo esc_attr( $this->get_shopping_cart_payment_url() ); ?>"><?php _e( 'Click here to go back to the checkout page.', 'wp-e-commerce' ) ?></a></p>
782
<?php
783
		$output = apply_filters( 'wpsc_paypal_express_checkout_generic_error_message', ob_get_clean() );
784
		return $output;
785
	}
786
787
	/**
788
	 * Settings Form Template
789
	 *
790
	 * @since 3.9
791
	 */
792
	public function setup_form() {
793
		$paypal_currency = $this->get_currency_code();
794
?>
795
796
<!-- Account Credentials -->
797
<tr>
798
	<td colspan="2">
799
		<h4><?php _e( 'Account Credentials', 'wp-e-commerce' ); ?></h4>
800
	</td>
801
</tr>
802
<tr>
803
	<td>
804
		<label for="wpsc-paypal-express-api-username"><?php _e( 'API Username', 'wp-e-commerce' ); ?></label>
805
	</td>
806
	<td>
807
		<input type="text" name="<?php echo esc_attr( $this->setting->get_field_name( 'api_username' ) ); ?>" value="<?php echo esc_attr( $this->setting->get( 'api_username' ) ); ?>" id="wpsc-paypal-express-api-username" />
808
	</td>
809
</tr>
810
<tr>
811
	<td>
812
		<label for="wpsc-paypal-express-api-password"><?php _e( 'API Password', 'wp-e-commerce' ); ?></label>
813
	</td>
814
	<td>
815
		<input type="text" name="<?php echo esc_attr( $this->setting->get_field_name( 'api_password' ) ); ?>" value="<?php echo esc_attr( $this->setting->get( 'api_password' ) ); ?>" id="wpsc-paypal-express-api-password" />
816
	</td>
817
</tr>
818
<tr>
819
	<td>
820
		<label for="wpsc-paypal-express-api-signature"><?php _e( 'API Signature', 'wp-e-commerce' ); ?></label>
821
	</td>
822
	<td>
823
		<input type="text" name="<?php echo esc_attr( $this->setting->get_field_name( 'api_signature' ) ); ?>" value="<?php echo esc_attr( $this->setting->get( 'api_signature' ) ); ?>" id="wpsc-paypal-express-api-signature" />
824
	</td>
825
</tr>
826
<tr>
827
	<td>
828
		<label for="wpsc-paypal-express-api-username"><?php _e( 'Merchant ID', 'wp-e-commerce' ); ?></label>
829
	</td>
830
	<td>
831
		<input type="text" name="<?php echo esc_attr( $this->setting->get_field_name( 'api_merchantid' ) ); ?>" value="<?php echo esc_attr( $this->setting->get( 'api_merchantid' ) ); ?>" id="wpsc-paypal-express-api-username" />
832
	</td>
833
</tr>
834
<tr>
835
	<td>
836
		<label><?php _e( 'Sandbox Mode', 'wp-e-commerce' ); ?></label>
837
	</td>
838
	<td>
839
		<label><input <?php checked( $this->setting->get( 'sandbox_mode' ) ); ?> type="radio" name="<?php echo esc_attr( $this->setting->get_field_name( 'sandbox_mode' ) ); ?>" value="1" /> <?php _e( 'Yes', 'wp-e-commerce' ); ?></label>&nbsp;&nbsp;&nbsp;
840
		<label><input <?php checked( (bool) $this->setting->get( 'sandbox_mode' ), false ); ?> type="radio" name="<?php echo esc_attr( $this->setting->get_field_name( 'sandbox_mode' ) ); ?>" value="0" /> <?php _e( 'No', 'wp-e-commerce' ); ?></label>
841
	</td>
842
</tr>
843
<tr>
844
	<td>
845
		<label><?php _e( 'IPN', 'wp-e-commerce' ); ?></label>
846
	</td>
847
	<td>
848
		<label><input <?php checked( $this->setting->get( 'ipn' ) ); ?> type="radio" name="<?php echo esc_attr( $this->setting->get_field_name( 'ipn' ) ); ?>" value="1" /> <?php _e( 'Yes', 'wp-e-commerce' ); ?></label>&nbsp;&nbsp;&nbsp;
849
		<label><input <?php checked( (bool) $this->setting->get( 'ipn' ), false ); ?> type="radio" name="<?php echo esc_attr( $this->setting->get_field_name( 'ipn' ) ); ?>" value="0" /> <?php _e( 'No', 'wp-e-commerce' ); ?></label>
850
	</td>
851
</tr>
852
853
<!-- Cart Customization -->
854
<tr>
855
	<td colspan="2">
856
		<label><h4><?php _e( 'Cart Customization', 'wp-e-commerce'); ?></h4></label>
857
	</td>
858
</tr>
859
<tr>
860
	<td>
861
		<label for="wpsc-paypal-express-cart-logo"><?php _e( 'Merchant Logo', 'wp-e-commerce' ); ?></label>
862
	</td>
863
	<td>
864
		<input type="text" name="<?php echo esc_attr( $this->setting->get_field_name( 'cart_logo' ) ); ?>" value="<?php echo esc_attr( $this->setting->get( 'cart_logo' ) ); ?>" id="wpsc-paypal-express-cart-logo" /><br><span class="small description"><?php _e( 'The image must be stored in a HTTPS Server. Limit the image to 190 pixels wide by 60 pixels high.', 'wp-e-commerce' ); ?></span>
865
	</td>
866
</tr>
867
<tr>
868
	<td>
869
		<label for="wpsc-paypal-express-cart-border"><?php _e( 'Cart Border Color', 'wp-e-commerce' ); ?></label>
870
	</td>
871
	<td>
872
		<input type="text" name="<?php echo esc_attr( $this->setting->get_field_name( 'cart_border' ) ); ?>" value="<?php echo esc_attr( $this->setting->get( 'cart_border' ) ); ?>" id="wpsc-paypal-express-cart-border" />
873
	</td>
874
</tr>
875
<tr>
876
	<td>
877
		<label for="wpsc-paypal-express-cart-border"><?php _e( 'Enable In-Context Checkout', 'wp-e-commerce' ); ?></label>
878
	</td>
879
	<td>
880
		<label><input <?php checked( $this->setting->get( 'incontext' ) ); ?> type="radio" name="<?php echo esc_attr( $this->setting->get_field_name( 'incontext' ) ); ?>" value="1" /> <?php _e( 'Yes', 'wp-e-commerce' ); ?></label>&nbsp;&nbsp;&nbsp;
881
		<label><input <?php checked( (bool) $this->setting->get( 'incontext' ), false ); ?> type="radio" name="<?php echo esc_attr( $this->setting->get_field_name( 'incontext' ) ); ?>" value="0" /> <?php _e( 'No', 'wp-e-commerce' ); ?></label>
882
	</td>
883
</tr>
884
885
<!-- Currency Conversion -->
886
<?php if ( ! $this->is_currency_supported() ) : ?>
887
<tr>
888
	<td colspan="2">
889
		<h4><?php _e( 'Currency Conversion', 'wp-e-commerce' ); ?></h4>
890
	</td>
891
</tr>
892
<tr>
893
	<td colspan="2">
894
		<p><?php _e( "Your base currency is currently not accepted by PayPal. As a result, before a payment request is sent to PayPal, WP eCommerce has to convert the amounts into one of PayPal's supported currencies. Please select your preferred currency below.", 'wp-e-commerce' ); ?></p>
895
	</td>
896
</tr>
897
<tr>
898
	<td>
899
		<label for "wpsc-paypal-express-currency"><?php _e( 'PayPal Currency', 'wp-e-commerce' ); ?></label>
900
	</td>
901
	<td>
902
		<select name="<?php echo esc_attr( $this->setting->get_field_name( 'currency' ) ); ?>" id="wpsc-paypal-express-currency">
903
			<?php foreach ( $this->gateway->get_supported_currencies() as $currency ) : ?>
904
			<option <?php selected( $currency, $paypal_currency ); ?> value="<?php echo esc_attr( $currency ); ?>"><?php echo esc_html( $currency ); ?></option>
905
			<?php endforeach ?>
906
		</select>
907
	</td>
908
</tr>
909
<?php endif ?>
910
911
<!-- Checkout Shortcut -->
912
<tr>
913
	<td colspan="2">
914
		<h4><?php _e( 'Express Checkout Shortcut', 'wp-e-commerce' ); ?></h4>
915
	</td>
916
</tr>
917
<tr>
918
	<td>
919
		<label><?php _e( 'Enable Shortcut', 'wp-e-commerce' ); ?></label>
920
	</td>
921
	<td>
922
		<label><input <?php checked( $this->setting->get( 'shortcut' ) ); ?> type="radio" name="<?php echo esc_attr( $this->setting->get_field_name( 'shortcut' ) ); ?>" value="1" /> <?php _e( 'Yes', 'wp-e-commerce' ); ?></label>&nbsp;&nbsp;&nbsp;
923
		<label><input <?php checked( (bool) $this->setting->get( 'shortcut' ), false ); ?> type="radio" name="<?php echo esc_attr( $this->setting->get_field_name( 'shortcut' ) ); ?>" value="0" /> <?php _e( 'No', 'wp-e-commerce' ); ?></label>
924
	</td>
925
</tr>
926
927
<!-- Error Logging -->
928
<tr>
929
	<td colspan="2">
930
		<h4><?php _e( 'Error Logging', 'wp-e-commerce' ); ?></h4>
931
	</td>
932
</tr>
933
<tr>
934
	<td>
935
		<label><?php _e( 'Enable Debugging', 'wp-e-commerce' ); ?></label>
936
	</td>
937
	<td>
938
		<label><input <?php checked( $this->setting->get( 'debugging' ) ); ?> type="radio" name="<?php echo esc_attr( $this->setting->get_field_name( 'debugging' ) ); ?>" value="1" /> <?php _e( 'Yes', 'wp-e-commerce' ); ?></label>&nbsp;&nbsp;&nbsp;
939
		<label><input <?php checked( (bool) $this->setting->get( 'debugging' ), false ); ?> type="radio" name="<?php echo esc_attr( $this->setting->get_field_name( 'debugging' ) ); ?>" value="0" /> <?php _e( 'No', 'wp-e-commerce' ); ?></label>
940
	</td>
941
</tr>
942
<?php
943
	}
944
945
	/**
946
	 * Check if the selected currency is supported by the gateway
947
	 *
948
	 * @return bool
949
	 *
950
	 * @since 3.9
951
	 */
952
	protected function is_currency_supported() {
953
		return in_array( parent::get_currency_code(), $this->gateway->get_supported_currencies() );
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (get_currency_code() instead of is_currency_supported()). Are you sure this is correct? If so, you might want to change this to $this->get_currency_code().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
954
	}
955
956
	/**
957
	 * Return the Currency ISO code
958
	 *
959
	 * @return string
960
	 *
961
	 * @since 3.9
962
	 */
963
	public function get_currency_code() {
964
		$code = parent::get_currency_code();
965
966
		if ( ! in_array( $code, $this->gateway->get_supported_currencies() ) ) {
967
			$code = $this->setting->get( 'currency', 'USD' );
968
		}
969
970
		return $code;
971
	}
972
973
	/**
974
	 * Convert an amount (integer) to the supported currency
975
	 * @param integer $amt
976
	 *
977
	 * @return integer
978
	 *
979
	 * @since 3.9
980
	 */
981
	protected function convert( $amt ) {
982
		if ( $this->is_currency_supported() ) {
983
			return $amt;
984
		}
985
986
		return wpsc_convert_currency( $amt, parent::get_currency_code(), $this->get_currency_code() );
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (get_currency_code() instead of convert()). Are you sure this is correct? If so, you might want to change this to $this->get_currency_code().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
987
	}
988
989
	/**
990
	 * Process the SetExpressCheckout API Call
991
	 *
992
	 * @return void
993
	 *
994
	 * @since 3.9
995
	 */
996
	public function process() {
997
		$total = $this->convert( $this->purchase_log->get( 'totalprice' ) );
998
		$options = array(
999
			'return_url'       => $this->get_return_url(),
1000
			'message_id'       => $this->purchase_log->get( 'id' ),
1001
			'invoice'          => $this->purchase_log->get( 'sessionid' ),
1002
			'address_override' => 1,
1003
		);
1004
1005
		$options += $this->checkout_data->get_gateway_data();
1006
		$options += $this->purchase_log->get_gateway_data( parent::get_currency_code(), $this->get_currency_code() );
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (get_currency_code() instead of process()). Are you sure this is correct? If so, you might want to change this to $this->get_currency_code().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
1007
1008
		if ( $this->setting->get( 'ipn', false ) ) {
1009
			$options['notify_url'] = $this->get_notify_url();
1010
		}
1011
1012
		// SetExpressCheckout
1013
		$response = $this->gateway->setup_purchase( $options );
1014
1015
		if ( $response->is_successful() ) {
1016
			$params = $response->get_params();
1017
			if ( $params['ACK'] == 'SuccessWithWarning' ) {
1018
				$this->log_error( $response );
1019
				wpsc_update_customer_meta( 'paypal_express_checkout_errors', $response->get_errors() );
1020
			}
1021
			// Successful redirect
1022
			$url = $this->get_redirect_url( array( 'token' => $response->get( 'token' ) ) );
1023
		} else {
1024
1025
			// SetExpressCheckout Failure
1026
			$this->log_error( $response );
1027
			wpsc_update_customer_meta( 'paypal_express_checkout_errors', $response->get_errors() );
1028
1029
			$url = add_query_arg( array(
1030
				'payment_gateway'          => 'paypal-express-checkout',
1031
				'payment_gateway_callback' => 'display_paypal_error',
1032
			), $this->get_return_url() );
1033
		}
1034
1035
		wp_redirect( $url );
1036
		exit;
1037
	}
1038
1039
	/**
1040
	 * Log an error message
1041
	 *
1042
	 * @param PHP_Merchant_Paypal_Express_Checkout_Response $response
1043
	 * @return void
1044
	 *
1045
	 * @since 3.9
1046
	 */
1047
	public function log_error( $response ) {
1048
		if ( $this->setting->get( 'debugging' ) ) {
1049
1050
			add_filter( 'wpsc_logging_post_type_args', 'WPSC_Logging::force_ui' );
1051
			add_filter( 'wpsc_logging_taxonomy_args ', 'WPSC_Logging::force_ui' );
1052
1053
			$log_data = array(
1054
				'post_title'    => 'PayPal ExpressCheckout Operation Failure',
1055
				'post_content'  =>  'There was an error processing the payment. Find details in the log entry meta fields.',
0 ignored issues
show
introduced by
Expected 1 space after "=>"; 2 found
Loading history...
1056
				'log_type'      => 'error'
1057
			);
1058
1059
			$log_meta = array(
1060
				'correlation_id'   => $response->get( 'correlation_id' ),
1061
				'time' => $response->get( 'datetime' ),
1062
				'errors' => $response->get_errors(),
1063
			);
1064
1065
			$log_entry = WPSC_Logging::insert_log( $log_data, $log_meta );
1066
		}
1067
	}
1068
}
1069