Completed
Push — master ( 0f47b4...2e9b05 )
by Justin
09:10 queued 03:34
created

__construct()   C

Complexity

Conditions 7
Paths 5

Size

Total Lines 32
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 22
nc 5
nop 2
dl 0
loc 32
rs 6.7272
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' ) && wpsc_is_checkout() ) {
51
				add_action( 'wp_enqueue_scripts', array( $this, 'incontext_load_scripts' ) );
52
			}
53
		}
54
	}
55
56
	public function incontext_load_scripts() {
57
		wp_register_script( 'ec-incontext', WPSC_URL . '/wpsc-components/merchant-core-v3/gateways/ec-incontext.js', '', null, true );
58
		wp_localize_script( 'ec-incontext', 'wpec_ppic', array(
59
			'mid' => esc_attr( $this->setting->get( 'api_merchantid' ) ),
60
			'env' => (bool) $this->setting->get( 'sandbox_mode' ) === true ? 'sandbox' : 'production',
61
			)
62
		);
63
		wp_enqueue_script( 'ec-incontext' );
64
		wp_enqueue_script( 'ppincontext', 'https://www.paypalobjects.com/api/checkout.js', array(), null, true );
65
	}
66
67
	/**
68
	 * Insert the ExpessCheckout Shortcut Button
69
	 *
70
	 * @return void
71
	 */
72
	public function add_ecs_button( $cart_table, $context ) {
73
74
		if ( ! wpsc_uses_shipping() && wpsc_is_gateway_active( 'paypal-digital-goods' ) || ! wpsc_is_gateway_active( 'paypal-express-checkout' ) ) {
75
			return;
76
		}
77
78
		if ( 'bottom' == $context ) {
79
			return;
80
		}
81
82
		if ( _wpsc_get_current_controller_name() === 'cart' ) {
83
			$url = $this->get_shortcut_url();
84
			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>';
85
		}
86
	}
87
88
	/**
89
	 * Return the ExpressCheckout Shortcut redirection URL
90
	 *
91
	 * @return void
92
	 */
93
	public function get_shortcut_url() {
94
		$location = add_query_arg( array(
95
			'payment_gateway'          => 'paypal-express-checkout',
96
			'payment_gateway_callback' => 'shortcut_process',
97
		), home_url( 'index.php' ) );
98
99
		return apply_filters( 'wpsc_paypal_express_checkout_shortcut_url', $location );
100
	}
101
102
	/**
103
	 * ExpressCheckout Shortcut Callback
104
	 *
105
	 * @return int
106
	 */
107
	public function callback_shortcut_process() {
108
		if ( ! isset( $_GET['payment_gateway'] ) ) {
109
			return;
110
		}
111
		$payment_gateway = $_GET['payment_gateway'];
112
113
		global $wpsc_cart;
114
		//	Create a new PurchaseLog Object
115
		$purchase_log = new WPSC_Purchase_Log();
116
117
		// Create a Sessionid
118
		$sessionid = ( mt_rand( 100, 999 ) . time() );
119
		wpsc_update_customer_meta( 'checkout_session_id', $sessionid );
120
		$purchase_log->set( array(
121
			'user_ID'        => get_current_user_id(),
122
			'date'           => time(),
123
			'plugin_version' => WPSC_VERSION,
124
			'statusno'       => '0',
125
			'sessionid'      => $sessionid,
126
		) );
127
128
		if ( wpsc_is_tax_included() ) {
129
			$tax            = $wpsc_cart->calculate_total_tax();
130
			$tax_percentage = $wpsc_cart->tax_percentage;
131
		} else {
132
			$tax            = 0;
133
			$tax_percentage = 0;
134
		}
135
		$purchase_log->set( array(
136
			'wpec_taxes_total' => $tax,
137
			'wpec_taxes_rate'  => $tax_percentage,
138
		) );
139
140
		// Save the purchase_log object to generate it's id
141
		$purchase_log->save();
142
		$purchase_log_id = $purchase_log->get( 'id' );
143
144
		$wpsc_cart->log_id = $purchase_log_id;
145
		wpsc_update_customer_meta( 'current_purchase_log_id', $purchase_log_id );
146
147
		$purchase_log->set( array(
148
			'gateway'       => $payment_gateway,
149
			'base_shipping' => $wpsc_cart->calculate_base_shipping(),
150
			'totalprice'    => $wpsc_cart->calculate_total_price(),
151
		) );
152
153
		$purchase_log->save();
154
155
		$wpsc_cart->empty_db( $purchase_log_id );
156
		$wpsc_cart->save_to_db( $purchase_log_id );
157
		$wpsc_cart->submit_stock_claims( $purchase_log_id );
158
159
		// Save an empty Form
160
		$form   = WPSC_Checkout_Form::get();
161
		$fields = $form->get_fields();
162
		WPSC_Checkout_Form_Data::save_form( $purchase_log, $fields );
163
164
		// Return Customer to Review Order Page if there is Shipping
165
		add_filter( 'wpsc_paypal_express_checkout_transact_url', array( &$this, 'review_order_url' ) );
166
		add_filter( 'wpsc_paypal_express_checkout_return_url', array( &$this, 'review_order_callback' ) );
167
168
		// Set a Temporary Option for EC Shortcut
169
		wpsc_update_customer_meta( 'esc-' . $sessionid, true );
170
171
		// Apply Checkout Actions
172
		do_action( 'wpsc_submit_checkout', array(
173
			'purchase_log_id' => $purchase_log_id,
174
			'our_user_id'     => get_current_user_id(),
175
		) );
176
		do_action( 'wpsc_submit_checkout_gateway', $payment_gateway, $purchase_log );
177
178
		return $sessionid;
179
	}
180
181
	/**
182
	 * Return Customer to Review Order Page if there are Shipping Costs.
183
	 *
184
	 * @param string $url
185
	 * @return string
186
	 */
187
	public function review_order_url( $url ) {
188
		if ( wpsc_uses_shipping() ) {
189
		   $url = wpsc_get_checkout_url( 'review-order' );
190
		}
191
192
		return $url;
193
	}
194
195
	/**
196
	 * Sets the Review Callback for Review Order page.
197
	 *
198
	 * @param string $url
199
	 * @return string
200
	 */
201
	public function review_order_callback( $url ) {
202
		$args = array(
203
			'payment_gateway_callback' => 'review_transaction',
204
			'payment_gateway'          => 'paypal-express-checkout',
205
		);
206
		$url = add_query_arg( $args, $url );
207
208
		return esc_url( $url );
209
	}
210
211
	/**
212
	 * Run the gateway hooks
213
	 *
214
	 * @access public
215
	 * @since 4.0
216
	 *
217
	 * @return void
218
	 */
219
	public function init() {
220
		add_filter(
221
			'wpsc_payment_method_form_fields',
222
			array( 'WPSC_Payment_Gateway_Paypal_Express_Checkout', 'filter_unselect_default' ), 100 , 1
223
		);
224
	}
225
226
	/**
227
	 * No payment gateway is selected by default
228
	 *
229
	 * @access public
230
	 * @param array $fields
231
	 * @return array
232
	 *
233
	 * @since 3.9
234
	 */
235
	public static function filter_unselect_default( $fields ) {
236
		foreach ( $fields as $i => $field ) {
237
			$fields[ $i ][ 'checked' ] = false;
238
		}
239
240
		return $fields;
241
	}
242
243
	/**
244
	 * Returns the HTML of the logo of the payment gateway.
245
	 *
246
	 * @access public
247
	 * @return string
248
	 *
249
	 * @since 3.9
250
	 */
251
	public function get_mark_html() {
252
		$html = '<img src="https://www.paypalobjects.com/webstatic/mktg/logo/pp_cc_mark_37x23.jpg" border="0" alt="PayPal Logo">';
253
254
		return apply_filters( 'wpsc_paypal-ec_mark_html', $html );
255
	}
256
257
	/**
258
	 * Returns the PayPal redirect URL
259
	 *
260
	 * @param array $data Arguments to encode with the URL
261
	 * @return string
262
	 *
263
	 * @since 3.9
264
	 */
265
	public function get_redirect_url( $data = array() ) {
266
267
		// Select either the Sandbox or the Live URL
268
		if ( $this->setting->get( 'sandbox_mode' ) ) {
269
			$url = $this->sandbox_url;
270
		} else {
271
			$url = $this->live_url;
272
		}
273
274
		// Common Vars
275
		$common = array(
276
			'cmd'        => '_express-checkout',
277
			'useraction' => 'commit',
278
		);
279
280
		if ( wp_is_mobile() ) {
281
			$common['cmd'] = '_express-checkout-mobile';
282
		}
283
284
		// Merge the two arrays
285
		$data = array_merge( $data, $common );
286
287
		// Build the URL
288
		$url = add_query_arg( $data, $url );
289
290
		return $url;
291
	}
292
293
	/**
294
	 * Returns the URL of the Return Page after the PayPal Checkout
295
	 *
296
	 * @return string
297
	 */
298
	protected function get_return_url() {
299
		$transact_url = get_option( 'transact_url' );
300
		$transact_url = apply_filters( 'wpsc_paypal_express_checkout_transact_url', $transact_url );
301
302
		$location = add_query_arg( array(
303
			'sessionid'                => $this->purchase_log->get( 'sessionid' ),
304
			'payment_gateway'          => 'paypal-express-checkout',
305
			'payment_gateway_callback' => 'confirm_transaction',
306
		),
307
		$transact_url
308
	);
309
		return apply_filters( 'wpsc_paypal_express_checkout_return_url', $location, $this );
310
	}
311
312
	/**
313
	 * Returns the URL of the IPN Page
314
	 *
315
	 * @return string
316
	 */
317
	protected function get_notify_url() {
318
		$location = add_query_arg( array(
319
			'payment_gateway'          => 'paypal-express-checkout',
320
			'payment_gateway_callback' => 'ipn',
321
		), home_url( 'index.php' ) );
322
323
		return apply_filters( 'wpsc_paypal_express_checkout_notify_url', $location );
324
	}
325
326
	/**
327
	 * Creates a new Purchase Log entry and set it to the current object
328
	 *
329
	 * @return null
330
	 */
331
	protected function set_purchase_log_for_callbacks( $sessionid = false ) {
332
		// Define the sessionid if it's not passed
333
		if ( $sessionid === false ) {
334
			$sessionid = $_REQUEST['sessionid'];
335
		}
336
337
		// Create a new Purchase Log entry
338
		$purchase_log = new WPSC_Purchase_Log( $sessionid, 'sessionid' );
339
340
		if ( ! $purchase_log->exists() ) {
341
			return null;
342
		}
343
344
		// Set the Purchase Log for the gateway object
345
		$this->set_purchase_log( $purchase_log );
346
	}
347
348
	/**
349
	 * IPN Callback function
350
	 *
351
	 * @return void
352
	 */
353
	public function callback_ipn() {
354
		$ipn = new PHP_Merchant_Paypal_IPN( false, (bool) $this->setting->get( 'sandbox_mode', false ) );
355
356
		if ( $ipn->is_verified() ) {
357
			$sessionid = $ipn->get( 'invoice' );
358
			$this->set_purchase_log_for_callbacks( $sessionid );
359
360
			if ( $ipn->is_payment_denied() ) {
361
				$this->purchase_log->set( 'processed', WPSC_Purchase_Log::PAYMENT_DECLINED );
362
			} elseif ( $ipn->is_payment_refunded() ) {
363
				$this->purchase_log->set( 'processed', WPSC_Purchase_Log::REFUNDED );
364
			} elseif ( $ipn->is_payment_completed() ) {
365
				$this->purchase_log->set( 'processed', WPSC_Purchase_Log::ACCEPTED_PAYMENT );
366
			} elseif ( $ipn->is_payment_pending() ) {
367
				if ( $ipn->is_payment_refund_pending() ) {
368
					$this->purchase_log->set( 'processed', WPSC_Purchase_Log::REFUND_PENDING );
369
				} else {
370
					$this->purchase_log->set( 'processed', WPSC_Purchase_Log::ORDER_RECEIVED );
371
				}
372
			}
373
374
			$this->purchase_log->save();
375
			transaction_results( $sessionid, false );
376
		}
377
378
		exit;
379
	}
380
381
	/**
382
	 * Pull and Record PayPal Details
383
	 *
384
	 * @return void
385
	 */
386
	public function pull_paypal_details() {
387
		$this->set_purchase_log_for_callbacks();
388
389
		// Pull the User Details from PayPal
390
		$this->paypal_data = $paypal = $this->gateway->get_details_for( $_GET['token'] );
391
		$payer = $paypal->get( 'payer' );
392
		$address = $paypal->get( 'shipping_address' );
393
394
		// PurchaseLog Update
395
		if ( isset( $address['country_code'] ) ) {
396
			$this->purchase_log->set( 'billing_country', $address['country_code'] );
397
			$this->purchase_log->set( 'shipping_country', $address['country_code'] );
398
		}
399
		if ( isset( $address['state'] ) ) {
400
			$this->purchase_log->set( 'billing_region', $address['state'] );
401
			$this->purchase_log->set( 'shipping_region', $address['state'] );
402
		}
403
404
		// Save Checkout Form Fields
405
		$form   = WPSC_Checkout_Form::get();
406
		$fields = $form->get_fields();
407
		$_POST['wpsc_checkout_details'] = array();
408
		foreach( $fields as $field ) {
409
			$this->set_post_var( $field, $payer, $address );
410
		}
411
412
		// Save details to the Forms Table
413
		WPSC_Checkout_Form_Data::save_form( $this->purchase_log, $fields );
414
	}
415
416
	/**
417
	 * To insert Data to the Form Table, we need to pass it
418
	 * to the global $_POST variable first
419
	 *
420
	 * @param object $payer
421
	 * @param object $field
422
	 * @param array $address
423
	 *
424
	 * @return void
425
	 */
426
	private function set_post_var( $field, $payer, $address ) {
427
		switch( $field->unique_name ) {
428
			// Shipping Details
429
		case 'shippingfirstname':
430
			$_POST['wpsc_checkout_details'][$field->id] = $this->validate_var( $address['name'] );
431
			break;
432
		case 'shippinglastname':
433
			$_POST['wpsc_checkout_details'][$field->id] = '';
434
			break;
435
		case 'shippingaddress':
436
				$_POST['wpsc_checkout_details'][$field->id] = $this->validate_var( $address['street'] );
437
			break;
438
		case 'shippingcity':
439
				$_POST['wpsc_checkout_details'][$field->id] = $this->validate_var( $address['city'] );
440
			break;
441
		case 'shippingstate':
442
				$_POST['wpsc_checkout_details'][$field->id] = $this->validate_var( $address['state'] );
443
			break;
444
		case 'shippingcountry':
445
				$_POST['wpsc_checkout_details'][$field->id] = $this->validate_var( $address['country_code'] );
446
			break;
447
		case 'shippingpostcode':
448
				$_POST['wpsc_checkout_details'][$field->id] = $this->validate_var( $address['zip'] );
449
			break;
450
			// Billing Details
451
		case 'billingfirstname':
452
				$_POST['wpsc_checkout_details'][$field->id] = $this->validate_var( $payer->first_name );
453
			break;
454
		case 'billinglastname':
455
				$_POST['wpsc_checkout_details'][$field->id] = $this->validate_var( $payer->last_name );
456
			break;
457
		case 'billingaddress':
458
		case 'billingcity':
459
		case 'billingstate':
460
		case 'billingpostcode':
461
		case 'billingphone':
462
			$_POST['wpsc_checkout_details'][$field->id] = '';
463
			break;
464
		case 'billingcountry':
465
				$_POST['wpsc_checkout_details'][$field->id] = $this->validate_var( $payer->country );
466
			break;
467
		case 'billingemail':
468
				$_POST['wpsc_checkout_details'][$field->id] = $this->validate_var( $payer->email );
469
			break;
470
		}
471
	}
472
473
	/**
474
	 * Verify that the variable isset and return it, otherwise return an empty
475
	 * string
476
	 *
477
	 * @param string $var
478
	 *
479
	 * @return string
480
	 */
481
	private function validate_var( $var ) {
482
		if ( isset( $var ) ) {
483
			return $var;
484
		}
485
		return '';
486
	}
487
488
	/**
489
	 * Review Transaction Callback
490
	 *
491
	 * @return void
492
	 */
493
	public function callback_review_transaction() {
494
		// Pull Customer Details from PayPal
495
		$this->pull_paypal_details();
496
497
		// If no Shipping is required, confirm the Transaction
498
		if ( !wpsc_uses_shipping() ) {
499
			$this->callback_confirm_transaction();
500
		}
501
502
		// Display Customer Details
503
		add_filter( 'wpsc_review_order_buyers_details', array( &$this, 'review_order_buyer_details' ) );
504
		add_filter( 'wpsc_review_order_shipping_details', array( &$this, 'review_order_shipping_details' ) );
505
	}
506
507
	/**
508
	 * Display Customer Details from PayPal
509
	 *
510
	 * @param string $output
511
	 * @return string
512
	 */
513
	public function review_order_buyer_details( $output ) {
514
		$payer = $this->paypal_data->get( 'payer' );
515
		$output .= '<ul>';
516
		$output .= '<li><strong>' . __( 'Email:', 'wp-e-commerce' ) . ' </strong>' . $payer->email . '</li>';
517
		$output .= '<li><strong>' . __( 'First Name:', 'wp-e-commerce' ) . ' </strong>' . $payer->first_name . '</li>';
518
		$output .= '<li><strong>' . __( 'Last Name:', 'wp-e-commerce' ) . ' </strong>' . $payer->last_name . '</li>';
519
		$output .= '</ul>';
520
		return $output;
521
	}
522
523
	/**
524
	 * Display Shipping Details from PayPal
525
	 *
526
	 * @param string $output
527
	 * @return string
528
	 */
529
	public function review_order_shipping_details( $output ) {
530
		$address = $this->paypal_data->get( 'shipping_address' );
531
		$output .= '<ul>';
532
		$output .= '<li>' . $address[ 'name' ] . '</li>';
533
		$output .= '<li>' . $address[ 'street' ] . '</li>';
534
		$output .= '<li>' . $address[ 'city' ] . '</li>';
535
		$output .= '<li>' . $address[ 'state' ] . '</li>';
536
		$output .= '<li>' . $address[ 'zip' ] . '</li>';
537
		$output .= '<li>' . $address[ 'country_code' ] . '</li>';
538
		$output .= '</ul>';
539
		return $output;
540
	}
541
542
	/**
543
	 * Confirm Transaction Callback
544
	 *
545
	 * @return bool
546
	 *
547
	 * @since 3.9
548
	 */
549
	public function callback_confirm_transaction() {
550
		if ( ! isset( $_REQUEST['sessionid'] ) || ! isset( $_REQUEST['token'] ) || ! isset( $_REQUEST['PayerID'] ) ) {
551
			return false;
552
		}
553
554
		// Set the Purchase Log
555
		$this->set_purchase_log_for_callbacks();
556
557
		// Display the Confirmation Page
558
		$this->do_transaction();
559
560
		// Remove Shortcut option if it exists
561
		$sessionid = $_REQUEST['sessionid'];
562
		wpsc_delete_customer_meta( 'esc-' . $sessionid );
563
	}
564
565
	/**
566
	 * Process the transaction through the PayPal APIs
567
	 *
568
	 * @since 3.9
569
	 */
570
	public function do_transaction() {
571
		$args = array_map( 'urldecode', $_GET );
572
		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...
573
574
		if ( ! isset( $sessionid ) || ! isset( $token ) || ! isset( $PayerID ) ) {
575
			return;
576
		}
577
578
		$this->set_purchase_log_for_callbacks();
579
580
		$total = $this->convert( $this->purchase_log->get( 'totalprice' ) );
581
		$options = array(
582
			'token'         => $token,
583
			'payer_id'      => $PayerID,
584
			'message_id'    => $this->purchase_log->get( 'id' ),
585
			'invoice'		=> $this->purchase_log->get( 'sessionid' ),
586
		);
587
		$options += $this->checkout_data->get_gateway_data();
588
		$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...
589
590
		if ( $this->setting->get( 'ipn', false ) ) {
591
			$options['notify_url'] = $this->get_notify_url();
592
		}
593
594
		// GetExpressCheckoutDetails
595
		$details = $this->gateway->get_details_for( $token );
596
		$this->log_payer_details( $details );
597
598
		$response = $this->gateway->purchase( $options );
599
		$this->log_protection_status( $response );
600
		$location = remove_query_arg( 'payment_gateway_callback' );
601
602
		if ( $response->has_errors() ) {
603
			$errors = $response->get_params();
604
605
			if ( isset( $errors['L_ERRORCODE0'] ) && '10486' == $errors['L_ERRORCODE0'] ) {
606
				wp_redirect( $this->get_redirect_url( array( 'token' => $token ) ) );
607
				exit;
608
			}
609
610
			wpsc_update_customer_meta( 'paypal_express_checkout_errors', $response->get_errors() );
611
			$location = add_query_arg( array( 'payment_gateway_callback' => 'display_paypal_error' ) );
612
613
		} elseif ( $response->is_payment_completed() || $response->is_payment_pending() ) {
614
			$location = remove_query_arg( 'payment_gateway' );
615
616
			if ( $response->is_payment_completed() ) {
617
				$this->purchase_log->set( 'processed', WPSC_Purchase_Log::ACCEPTED_PAYMENT );
618
			} else {
619
				$this->purchase_log->set( 'processed', WPSC_Purchase_Log::ORDER_RECEIVED );
620
			}
621
622
			$this->purchase_log->set( 'transactid', $response->get( 'transaction_id' ) )
623
				->set( 'date', time() )
624
				->save();
625
		} else {
626
			$location = add_query_arg( array( 'payment_gateway_callback' => 'display_generic_error' ) );
627
		}
628
629
		wp_redirect( esc_url_raw( $location ) );
630
		exit;
631
	}
632
633
	public function callback_display_paypal_error() {
634
		add_filter( 'wpsc_get_transaction_html_output', array( $this, 'filter_paypal_error_page' ) );
635
	}
636
637
	public function callback_display_generic_error() {
638
		add_filter( 'wpsc_get_transaction_html_output', array( $this, 'filter_generic_error_page' ) );
639
	}
640
641
	/**
642
	 * Records the Payer ID, Payer Status and Shipping Status to the Purchase
643
	 * Log on GetExpressCheckout Call
644
	 *
645
	 * @return void
646
	 */
647
	public function log_payer_details( $details ) {
648
		if ( isset( $details->get( 'payer' )->id ) && !empty( $details->get( 'payer' )->id ) ) {
649
			$payer_id = $details->get( 'payer' )->id;
650
		} else {
651
			$payer_id = 'not set';
652
		}
653
		if ( isset( $details->get( 'payer' )->status ) && !empty( $details->get( 'payer' )->status ) ) {
654
			$payer_status = $details->get( 'payer' )->status;
655
		} else {
656
			$payer_status = 'not set';
657
		}
658
		if ( isset( $details->get( 'payer' )->shipping_status ) && !empty( $details->get( 'payer' )->shipping_status ) ) {
659
			$payer_shipping_status = $details->get( 'payer' )->shipping_status;
660
		} else {
661
			$payer_shipping_status = 'not set';
662
		}
663
		$paypal_log = array(
664
			'payer_id'        => $payer_id,
665
			'payer_status'    => $payer_status,
666
			'shipping_status' => $payer_shipping_status,
667
			'protection'      => null,
668
		);
669
670
		wpsc_update_purchase_meta( $this->purchase_log->get( 'id' ), 'paypal_ec_details' , $paypal_log );
671
	}
672
673
	/**
674
	 * Records the Protection Eligibility status to the Purchase Log on
675
	 * DoExpressCheckout Call
676
	 *
677
	 * @return void
678
	 */
679
	public function log_protection_status( $response ) {
680
		$params = $response->get_params();
681
682
		if ( isset( $params['PAYMENTINFO_0_PROTECTIONELIGIBILITY'] ) ) {
683
			$elg                      = $params['PAYMENTINFO_0_PROTECTIONELIGIBILITY'];
684
		} else {
685
			$elg = false;
686
		}
687
		$paypal_log               = wpsc_get_purchase_meta( $this->purchase_log->get( 'id' ), 'paypal_ec_details', true );
688
		$paypal_log['protection'] = $elg;
689
		wpsc_update_purchase_meta( $this->purchase_log->get( 'id' ), 'paypal_ec_details' , $paypal_log );
690
	}
691
692
	public function callback_process_confirmed_payment() {
693
		$args = array_map( 'urldecode', $_GET );
694
		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...
695
696
		if ( ! isset( $sessionid ) || ! isset( $token ) || ! isset( $PayerID ) ) {
697
			return;
698
		}
699
700
		$this->set_purchase_log_for_callbacks();
701
702
		$total = $this->convert( $this->purchase_log->get( 'totalprice' ) );
703
		$options = array(
704
			'token'         => $token,
705
			'payer_id'      => $PayerID,
706
			'message_id'    => $this->purchase_log->get( 'id' ),
707
			'invoice'       => $this->purchase_log->get( 'sessionid' ),
708
		);
709
		$options += $this->checkout_data->get_gateway_data();
710
		$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...
711
712
		if ( $this->setting->get( 'ipn', false ) ) {
713
			$options['notify_url'] = $this->get_notify_url();
714
		}
715
716
		// GetExpressCheckoutDetails
717
		$details = $this->gateway->get_details_for( $token );
718
		$this->log_payer_details( $details );
719
720
		$response = $this->gateway->purchase( $options );
721
		$this->log_protection_status( $response );
722
		$location = remove_query_arg( 'payment_gateway_callback' );
723
724
		if ( $response->has_errors() ) {
725
			wpsc_update_customer_meta( 'paypal_express_checkout_errors', $response->get_errors() );
726
			$location = add_query_arg( array( 'payment_gateway_callback' => 'display_paypal_error' ) );
727
		} elseif ( $response->is_payment_completed() || $response->is_payment_pending() ) {
728
			$location = remove_query_arg( 'payment_gateway' );
729
730
			if ( $response->is_payment_completed() ) {
731
				$this->purchase_log->set( 'processed', WPSC_Purchase_Log::ACCEPTED_PAYMENT );
732
			} else {
733
				$this->purchase_log->set( 'processed', WPSC_Purchase_Log::ORDER_RECEIVED );
734
			}
735
736
			$this->purchase_log->set( 'transactid', $response->get( 'transaction_id' ) )
737
				->set( 'date', time() )
738
				->save();
739
		} else {
740
			$location = add_query_arg( array( 'payment_gateway_callback' => 'display_generic_error' ) );
741
		}
742
743
		wp_redirect( esc_url_raw( $location ) );
744
		exit;
745
	}
746
747
	/**
748
	 * Error Page Template
749
	 *
750
	 * @since 3.9
751
	 */
752
	public function filter_paypal_error_page() {
753
		$errors = wpsc_get_customer_meta( 'paypal_express_checkout_errors' );
754
		ob_start();
755
?>
756
	<p>
757
	<?php _e( 'Sorry, your transaction could not be processed by PayPal. Please contact the site administrator. The following errors are returned:' , 'wp-e-commerce' ); ?>
758
		</p>
759
			<ul>
760
			<?php foreach ( $errors as $error ): ?>
761
			<li><?php echo esc_html( $error['details'] ) ?> (<?php echo esc_html( $error['code'] ); ?>)</li>
762
			<?php endforeach; ?>
763
		</ul>
764
			<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>
765
<?php
766
		$output = apply_filters( 'wpsc_paypal_express_checkout_gateway_error_message', ob_get_clean(), $errors );
767
		return $output;
768
	}
769
770
	/**
771
	 * Generic Error Page Template
772
	 *
773
	 * @since 3.9
774
	 */
775
	public function filter_generic_error_page() {
776
		ob_start();
777
?>
778
<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>
779
<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>
780
<?php
781
		$output = apply_filters( 'wpsc_paypal_express_checkout_generic_error_message', ob_get_clean() );
782
		return $output;
783
	}
784
785
	/**
786
	 * Settings Form Template
787
	 *
788
	 * @since 3.9
789
	 */
790
	public function setup_form() {
791
		$paypal_currency = $this->get_currency_code();
792
?>
793
794
<!-- Account Credentials -->
795
<tr>
796
	<td colspan="2">
797
		<h4><?php _e( 'Account Credentials', 'wp-e-commerce' ); ?></h4>
798
	</td>
799
</tr>
800
<tr>
801
	<td>
802
		<label for="wpsc-paypal-express-api-username"><?php _e( 'API Username', 'wp-e-commerce' ); ?></label>
803
	</td>
804
	<td>
805
		<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" />
806
	</td>
807
</tr>
808
<tr>
809
	<td>
810
		<label for="wpsc-paypal-express-api-password"><?php _e( 'API Password', 'wp-e-commerce' ); ?></label>
811
	</td>
812
	<td>
813
		<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" />
814
	</td>
815
</tr>
816
<tr>
817
	<td>
818
		<label for="wpsc-paypal-express-api-signature"><?php _e( 'API Signature', 'wp-e-commerce' ); ?></label>
819
	</td>
820
	<td>
821
		<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" />
822
	</td>
823
</tr>
824
<tr>
825
	<td>
826
		<label for="wpsc-paypal-express-api-username"><?php _e( 'Merchant ID', 'wp-e-commerce' ); ?></label>
827
	</td>
828
	<td>
829
		<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" />
830
	</td>
831
</tr>
832
<tr>
833
	<td>
834
		<label><?php _e( 'Sandbox Mode', 'wp-e-commerce' ); ?></label>
835
	</td>
836
	<td>
837
		<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;
838
		<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>
839
	</td>
840
</tr>
841
<tr>
842
	<td>
843
		<label><?php _e( 'IPN', 'wp-e-commerce' ); ?></label>
844
	</td>
845
	<td>
846
		<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;
847
		<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>
848
	</td>
849
</tr>
850
851
<!-- Cart Customization -->
852
<tr>
853
	<td colspan="2">
854
		<label><h4><?php _e( 'Cart Customization', 'wp-e-commerce'); ?></h4></label>
855
	</td>
856
</tr>
857
<tr>
858
	<td>
859
		<label for="wpsc-paypal-express-cart-logo"><?php _e( 'Merchant Logo', 'wp-e-commerce' ); ?></label>
860
	</td>
861
	<td>
862
		<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>
863
	</td>
864
</tr>
865
<tr>
866
	<td>
867
		<label for="wpsc-paypal-express-cart-border"><?php _e( 'Cart Border Color', 'wp-e-commerce' ); ?></label>
868
	</td>
869
	<td>
870
		<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" />
871
	</td>
872
</tr>
873
<tr>
874
	<td>
875
		<label for="wpsc-paypal-express-cart-border"><?php _e( 'Enable In-Context Checkout', 'wp-e-commerce' ); ?></label>
876
	</td>
877
	<td>
878
		<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;
879
		<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>
880
	</td>
881
</tr>
882
883
<!-- Currency Conversion -->
884
<?php if ( ! $this->is_currency_supported() ) : ?>
885
<tr>
886
	<td colspan="2">
887
		<h4><?php _e( 'Currency Conversion', 'wp-e-commerce' ); ?></h4>
888
	</td>
889
</tr>
890
<tr>
891
	<td colspan="2">
892
		<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>
893
	</td>
894
</tr>
895
<tr>
896
	<td>
897
		<label for "wpsc-paypal-express-currency"><?php _e( 'PayPal Currency', 'wp-e-commerce' ); ?></label>
898
	</td>
899
	<td>
900
		<select name="<?php echo esc_attr( $this->setting->get_field_name( 'currency' ) ); ?>" id="wpsc-paypal-express-currency">
901
			<?php foreach ( $this->gateway->get_supported_currencies() as $currency ) : ?>
902
			<option <?php selected( $currency, $paypal_currency ); ?> value="<?php echo esc_attr( $currency ); ?>"><?php echo esc_html( $currency ); ?></option>
903
			<?php endforeach ?>
904
		</select>
905
	</td>
906
</tr>
907
<?php endif ?>
908
909
<!-- Checkout Shortcut -->
910
<tr>
911
	<td colspan="2">
912
		<h4><?php _e( 'Express Checkout Shortcut', 'wp-e-commerce' ); ?></h4>
913
	</td>
914
</tr>
915
<tr>
916
	<td>
917
		<label><?php _e( 'Enable Shortcut', 'wp-e-commerce' ); ?></label>
918
	</td>
919
	<td>
920
		<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;
921
		<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>
922
	</td>
923
</tr>
924
925
<!-- Error Logging -->
926
<tr>
927
	<td colspan="2">
928
		<h4><?php _e( 'Error Logging', 'wp-e-commerce' ); ?></h4>
929
	</td>
930
</tr>
931
<tr>
932
	<td>
933
		<label><?php _e( 'Enable Debugging', 'wp-e-commerce' ); ?></label>
934
	</td>
935
	<td>
936
		<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;
937
		<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>
938
	</td>
939
</tr>
940
<?php
941
	}
942
943
	/**
944
	 * Check if the selected currency is supported by the gateway
945
	 *
946
	 * @return bool
947
	 *
948
	 * @since 3.9
949
	 */
950
	protected function is_currency_supported() {
951
		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...
952
	}
953
954
	/**
955
	 * Return the Currency ISO code
956
	 *
957
	 * @return string
958
	 *
959
	 * @since 3.9
960
	 */
961
	public function get_currency_code() {
962
		$code = parent::get_currency_code();
963
964
		if ( ! in_array( $code, $this->gateway->get_supported_currencies() ) ) {
965
			$code = $this->setting->get( 'currency', 'USD' );
966
		}
967
968
		return $code;
969
	}
970
971
	/**
972
	 * Convert an amount (integer) to the supported currency
973
	 * @param integer $amt
974
	 *
975
	 * @return integer
976
	 *
977
	 * @since 3.9
978
	 */
979
	protected function convert( $amt ) {
980
		if ( $this->is_currency_supported() ) {
981
			return $amt;
982
		}
983
984
		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...
985
	}
986
987
	/**
988
	 * Process the SetExpressCheckout API Call
989
	 *
990
	 * @return void
991
	 *
992
	 * @since 3.9
993
	 */
994
	public function process() {
995
		$total = $this->convert( $this->purchase_log->get( 'totalprice' ) );
996
		$options = array(
997
			'return_url'       => $this->get_return_url(),
998
			'message_id'       => $this->purchase_log->get( 'id' ),
999
			'invoice'          => $this->purchase_log->get( 'sessionid' ),
1000
			'address_override' => 1,
1001
		);
1002
1003
		$options += $this->checkout_data->get_gateway_data();
1004
		$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...
1005
1006
		if ( $this->setting->get( 'ipn', false ) ) {
1007
			$options['notify_url'] = $this->get_notify_url();
1008
		}
1009
1010
		// SetExpressCheckout
1011
		$response = $this->gateway->setup_purchase( $options );
1012
1013
		if ( $response->is_successful() ) {
1014
			$params = $response->get_params();
1015
			if ( $params['ACK'] == 'SuccessWithWarning' ) {
1016
				$this->log_error( $response );
1017
				wpsc_update_customer_meta( 'paypal_express_checkout_errors', $response->get_errors() );
1018
			}
1019
			// Successful redirect
1020
			$url = $this->get_redirect_url( array( 'token' => $response->get( 'token' ) ) );
1021
		} else {
1022
1023
			// SetExpressCheckout Failure
1024
			$this->log_error( $response );
1025
			wpsc_update_customer_meta( 'paypal_express_checkout_errors', $response->get_errors() );
1026
1027
			$url = add_query_arg( array(
1028
				'payment_gateway'          => 'paypal-express-checkout',
1029
				'payment_gateway_callback' => 'display_paypal_error',
1030
			), $this->get_return_url() );
1031
		}
1032
1033
		wp_redirect( $url );
1034
		exit;
1035
	}
1036
1037
	/**
1038
	 * Log an error message
1039
	 *
1040
	 * @param PHP_Merchant_Paypal_Express_Checkout_Response $response
1041
	 * @return void
1042
	 *
1043
	 * @since 3.9
1044
	 */
1045
	public function log_error( $response ) {
1046
		if ( $this->setting->get( 'debugging' ) ) {
1047
1048
			add_filter( 'wpsc_logging_post_type_args', 'WPSC_Logging::force_ui' );
1049
			add_filter( 'wpsc_logging_taxonomy_args ', 'WPSC_Logging::force_ui' );
1050
1051
			$log_data = array(
1052
				'post_title'    => 'PayPal ExpressCheckout Operation Failure',
1053
				'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...
1054
				'log_type'      => 'error'
1055
			);
1056
1057
			$log_meta = array(
1058
				'correlation_id'   => $response->get( 'correlation_id' ),
1059
				'time' => $response->get( 'datetime' ),
1060
				'errors' => $response->get_errors(),
1061
			);
1062
1063
			$log_entry = WPSC_Logging::insert_log( $log_data, $log_meta );
1064
		}
1065
	}
1066
}
1067