Completed
Push — master ( 6d8164...bf7249 )
by Roy
03:26
created

WC_Stripe_Order_Handler   D

Complexity

Total Complexity 109

Size/Duplication

Total Lines 484
Duplicated Lines 12.81 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
dl 62
loc 484
rs 4.8717
c 0
b 0
f 0
wmc 109
lcom 1
cbo 4

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 1
A get_instance() 0 3 1
D process_redirect_payment() 35 120 24
A maybe_process_redirect_order() 0 9 4
F capture_payment() 8 49 18
D cancel_payment() 0 29 9
A normalize_field() 0 20 1
F validate_checkout() 19 151 47
A send_ajax_failure_response() 0 21 4

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like WC_Stripe_Order_Handler often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use WC_Stripe_Order_Handler, and based on these observations, apply Extract Interface, too.

1
<?php
2
if ( ! defined( 'ABSPATH' ) ) {
3
	exit;
4
}
5
6
/**
7
 * Handles and process orders from asyncronous flows.
8
 *
9
 * @since 4.0.0
10
 */
11
class WC_Stripe_Order_Handler extends WC_Stripe_Payment_Gateway {
12
	private static $_this;
13
14
	/**
15
	 * Constructor.
16
	 *
17
	 * @since 4.0.0
18
	 * @version 4.0.0
19
	 */
20
	public function __construct() {
21
		self::$_this = $this;
22
23
		add_action( 'wp', array( $this, 'maybe_process_redirect_order' ) );
24
		add_action( 'woocommerce_order_status_on-hold_to_processing', array( $this, 'capture_payment' ) );
25
		add_action( 'woocommerce_order_status_on-hold_to_completed', array( $this, 'capture_payment' ) );
26
		add_action( 'woocommerce_order_status_on-hold_to_cancelled', array( $this, 'cancel_payment' ) );
27
		add_action( 'woocommerce_order_status_on-hold_to_refunded', array( $this, 'cancel_payment' ) );
28
		add_action( 'wc_ajax_wc_stripe_validate_checkout', array( $this, 'validate_checkout' ) );
29
	}
30
31
	/**
32
	 * Public access to instance object.
33
	 *
34
	 * @since 4.0.0
35
	 * @version 4.0.0
36
	 */
37
	public static function get_instance() {
38
		return self::$_this;
39
	}
40
41
	/**
42
	 * Processes payments.
43
	 * Note at this time the original source has already been
44
	 * saved to a customer card (if applicable) from process_payment.
45
	 *
46
	 * @since 4.0.0
47
	 * @version 4.0.0
48
	 */
49
	public function process_redirect_payment( $order_id, $retry = true ) {
50
		try {
51
			$source = wc_clean( $_GET['source'] );
52
53
			if ( empty( $source ) ) {
54
				return;
55
			}
56
57
			if ( empty( $order_id ) ) {
58
				return;
59
			}
60
61
			$order = wc_get_order( $order_id );
62
63
			if ( ! is_object( $order ) ) {
64
				return;
65
			}
66
67
			if ( 'processing' === $order->get_status() || 'completed' === $order->get_status() || 'on-hold' === $order->get_status() ) {
68
				return;
69
			}
70
71
			// Result from Stripe API request.
72
			$response = null;
73
74
			// This will throw exception if not valid.
75
			$this->validate_minimum_order_amount( $order );
76
77
			WC_Stripe_Logger::log( "Info: (Redirect) Begin processing payment for order $order_id for the amount of {$order->get_total()}" );
78
79
			// Prep source object.
80
			$source_object           = new stdClass();
81
			$source_object->token_id = '';
82
			$source_object->customer = $this->get_stripe_customer_id( $order );
83
			$source_object->source   = $source;
84
85
			/**
86
			 * First check if the source is chargeable at this time. If not,
87
			 * webhook will take care of it later.
88
			 */
89
			$source_info = WC_Stripe_API::retrieve( 'sources/' . $source );
90
91
			if ( ! empty( $source_info->error ) ) {
92
				throw new Exception( $source_info->error->message );
93
			}
94
95
			if ( 'failed' === $source_info->status || 'canceled' === $source_info->status ) {
96
				throw new Exception( __( 'Unable to process this payment, please try again or use alternative method.', 'woocommerce-gateway-stripe' ) );
97
			}
98
99
			// If already consumed, then ignore request.
100
			if ( 'consumed' === $source_info->status ) {
101
				return;
102
			}
103
104
			// If not chargeable, then ignore request.
105
			if ( 'chargeable' !== $source_info->status ) {
106
				return;
107
			}
108
109
			// Make the request.
110
			$response = WC_Stripe_API::request( $this->generate_payment_request( $order, $source_object ) );
111
112 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...
113
				// If it is an API error such connection or server, let's retry.
114
				if ( 'api_connection_error' === $response->error->type || 'api_error' === $response->error->type ) {
115
					if ( $retry ) {
116
						sleep( 5 );
117
						return $this->process_redirect_payment( $order_id, false );
118
					} else {
119
						$message = 'API connection error and retries exhausted.';
120
						$order->add_order_note( $message );
121
						throw new Exception( $message );
122
					}
123
				}
124
125
				// Customer param wrong? The user may have been deleted on stripe's end. Remove customer_id. Can be retried without.
126
				if ( preg_match( '/No such customer/i', $response->error->message ) && $retry ) {
127
					delete_user_meta( WC_Stripe_Helper::is_pre_30() ? $order->customer_user : $order->get_customer_id(), '_stripe_customer_id' );
128
129
					return $this->process_redirect_payment( $order_id, false );
130
131
				} elseif ( preg_match( '/No such token/i', $response->error->message ) && $source_object->token_id ) {
132
					// Source param wrong? The CARD may have been deleted on stripe's end. Remove token and show message.
133
134
					$wc_token = WC_Payment_Tokens::get( $source_object->token_id );
135
					$wc_token->delete();
136
					$message = __( 'This card is no longer available and has been removed.', 'woocommerce-gateway-stripe' );
137
					$order->add_order_note( $message );
138
					throw new Exception( $message );
139
				}
140
141
				$localized_messages = WC_Stripe_Helper::get_localized_messages();
142
143
				$message = isset( $localized_messages[ $response->error->type ] ) ? $localized_messages[ $response->error->type ] : $response->error->message;
144
145
				throw new Exception( $message );
146
			}
147
148
			do_action( 'wc_gateway_stripe_process_redirect_payment', $response, $order );
149
150
			$this->process_response( $response, $order );
151
152
		} catch ( Exception $e ) {
153
			WC_Stripe_Logger::log( 'Error: ' . $e->getMessage() );
154
155
			do_action( 'wc_gateway_stripe_process_redirect_payment_error', $e, $order );
0 ignored issues
show
Bug introduced by
The variable $order does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
156
157
			/* translators: error message */
158
			$order->update_status( 'failed', sprintf( __( 'Stripe payment failed: %s', 'woocommerce-gateway-stripe' ), $e->getMessage() ) );
159
160
			if ( $order->has_status( array( 'pending', 'failed' ) ) ) {
161
				$this->send_failed_order_email( $order_id );
162
			}
163
164
			wc_add_notice( $e->getMessage(), 'error' );
165
			wp_safe_redirect( wc_get_checkout_url() );
166
			exit;
167
		}
168
	}
169
170
	/**
171
	 * Processses the orders that are redirected.
172
	 *
173
	 * @since 4.0.0
174
	 * @version 4.0.0
175
	 */
176
	public function maybe_process_redirect_order() {
177
		if ( ! is_order_received_page() || empty( $_GET['client_secret'] ) || empty( $_GET['source'] ) ) {
178
			return;
179
		}
180
181
		$order_id = wc_clean( $_GET['order_id'] );
182
183
		$this->process_redirect_payment( $order_id );
184
	}
185
186
	/**
187
	 * Capture payment when the order is changed from on-hold to complete or processing.
188
	 *
189
	 * @since 3.1.0
190
	 * @version 4.0.0
191
	 * @param  int $order_id
192
	 */
193
	public function capture_payment( $order_id ) {
194
		$order = wc_get_order( $order_id );
195
196
		if ( 'stripe' === ( WC_Stripe_Helper::is_pre_30() ? $order->payment_method : $order->get_payment_method() ) ) {
197
			$charge   = WC_Stripe_Helper::is_pre_30() ? get_post_meta( $order_id, '_transaction_id', true ) : $order->get_transaction_id();
198
			$captured = WC_Stripe_Helper::is_pre_30() ? get_post_meta( $order_id, '_stripe_charge_captured', true ) : $order->get_meta( '_stripe_charge_captured', true );
199
200
			if ( $charge && 'no' === $captured ) {
201
				$order_total = $order->get_total();
202
203
				if ( 0 < $order->get_total_refunded() ) {
204
					$order_total = $order_total - $order->get_total_refunded();
205
				}
206
207
				$result = WC_Stripe_API::request( array(
208
					'amount'   => WC_Stripe_Helper::get_stripe_amount( $order_total ),
209
					'expand[]' => 'balance_transaction',
210
				), 'charges/' . $charge . '/capture' );
211
212
				if ( ! empty( $result->error ) ) {
213
					/* translators: error message */
214
					$order->update_status( 'failed', sprintf( __( 'Unable to capture charge! %s', 'woocommerce-gateway-stripe' ), $result->error->message ) );
215
				} else {
216
					/* translators: transaction id */
217
					$order->add_order_note( sprintf( __( 'Stripe charge complete (Charge ID: %s)', 'woocommerce-gateway-stripe' ), $result->id ) );
218
					WC_Stripe_Helper::is_pre_30() ? update_post_meta( $order_id, '_stripe_charge_captured', 'yes' ) : $order->update_meta_data( '_stripe_charge_captured', 'yes' );
219
220
					// Store other data such as fees
221
					WC_Stripe_Helper::is_pre_30() ? update_post_meta( $order_id, '_transaction_id', $result->id ) : $order->set_transaction_id( $result->id );
222
223 View Code Duplication
					if ( isset( $result->balance_transaction ) && isset( $result->balance_transaction->fee ) ) {
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...
224
						// Fees and Net needs to both come from Stripe to be accurate as the returned
225
						// values are in the local currency of the Stripe account, not from WC.
226
						$fee = ! empty( $result->balance_transaction->fee ) ? WC_Stripe_Helper::format_balance_fee( $result->balance_transaction, 'fee' ) : 0;
227
						$net = ! empty( $result->balance_transaction->net ) ? WC_Stripe_Helper::format_balance_fee( $result->balance_transaction, 'net' ) : 0;
228
						WC_Stripe_Helper::is_pre_30() ? update_post_meta( $order_id, parent::META_NAME_FEE, $fee ) : $order->update_meta_data( parent::META_NAME_FEE, $fee );
229
						WC_Stripe_Helper::is_pre_30() ? update_post_meta( $order_id, parent::META_NAME_NET, $net ) : $order->update_meta_data( parent::META_NAME_NET, $net );
230
					}
231
232
					if ( is_callable( array( $order, 'save' ) ) ) {
233
						$order->save();
234
					}
235
				}
236
237
				// This hook fires when admin manually changes order status to processing or completed.
238
				do_action( 'woocommerce_stripe_process_manual_capture', $order, $result );
239
			}
240
		}
241
	}
242
243
	/**
244
	 * Cancel pre-auth on refund/cancellation.
245
	 *
246
	 * @since 3.1.0
247
	 * @version 4.0.0
248
	 * @param  int $order_id
249
	 */
250
	public function cancel_payment( $order_id ) {
251
		$order = wc_get_order( $order_id );
252
253
		if ( 'stripe' === ( WC_Stripe_Helper::is_pre_30() ? $order->payment_method : $order->get_payment_method() ) ) {
254
			$charge_id = WC_Stripe_Helper::is_pre_30() ? get_post_meta( $order_id, '_transaction_id', true ) : $order->get_transaction_id();
255
256
			if ( $charge_id ) {
257
				$result = WC_Stripe_API::request( array(
258
					'amount' => WC_Stripe_Helper::get_stripe_amount( $order->get_total() ),
259
				), 'charges/' . $charge_id . '/refund' );
260
261
				if ( ! empty( $result->error ) ) {
262
					$order->add_order_note( __( 'Unable to refund charge!', 'woocommerce-gateway-stripe' ) . ' ' . $result->error->message );
263
				} else {
264
					/* translators: transaction id */
265
					$order->add_order_note( sprintf( __( 'Stripe charge refunded (Charge ID: %s)', 'woocommerce-gateway-stripe' ), $result->id ) );
266
					WC_Stripe_Helper::is_pre_30() ? delete_post_meta( $order_id, '_stripe_charge_captured' ) : $order->delete_meta_data( '_stripe_charge_captured' );
267
					WC_Stripe_Helper::is_pre_30() ? delete_post_meta( $order_id, '_transaction_id' ) : $order->delete_meta_data( '_stripe_transaction_id' );
268
269
					if ( is_callable( array( $order, 'save' ) ) ) {
270
						$order->save();
271
					}
272
				}
273
274
				// This hook fires when admin manually changes order status to cancel.
275
				do_action( 'woocommerce_stripe_process_manual_cancel', $order, $result );
276
			}
277
		}
278
	}
279
280
	/**
281
	 * Normalize the error field name.
282
	 *
283
	 * @since 4.0.0
284
	 * @version 4.0.0
285
	 * @param string $field
286
	 * @return string $error_field
287
	 */
288
	public function normalize_field( $field ) {
289
		$error_field = ucfirst( str_replace( '_', ' ', $field ) );
290
291
		$org_str     = array();
292
		$replace_str = array();
293
294
		$org_str[]     = 'Stripe';
295
		$replace_str[] = '';
296
297
		$org_str[]     = 'sepa';
298
		$replace_str[] = 'SEPA';
299
300
		$org_str[]     = 'iban';
301
		$replace_str[] = 'IBAN';
302
303
		$org_str[]     = 'sofort';
304
		$replace_str[] = 'SOFORT';
305
306
		return str_replace( $org_str, $replace_str, $error_field );
307
	}
308
309
	/**
310
	 * Validates the checkout before submitting checkout form.
311
	 *
312
	 * @since 4.0.0
313
	 * @version 4.0.0
314
	 */
315
	public function validate_checkout() {
316
		if ( ! wp_verify_nonce( $_POST['nonce'], '_wc_stripe_nonce' ) ) {
317
			wp_die( __( 'Cheatin&#8217; huh?', 'woocommerce-gateway-stripe' ) );
318
		}
319
320
		$errors = new WP_Error();
321
		parse_str( $_POST['required_fields'], $required_fields );
322
		parse_str( $_POST['all_fields'], $all_fields );
323
		$source_type = isset( $_POST['source_type'] ) ? wc_clean( $_POST['source_type'] ) : '';
324
		$validate_shipping_fields = false;
325
		$create_account = false;
326
327
		array_walk_recursive( $required_fields, 'wc_clean' );
328
		array_walk_recursive( $all_fields, 'wc_clean' );
329
330
		// Remove unneeded required fields depending on source type.
331
		if ( 'stripe_sepa' !== $source_type ) {
332
			unset( $required_fields['stripe_sepa_owner'] );
333
			unset( $required_fields['stripe_sepa_iban'] );
334
		}
335
336
		if ( 'stripe_sofort' !== $source_type ) {
337
			unset( $required_fields['stripe_sofort_bank_country'] );
338
		}
339
340
		/**
341
		 * If ship to different address checkbox is checked then we need
342
		 * to validate shipping fields too.
343
		 */
344
		if ( isset( $all_fields['ship_to_different_address'] ) ) {
345
			$validate_shipping_fields = true;
346
		}
347
348
		// Check if createaccount is checked.
349
		if ( isset( $all_fields['createaccount'] ) ) {
350
			$create_account = true;
351
		}
352
353
		// Check if required fields are empty.
354
		foreach ( $required_fields as $field => $field_value ) {
355
			// Check for shipping field.
356
			if ( preg_match( '/^shipping_/', $field ) && ! $validate_shipping_fields ) {
357
				continue;
358
			}
359
360
			// Check create account name.
361
			if ( 'account_username' === $field && ! $create_account ) {
362
				continue;
363
			}
364
365
			// Check create account password.
366
			if ( 'account_password' === $field && ! $create_account ) {
367
				continue;
368
			}
369
370
			// Check if is SEPA.
371
			if ( 'stripe_sepa' !== $source_type && 'stripe_sepa_owner' === $field ) {
372
				continue;
373
			}
374
375
			if ( 'stripe_sepa' !== $source_type && 'stripe_sepa_iban' === $field ) {
376
				$continue;
0 ignored issues
show
Bug introduced by
The variable $continue does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
377
			}
378
379
			if ( empty( $field_value ) || '-1' === $field_value ) {
380
				$error_field = $this->normalize_field( $field );
381
				/* translators: error field name */
382
				$errors->add( 'validation', sprintf( __( '%s cannot be empty', 'woocommerce-gateway-stripe' ), $error_field ) );
383
			}
384
		}
385
386
		// Check if email is valid format.
387
		if ( ! empty( $required_fields['billing_email'] ) && ! is_email( $required_fields['billing_email'] ) ) {
388
			$errors->add( 'validation', __( 'Email is not valid', 'woocommerce-gateway-stripe' ) );
389
		}
390
391
		// Check if phone number is valid format.
392
		if ( ! empty( $required_fields['billing_phone'] ) ) {
393
			$phone = wc_format_phone_number( $required_fields['billing_phone'] );
394
395
			if ( '' !== $phone && ! WC_Validation::is_phone( $phone ) ) {
396
				/* translators: %s: phone number */
397
				$errors->add( 'validation', __( 'Please enter a valid phone number.', 'woocommerce-gateway-stripe' ) );
398
			}
399
		}
400
401
		// Check if postal code is valid format.
402 View Code Duplication
		if ( ! empty( $required_fields['billing_postcode'] ) ) {
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...
403
			$country = isset( $required_fields['billing_country'] ) ? $required_fields['billing_country'] : WC()->customer->get_billing_country();
404
			$postcode = wc_format_postcode( $required_fields['billing_postcode'], $country );
405
406
			if ( '' !== $required_fields['billing_postcode'] && ! WC_Validation::is_postcode( $postcode, $country ) ) {
407
				$errors->add( 'validation', __( 'Please enter a valid billing postcode / ZIP.', 'woocommerce-gateway-stripe' ) );
408
			}
409
		}
410
411
		if ( empty( $all_fields['woocommerce_checkout_update_totals'] ) && empty( $all_fields['terms'] ) && apply_filters( 'woocommerce_checkout_show_terms', wc_get_page_id( 'terms' ) > 0 ) ) {
412
			$errors->add( 'terms', __( 'You must accept our Terms &amp; Conditions.', 'woocommerce-gateway-stripe' ) );
413
		}
414
415 View Code Duplication
		if ( WC()->cart->needs_shipping() && $validate_shipping_fields ) {
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...
416
			// Check if postal code is valid format.
417
			if ( ! empty( $required_fields['shipping_postcode'] ) ) {
418
				$country = isset( $required_fields['shipping_country'] ) ? $required_fields['shipping_country'] : WC()->customer->get_shipping_country();
419
				$postcode = wc_format_postcode( $required_fields['shipping_postcode'], $country );
420
421
				if ( '' !== $required_fields['shipping_postcode'] && ! WC_Validation::is_postcode( $postcode, $country ) ) {
422
					$errors->add( 'validation', __( 'Please enter a valid shipping postcode / ZIP.', 'woocommerce-gateway-stripe' ) );
423
				}
424
			}
425
		}
426
427
		if ( WC()->cart->needs_shipping() ) {
428
			$shipping_country = WC()->customer->get_shipping_country();
429
430
			if ( empty( $shipping_country ) ) {
431
				$errors->add( 'shipping', __( 'Please enter an address to continue.', 'woocommerce-gateway-stripe' ) );
432
			} elseif ( ! in_array( WC()->customer->get_shipping_country(), array_keys( WC()->countries->get_shipping_countries() ) ) ) {
433
				/* translators: country name */
434
				$errors->add( 'shipping', sprintf( __( 'Unfortunately <strong>we do not ship %s</strong>. Please enter an alternative shipping address.', 'woocommerce-gateway-stripe' ), WC()->countries->shipping_to_prefix() . ' ' . WC()->customer->get_shipping_country() ) );
435
			} else {
436
				$chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods' );
437
438
				foreach ( WC()->shipping->get_packages() as $i => $package ) {
439
					if ( ! isset( $chosen_shipping_methods[ $i ], $package['rates'][ $chosen_shipping_methods[ $i ] ] ) ) {
440
						$errors->add( 'shipping', __( 'No shipping method has been selected. Please double check your address, or contact us if you need any help.', 'woocommerce-gateway-stripe' ) );
441
					}
442
				}
443
			}
444
		}
445
446
		if ( WC()->cart->needs_payment() ) {
447
			$available_gateways = WC()->payment_gateways->get_available_payment_gateways();
448
449
			if ( ! isset( $available_gateways[ $all_fields['payment_method'] ] ) ) {
450
				$errors->add( 'payment', __( 'Invalid payment method.', 'woocommerce-gateway-stripe' ) );
451
			} else {
452
				$available_gateways[ $all_fields['payment_method'] ]->validate_fields();
453
			}
454
		}
455
456
		if ( 0 === count( $errors->errors ) ) {
457
			wp_send_json( 'success' );
458
		} else {
459
			foreach ( $errors->get_error_messages() as $message ) {
460
				wc_add_notice( $message, 'error' );
461
			}
462
463
			$this->send_ajax_failure_response();
464
		}
465
	}
466
467
	/**
468
	 * Preps the error messages to be displayed.
469
	 *
470
	 * @since 4.0.0
471
	 * @version 4.0.0
472
	 */
473
	public function send_ajax_failure_response() {
474
		if ( is_ajax() ) {
475
			// only print notices if not reloading the checkout, otherwise they're lost in the page reload.
476
			if ( ! isset( WC()->session->reload_checkout ) ) {
477
				ob_start();
478
				wc_print_notices();
479
				$messages = ob_get_clean();
480
			}
481
482
			$response = array(
483
				'result'   => 'failure',
484
				'messages' => isset( $messages ) ? $messages : '',
485
				'refresh'  => isset( WC()->session->refresh_totals ),
486
				'reload'   => isset( WC()->session->reload_checkout ),
487
			);
488
489
			unset( WC()->session->refresh_totals, WC()->session->reload_checkout );
490
491
			wp_send_json( $response );
492
		}
493
	}
494
}
495
496
new WC_Stripe_Order_Handler();
497