Completed
Push — master ( bfd238...389d1b )
by Justin
05:35
created

WPSC_Payment_Gateways::flush_cache()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 3
rs 10
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
	 * @param string $gateway Name of the payment gateway you want to get
54
	 * @return object
55
	 * @since 3.9
56
	 */
57
	public static function &get( $gateway, $meta = false ) {
58
59
		$errors = new WP_Error();
60
61
		if ( empty( $gateway ) ) {
62
			$errors->add( 'empty_gateway', __( 'You cannot pass an empty string as a gateway object.', 'wp-e-commerce' ) );
63
			return $errors;
64
		}
65
66
		if ( empty( self::$instances[ $gateway ] ) ) {
67
68
			// If no meta is found, it is likely that a legacy or unsupported gateway is being used, and we should exit early.
69
			if ( ! array_key_exists( $gateway, $meta ) ) {
70
				$errors->add( 'empty_meta', __( 'The gateway used is out of date. We recommend no longer using this gateway.', 'wp-e-commerce' ) );
71
				return $errors;
72
			}
73
74
			if ( ! $meta ) {
75
				$meta = self::$gateways[ $gateway ];
76
			}
77
78
			if ( ! file_exists( $meta['path'] ) ) {
79
				WPSC_Payment_Gateways::flush_cache();
80
			}
81
82
			require_once( $meta['path'] );
83
84
			$class_name = $meta['class'];
85
86
			$options = array(
87
				'http_client' => new WPSC_Payment_Gateway_HTTP(),
88
			);
89
90
			if ( ! class_exists( $class_name ) ) {
91
				$error = new WP_Error( 'wpsc_invalid_payment_gateway', sprintf( __( 'Invalid payment gateway: Class %s does not exist.', 'wp-e-commerce' ), $class_name ) );
92
				return $error;
93
			}
94
95
			self::$instances[ $gateway ] = new $class_name( $options );
96
		}
97
98
		return self::$instances[ $gateway ];
99
	}
100
101
	public static function init() {
102
103
		add_action( 'wpsc_submit_gateway_options', array( 'WPSC_Payment_Gateway_Setting', 'action_update_payment_gateway_settings' ) );
104
105
		if ( ! defined( 'WPSC_PAYMENT_GATEWAY_DEBUG' ) || WPSC_PAYMENT_GATEWAY_DEBUG == false ) {
106
			add_action( 'init', array( 'WPSC_Payment_Gateways', 'action_save_payment_gateway_cache' ), 99 );
107
		 } else {
108
			WPSC_Payment_Gateways::flush_cache();
109
		 }
110
111
		WPSC_Payment_Gateways::register_dir( WPSC_MERCHANT_V3_PATH . '/gateways' );
112
113
		// Call the Active Gateways init function
114
		add_action( 'wpsc_ready', array( __CLASS__, 'initialize_gateways' ) );
115
116
		if ( isset( $_REQUEST['payment_gateway'] ) && isset( $_REQUEST['payment_gateway_callback'] ) ) {
117
			add_action( 'init', array( 'WPSC_Payment_Gateways', 'action_process_callbacks' ) );
118
		}
119
	}
120
121
	public static function action_process_callbacks() {
122
		$gateway = self::get( $_REQUEST['payment_gateway'] );
123
		$function_name = "callback_{$_REQUEST['payment_gateway_callback']}";
124
		$callback = array( $gateway, $function_name );
125
126
		if ( is_callable( $callback ) ) {
127
			$gateway->$function_name();
128
		}
129
	}
130
131
	/**
132
	 * Check to see whether a gateway is registered using this new API
133
	 *
134
	 * @access public
135
	 * @since 3.9
136
	 *
137
	 * @param string $gateway Gateway name (derived from the filename without .php extension)
138
	 * @return bool True if it's already registered.
139
	 */
140
	public static function is_registered( $gateway ) {
141
		return ! empty( self::$gateways[ $gateway ] );
142
	}
143
144
	/**
145
	 * Automatically scan a directory for payment gateways and load the classes.
146
	 *
147
	 * The structure of this directory should follow the same rules of the wp-content/plugins
148
	 * structure.
149
	 *
150
	 * All of the files inside the directory will be assumed as payment gateway modules.
151
	 * Files with the same name as those sub-folders will be included as payment
152
	 * gateway modules.
153
	 *
154
	 * For example, if we have the following directory structure:
155
	 * payment-gateways/
156
	 * |-- test-gateway-1.php
157
	 * |-- test-gateway-2.php
158
	 * |-- some-folder/
159
	 *     |-- class.php
160
	 *     |-- functions.php
161
	 *
162
	 * The following files will be loaded as payment gateway modules: test-gateway-1.php,
163
	 * test-gateway-2.php
164
	 * See WPSC_Payment_Gateways::register_file() for file and class naming convention
165
	 *
166
	 * @access public
167
	 * @since 3.9
168
	 * @uses WPSC_Payment_Gateways::register_file()
169
	 *
170
	 * @param string $dir Path to the directory
171
	 * @param string $main_file File name of the class to load
172
	 * @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 object|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...
173
	 * the directory.
174
	 * Otherwise return a WP_Error object.
175
	 */
176
	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...
177
		$dir = trailingslashit( $dir );
178
		$main_file = basename( $dir ) . '.php';
179
180
		// scan files in dir
181
		$files = scandir( $dir );
182
183
		if ( in_array( $main_file, $files ) ) {
184
			return self::register_file( $dir . $main_file );
185
		}
186
187
		foreach ( $files as $file ) {
188
			$path = $dir . $file;
189
190
			if ( pathinfo( $path, PATHINFO_EXTENSION ) != 'php' || in_array( $file, array( '.', '..' ) ) || is_dir( $path ) ) {
191
				continue;
192
			}
193
194
			$return = self::register_file( $path );
195
196
			if ( is_wp_error( $return ) ) {
197
				//We should log this
198
			}
199
		}
200
	}
201
202
	/**
203
	 * Register a file as a payment gateway module.
204
	 *
205
	 * The payment gateway inside the file must be defined as a subclass of WPSC_Payment_Gateway.
206
	 *
207
	 * The file name should be lowercase, using hyphens or underscores between words
208
	 * instead of spaces. The class name must have "WPSC_Payment_Gateway_" as the
209
	 * prefix, followed by the file name, in which words are capitalized and connected
210
	 * by underscore.
211
	 *
212
	 * For example, if the file name is "paypal-pro.php", then the class name inside
213
	 * the file must be WPSC_Payment_Gateway_Paypal_Pro.
214
	 *
215
	 * @access public
216
	 * @since 3.9
217
	 * @see WPSC_Payment_Gateways::register_dir()
218
	 *
219
	 * @param string $file Absolute path to the file containing the payment gateway
220
	 * class
221
	 * @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 object|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...
222
	 * a valid class. Otherwise, a WP_Error object is returned.
223
	 */
224
	public static function register_file( $file ) {
225
226
		if ( empty( self::$payment_gateway_cache ) ) {
227
			self::$payment_gateway_cache = get_option( 'wpsc_payment_gateway_cache', array() );
228
		}
229
230
		$filename = basename( $file, '.php' );
231
232
		// payment gateway already exists in cache
233
		if ( isset( self::$payment_gateway_cache[ $filename ] ) ) {
234
			self::$gateways[ $filename ] = self::$payment_gateway_cache[ $filename ];
235
		}
236
237
		// if payment gateway is not in cache, load metadata
238
		$classname = ucwords( str_replace( '-', ' ', $filename ) );
239
		$classname = 'WPSC_Payment_Gateway_' . str_replace( ' ', '_', $classname );
240
241
		if ( file_exists( $file ) ) {
242
			require_once $file;
243
		}
244
245
		if ( is_callable( array( $classname, 'load' ) ) && ! call_user_func( array( $classname, 'load' ) ) ) {
246
247
			self::unregister_file( $filename );
248
249
			$error = new WP_Error( 'wpsc-payment', __( 'Error', 'wp-e-commerce' ) );
250
251
			return $error;
252
		}
253
254
		$meta = array(
255
			'class'        => $classname,
256
			'path'         => $file,
257
			'internalname' => $filename, // compat with older API
258
		);
259
260
		$gateway = self::get( $filename, $meta );
261
262
		if ( is_wp_error( $gateway ) ) {
263
			return $gateway;
264
		}
265
266
		$meta['name']  = $gateway->get_title();
267
		$meta['image'] = $gateway->get_image_url();
268
		$meta['mark']  = $gateway->get_mark_html();
269
270
		self::$gateways[ $filename ] = $meta;
271
272
		return true;
273
	}
274
275
	public static function unregister_file( $filename ) {
276
		if ( isset( self::$gateways[ $filename ] ) ) {
277
			unset( self::$gateways[ $filename ] );
278
		}
279
	}
280
281
	/**
282
	 * Updates the payment gateway cache when it's changed.
283
	 *
284
	 * This function is hooked into WordPress' wp_loaded action
285
	 *
286
	 * @access public
287
	 * @static
288
	 * @since 3.9
289
	 *
290
	 * @return void
291
	 */
292
	public static function action_save_payment_gateway_cache() {
293
		if ( self::$payment_gateway_cache != self::$gateways ) {
294
			update_option( 'wpsc_payment_gateway_cache', self::$gateways );
295
		}
296
	}
297
298
	/**
299
	 * Flush the payment gateways cache.
300
	 *
301
	 * @access public
302
	 * @static
303
	 * @since 3.9
304
	 * @return void
305
	 */
306
	public static function flush_cache() {
307
		delete_option( 'wpsc_payment_gateway_cache' );
308
	}
309
310
	/**
311
	 * Gets metadata of a certain payment gateway. This is better than calling WPSC_Payment_Gateways->get( $gateway_name )->get_title()
312
	 * and the likes of it, since it doesn't require the gateway itself to be loaded.
313
	 *
314
	 * @access public
315
	 * @static
316
	 * @since 3.9
317
	 *
318
	 * @param string $gateway
319
	 * @return mixed Array containing the metadata. If the gateway is not registered,
320
	 *               returns false.
321
	 */
322
	public static function get_meta( $gateway ) {
323
		return isset( self::$gateways[ $gateway ] ) ? self::$gateways[ $gateway ] : false;
324
	}
325
326
	/**
327
	 *
328
	 * Return an array containing registered gateway names.
329
	 *
330
	 * @access public
331
	 * @since 3.9
332
	 *
333
	 * @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...
334
	 */
335
	public static function get_gateways() {
336
		return array_keys( self::$gateways );
337
	}
338
339
	/**
340
	 *
341
	 * Return an array containing active gateway names.
342
	 *
343
	 * @access public
344
	 * @since 3.9
345
	 *
346
	 * @return array
347
	 */
348
	public static function get_active_gateways() {
349
		if ( empty( self::$active_gateways ) ) {
350
			$selected_gateways     = get_option( 'custom_gateway_options', array() );
351
			$registered_gateways   = self::get_gateways();
352
			self::$active_gateways = array_intersect( $selected_gateways, $registered_gateways );
353
		}
354
355
		return apply_filters( 'wpsc_get_active_gateways', array_values( self::$active_gateways ) );
356
	}
357
358
	/**
359
	 * Initialize the Active Gateways
360
	 *
361
	 * @access public
362
	 * @since 4.0
363
	 *
364
	 * @return void
365
	 */
366
	public static function initialize_gateways() {
367
		$active_gateways = self::get_active_gateways();
368
369
		foreach( $active_gateways as $gateway_id ) {
370
			$gateway = self::get( $gateway_id );
371
372
			if ( ! is_wp_error( $gateway ) ) {
373
				$gateway->init();
374
			}
375
		}
376
	}
377
378
	/**
379
	 * Returns all known currencies without fractions.
380
	 *
381
	 * Our internal list has not been updated in some time, so returning a filterable list
382
	 * for ever-changing economies and currencies should prove helpful.
383
	 *
384
	 * @link http://www.currency-iso.org/dam/downloads/table_a1.xml
385
	 *
386
	 * @since  4.0
387
	 *
388
	 * @return array Currency ISO codes that do not use fractions.
389
	 */
390
	public static function currencies_without_fractions() {
391
392
		$currencies = array(
393
			'JPY',
394
			'HUF',
395
			'VND',
396
			'BYR',
397
			'XOF',
398
			'BIF',
399
			'XAF',
400
			'CLP',
401
			'KMF',
402
			'DJF',
403
			'XPF',
404
			'GNF',
405
			'ISK',
406
			'GNF',
407
			'KRW',
408
			'PYG',
409
			'RWF',
410
			'UGX',
411
			'UYI',
412
			'VUV',
413
		);
414
415
		return (array) apply_filters( 'wpsc_currencies_without_fractions', $currencies );
416
	}
417
418
	/**
419
	 * Gets an array of countries in the EU.
420
	 *
421
	 * MC (monaco) and IM (Isle of Man, part of UK) also use VAT.
422
	 *
423
	 * @since  4.0
424
	 * @param  $type Type of countries to retrieve. Blank for EU member countries. eu_vat for EU VAT countries.
425
	 * @return string[]
426
	 */
427
	public function get_european_union_countries( $type = '' ) {
428
		$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' );
429
430
		if ( 'eu_vat' === $type ) {
431
			$countries[] = 'MC';
432
			$countries[] = 'IM';
433
		}
434
435
		return $countries;
436
	}
437
438
	/**
439
	 * No instantiation for this class
440
	 *
441
	 * @access private
442
	 * @since 3.9
443
	 *
444
	 */
445
	private function __construct() {}
446
}
447
448
abstract class WPSC_Payment_Gateway {
449
450
	/**
451
	 * Object that allows manipulation of payment gateway settings in a consistent
452
	 * manner
453
	 *
454
	 * @access public
455
	 * @var WPSC_Payment_Gateway_Setting
456
	 */
457
	public $setting;
458
459
	public $purchase_log;
460
461
	public $checkout_data;
462
463
	public $currency_code;
464
465
	public $title;
466
467
	/**
468
	 * Supported features such as 'default_credit_card_form', 'refunds'.
469
	 * @var array
470
	 */
471
	public $supports = array();
472
473
	/**
474
	 * Display default credit card form.
475
	 *
476
	 * @param  array $args
477
	 * @param  array $fields
478
	 * @since
479
	 */
480
	public function default_credit_card_form( $args = array(), $fields = array() ) {
481
482
		if ( $this->supports( 'tev1' ) && '1.0' == get_option( 'wpsc_get_active_theme_engine' ) ) {
483
			// Show 2.0 gateway API table-based code
484
			?>
485
				<table class="wpsc_checkout_table <?php echo wpsc_gateway_form_field_style(); ?>">
486
					<tr>
487
						<td><?php _e( 'Card Number', 'wp-e-commerce' ); ?></td>
488
						<td>
489
							<input type='text' id='card_number' value='' autocomplete="off" />
490
						</td>
491
					</tr>
492
					<tr>
493
						<td><?php _e( 'Expiration Date', 'wp-e-commerce' ); ?></td>
494
						<td>
495
							<input type='text' id='card_expiry_month' value='' autocomplete="off" maxlength='2' size='3' placeholder="<?php esc_attr_e( 'MM', 'wp-e-commerce' ); ?>" />&nbsp;
496
							<input type='text' id='card_expiry_year' value='' autocomplete="off" maxlength='2' size='3' placeholder="<?php esc_attr_e( 'YY', 'wp-e-commerce' ); ?>" />
497
						</td>
498
					</tr>
499
					<tr>
500
						<td><?php _e( 'Card Code', 'wp-e-commerce' ); ?></td>
501
						<td>
502
							<input type='text' id='card_code' value='' autocomplete="off" size='5' maxlength='4' placeholder="<?php esc_attr_e( 'CVC', 'wp-e-commerce' ); ?>" />
503
						</td>
504
					</tr>
505
				</table>
506
			<?php
507
		} else {
508
			$default_args = array(
509
				'fields_have_names' => true, // Some gateways like stripe don't need names as the form is tokenized.
510
			);
511
512
			$args = wp_parse_args( $args, apply_filters( 'wpsc_default_credit_card_form_args', $default_args, $this->setting->gateway_name ) );
513
			$default_fields = array(
514
				'card-number-field' => '<p class="form-row form-row-wide">
515
					<label for="' . esc_attr( $this->setting->gateway_name ) . '-card-number">' . __( 'Card Number', 'wp-e-commerce' ) . ' <span class="required">*</span></label>
516
					<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="•••• •••• •••• ••••" />
517
				</p>',
518
				'card-expiry-field' => '<p class="form-row form-row-first">
519
					<label for="' . esc_attr( $this->setting->gateway_name ) . '-card-expiry">' . __( 'Expiration Date (MM/YY)', 'wp-e-commerce' ) . ' <span class="required">*</span></label>
520
					<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' ) . '" />
521
				</p>',
522
				'card-cvc-field' => '<p class="form-row form-row-last">
523
					<label for="' . esc_attr( $this->setting->gateway_name ) . '-card-cvc">' . __( 'Card Code', 'wp-e-commerce' ) . ' <span class="required">*</span></label>
524
					<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' ) . '" />
525
				</p>'
526
			);
527
			$fields = wp_parse_args( $fields, apply_filters( 'wpsc_default_credit_card_form_fields', $default_fields, $this->setting->gateway_name ) );
528
			?>
529
			<fieldset id="<?php echo $this->setting->gateway_name; ?>-cc-form">
530
				<?php do_action( 'wpsc_default_credit_card_form_start', $this->setting->gateway_name ); ?>
531
				<?php
532
					foreach ( $fields as $field ) {
533
						echo $field;
534
					}
535
				?>
536
				<?php do_action( 'wpsc_default_credit_card_form_end', $this->setting->gateway_name ); ?>
537
				<div class="clear"></div>
538
			</fieldset>
539
		<?php
540
		}
541
	}
542
543
544
	/**
545
	 * Check if a gateway supports a given feature.
546
	 *
547
	 * Gateways should override this to declare support (or lack of support) for a feature.
548
	 *
549
	 * @param string $feature string The name of a feature to test support for.
550
	 * @return bool True if the gateway supports the feature, false otherwise.
551
	 * @since 4.0
552
	 */
553
	public function supports( $feature ) {
554
		return apply_filters( 'wpsc_payment_gateway_supports', in_array( $feature, $this->supports ) ? true : false, $feature, $this );
555
	}
556
557
	/**
558
	 * If There are no payment fields show the description if set.
559
	 * Override this in your gateway if you have some.
560
	 */
561
	public function payment_fields() {
562
		if ( $this->supports( 'default_credit_card_form' ) ) {
563
			$this->default_credit_card_form();
564
		}
565
	}
566
567
	/**
568
	 * Return the title of the payment gateway. For this to work, $this->title must
569
	 * be set already.
570
	 *
571
	 * It is recommended that the payment gateway title be properly localized using __()
572
	 *
573
	 * @access public
574
	 * @since 3.9
575
	 * @see __()
576
	 *
577
	 * @return string
578
	 */
579
	public function get_title() {
580
		$title = empty( $this->title ) ? '' : $this->title;
581
		return apply_filters( 'wpsc_payment_gateway_title', $title );
582
	}
583
584
	/**
585
	 * Display the payment gateway settings form as seen in WP eCommerce Settings area.
586
	 * This method must be overridden by subclasses.
587
	 *
588
	 * @abstract
589
	 * @access public
590
	 * @since 3.9
591
	 *
592
	 * @return void
593
	 */
594
	public function setup_form() {
595
		$checkout_field_types = array(
596
			'billing'  => __( 'Billing Fields' , 'wp-e-commerce' ),
597
			'shipping' => __( 'Shipping Fields', 'wp-e-commerce' ),
598
		);
599
600
		$fields = array(
601
			'firstname' => __( 'First Name' , 'wp-e-commerce' ),
602
			'lastname'  => __( 'Last Name'  , 'wp-e-commerce' ),
603
			'address'   => __( 'Address'    , 'wp-e-commerce' ),
604
			'city'      => __( 'City'       , 'wp-e-commerce' ),
605
			'state'     => __( 'State'      , 'wp-e-commerce' ),
606
			'country'   => __( 'Country'    , 'wp-e-commerce' ),
607
			'postcode'  => __( 'Postal Code', 'wp-e-commerce' ),
608
		);
609
610
		$checkout_form = WPSC_Checkout_Form::get();
611
612
		foreach ( $checkout_field_types as $field_type => $title ): ?>
613
			<tr>
614
				<td colspan="2">
615
					<h4><?php echo esc_html( $title ); ?></h4>
616
				</td>
617
			</tr>
618
			<?php foreach ( $fields as $field_name => $field_title ):
619
				$unique_name = $field_type . $field_name;
620
				$selected_id = $this->setting->get( "checkout_field_{$unique_name}", $checkout_form->get_field_id_by_unique_name( $unique_name ) );
621
			?>
622
				<tr>
623
					<td>
624
						<label for="manual-form-<?php echo esc_attr( $unique_name ); ?>"><?php echo esc_html( $field_title ); ?></label>
625
					</td>
626
					<td>
627
						<select name="<?php echo $this->setting->get_field_name( "checkout_field_{$unique_name}" ); ?>" id="manual-form-<?php echo esc_attr( $unique_name ); ?>">
628
							<?php $checkout_form->field_drop_down_options( $selected_id ); ?>
629
						</select>
630
					</td>
631
				</tr>
632
			<?php endforeach;
633
		endforeach;
634
	}
635
636
	/**
637
	 * Process and send payment details to payment gateways
638
	 *
639
	 * @abstract
640
	 * @access public
641
	 * @since 3.9
642
	 *
643
	 * @return void
644
	 */
645
	abstract public function process();
646
647
	/**
648
	 * Returns the URL to the logo of the payment gateway (or any representative image).
649
	 *
650
	 * @access public
651
	 * @since 3.9
652
	 *
653
	 * @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...
654
	 */
655
	public function get_image_url() {
656
		return false;
657
	}
658
659
	/**
660
	 * Returns the HTML of the logo of the payment gateway.
661
	 *
662
	 * @access public
663
	 * @since 3.9
664
	 *
665
	 * @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...
666
	 */
667
	public function get_mark_html() {
668
		return false;
669
	}
670
671
	public function set_purchase_log( &$purchase_log ) {
672
		$this->purchase_log = &$purchase_log;
673
		$this->checkout_data = new WPSC_Checkout_Form_Data( $purchase_log->get( 'id' ) );
674
	}
675
676
	public function get_currency_code() {
677
		if ( ! $this->currency_code ) {
678
			$country = new WPSC_Country( get_option( 'currency_type' ) );
679
			$currency = $country->get( 'currency_code' );
680
		} else {
681
			$currency = $this->currency_code;
682
		}
683
684
		return $currency;
685
	}
686
687
	public function get_notification_url() {
688
		return add_query_arg( 'wpsc_action', 'gateway_notification', (get_option( 'siteurl' ) . "/index.php" ) );
689
	}
690
691
	public function get_transaction_results_url() {
692
		return get_option( 'transact_url' );
693
	}
694
695
	public function get_shopping_cart_url() {
696
		return get_option( 'shopping_cart_url' );
697
	}
698
699
	public function get_shopping_cart_payment_url() {
700
701
		$te = get_option( 'wpsc_get_active_theme_engine', '1.0' );
702
703
		return '1.0' !== $te ? wpsc_get_checkout_url( 'shipping-and-billing' ) : get_option( 'shopping_cart_url' );
704
	}
705
706
	public function get_products_page_url() {
707
		return get_option( 'product_list_url' );
708
	}
709
710
	public function go_to_transaction_results() {
711
		//Now to do actions once the payment has been attempted
712
		switch ( $this->purchase_log->get( 'processed' ) ) {
713
			case 3:
714
				// payment worked
715
				do_action( 'wpsc_payment_successful' );
716
				break;
717
			case 1:
718
				// payment declined
719
				do_action( 'wpsc_payment_failed' );
720
				break;
721
			case 2:
722
				// something happened with the payment
723
				do_action( 'wpsc_payment_incomplete' );
724
				break;
725
		}
726
727
		$transaction_url_with_sessionid = add_query_arg( 'sessionid', $this->purchase_log->get( 'sessionid' ), get_option( 'transact_url' ) );
728
		wp_redirect( $transaction_url_with_sessionid );
729
730
		exit();
731
	}
732
733
	/**
734
	 * Payment gateway constructor.
735
	 *
736
	 * Use WPSC_Payment_Gateways::get( $gateway_name ) instead.
737
	 *
738
	 * @access public
739
	 * @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...
740
	 */
741
	public function __construct() {
742
743
		$this->setting = new WPSC_Payment_Gateway_Setting( get_class( $this ) );
744
	}
745
746
	/**
747
	 * Gateway initialization function.
748
	 *
749
	 * You should use this function for hooks with actions and filters that are required by the gateway.
750
	 *
751
	 * @access public
752
	 * @since 4.0
753
	 *
754
	 * @return void
755
	 */
756
	public function init() {}
757
758
	/**
759
	 * Process refund
760
	 *
761
	 * If the gateway declares 'refunds' support, this will allow it to refund
762
	 * a passed in amount.
763
	 *
764
	 * @param  int    $order_id
765
	 * @param  float   $amount
766
	 * @param  string  $reason
767
	 * @param  boolean $manual If refund is a manual refund.
768
	 *
769
	 * @since 4.0.0
770
	 * @return bool|WP_Error True or false based on success, or a WP_Error object
771
	 */
772
	public function process_refund( $order_id, $amount = 0.00, $reason = '', $manual = false ) {
773
		return false;
774
	}
775
}
776
777
class WPSC_Payment_Gateway_Setting {
778
	/**
779
	 * Contain settings of the payment gateway
780
	 *
781
	 * @access private
782
	 * @var array
783
	 */
784
	private $settings;
785
786
	/**
787
	 * Contain unsaved settings of the payment gateway. This is useful when the saving of the settings
788
	 * are deferred.
789
	 *
790
	 * @access private
791
	 * @var array
792
	 */
793
	private $unsaved_settings = array();
794
795
	/**
796
	 * Name of the gateway
797
	 *
798
	 * @access private
799
	 * @var string
800
	 */
801
	public $gateway_name = '';
802
803
	/**
804
	 * Name of the option containing all the settings in WP DB
805
	 *
806
	 * @access private
807
	 * @var string
808
	 */
809
	private $option_name = '';
810
811
	/**
812
	 * Save settings when the payment gateway setup form is updated
813
	 *
814
	 * @access public
815
	 * @static
816
	 * @return void
817
	 *
818
	 * @since 3.9
819
	 */
820
	public static function action_update_payment_gateway_settings() {
821
		if ( ! empty( $_POST['wpsc_payment_gateway_settings'] ) )
822
			foreach ( $_POST['wpsc_payment_gateway_settings'] as $gateway_name => $new_settings ) {
823
				$settings = new WPSC_Payment_Gateway_Setting( $gateway_name );
824
				$settings->merge( $new_settings );
825
			}
826
	}
827
828
	/**
829
	 * Constructor
830
	 *
831
	 * @access public
832
	 *
833
	 * @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...
834
	 * @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...
835
	 */
836
	public function __construct( $gateway_name_or_class ) {
837
		$name = str_replace( 'wpsc_payment_gateway_', '', strtolower( $gateway_name_or_class ) );
838
		$name = str_replace( array( ' ', '-' ), '_', $name );
839
		$this->gateway_name = $name;
840
		$this->option_name = 'wpsc_payment_gateway_' . $this->gateway_name;
841
	}
842
843
	/**
844
	 * Lazy load the settings from the DB when necessary
845
	 *
846
	 * @access private
847
	 * @return void
848
	 */
849
	private function lazy_load() {
850
		if ( is_null( $this->settings ) ) {
851
			$this->settings = get_option( $this->option_name, array() );
852
		}
853
	}
854
855
	/**
856
	 * Get the value of a setting
857
	 *
858
	 * @param string $setting
859
	 * @return mixed
860
	 * @since 3.9
861
	 */
862
	public function get( $setting, $default = false ) {
863
		$this->lazy_load();
864
		return isset( $this->settings[ $setting ] ) ? $this->settings[ $setting ] : $default;
865
	}
866
867
	/**
868
	 * Set the value of a setting
869
	 *
870
	 * @param string $setting
871
	 * @param mixed $value
872
	 * @param bool $defer True if you want to defer saving the settings array to the database
873
	 * @return void
874
	 * @since 3.9
875
	 */
876
	public function set( $setting, $value, $defer = false ) {
877
		$this->lazy_load();
878
		$this->unsaved_settings[ $setting ] = $value;
879
		if ( ! $defer ) {
880
			$this->save();
881
		}
882
	}
883
884
	/**
885
	 * Overwrite current settings with an array of settings
886
	 *
887
	 * @access public
888
	 * @param string $settings Settings that you want to overwrite upon current settings
889
	 * @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...
890
	 *                      saving the settings array to the database.
891
	 * @return void
892
	 * @since 3.9
893
	 */
894
	public function merge( $settings, $defer = false ) {
895
		$this->lazy_load();
896
		$this->unsaved_settings = array_merge( $this->unsaved_settings, $settings );
897
		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...
898
			$this->save();
899
		}
900
	}
901
902
	/**
903
	 * Returns the field name of the setting on payment gateway setup form
904
	 *
905
	 * @access public
906
	 * @param string $setting Setting names
907
	 * @return string
908
	 * @since 3.9
909
	 */
910
	public function get_field_name( $setting ) {
911
		return "wpsc_payment_gateway_settings[{$this->gateway_name}][{$setting}]";
912
	}
913
914
	/**
915
	 * Save the settings into the database
916
	 *
917
	 * @return void
918
	 * @since 3.9
919
	 */
920
	public function save() {
921
		$this->settings = array_merge( $this->settings, $this->unsaved_settings );
922
		$this->unsaved_settings = array();
923
		update_option( $this->option_name, $this->settings );
924
	}
925
}
926
927
add_action( 'wpsc_loaded', array( 'WPSC_Payment_Gateways', 'init' ) );
928