WC_Gateway_Stripe_Sepa::form()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 22
rs 9.568
c 0
b 0
f 0
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->has_fields         = true;
79
		$this->supports           = array(
80
			'products',
81
			'refunds',
82
			'tokenization',
83
			'add_payment_method',
84
			'subscriptions',
85
			'subscription_cancellation',
86
			'subscription_suspension',
87
			'subscription_reactivation',
88
			'subscription_amount_changes',
89
			'subscription_date_changes',
90
			'subscription_payment_method_change',
91
			'subscription_payment_method_change_customer',
92
			'subscription_payment_method_change_admin',
93
			'multiple_subscriptions',
94
			'pre-orders',
95
		);
96
97
		// Load the form fields.
98
		$this->init_form_fields();
99
100
		// Load the settings.
101
		$this->init_settings();
102
103
		$main_settings              = get_option( 'woocommerce_stripe_settings' );
104
		$this->title                = $this->get_option( 'title' );
105
		$this->description          = $this->get_option( 'description' );
106
		$this->enabled              = $this->get_option( 'enabled' );
107
		$this->testmode             = ( ! empty( $main_settings['testmode'] ) && 'yes' === $main_settings['testmode'] ) ? true : false;
108
		$this->saved_cards          = ( ! empty( $main_settings['saved_cards'] ) && 'yes' === $main_settings['saved_cards'] ) ? true : false;
109
		$this->publishable_key      = ! empty( $main_settings['publishable_key'] ) ? $main_settings['publishable_key'] : '';
110
		$this->secret_key           = ! empty( $main_settings['secret_key'] ) ? $main_settings['secret_key'] : '';
111
		$this->statement_descriptor = ! empty( $main_settings['statement_descriptor'] ) ? $main_settings['statement_descriptor'] : '';
112
113
		if ( $this->testmode ) {
114
			$this->publishable_key = ! empty( $main_settings['test_publishable_key'] ) ? $main_settings['test_publishable_key'] : '';
115
			$this->secret_key      = ! empty( $main_settings['test_secret_key'] ) ? $main_settings['test_secret_key'] : '';
116
		}
117
118
		add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );
119
		add_action( 'wp_enqueue_scripts', array( $this, 'payment_scripts' ) );
120
121 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...
122
			$this->pre_orders = new WC_Stripe_Pre_Orders_Compat();
123
124
			add_action( 'wc_pre_orders_process_pre_order_completion_payment_' . $this->id, array( $this->pre_orders, 'process_pre_order_release_payment' ) );
125
		}
126
	}
127
128
	/**
129
	 * Returns all supported currencies for this payment method.
130
	 *
131
	 * @since 4.0.0
132
	 * @version 4.0.0
133
	 * @return array
134
	 */
135
	public function get_supported_currency() {
136
		return apply_filters(
137
			'wc_stripe_sepa_supported_currencies',
138
			array(
139
				'EUR',
140
			)
141
		);
142
	}
143
144
	/**
145
	 * Checks to see if all criteria is met before showing payment method.
146
	 *
147
	 * @since 4.0.0
148
	 * @version 4.0.0
149
	 * @return bool
150
	 */
151
	public function is_available() {
152
		if ( ! in_array( get_woocommerce_currency(), $this->get_supported_currency() ) ) {
153
			return false;
154
		}
155
156
		if ( is_add_payment_method_page() && ! $this->saved_cards ) {
157
			return false;
158
		}
159
160
		return parent::is_available();
161
	}
162
163
	/**
164
	 * Get_icon function.
165
	 *
166
	 * @since 1.0.0
167
	 * @version 4.0.0
168
	 * @return string
169
	 */
170
	public function get_icon() {
171
		$icons = $this->payment_icons();
172
173
		$icons_str = '';
174
175
		$icons_str .= isset( $icons['sepa'] ) ? $icons['sepa'] : '';
176
177
		return apply_filters( 'woocommerce_gateway_icon', $icons_str, $this->id );
178
	}
179
180
	/**
181
	 * payment_scripts function.
182
	 *
183
	 * Outputs scripts used for stripe payment
184
	 *
185
	 * @access public
186
	 */
187
	public function payment_scripts() {
188
		if ( ! is_cart() && ! is_checkout() && ! isset( $_GET['pay_for_order'] ) && ! is_add_payment_method_page() ) {
189
			return;
190
		}
191
192
		wp_enqueue_style( 'stripe_styles' );
193
		wp_enqueue_script( 'woocommerce_stripe' );
194
	}
195
196
	/**
197
	 * Initialize Gateway Settings Form Fields.
198
	 */
199
	public function init_form_fields() {
200
		$this->form_fields = require( WC_STRIPE_PLUGIN_PATH . '/includes/admin/stripe-sepa-settings.php' );
201
	}
202
203
	/**
204
	 * Displays the mandate acceptance notice to customer.
205
	 *
206
	 * @since 4.0.0
207
	 * @version 4.0.0
208
	 * @return string
209
	 */
210
	public function mandate_display() {
211
		/* translators: statement descriptor */
212
		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...
213
	}
214
215
	/**
216
	 * Renders the Stripe elements form.
217
	 *
218
	 * @since 4.0.0
219
	 * @version 4.0.0
220
	 */
221
	public function form() {
222
		?>
223
		<fieldset id="wc-<?php echo esc_attr( $this->id ); ?>-form" class="wc-payment-form">
224
			<?php do_action( 'woocommerce_credit_card_form_start', $this->id ); ?>
225
			<p class="wc-stripe-sepa-mandate" style="margin-bottom:40px;"><?php $this->mandate_display(); ?></p>
226
			<p class="form-row form-row-wide">
227
				<label for="stripe-iban-element">
228
					<?php esc_html_e( 'IBAN.', 'woocommerce-gateway-stripe' ); ?> <span class="required">*</span>
229
				</label>
230
				<div id="stripe-iban-element" class="wc-stripe-iban-element-field">
231
					<!-- A Stripe Element will be inserted here. -->
232
				</div>
233
			</p>
234
235
			<!-- Used to display form errors -->
236
			<div class="stripe-source-errors" role="alert"></div>
237
			<br />
238
			<?php do_action( 'woocommerce_credit_card_form_end', $this->id ); ?>
239
			<div class="clear"></div>
240
		</fieldset>
241
		<?php
242
	}
243
244
	/**
245
	 * Payment form on checkout page
246
	 */
247
	public function payment_fields() {
248
		global $wp;
249
		$total                = WC()->cart->total;
250
		$display_tokenization = $this->supports( 'tokenization' ) && is_checkout() && $this->saved_cards;
251
		$description          = $this->get_description();
252
		$description          = ! empty( $description ) ? $description : '';
253
254
		// If paying from order, we need to get total from order not cart.
255
		if ( isset( $_GET['pay_for_order'] ) && ! empty( $_GET['key'] ) ) {
256
			$order = wc_get_order( wc_clean( $wp->query_vars['order-pay'] ) );
257
			$total = $order->get_total();
258
		}
259
260
		if ( is_add_payment_method_page() ) {
261
			$total = '';
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
		if ( $this->testmode ) {
270
			$description .= ' ' . __( 'TEST MODE ENABLED. In test mode, you can use IBAN number DE89370400440532013000.', 'woocommerce-gateway-stripe' );
271
		}
272
273
		$description = trim( $description );
274
275
		echo apply_filters( 'wc_stripe_description', wpautop( wp_kses_post( $description ) ), $this->id );
276
277
		if ( $display_tokenization ) {
278
			$this->tokenization_script();
279
			$this->saved_payment_methods();
280
		}
281
282
		$this->form();
283
284 View Code Duplication
		if ( apply_filters( 'wc_stripe_display_save_payment_method_checkbox', $display_tokenization ) && ! is_add_payment_method_page() && ! isset( $_GET['change_payment_method'] ) ) {
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...
285
			$this->save_payment_method_checkbox();
286
		}
287
288
		do_action( 'wc_stripe_sepa_payment_fields', $this->id );
289
290
		echo '</div>';
291
	}
292
293
	/**
294
	 * Process the payment
295
	 *
296
	 * @param int  $order_id Reference.
297
	 * @param bool $retry Should we retry on fail.
298
	 * @param bool $force_save_source Force save the payment source.
299
	 *
300
	 * @throws Exception If payment will not be accepted.
301
	 *
302
	 * @return array|void
303
	 */
304
	public function process_payment( $order_id, $retry = true, $force_save_source = false ) {
305
		try {
306
			$order = wc_get_order( $order_id );
307
308
			if ( $this->maybe_process_pre_orders( $order_id ) ) {
309
				return $this->pre_orders->process_pre_order( $order_id );
310
			}
311
312
			// This comes from the create account checkbox in the checkout page.
313
			$create_account = ! empty( $_POST['createaccount'] ) ? true : false;
314
315
			if ( $create_account ) {
316
				$new_customer_id     = $order->get_customer_id();
317
				$new_stripe_customer = new WC_Stripe_Customer( $new_customer_id );
318
				$new_stripe_customer->create_customer();
319
			}
320
321
			$prepared_source = $this->prepare_source( get_current_user_id(), $force_save_source );
322
323
			$this->save_source_to_order( $order, $prepared_source );
324
325
			// Result from Stripe API request.
326
			$response = null;
327
328
			if ( $order->get_total() > 0 ) {
329
				// This will throw exception if not valid.
330
				$this->validate_minimum_order_amount( $order );
331
332
				WC_Stripe_Logger::log( "Info: Begin processing payment for order $order_id for the amount of {$order->get_total()}" );
333
334
				// Make the request.
335
				$response = WC_Stripe_API::request( $this->generate_payment_request( $order, $prepared_source ) );
336
337 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...
338
					// Customer param wrong? The user may have been deleted on stripe's end. Remove customer_id. Can be retried without.
339
					if ( $this->is_no_such_customer_error( $response->error ) ) {
340
						delete_user_option( $order->get_customer_id(), '_stripe_customer_id' );
341
						$order->delete_meta_data( '_stripe_customer_id' );
342
						$order->save();
343
					}
344
345
					if ( $this->is_no_such_token_error( $response->error ) && $prepared_source->token_id ) {
346
						// Source param wrong? The CARD may have been deleted on stripe's end. Remove token and show message.
347
						$wc_token = WC_Payment_Tokens::get( $prepared_source->token_id );
348
						$wc_token->delete();
349
						$localized_message = __( 'This card is no longer available and has been removed.', 'woocommerce-gateway-stripe' );
350
						$order->add_order_note( $localized_message );
351
						throw new WC_Stripe_Exception( print_r( $response, true ), $localized_message );
352
					}
353
354
					// We want to retry.
355
					if ( $this->is_retryable_error( $response->error ) ) {
356
						if ( $retry ) {
357
							// Don't do anymore retries after this.
358
							if ( 5 <= $this->retry_interval ) {
359
360
								return $this->process_payment( $order_id, false, $force_save_source );
361
							}
362
363
							sleep( $this->retry_interval );
364
365
							$this->retry_interval++;
366
367
							return $this->process_payment( $order_id, true, $force_save_source );
368
						} else {
369
							$localized_message = __( 'Sorry, we are unable to process your payment at this time. Please retry later.', 'woocommerce-gateway-stripe' );
370
							$order->add_order_note( $localized_message );
371
							throw new WC_Stripe_Exception( print_r( $response, true ), $localized_message );
372
						}
373
					}
374
375
					$localized_messages = WC_Stripe_Helper::get_localized_messages();
376
377
					if ( 'card_error' === $response->error->type ) {
378
						$localized_message = isset( $localized_messages[ $response->error->code ] ) ? $localized_messages[ $response->error->code ] : $response->error->message;
379
					} else {
380
						$localized_message = isset( $localized_messages[ $response->error->type ] ) ? $localized_messages[ $response->error->type ] : $response->error->message;
381
					}
382
383
					$order->add_order_note( $localized_message );
384
385
					throw new WC_Stripe_Exception( print_r( $response, true ), $localized_message );
386
				}
387
388
				do_action( 'wc_gateway_stripe_process_payment', $response, $order );
389
390
				// Process valid response.
391
				$this->process_response( $response, $order );
392
			} else {
393
				$order->payment_complete();
394
			}
395
396
			// Remove cart.
397
			WC()->cart->empty_cart();
398
399
			// Return thank you page redirect.
400
			return array(
401
				'result'   => 'success',
402
				'redirect' => $this->get_return_url( $order ),
403
			);
404
405
		} catch ( WC_Stripe_Exception $e ) {
406
			wc_add_notice( $e->getLocalizedMessage(), 'error' );
407
			WC_Stripe_Logger::log( 'Error: ' . $e->getMessage() );
408
409
			do_action( 'wc_gateway_stripe_process_payment_error', $e, $order );
410
411
			if ( $order->has_status( array( 'pending', 'failed' ) ) ) {
412
				$this->send_failed_order_email( $order_id );
413
			}
414
415
			return array(
416
				'result'   => 'fail',
417
				'redirect' => '',
418
			);
419
		}
420
	}
421
}
422