Completed
Push — master ( 60eba6...3f5bac )
by Roy
02:10
created

WC_Gateway_Stripe_Sepa::payment_fields()   F

Complexity

Conditions 13
Paths 288

Size

Total Lines 45
Code Lines 28

Duplication

Lines 7
Ratio 15.56 %

Importance

Changes 0
Metric Value
cc 13
eloc 28
nc 288
nop 0
dl 7
loc 45
rs 3.7737
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
if ( ! defined( 'ABSPATH' ) ) {
3
	exit;
4
}
5
6
/**
7
 * Class that handles SEPA payment method.
8
 *
9
 * @extends WC_Gateway_Stripe
10
 *
11
 * @since 4.0.0
12
 */
13
class WC_Gateway_Stripe_Sepa extends WC_Stripe_Payment_Gateway {
14
	/**
15
	 * The delay between retries.
16
	 *
17
	 * @var int
18
	 */
19
	public $retry_interval;
20
21
	/**
22
	 * Notices (array)
23
	 * @var array
24
	 */
25
	public $notices = array();
26
27
	/**
28
	 * Is test mode active?
29
	 *
30
	 * @var bool
31
	 */
32
	public $testmode;
33
34
	/**
35
	 * Alternate credit card statement name
36
	 *
37
	 * @var bool
38
	 */
39
	public $statement_descriptor;
40
41
	/**
42
	 * API access secret key
43
	 *
44
	 * @var string
45
	 */
46
	public $secret_key;
47
48
	/**
49
	 * Api access publishable key
50
	 *
51
	 * @var string
52
	 */
53
	public $publishable_key;
54
55
	/**
56
	 * Should we store the users credit cards?
57
	 *
58
	 * @var bool
59
	 */
60
	public $saved_cards;
61
62
	/**
63
	 * Pre Orders Object
64
	 *
65
	 * @var object
66
	 */
67
	public $pre_orders;
68
69
	/**
70
	 * Constructor
71
	 */
72
	public function __construct() {
73
		$this->retry_interval       = 1;
74
		$this->id                   = 'stripe_sepa';
75
		$this->method_title         = __( 'Stripe SEPA Direct Debit', 'woocommerce-gateway-stripe' );
76
		/* translators: link */
77
		$this->method_description   = sprintf( __( 'All other general Stripe settings can be adjusted <a href="%s">here</a>.', 'woocommerce-gateway-stripe' ), admin_url( 'admin.php?page=wc-settings&tab=checkout&section=stripe' ) );
78
		$this->supports             = array(
79
			'products',
80
			'refunds',
81
			'tokenization',
82
			'add_payment_method',
83
			'subscriptions',
84
			'subscription_cancellation',
85
			'subscription_suspension',
86
			'subscription_reactivation',
87
			'subscription_amount_changes',
88
			'subscription_date_changes',
89
			'subscription_payment_method_change',
90
			'subscription_payment_method_change_customer',
91
			'subscription_payment_method_change_admin',
92
			'multiple_subscriptions',
93
			'pre-orders',
94
		);
95
96
		// Load the form fields.
97
		$this->init_form_fields();
98
99
		// Load the settings.
100
		$this->init_settings();
101
102
		$main_settings              = get_option( 'woocommerce_stripe_settings' );
103
		$this->title                = $this->get_option( 'title' );
104
		$this->description          = $this->get_option( 'description' );
105
		$this->enabled              = $this->get_option( 'enabled' );
106
		$this->testmode             = ( ! empty( $main_settings['testmode'] ) && 'yes' === $main_settings['testmode'] ) ? true : false;
107
		$this->saved_cards          = ( ! empty( $main_settings['saved_cards'] ) && 'yes' === $main_settings['saved_cards'] ) ? true : false;
108
		$this->publishable_key      = ! empty( $main_settings['publishable_key'] ) ? $main_settings['publishable_key'] : '';
109
		$this->secret_key           = ! empty( $main_settings['secret_key'] ) ? $main_settings['secret_key'] : '';
110
		$this->statement_descriptor = ! empty( $main_settings['statement_descriptor'] ) ? $main_settings['statement_descriptor'] : '';
111
112
		if ( $this->testmode ) {
113
			$this->publishable_key = ! empty( $main_settings['test_publishable_key'] ) ? $main_settings['test_publishable_key'] : '';
114
			$this->secret_key      = ! empty( $main_settings['test_secret_key'] ) ? $main_settings['test_secret_key'] : '';
115
		}
116
117
		add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );
118
		add_action( 'wp_enqueue_scripts', array( $this, 'payment_scripts' ) );
119
120 View Code Duplication
		if ( WC_Stripe_Helper::is_pre_orders_exists() ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
121
			$this->pre_orders = new WC_Stripe_Pre_Orders_Compat();
122
123
			add_action( 'wc_pre_orders_process_pre_order_completion_payment_' . $this->id, array( $this->pre_orders, 'process_pre_order_release_payment' ) );
124
		}
125
	}
126
127
	/**
128
	 * Returns all supported currencies for this payment method.
129
	 *
130
	 * @since 4.0.0
131
	 * @version 4.0.0
132
	 * @return array
133
	 */
134
	public function get_supported_currency() {
135
		return apply_filters( 'wc_stripe_sepa_supported_currencies', array(
136
			'EUR',
137
		) );
138
	}
139
140
	/**
141
	 * Checks to see if all criteria is met before showing payment method.
142
	 *
143
	 * @since 4.0.0
144
	 * @version 4.0.0
145
	 * @return bool
146
	 */
147
	public function is_available() {
148
		if ( ! in_array( get_woocommerce_currency(), $this->get_supported_currency() ) ) {
149
			return false;
150
		}
151
152
		if ( is_add_payment_method_page() && ! $this->saved_cards ) {
153
			return false;
154
		}
155
156
		return parent::is_available();
157
	}
158
159
	/**
160
	 * Get_icon function.
161
	 *
162
	 * @since 1.0.0
163
	 * @version 4.0.0
164
	 * @return string
165
	 */
166
	public function get_icon() {
167
		$icons = $this->payment_icons();
168
169
		$icons_str = '';
170
171
		$icons_str .= $icons['sepa'];
172
173
		return apply_filters( 'woocommerce_gateway_icon', $icons_str, $this->id );
174
	}
175
176
	/**
177
	 * payment_scripts function.
178
	 *
179
	 * Outputs scripts used for stripe payment
180
	 *
181
	 * @access public
182
	 */
183
	public function payment_scripts() {
184
		if ( ! is_cart() && ! is_checkout() && ! isset( $_GET['pay_for_order'] ) && ! is_add_payment_method_page() ) {
185
			return;
186
		}
187
188
		wp_enqueue_style( 'stripe_styles' );
189
		wp_enqueue_script( 'woocommerce_stripe' );
190
	}
191
192
	/**
193
	 * Initialize Gateway Settings Form Fields.
194
	 */
195
	public function init_form_fields() {
196
		$this->form_fields = require( WC_STRIPE_PLUGIN_PATH . '/includes/admin/stripe-sepa-settings.php' );
197
	}
198
199
	/**
200
	 * Displays the mandate acceptance notice to customer.
201
	 *
202
	 * @since 4.0.0
203
	 * @version 4.0.0
204
	 * @return string
205
	 */
206
	public function mandate_display() {
207
		/* translators: statement descriptor */
208
		printf( __( 'By providing your IBAN and confirming this payment, you are authorizing %s and Stripe, our payment service provider, to send instructions to your bank to debit your account and your bank to debit your account in accordance with those instructions. You are entitled to a refund from your bank under the terms and conditions of your agreement with your bank. A refund must be claimed within 8 weeks starting from the date on which your account was debited.', 'woocommerce-gateway-stripe' ), WC_Stripe_Helper::clean_statement_descriptor( $this->statement_descriptor ) );
0 ignored issues
show
Documentation introduced by
$this->statement_descriptor is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
209
	}
210
211
	/**
212
	 * Renders the Stripe elements form.
213
	 *
214
	 * @since 4.0.0
215
	 * @version 4.0.0
216
	 */
217
	public function form() {
218
		?>
219
		<fieldset id="wc-<?php echo esc_attr( $this->id ); ?>-form" class="wc-payment-form">
220
			<?php do_action( 'woocommerce_credit_card_form_start', $this->id ); ?>
221
			<p class="wc-stripe-sepa-mandate" style="margin-bottom:40px;"><?php $this->mandate_display(); ?></p>
222
			<p class="form-row form-row-wide">
223
				<label for="stripe-sepa-owner">
224
					<?php esc_html_e( 'IBAN Account Name.', 'woocommerce-gateway-stripe' ); ?> <span class="required">*</span>
225
				</label>
226
				<input id="stripe-sepa-owner" name="stripe_sepa_owner" value="" style="border:1px solid #ddd;margin:5px 0;padding:10px 5px;background-color:#fff;outline:0;" />
227
			</p>
228
			<p class="form-row form-row-wide">
229
				<label for="stripe-sepa-iban">
230
					<?php esc_html_e( 'IBAN Account Number.', 'woocommerce-gateway-stripe' ); ?> <span class="required">*</span>
231
				</label>
232
				<input id="stripe-sepa-iban" name="stripe_sepa_iban" value="" style="border:1px solid #ddd;margin:5px 0;padding:10px 5px;background-color:#fff;outline:0;" />
233
			</p>
234
			<!-- Used to display form errors -->
235
			<div class="stripe-source-errors" role="alert"></div>
236
			<?php do_action( 'woocommerce_credit_card_form_end', $this->id ); ?>
237
			<div class="clear"></div>
238
		</fieldset>
239
		<?php
240
	}
241
242
	/**
243
	 * Payment form on checkout page
244
	 */
245
	public function payment_fields() {
246
		$user                 = wp_get_current_user();
0 ignored issues
show
Unused Code introduced by
$user is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
247
		$total                = WC()->cart->total;
248
		$display_tokenization = $this->supports( 'tokenization' ) && is_checkout() && $this->saved_cards;
249
		$description          = $this->get_description() ? $this->get_description() : '';
250
251
		// If paying from order, we need to get total from order not cart.
252
		if ( isset( $_GET['pay_for_order'] ) && ! empty( $_GET['key'] ) ) {
253
			$order = wc_get_order( wc_get_order_id_by_order_key( wc_clean( $_GET['key'] ) ) );
254
			$total = $order->get_total();
255
		}
256
257
		if ( is_add_payment_method_page() ) {
258
			$pay_button_text = __( 'Add Payment', 'woocommerce-gateway-stripe' );
0 ignored issues
show
Unused Code introduced by
$pay_button_text is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
259
			$total        = '';
260
		} else {
261
			$pay_button_text = '';
0 ignored issues
show
Unused Code introduced by
$pay_button_text is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
262
		}
263
264
		echo '<div
265
			id="stripe-sepa_debit-payment-data"
266
			data-amount="' . esc_attr( WC_Stripe_Helper::get_stripe_amount( $total ) ) . '"
267
			data-currency="' . esc_attr( strtolower( get_woocommerce_currency() ) ) . '">';
268
269 View Code Duplication
		if ( $description ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
270
			if ( $this->testmode ) {
271
				$description .= ' ' . __( 'TEST MODE ENABLED. In test mode, you can use IBAN number DE89370400440532013000.', 'woocommerce-gateway-stripe' );
272
				$description  = trim( $description );
273
			}
274
			echo apply_filters( 'wc_stripe_description', wpautop( wp_kses_post( $description ) ), $this->id );
275
		}
276
277
		if ( $display_tokenization ) {
278
			$this->tokenization_script();
279
			$this->saved_payment_methods();
280
		}
281
282
		$this->form();
283
284
		if ( apply_filters( 'wc_stripe_display_save_payment_method_checkbox', $display_tokenization ) && ! is_add_payment_method_page() && ! isset( $_GET['change_payment_method'] ) ) {
285
			$this->save_payment_method_checkbox();
286
		}
287
288
		echo '</div>';
289
	}
290
291
	/**
292
	 * Process the payment
293
	 *
294
	 * @param int  $order_id Reference.
295
	 * @param bool $retry Should we retry on fail.
296
	 * @param bool $force_save_source Force save the payment source.
297
	 *
298
	 * @throws Exception If payment will not be accepted.
299
	 *
300
	 * @return array|void
301
	 */
302
	public function process_payment( $order_id, $retry = true, $force_save_source = false ) {
303
		try {
304
			$order = wc_get_order( $order_id );
305
306
			if ( $this->maybe_process_pre_orders( $order_id ) ) {
307
				return $this->pre_orders->process_pre_order( $order_id );
308
			}
309
310
			// This comes from the create account checkbox in the checkout page.
311
			$create_account = ! empty( $_POST['createaccount'] ) ? true : false;
312
313
			if ( $create_account ) {
314
				$new_customer_id     = WC_Stripe_Helper::is_pre_30() ? $order->customer_user : $order->get_customer_id();
315
				$new_stripe_customer = new WC_Stripe_Customer( $new_customer_id );
316
				$new_stripe_customer->create_customer();
317
			}
318
319
			$prepared_source = $this->prepare_source( get_current_user_id(), $force_save_source );
320
321
			$this->save_source_to_order( $order, $prepared_source );
322
323
			// Result from Stripe API request.
324
			$response = null;
325
326
			if ( $order->get_total() > 0 ) {
327
				// This will throw exception if not valid.
328
				$this->validate_minimum_order_amount( $order );
329
330
				WC_Stripe_Logger::log( "Info: Begin processing payment for order $order_id for the amount of {$order->get_total()}" );
331
332
				// Make the request.
333
				$response = WC_Stripe_API::request( $this->generate_payment_request( $order, $prepared_source ) );
334
335 View Code Duplication
				if ( ! empty( $response->error ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
336
					// Customer param wrong? The user may have been deleted on stripe's end. Remove customer_id. Can be retried without.
337
					if ( $this->is_no_such_customer_error( $response->error ) ) {
338
						if ( WC_Stripe_Helper::is_pre_30() ) {
339
							delete_user_meta( $order->customer_user, '_stripe_customer_id' );
340
							delete_post_meta( $order_id, '_stripe_customer_id' );
341
						} else {
342
							delete_user_meta( $order->get_customer_id(), '_stripe_customer_id' );
343
							$order->delete_meta_data( '_stripe_customer_id' );
344
							$order->save();
345
						}
346
					}
347
348
					if ( $this->is_no_such_token_error( $response->error ) && $prepared_source->token_id ) {
349
						// Source param wrong? The CARD may have been deleted on stripe's end. Remove token and show message.
350
						$wc_token = WC_Payment_Tokens::get( $prepared_source->token_id );
351
						$wc_token->delete();
352
						$localized_message = __( 'This card is no longer available and has been removed.', 'woocommerce-gateway-stripe' );
353
						$order->add_order_note( $localized_message );
354
						throw new WC_Stripe_Exception( print_r( $response, true ), $localized_message );
355
					}
356
357
					// We want to retry.
358
					if ( $this->is_retryable_error( $response->error ) ) {
359
						if ( $retry ) {
360
							// Don't do anymore retries after this.
361
							if ( 5 <= $this->retry_interval ) {
362
363
								return $this->process_payment( $order_id, false, $force_save_source );
364
							}
365
366
							sleep( $this->retry_interval );
367
368
							$this->retry_interval++;
369
370
							return $this->process_payment( $order_id, true, $force_save_source );
371
						} else {
372
							$localized_message = __( 'Sorry, we are unable to process your payment at this time. Please retry later.', 'woocommerce-gateway-stripe' );
373
							$order->add_order_note( $localized_message );
374
							throw new WC_Stripe_Exception( print_r( $response, true ), $localized_message );
375
						}
376
					}
377
378
					$localized_messages = WC_Stripe_Helper::get_localized_messages();
379
380
					if ( 'card_error' === $response->error->type ) {
381
						$localized_message = isset( $localized_messages[ $response->error->code ] ) ? $localized_messages[ $response->error->code ] : $response->error->message;
382
					} else {
383
						$localized_message = isset( $localized_messages[ $response->error->type ] ) ? $localized_messages[ $response->error->type ] : $response->error->message;
384
					}
385
386
					$order->add_order_note( $localized_message );
387
388
					throw new WC_Stripe_Exception( print_r( $response, true ), $localized_message );
389
				}
390
391
				do_action( 'wc_gateway_stripe_process_payment', $response, $order );
392
393
				// Process valid response.
394
				$this->process_response( $response, $order );
395
			} else {
396
				$order->payment_complete();
397
			}
398
399
			// Remove cart.
400
			WC()->cart->empty_cart();
401
402
			// Return thank you page redirect.
403
			return array(
404
				'result'   => 'success',
405
				'redirect' => $this->get_return_url( $order ),
406
			);
407
408
		} catch ( WC_Stripe_Exception $e ) {
409
			wc_add_notice( $e->getLocalizedMessage(), 'error' );
410
			WC_Stripe_Logger::log( 'Error: ' . $e->getMessage() );
411
412
			do_action( 'wc_gateway_stripe_process_payment_error', $e, $order );
413
414
			if ( $order->has_status( array( 'pending', 'failed' ) ) ) {
415
				$this->send_failed_order_email( $order_id );
416
			}
417
418
			return array(
419
				'result'   => 'fail',
420
				'redirect' => '',
421
			);
422
		}
423
	}
424
}
425