Completed
Push — master ( bb9186...e1e9c9 )
by Roy
03:08
created

WC_Stripe_Order_Handler   D

Complexity

Total Complexity 105

Size/Duplication

Total Lines 479
Duplicated Lines 15.24 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
dl 73
loc 479
rs 4.8717
c 0
b 0
f 0
wmc 105
lcom 1
cbo 5

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 1
A get_instance() 0 3 1
D process_redirect_payment() 46 131 26
A maybe_process_redirect_order() 0 9 4
F capture_payment() 8 49 18
A cancel_payment() 0 10 3
B normalize_field() 0 36 5
F validate_checkout() 19 138 43
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
			/**
80
			 * First check if the source is chargeable at this time. If not,
81
			 * webhook will take care of it later.
82
			 */
83
			$source_info = WC_Stripe_API::retrieve( 'sources/' . $source );
84
85
			if ( ! empty( $source_info->error ) ) {
86
				throw new WC_Stripe_Exception( print_r( $source_info, true ), $source_info->error->message );
87
			}
88
89
			if ( 'failed' === $source_info->status || 'canceled' === $source_info->status ) {
90
				throw new WC_Stripe_Exception( print_r( $source_info, true ), __( 'Unable to process this payment, please try again or use alternative method.', 'woocommerce-gateway-stripe' ) );
91
			}
92
93
			// If already consumed, then ignore request.
94
			if ( 'consumed' === $source_info->status ) {
95
				return;
96
			}
97
98
			// If not chargeable, then ignore request.
99
			if ( 'chargeable' !== $source_info->status ) {
100
				return;
101
			}
102
103
			// Prep source object.
104
			$source_object           = new stdClass();
105
			$source_object->token_id = '';
106
			$source_object->customer = $this->get_stripe_customer_id( $order );
107
			$source_object->source   = $source_info->id;
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 WC_Stripe_Exception( print_r( $response, true ), $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
					if ( WC_Stripe_Helper::is_pre_30() ) {
128
						delete_user_meta( $order->customer_user, '_stripe_customer_id' );
129
						delete_post_meta( $order_id, '_stripe_customer_id' );
130
					} else {
131
						delete_user_meta( $order->get_customer_id(), '_stripe_customer_id' );
132
						$order->delete_meta_data( '_stripe_customer_id' );
133
						$order->save();
134
					}
135
136
					return $this->process_redirect_payment( $order_id, false );
137
138
				} elseif ( preg_match( '/No such token/i', $response->error->message ) && $source_object->token_id ) {
139
					// Source param wrong? The CARD may have been deleted on stripe's end. Remove token and show message.
140
141
					$wc_token = WC_Payment_Tokens::get( $source_object->token_id );
142
					$wc_token->delete();
143
					$message = __( 'This card is no longer available and has been removed.', 'woocommerce-gateway-stripe' );
144
					$order->add_order_note( $message );
145
					throw new WC_Stripe_Exception( print_r( $response, true ), $message );
146
				}
147
148
				$localized_messages = WC_Stripe_Helper::get_localized_messages();
149
150
				if ( 'card_error' === $response->error->type ) {
151
					$message = isset( $localized_messages[ $response->error->code ] ) ? $localized_messages[ $response->error->code ] : $response->error->message;
152
				} else {
153
					$message = isset( $localized_messages[ $response->error->type ] ) ? $localized_messages[ $response->error->type ] : $response->error->message;
154
				}
155
156
				throw new WC_Stripe_Exception( print_r( $response, true ), $message );
157
			}
158
159
			do_action( 'wc_gateway_stripe_process_redirect_payment', $response, $order );
160
161
			$this->process_response( $response, $order );
162
163
		} catch ( WC_Stripe_Exception $e ) {
164
			WC_Stripe_Logger::log( 'Error: ' . $e->getMessage() );
165
166
			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...
167
168
			/* translators: error message */
169
			$order->update_status( 'failed', sprintf( __( 'Stripe payment failed: %s', 'woocommerce-gateway-stripe' ), $e->getLocalizedMessage() ) );
170
171
			if ( $order->has_status( array( 'pending', 'failed' ) ) ) {
172
				$this->send_failed_order_email( $order_id );
173
			}
174
175
			wc_add_notice( $e->getLocalizedMessage(), 'error' );
176
			wp_safe_redirect( wc_get_checkout_url() );
177
			exit;
178
		}
179
	}
180
181
	/**
182
	 * Processses the orders that are redirected.
183
	 *
184
	 * @since 4.0.0
185
	 * @version 4.0.0
186
	 */
187
	public function maybe_process_redirect_order() {
188
		if ( ! is_order_received_page() || empty( $_GET['client_secret'] ) || empty( $_GET['source'] ) ) {
189
			return;
190
		}
191
192
		$order_id = wc_clean( $_GET['order_id'] );
193
194
		$this->process_redirect_payment( $order_id );
195
	}
196
197
	/**
198
	 * Capture payment when the order is changed from on-hold to complete or processing.
199
	 *
200
	 * @since 3.1.0
201
	 * @version 4.0.0
202
	 * @param  int $order_id
203
	 */
204
	public function capture_payment( $order_id ) {
205
		$order = wc_get_order( $order_id );
206
207
		if ( 'stripe' === ( WC_Stripe_Helper::is_pre_30() ? $order->payment_method : $order->get_payment_method() ) ) {
208
			$charge   = WC_Stripe_Helper::is_pre_30() ? get_post_meta( $order_id, '_transaction_id', true ) : $order->get_transaction_id();
209
			$captured = WC_Stripe_Helper::is_pre_30() ? get_post_meta( $order_id, '_stripe_charge_captured', true ) : $order->get_meta( '_stripe_charge_captured', true );
210
211
			if ( $charge && 'no' === $captured ) {
212
				$order_total = $order->get_total();
213
214
				if ( 0 < $order->get_total_refunded() ) {
215
					$order_total = $order_total - $order->get_total_refunded();
216
				}
217
218
				$result = WC_Stripe_API::request( array(
219
					'amount'   => WC_Stripe_Helper::get_stripe_amount( $order_total ),
220
					'expand[]' => 'balance_transaction',
221
				), 'charges/' . $charge . '/capture' );
222
223
				if ( ! empty( $result->error ) ) {
224
					/* translators: error message */
225
					$order->update_status( 'failed', sprintf( __( 'Unable to capture charge! %s', 'woocommerce-gateway-stripe' ), $result->error->message ) );
226
				} else {
227
					/* translators: transaction id */
228
					$order->add_order_note( sprintf( __( 'Stripe charge complete (Charge ID: %s)', 'woocommerce-gateway-stripe' ), $result->id ) );
229
					WC_Stripe_Helper::is_pre_30() ? update_post_meta( $order_id, '_stripe_charge_captured', 'yes' ) : $order->update_meta_data( '_stripe_charge_captured', 'yes' );
230
231
					// Store other data such as fees
232
					WC_Stripe_Helper::is_pre_30() ? update_post_meta( $order_id, '_transaction_id', $result->id ) : $order->set_transaction_id( $result->id );
233
234 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...
235
						// Fees and Net needs to both come from Stripe to be accurate as the returned
236
						// values are in the local currency of the Stripe account, not from WC.
237
						$fee = ! empty( $result->balance_transaction->fee ) ? WC_Stripe_Helper::format_balance_fee( $result->balance_transaction, 'fee' ) : 0;
238
						$net = ! empty( $result->balance_transaction->net ) ? WC_Stripe_Helper::format_balance_fee( $result->balance_transaction, 'net' ) : 0;
239
						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 );
240
						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 );
241
					}
242
243
					if ( is_callable( array( $order, 'save' ) ) ) {
244
						$order->save();
245
					}
246
				}
247
248
				// This hook fires when admin manually changes order status to processing or completed.
249
				do_action( 'woocommerce_stripe_process_manual_capture', $order, $result );
250
			}
251
		}
252
	}
253
254
	/**
255
	 * Cancel pre-auth on refund/cancellation.
256
	 *
257
	 * @since 3.1.0
258
	 * @version 4.0.0
259
	 * @param  int $order_id
260
	 */
261
	public function cancel_payment( $order_id ) {
262
		$order = wc_get_order( $order_id );
263
264
		if ( 'stripe' === ( WC_Stripe_Helper::is_pre_30() ? $order->payment_method : $order->get_payment_method() ) ) {
265
			$this->process_refund( $order_id );
266
267
			// This hook fires when admin manually changes order status to cancel.
268
			do_action( 'woocommerce_stripe_process_manual_cancel', $order );
269
		}
270
	}
271
272
	/**
273
	 * Normalize the error field name with appropriate locale.
274
	 *
275
	 * @since 4.0.0
276
	 * @since 4.0.1 Map localized checkout fields.
277
	 * @param string $field
278
	 * @return string $error_field
279
	 */
280
	public function normalize_field( $field ) {
281
		$checkout_fields = WC()->checkout->get_checkout_fields();
282
		$org_str         = array();
283
		$replace_str     = array();
284
285
		if ( array_key_exists( $field, $checkout_fields['billing'] ) ) {
286
			$error_field = __( 'Billing', 'woocommerce-gateway-stripe' ) . ' ' . $checkout_fields['billing'][ $field ]['label'];
287
		} elseif ( array_key_exists( $field, $checkout_fields['shipping'] ) ) {
288
			$error_field = __( 'Shipping', 'woocommerce-gateway-stripe' ) . ' ' . $checkout_fields['shipping'][ $field ]['label'];
289
		} elseif ( array_key_exists( $field, $checkout_fields['order'] ) ) {
290
			$error_field = $checkout_fields['order'][ $field ]['label'];
291
		} elseif ( array_key_exists( $field, $checkout_fields['account'] ) ) {
292
			$error_field = $checkout_fields['account'][ $field ]['label'];
293
		} else {
294
			$error_field = str_replace( '_', ' ', $field );
295
296
			$org_str[]     = 'stripe';
297
			$replace_str[] = '';
298
299
			$org_str[]     = 'sepa';
300
			$replace_str[] = 'SEPA';
301
302
			$org_str[]     = 'iban';
303
			$replace_str[] = 'IBAN';
304
305
			$org_str[]     = 'sofort';
306
			$replace_str[] = 'SOFORT';
307
308
			$org_str[]     = 'owner';
309
			$replace_str[] = __( 'Owner', 'woocommerce-gateway-stripe' );
310
311
			$error_field   = str_replace( $org_str, $replace_str, $error_field );
312
		}
313
314
		return $error_field;
315
	}
316
317
	/**
318
	 * Validates the checkout before submitting checkout form.
319
	 *
320
	 * @since 4.0.0
321
	 * @version 4.0.0
322
	 */
323
	public function validate_checkout() {
324
		if ( ! wp_verify_nonce( $_POST['nonce'], '_wc_stripe_nonce' ) ) {
325
			wp_die( __( 'Cheatin&#8217; huh?', 'woocommerce-gateway-stripe' ) );
326
		}
327
328
		$errors = new WP_Error();
329
		parse_str( $_POST['required_fields'], $required_fields );
330
		parse_str( $_POST['all_fields'], $all_fields );
331
		$source_type = isset( $_POST['source_type'] ) ? wc_clean( $_POST['source_type'] ) : '';
0 ignored issues
show
Unused Code introduced by
$source_type 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...
332
		$validate_shipping_fields = false;
333
		$create_account = false;
334
335
		$all_fields      = apply_filters( 'wc_stripe_validate_checkout_all_fields', $all_fields );
336
		$required_fields = apply_filters( 'wc_stripe_validate_checkout_required_fields', $required_fields );
337
338
		array_walk_recursive( $required_fields, 'wc_clean' );
339
		array_walk_recursive( $all_fields, 'wc_clean' );
340
341
		/**
342
		 * If ship to different address checkbox is checked then we need
343
		 * to validate shipping fields too.
344
		 */
345
		if ( isset( $all_fields['ship_to_different_address'] ) ) {
346
			$validate_shipping_fields = true;
347
		}
348
349
		// Check if createaccount is checked.
350
		if ( isset( $all_fields['createaccount'] ) ) {
351
			$create_account = true;
352
		}
353
354
		// Check if required fields are empty.
355
		foreach ( $required_fields as $field => $field_value ) {
356
			// Check for shipping field.
357
			if ( preg_match( '/^shipping_/', $field ) && ! $validate_shipping_fields ) {
358
				continue;
359
			}
360
361
			// Check create account name.
362
			if ( 'account_username' === $field && ! $create_account ) {
363
				continue;
364
			}
365
366
			// Check create account password.
367
			if ( 'account_password' === $field && ! $create_account ) {
368
				continue;
369
			}
370
371
			if ( empty( $field_value ) || '-1' === $field_value ) {
372
				$error_field = $this->normalize_field( $field );
373
				/* translators: error field name */
374
				$errors->add( 'validation', sprintf( __( '<strong>%s</strong> cannot be empty', 'woocommerce-gateway-stripe' ), $error_field ) );
375
			}
376
		}
377
378
		// Check if email is valid format.
379
		if ( ! empty( $required_fields['billing_email'] ) && ! is_email( $required_fields['billing_email'] ) ) {
380
			$errors->add( 'validation', __( 'Email is not valid', 'woocommerce-gateway-stripe' ) );
381
		}
382
383
		// Check if phone number is valid format.
384
		if ( ! empty( $required_fields['billing_phone'] ) ) {
385
			$phone = wc_format_phone_number( $required_fields['billing_phone'] );
386
387
			if ( '' !== $phone && ! WC_Validation::is_phone( $phone ) ) {
388
				/* translators: %s: phone number */
389
				$errors->add( 'validation', __( 'Please enter a valid phone number.', 'woocommerce-gateway-stripe' ) );
390
			}
391
		}
392
393
		// Check if postal code is valid format.
394 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...
395
			$country = isset( $required_fields['billing_country'] ) ? $required_fields['billing_country'] : WC()->customer->get_billing_country();
396
			$postcode = wc_format_postcode( $required_fields['billing_postcode'], $country );
397
398
			if ( '' !== $required_fields['billing_postcode'] && ! WC_Validation::is_postcode( $postcode, $country ) ) {
399
				$errors->add( 'validation', __( 'Please enter a valid billing postcode / ZIP.', 'woocommerce-gateway-stripe' ) );
400
			}
401
		}
402
403
		// Don't check this on add payment method page.
404
		if ( ( isset( $_POST['is_add_payment_page'] ) && 'no' === $_POST['is_add_payment_page'] ) ) {
405
			if ( empty( $all_fields['woocommerce_checkout_update_totals'] ) && empty( $all_fields['terms'] ) && apply_filters( 'woocommerce_checkout_show_terms', wc_get_page_id( 'terms' ) > 0 ) ) {
406
				$errors->add( 'terms', __( 'You must accept our Terms &amp; Conditions.', 'woocommerce-gateway-stripe' ) );
407
			}
408
		}
409
410 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...
411
			// Check if postal code is valid format.
412
			if ( ! empty( $required_fields['shipping_postcode'] ) ) {
413
				$country = isset( $required_fields['shipping_country'] ) ? $required_fields['shipping_country'] : WC()->customer->get_shipping_country();
414
				$postcode = wc_format_postcode( $required_fields['shipping_postcode'], $country );
415
416
				if ( '' !== $required_fields['shipping_postcode'] && ! WC_Validation::is_postcode( $postcode, $country ) ) {
417
					$errors->add( 'validation', __( 'Please enter a valid shipping postcode / ZIP.', 'woocommerce-gateway-stripe' ) );
418
				}
419
			}
420
		}
421
422
		if ( WC()->cart->needs_shipping() ) {
423
			$shipping_country = WC()->customer->get_shipping_country();
424
425
			if ( empty( $shipping_country ) ) {
426
				$errors->add( 'shipping', __( 'Please enter an address to continue.', 'woocommerce-gateway-stripe' ) );
427
			} elseif ( ! in_array( WC()->customer->get_shipping_country(), array_keys( WC()->countries->get_shipping_countries() ) ) ) {
428
				/* translators: country name */
429
				$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() ) );
430
			} else {
431
				$chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods' );
432
433
				foreach ( WC()->shipping->get_packages() as $i => $package ) {
434
					if ( ! isset( $chosen_shipping_methods[ $i ], $package['rates'][ $chosen_shipping_methods[ $i ] ] ) ) {
435
						$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' ) );
436
					}
437
				}
438
			}
439
		}
440
441
		if ( WC()->cart->needs_payment() ) {
442
			$available_gateways = WC()->payment_gateways->get_available_payment_gateways();
443
444
			if ( ! isset( $available_gateways[ $all_fields['payment_method'] ] ) ) {
445
				$errors->add( 'payment', __( 'Invalid payment method.', 'woocommerce-gateway-stripe' ) );
446
			} else {
447
				$available_gateways[ $all_fields['payment_method'] ]->validate_fields();
448
			}
449
		}
450
451
		if ( 0 === count( $errors->errors ) ) {
452
			wp_send_json( 'success' );
453
		} else {
454
			foreach ( $errors->get_error_messages() as $message ) {
455
				wc_add_notice( $message, 'error' );
456
			}
457
458
			$this->send_ajax_failure_response();
459
		}
460
	}
461
462
	/**
463
	 * Preps the error messages to be displayed.
464
	 *
465
	 * @since 4.0.0
466
	 * @version 4.0.0
467
	 */
468
	public function send_ajax_failure_response() {
469
		if ( is_ajax() ) {
470
			// only print notices if not reloading the checkout, otherwise they're lost in the page reload.
471
			if ( ! isset( WC()->session->reload_checkout ) ) {
472
				ob_start();
473
				wc_print_notices();
474
				$messages = ob_get_clean();
475
			}
476
477
			$response = array(
478
				'result'   => 'failure',
479
				'messages' => isset( $messages ) ? $messages : '',
480
				'refresh'  => isset( WC()->session->refresh_totals ),
481
				'reload'   => isset( WC()->session->reload_checkout ),
482
			);
483
484
			unset( WC()->session->refresh_totals, WC()->session->reload_checkout );
485
486
			wp_send_json( $response );
487
		}
488
	}
489
}
490
491
new WC_Stripe_Order_Handler();
492