Completed
Push — master ( 39bc1e...963c79 )
by Justin
05:14
created

WPSC_Payment_Gateways::get()   B

Complexity

Conditions 6
Paths 10

Size

Total Lines 37
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 19
nc 10
nop 2
dl 0
loc 37
rs 8.439
c 0
b 0
f 0
1
<?php
2
3
final class WPSC_Payment_Gateways {
4
5
	/**
6
	 * Contain a key-value array of gateway names and gateway class names
7
	 *
8
	 * @access private
9
	 * @static
10
	 * @var array
11
	 * @since 3.9
12
	 */
13
	private static $gateways = array();
14
15
	/**
16
	 * Contain an array of payment gateway objects
17
	 *
18
	 * @access private
19
	 * @static
20
	 * @var array
21
	 * @since 3.9
22
	 */
23
	private static $instances = array();
24
25
	/**
26
	 * Contains the cached metadata of the registered payment gateways, so that the
27
	 * plugin doesn't have to load the gateway's files to determine its metadata
28
	 *
29
	 * @access private
30
	 * @static
31
	 *
32
	 * @since 3.9
33
	 *
34
	 * @var array
35
	 */
36
	private static $payment_gateway_cache = array();
37
38
	/**
39
	 * Contains the names of active gateways that use this API
40
	 *
41
	 * @access private
42
	 * @static
43
	 * @since 3.9
44
	 *
45
	 * @var array
46
	 */
47
	private static $active_gateways = array();
48
49
	/**
50
	 * Return a particular payment gateway object
51
	 *
52
	 * @access public
53
	 *
54
	 * @param string $gateway Name of the payment gateway you want to get
55
	 * @param string $meta    Pass-through parameter for meta
0 ignored issues
show
Documentation introduced by
Should the type for parameter $meta not be string|array? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
56
	 *
57
	 * @return WPSC_Payment_Gateway|WP_Error Returns an error if gateway cannot be found, 3.0 gateway otherwise.
58
	 * @since 3.9
59
	 */
60
	public static function &get( $gateway, $meta = array() ) {
61
62
		$errors = new WP_Error();
63
64
		if ( empty( $gateway ) ) {
65
			$errors->add( 'empty_gateway', __( 'You cannot pass an empty string as a gateway object.', 'wp-e-commerce' ) );
66
			return $errors;
67
		}
68
69
		if ( empty( self::$instances[ $gateway ] ) ) {
70
71
            if ( ! $meta ) {
72
                $meta = self::$gateways[ $gateway ];
73
            }
74
75
            if ( ! file_exists( $meta['path'] ) ) {
76
                WPSC_Payment_Gateways::flush_cache();
77
            }
78
79
            require_once( $meta['path'] );
80
81
            $class_name = $meta['class'];
82
83
			$options = array(
84
				'http_client' => new WPSC_Payment_Gateway_HTTP(),
85
			);
86
87
			if ( ! class_exists( $class_name ) ) {
88
				$error = new WP_Error( 'wpsc_invalid_payment_gateway', sprintf( __( 'Invalid payment gateway: Class %s does not exist.', 'wp-e-commerce' ), $class_name ) );
89
				return $error;
90
			}
91
92
			self::$instances[ $gateway ] = new $class_name( $options );
93
		}
94
95
		return self::$instances[ $gateway ];
96
	}
97
98
	public static function init() {
99
100
		add_action( 'wpsc_submit_gateway_options', array( 'WPSC_Payment_Gateway_Setting', 'action_update_payment_gateway_settings' ) );
101
102
		if ( ! defined( 'WPSC_PAYMENT_GATEWAY_DEBUG' ) || WPSC_PAYMENT_GATEWAY_DEBUG == false ) {
103
			add_action( 'init', array( 'WPSC_Payment_Gateways', 'action_save_payment_gateway_cache' ), 99 );
104
		 } else {
105
			WPSC_Payment_Gateways::flush_cache();
106
		 }
107
108
		WPSC_Payment_Gateways::register_dir( WPSC_MERCHANT_V3_PATH . '/gateways' );
109
110
		// Call the Active Gateways init function
111
		add_action( 'wpsc_ready', array( __CLASS__, 'initialize_gateways' ) );
112
113
		if ( isset( $_REQUEST['payment_gateway'] ) && isset( $_REQUEST['payment_gateway_callback'] ) ) {
114
			add_action( 'init', array( 'WPSC_Payment_Gateways', 'action_process_callbacks' ) );
115
		}
116
	}
117
118
	public static function action_process_callbacks() {
119
		$gateway = self::get( $_REQUEST['payment_gateway'] );
120
		$function_name = "callback_{$_REQUEST['payment_gateway_callback']}";
121
		$callback = array( $gateway, $function_name );
122
123
		if ( is_callable( $callback ) ) {
124
			$gateway->$function_name();
125
		}
126
	}
127
128
	/**
129
	 * Check to see whether a gateway is registered using this new API
130
	 *
131
	 * @access public
132
	 * @since 3.9
133
	 *
134
	 * @param string $gateway Gateway name (derived from the filename without .php extension)
135
	 * @return bool True if it's already registered.
136
	 */
137
	public static function is_registered( $gateway ) {
138
		return ! empty( self::$gateways[ $gateway ] );
139
	}
140
141
	/**
142
	 * Automatically scan a directory for payment gateways and load the classes.
143
	 *
144
	 * The structure of this directory should follow the same rules of the wp-content/plugins
145
	 * structure.
146
	 *
147
	 * All of the files inside the directory will be assumed as payment gateway modules.
148
	 * Files with the same name as those sub-folders will be included as payment
149
	 * gateway modules.
150
	 *
151
	 * For example, if we have the following directory structure:
152
	 * payment-gateways/
153
	 * |-- test-gateway-1.php
154
	 * |-- test-gateway-2.php
155
	 * |-- some-folder/
156
	 *     |-- class.php
157
	 *     |-- functions.php
158
	 *
159
	 * The following files will be loaded as payment gateway modules: test-gateway-1.php,
160
	 * test-gateway-2.php
161
	 * See WPSC_Payment_Gateways::register_file() for file and class naming convention
162
	 *
163
	 * @access public
164
	 * @since 3.9
165
	 * @uses WPSC_Payment_Gateways::register_file()
166
	 *
167
	 * @param string $dir Path to the directory
168
	 * @param string $main_file File name of the class to load
169
	 * @return mixed Return true if successfully loaded all the payment gateway in
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use WP_Error|WPSC_Payment_Gateway|boolean|null.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
170
	 * the directory.
171
	 * Otherwise return a WP_Error object.
172
	 */
173
	public static function register_dir( $dir, $main_file = '' ) {
0 ignored issues
show
Unused Code introduced by
The parameter $main_file is not used and could be removed.

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

Loading history...
174
		$dir = trailingslashit( $dir );
175
		$main_file = basename( $dir ) . '.php';
176
177
		// scan files in dir
178
		$files = scandir( $dir );
179
180
		if ( in_array( $main_file, $files ) ) {
181
			return self::register_file( $dir . $main_file );
182
		}
183
184
		foreach ( $files as $file ) {
185
			$path = $dir . $file;
186
187
			if ( pathinfo( $path, PATHINFO_EXTENSION ) != 'php' || in_array( $file, array( '.', '..' ) ) || is_dir( $path ) ) {
188
				continue;
189
			}
190
191
			$return = self::register_file( $path );
192
193
			if ( is_wp_error( $return ) ) {
194
				//We should log this
195
			}
196
		}
197
	}
198
199
	/**
200
	 * Register a file as a payment gateway module.
201
	 *
202
	 * The payment gateway inside the file must be defined as a subclass of WPSC_Payment_Gateway.
203
	 *
204
	 * The file name should be lowercase, using hyphens or underscores between words
205
	 * instead of spaces. The class name must have "WPSC_Payment_Gateway_" as the
206
	 * prefix, followed by the file name, in which words are capitalized and connected
207
	 * by underscore.
208
	 *
209
	 * For example, if the file name is "paypal-pro.php", then the class name inside
210
	 * the file must be WPSC_Payment_Gateway_Paypal_Pro.
211
	 *
212
	 * @access public
213
	 * @since 3.9
214
	 * @see WPSC_Payment_Gateways::register_dir()
215
	 *
216
	 * @param string $file Absolute path to the file containing the payment gateway
217
	 * class
218
	 * @return mixed Return true if the file is successfully included and contains
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use WP_Error|WPSC_Payment_Gateway|boolean.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
219
	 * a valid class. Otherwise, a WP_Error object is returned.
220
	 */
221
	public static function register_file( $file ) {
222
223
		if ( empty( self::$payment_gateway_cache ) ) {
224
			self::$payment_gateway_cache = get_option( 'wpsc_payment_gateway_cache', array() );
225
		}
226
227
		$filename = basename( $file, '.php' );
228
229
		// payment gateway already exists in cache
230
		if ( isset( self::$payment_gateway_cache[ $filename ] ) ) {
231
			self::$gateways[ $filename ] = self::$payment_gateway_cache[ $filename ];
232
		}
233
234
		// if payment gateway is not in cache, load metadata
235
		$classname = ucwords( str_replace( '-', ' ', $filename ) );
236
		$classname = 'WPSC_Payment_Gateway_' . str_replace( ' ', '_', $classname );
237
238
		if ( file_exists( $file ) ) {
239
			require_once $file;
240
		}
241
242
		if ( is_callable( array( $classname, 'load' ) ) && ! call_user_func( array( $classname, 'load' ) ) ) {
243
244
			self::unregister_file( $filename );
245
246
			$error = new WP_Error( 'wpsc-payment', __( 'Error', 'wp-e-commerce' ) );
247
248
			return $error;
249
		}
250
251
		$meta = array(
252
			'class'        => $classname,
253
			'path'         => $file,
254
			'internalname' => $filename, // compat with older API
255
		);
256
257
		$gateway = self::get( $filename, $meta );
258
259
		if ( is_wp_error( $gateway ) ) {
260
			return $gateway;
261
		}
262
263
		$meta['name']  = $gateway->get_title();
264
		$meta['image'] = $gateway->get_image_url();
265
		$meta['mark']  = $gateway->get_mark_html();
266
267
		self::$gateways[ $filename ] = $meta;
268
269
		return true;
270
	}
271
272
	public static function unregister_file( $filename ) {
273
		if ( isset( self::$gateways[ $filename ] ) ) {
274
			unset( self::$gateways[ $filename ] );
275
		}
276
	}
277
278
	/**
279
	 * Updates the payment gateway cache when it's changed.
280
	 *
281
	 * This function is hooked into WordPress' wp_loaded action
282
	 *
283
	 * @access public
284
	 * @static
285
	 * @since 3.9
286
	 *
287
	 * @return void
288
	 */
289
	public static function action_save_payment_gateway_cache() {
290
		if ( self::$payment_gateway_cache != self::$gateways ) {
291
			update_option( 'wpsc_payment_gateway_cache', self::$gateways );
292
		}
293
	}
294
295
	/**
296
	 * Flush the payment gateways cache.
297
	 *
298
	 * @access public
299
	 * @static
300
	 * @since 3.9
301
	 * @return void
302
	 */
303
	public static function flush_cache() {
304
		delete_option( 'wpsc_payment_gateway_cache' );
305
	}
306
307
	/**
308
	 * Gets metadata of a certain payment gateway. This is better than calling WPSC_Payment_Gateways->get( $gateway_name )->get_title()
309
	 * and the likes of it, since it doesn't require the gateway itself to be loaded.
310
	 *
311
	 * @access public
312
	 * @static
313
	 * @since 3.9
314
	 *
315
	 * @param string $gateway
316
	 * @return mixed Array containing the metadata. If the gateway is not registered,
317
	 *               returns false.
318
	 */
319
	public static function get_meta( $gateway ) {
320
		return isset( self::$gateways[ $gateway ] ) ? self::$gateways[ $gateway ] : false;
321
	}
322
323
	/**
324
	 *
325
	 * Return an array containing registered gateway names.
326
	 *
327
	 * @access public
328
	 * @since 3.9
329
	 *
330
	 * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<integer|string>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
331
	 */
332
	public static function get_gateways() {
333
		return array_keys( self::$gateways );
334
	}
335
336
	/**
337
	 *
338
	 * Return an array containing active gateway names.
339
	 *
340
	 * @access public
341
	 * @since 3.9
342
	 *
343
	 * @return array
344
	 */
345
	public static function get_active_gateways() {
346
		if ( empty( self::$active_gateways ) ) {
347
			$selected_gateways     = get_option( 'custom_gateway_options', array() );
348
			$registered_gateways   = self::get_gateways();
349
			self::$active_gateways = array_intersect( $selected_gateways, $registered_gateways );
350
		}
351
352
		return apply_filters( 'wpsc_get_active_gateways', array_values( self::$active_gateways ) );
353
	}
354
355
	/**
356
	 * Initialize the Active Gateways
357
	 *
358
	 * @access public
359
	 * @since 4.0
360
	 *
361
	 * @return void
362
	 */
363
	public static function initialize_gateways() {
364
		$active_gateways = self::get_active_gateways();
365
366
		foreach( $active_gateways as $gateway_id ) {
367
			$gateway = self::get( $gateway_id );
368
369
			if ( ! is_wp_error( $gateway ) ) {
370
				$gateway->init();
371
			}
372
		}
373
	}
374
375
	/**
376
	 * Returns all known currencies without fractions.
377
	 *
378
	 * Our internal list has not been updated in some time, so returning a filterable list
379
	 * for ever-changing economies and currencies should prove helpful.
380
	 *
381
	 * @link http://www.currency-iso.org/dam/downloads/table_a1.xml
382
	 *
383
	 * @since  4.0
384
	 *
385
	 * @return array Currency ISO codes that do not use fractions.
386
	 */
387
	public static function currencies_without_fractions() {
388
389
		$currencies = array(
390
			'JPY',
391
			'HUF',
392
			'VND',
393
			'BYR',
394
			'XOF',
395
			'BIF',
396
			'XAF',
397
			'CLP',
398
			'KMF',
399
			'DJF',
400
			'XPF',
401
			'GNF',
402
			'ISK',
403
			'GNF',
404
			'KRW',
405
			'PYG',
406
			'RWF',
407
			'UGX',
408
			'UYI',
409
			'VUV',
410
		);
411
412
		return (array) apply_filters( 'wpsc_currencies_without_fractions', $currencies );
413
	}
414
415
	/**
416
	 * Gets an array of countries in the EU.
417
	 *
418
	 * MC (monaco) and IM (Isle of Man, part of UK) also use VAT.
419
	 *
420
	 * @since  4.0
421
	 * @param  $type Type of countries to retrieve. Blank for EU member countries. eu_vat for EU VAT countries.
422
	 * @return string[]
423
	 */
424
	public function get_european_union_countries( $type = '' ) {
425
		$countries = array( 'AT', 'BE', 'BG', 'CY', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI', 'FR', 'GB', 'GR', 'HU', 'HR', 'IE', 'IT', 'LT', 'LU', 'LV', 'MT', 'NL', 'PL', 'PT', 'RO', 'SE', 'SI', 'SK' );
426
427
		if ( 'eu_vat' === $type ) {
428
			$countries[] = 'MC';
429
			$countries[] = 'IM';
430
		}
431
432
		return $countries;
433
	}
434
435
	/**
436
	 * No instantiation for this class
437
	 *
438
	 * @access private
439
	 * @since 3.9
440
	 *
441
	 */
442
	private function __construct() {}
443
}
444
445
abstract class WPSC_Payment_Gateway {
446
447
	/**
448
	 * Object that allows manipulation of payment gateway settings in a consistent
449
	 * manner
450
	 *
451
	 * @access public
452
	 * @var WPSC_Payment_Gateway_Setting
453
	 */
454
	public $setting;
455
456
	public $purchase_log;
457
458
	public $checkout_data;
459
460
	public $currency_code;
461
462
	public $title;
463
464
	/**
465
	 * Supported features such as 'default_credit_card_form', 'refunds'.
466
	 * @var array
467
	 */
468
	public $supports = array();
469
470
	/**
471
	 * Display default credit card form.
472
	 *
473
	 * @param  array $args
474
	 * @param  array $fields
475
	 * @since
476
	 */
477
	public function default_credit_card_form( $args = array(), $fields = array() ) {
478
479
		if ( $this->supports( 'tev1' ) && '1.0' == get_option( 'wpsc_get_active_theme_engine' ) ) {
480
			// Show 2.0 gateway API table-based code
481
			?>
482
				<table class="wpsc_checkout_table <?php echo wpsc_gateway_form_field_style(); ?>">
483
					<tr>
484
						<td><?php _e( 'Card Number', 'wp-e-commerce' ); ?></td>
485
						<td>
486
							<input type='text' id='card_number' value='' autocomplete="off" />
487
						</td>
488
					</tr>
489
					<tr>
490
						<td><?php _e( 'Expiration Date', 'wp-e-commerce' ); ?></td>
491
						<td>
492
							<input type='text' id='card_expiry_month' value='' autocomplete="off" maxlength='2' size='3' placeholder="<?php esc_attr_e( 'MM', 'wp-e-commerce' ); ?>" />&nbsp;
493
							<input type='text' id='card_expiry_year' value='' autocomplete="off" maxlength='2' size='3' placeholder="<?php esc_attr_e( 'YY', 'wp-e-commerce' ); ?>" />
494
						</td>
495
					</tr>
496
					<tr>
497
						<td><?php _e( 'Card Code', 'wp-e-commerce' ); ?></td>
498
						<td>
499
							<input type='text' id='card_code' value='' autocomplete="off" size='5' maxlength='4' placeholder="<?php esc_attr_e( 'CVC', 'wp-e-commerce' ); ?>" />
500
						</td>
501
					</tr>
502
				</table>
503
			<?php
504
		} else {
505
			$default_args = array(
506
				'fields_have_names' => true, // Some gateways like stripe don't need names as the form is tokenized.
507
			);
508
509
			$args = wp_parse_args( $args, apply_filters( 'wpsc_default_credit_card_form_args', $default_args, $this->setting->gateway_name ) );
510
			$default_fields = array(
511
				'card-number-field' => '<p class="form-row form-row-wide">
512
					<label for="' . esc_attr( $this->setting->gateway_name ) . '-card-number">' . __( 'Card Number', 'wp-e-commerce' ) . ' <span class="required">*</span></label>
513
					<input id="' . esc_attr( $this->setting->gateway_name ) . '-card-number" class="input-text wpsc-credit-card-form-card-number" type="text" maxlength="20" autocomplete="off" placeholder="•••• •••• •••• ••••" />
514
				</p>',
515
				'card-expiry-field' => '<p class="form-row form-row-first">
516
					<label for="' . esc_attr( $this->setting->gateway_name ) . '-card-expiry">' . __( 'Expiration Date (MM/YY)', 'wp-e-commerce' ) . ' <span class="required">*</span></label>
517
					<input id="' . esc_attr( $this->setting->gateway_name ) . '-card-expiry" class="input-text wpsc-credit-card-form-card-expiry" type="text" autocomplete="off" placeholder="' . esc_attr__( 'MM / YY', 'wp-e-commerce' ) . '" />
518
				</p>',
519
				'card-cvc-field' => '<p class="form-row form-row-last">
520
					<label for="' . esc_attr( $this->setting->gateway_name ) . '-card-cvc">' . __( 'Card Code', 'wp-e-commerce' ) . ' <span class="required">*</span></label>
521
					<input id="' . esc_attr( $this->setting->gateway_name ) . '-card-cvc" class="input-text wpsc-credit-card-form-card-cvc" type="text" autocomplete="off" placeholder="' . esc_attr__( 'CVC', 'wp-e-commerce' ) . '" />
522
				</p>'
523
			);
524
			$fields = wp_parse_args( $fields, apply_filters( 'wpsc_default_credit_card_form_fields', $default_fields, $this->setting->gateway_name ) );
525
			?>
526
			<fieldset id="<?php echo $this->setting->gateway_name; ?>-cc-form">
527
				<?php do_action( 'wpsc_default_credit_card_form_start', $this->setting->gateway_name ); ?>
528
				<?php
529
					foreach ( $fields as $field ) {
530
						echo $field;
531
					}
532
				?>
533
				<?php do_action( 'wpsc_default_credit_card_form_end', $this->setting->gateway_name ); ?>
534
				<div class="clear"></div>
535
			</fieldset>
536
		<?php
537
		}
538
	}
539
540
541
	/**
542
	 * Check if a gateway supports a given feature.
543
	 *
544
	 * Gateways should override this to declare support (or lack of support) for a feature.
545
	 *
546
	 * @param string $feature string The name of a feature to test support for.
547
	 * @return bool True if the gateway supports the feature, false otherwise.
548
	 * @since 4.0
549
	 */
550
	public function supports( $feature ) {
551
		return apply_filters( 'wpsc_payment_gateway_supports', in_array( $feature, $this->supports ) ? true : false, $feature, $this );
552
	}
553
554
	/**
555
	 * If There are no payment fields show the description if set.
556
	 * Override this in your gateway if you have some.
557
	 */
558
	public function payment_fields() {
559
		if ( $this->supports( 'default_credit_card_form' ) ) {
560
			$this->default_credit_card_form();
561
		}
562
	}
563
564
	/**
565
	 * Return the title of the payment gateway. For this to work, $this->title must
566
	 * be set already.
567
	 *
568
	 * It is recommended that the payment gateway title be properly localized using __()
569
	 *
570
	 * @access public
571
	 * @since 3.9
572
	 * @see __()
573
	 *
574
	 * @return string
575
	 */
576
	public function get_title() {
577
		$title = empty( $this->title ) ? '' : $this->title;
578
		return apply_filters( 'wpsc_payment_gateway_title', $title );
579
	}
580
581
	/**
582
	 * Display the payment gateway settings form as seen in WP eCommerce Settings area.
583
	 * This method must be overridden by subclasses.
584
	 *
585
	 * @abstract
586
	 * @access public
587
	 * @since 3.9
588
	 *
589
	 * @return void
590
	 */
591
	public function setup_form() {
592
		$checkout_field_types = array(
593
			'billing'  => __( 'Billing Fields' , 'wp-e-commerce' ),
594
			'shipping' => __( 'Shipping Fields', 'wp-e-commerce' ),
595
		);
596
597
		$fields = array(
598
			'firstname' => __( 'First Name' , 'wp-e-commerce' ),
599
			'lastname'  => __( 'Last Name'  , 'wp-e-commerce' ),
600
			'address'   => __( 'Address'    , 'wp-e-commerce' ),
601
			'city'      => __( 'City'       , 'wp-e-commerce' ),
602
			'state'     => __( 'State'      , 'wp-e-commerce' ),
603
			'country'   => __( 'Country'    , 'wp-e-commerce' ),
604
			'postcode'  => __( 'Postal Code', 'wp-e-commerce' ),
605
		);
606
607
		$checkout_form = WPSC_Checkout_Form::get();
608
609
		foreach ( $checkout_field_types as $field_type => $title ): ?>
610
			<tr>
611
				<td colspan="2">
612
					<h4><?php echo esc_html( $title ); ?></h4>
613
				</td>
614
			</tr>
615
			<?php foreach ( $fields as $field_name => $field_title ):
616
				$unique_name = $field_type . $field_name;
617
				$selected_id = $this->setting->get( "checkout_field_{$unique_name}", $checkout_form->get_field_id_by_unique_name( $unique_name ) );
618
			?>
619
				<tr>
620
					<td>
621
						<label for="manual-form-<?php echo esc_attr( $unique_name ); ?>"><?php echo esc_html( $field_title ); ?></label>
622
					</td>
623
					<td>
624
						<select name="<?php echo $this->setting->get_field_name( "checkout_field_{$unique_name}" ); ?>" id="manual-form-<?php echo esc_attr( $unique_name ); ?>">
625
							<?php $checkout_form->field_drop_down_options( $selected_id ); ?>
626
						</select>
627
					</td>
628
				</tr>
629
			<?php endforeach;
630
		endforeach;
631
	}
632
633
	/**
634
	 * Process and send payment details to payment gateways
635
	 *
636
	 * @abstract
637
	 * @access public
638
	 * @since 3.9
639
	 *
640
	 * @return void
641
	 */
642
	abstract public function process();
643
644
	/**
645
	 * Returns the URL to the logo of the payment gateway (or any representative image).
646
	 *
647
	 * @access public
648
	 * @since 3.9
649
	 *
650
	 * @return mixed False if there's no image defined.
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use boolean.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
651
	 */
652
	public function get_image_url() {
653
		return false;
654
	}
655
656
	/**
657
	 * Returns the HTML of the logo of the payment gateway.
658
	 *
659
	 * @access public
660
	 * @since 3.9
661
	 *
662
	 * @return mixed False if there's no html defined.
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use boolean.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
663
	 */
664
	public function get_mark_html() {
665
		return false;
666
	}
667
668
	public function set_purchase_log( &$purchase_log ) {
669
		$this->purchase_log = &$purchase_log;
670
		$this->checkout_data = new WPSC_Checkout_Form_Data( $purchase_log->get( 'id' ) );
671
	}
672
673
	public function get_currency_code() {
674
		if ( ! $this->currency_code ) {
675
			$country = new WPSC_Country( get_option( 'currency_type' ) );
676
			$currency = $country->get( 'currency_code' );
677
		} else {
678
			$currency = $this->currency_code;
679
		}
680
681
		return $currency;
682
	}
683
684
	public function get_notification_url() {
685
		return add_query_arg( 'wpsc_action', 'gateway_notification', (get_option( 'siteurl' ) . "/index.php" ) );
686
	}
687
688
	public function get_transaction_results_url() {
689
		return get_option( 'transact_url' );
690
	}
691
692
	public function get_cart_url() {
693
		$te = get_option( 'wpsc_get_active_theme_engine', '1.0' );
694
695
		return '1.0' !== $te ? wpsc_get_cart_url() : get_option( 'shopping_cart_url' );
696
697
		return get_option( 'shopping_cart_url' );
0 ignored issues
show
Unused Code introduced by
return get_option('shopping_cart_url'); does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
698
	}
699
700
	public function get_shopping_cart_payment_url() {
701
702
		$te = get_option( 'wpsc_get_active_theme_engine', '1.0' );
703
704
		return '1.0' !== $te ? wpsc_get_checkout_url( 'shipping-and-billing' ) : get_option( 'shopping_cart_url' );
705
	}
706
707
	public function get_products_page_url() {
708
		return get_option( 'product_list_url' );
709
	}
710
711
	public function go_to_transaction_results() {
712
		//Now to do actions once the payment has been attempted
713
		switch ( $this->purchase_log->get( 'processed' ) ) {
714
			case 3:
715
				// payment worked
716
				do_action( 'wpsc_payment_successful' );
717
				break;
718
			case 1:
719
				// payment declined
720
				do_action( 'wpsc_payment_failed' );
721
				break;
722
			case 2:
723
				// something happened with the payment
724
				do_action( 'wpsc_payment_incomplete' );
725
				break;
726
		}
727
728
		$transaction_url_with_sessionid = add_query_arg( 'sessionid', $this->purchase_log->get( 'sessionid' ), get_option( 'transact_url' ) );
729
		wp_redirect( $transaction_url_with_sessionid );
730
731
		exit();
732
	}
733
734
	/**
735
	 * Payment gateway constructor.
736
	 *
737
	 * Use WPSC_Payment_Gateways::get( $gateway_name ) instead.
738
	 *
739
	 * @access public
740
	 * @return WPSC_Payment_Gateway
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
741
	 */
742
	public function __construct() {
743
744
		$this->setting = new WPSC_Payment_Gateway_Setting( get_class( $this ) );
745
	}
746
747
	/**
748
	 * Gateway initialization function.
749
	 *
750
	 * You should use this function for hooks with actions and filters that are required by the gateway.
751
	 *
752
	 * @access public
753
	 * @since 4.0
754
	 *
755
	 * @return void
756
	 */
757
	public function init() {}
758
759
	/**
760
	 * Process refund
761
	 *
762
	 * If the gateway declares 'refunds' support, this will allow it to refund
763
	 * a passed in amount.
764
	 *
765
	 * @param  int    $order_id
766
	 * @param  float   $amount
767
	 * @param  string  $reason
768
	 * @param  boolean $manual If refund is a manual refund.
769
	 *
770
	 * @since 4.0.0
771
	 * @return bool|WP_Error True or false based on success, or a WP_Error object
772
	 */
773
	public function process_refund( $order_id, $amount = 0.00, $reason = '', $manual = false ) {
774
		return false;
775
	}
776
}
777
778
class WPSC_Payment_Gateway_Setting {
779
	/**
780
	 * Contain settings of the payment gateway
781
	 *
782
	 * @access private
783
	 * @var array
784
	 */
785
	private $settings;
786
787
	/**
788
	 * Contain unsaved settings of the payment gateway. This is useful when the saving of the settings
789
	 * are deferred.
790
	 *
791
	 * @access private
792
	 * @var array
793
	 */
794
	private $unsaved_settings = array();
795
796
	/**
797
	 * Name of the gateway
798
	 *
799
	 * @access private
800
	 * @var string
801
	 */
802
	public $gateway_name = '';
803
804
	/**
805
	 * Name of the option containing all the settings in WP DB
806
	 *
807
	 * @access private
808
	 * @var string
809
	 */
810
	private $option_name = '';
811
812
	/**
813
	 * Save settings when the payment gateway setup form is updated
814
	 *
815
	 * @access public
816
	 * @static
817
	 * @return void
818
	 *
819
	 * @since 3.9
820
	 */
821
	public static function action_update_payment_gateway_settings() {
822
		if ( ! empty( $_POST['wpsc_payment_gateway_settings'] ) )
823
			foreach ( $_POST['wpsc_payment_gateway_settings'] as $gateway_name => $new_settings ) {
824
				$settings = new WPSC_Payment_Gateway_Setting( $gateway_name );
825
				$settings->merge( $new_settings );
826
			}
827
	}
828
829
	/**
830
	 * Constructor
831
	 *
832
	 * @access public
833
	 *
834
	 * @param string $gateway_name Name of the gateway
0 ignored issues
show
Documentation introduced by
There is no parameter named $gateway_name. Did you maybe mean $gateway_name_or_class?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
835
	 * @return WPSC_Payment_Gateway
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
836
	 */
837
	public function __construct( $gateway_name_or_class ) {
838
		$name = str_replace( 'wpsc_payment_gateway_', '', strtolower( $gateway_name_or_class ) );
839
		$name = str_replace( array( ' ', '-' ), '_', $name );
840
		$this->gateway_name = $name;
841
		$this->option_name = 'wpsc_payment_gateway_' . $this->gateway_name;
842
	}
843
844
	/**
845
	 * Lazy load the settings from the DB when necessary
846
	 *
847
	 * @access private
848
	 * @return void
849
	 */
850
	private function lazy_load() {
851
		if ( is_null( $this->settings ) ) {
852
			$this->settings = get_option( $this->option_name, array() );
853
		}
854
	}
855
856
	/**
857
	 * Get the value of a setting
858
	 *
859
	 * @param string $setting
860
	 * @return mixed
861
	 * @since 3.9
862
	 */
863
	public function get( $setting, $default = false ) {
864
		$this->lazy_load();
865
		return isset( $this->settings[ $setting ] ) ? $this->settings[ $setting ] : $default;
866
	}
867
868
	/**
869
	 * Set the value of a setting
870
	 *
871
	 * @param string $setting
872
	 * @param mixed $value
873
	 * @param bool $defer True if you want to defer saving the settings array to the database
874
	 * @return void
875
	 * @since 3.9
876
	 */
877
	public function set( $setting, $value, $defer = false ) {
878
		$this->lazy_load();
879
		$this->unsaved_settings[ $setting ] = $value;
880
		if ( ! $defer ) {
881
			$this->save();
882
		}
883
	}
884
885
	/**
886
	 * Overwrite current settings with an array of settings
887
	 *
888
	 * @access public
889
	 * @param string $settings Settings that you want to overwrite upon current settings
890
	 * @param string $defer Optional. Defaults to false. True if you want to defer
0 ignored issues
show
Documentation introduced by
Should the type for parameter $defer not be false|string?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
891
	 *                      saving the settings array to the database.
892
	 * @return void
893
	 * @since 3.9
894
	 */
895
	public function merge( $settings, $defer = false ) {
896
		$this->lazy_load();
897
		$this->unsaved_settings = array_merge( $this->unsaved_settings, $settings );
898
		if ( ! $defer ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $defer of type false|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

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

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
899
			$this->save();
900
		}
901
	}
902
903
	/**
904
	 * Returns the field name of the setting on payment gateway setup form
905
	 *
906
	 * @access public
907
	 * @param string $setting Setting names
908
	 * @return string
909
	 * @since 3.9
910
	 */
911
	public function get_field_name( $setting ) {
912
		return "wpsc_payment_gateway_settings[{$this->gateway_name}][{$setting}]";
913
	}
914
915
	/**
916
	 * Save the settings into the database
917
	 *
918
	 * @return void
919
	 * @since 3.9
920
	 */
921
	public function save() {
922
		$this->settings = array_merge( $this->settings, $this->unsaved_settings );
923
		$this->unsaved_settings = array();
924
		update_option( $this->option_name, $this->settings );
925
	}
926
}
927
928
add_action( 'wpsc_loaded', array( 'WPSC_Payment_Gateways', 'init' ) );
929