Completed
Push — master ( 22bdab...c92d4b )
by Roy
02:24
created

WC_Stripe_Order_Handler::normalize_field()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 32
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 22
nc 3
nop 1
dl 0
loc 32
rs 8.8571
c 0
b 0
f 0
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 with appropriate locale.
282
	 *
283
	 * @since 4.0.0
284
	 * @since 4.0.1 Map localized checkout fields.
285
	 * @param string $field
286
	 * @return string $error_field
287
	 */
288
	public function normalize_field( $field ) {
289
		$checkout_fields = WC()->checkout->get_checkout_fields();
290
		$org_str         = array();
291
		$replace_str     = array();
292
293
		if ( array_key_exists( $field, $checkout_fields['billing'] ) ) {
294
			$error_field = $checkout_fields['billing'][ $field ]['label'];
295
		} elseif ( array_key_exists( $field, $checkout_fields['shipping'] ) ) {
296
			$error_field = $checkout_fields['shipping'][ $field ]['label'];
297
		} else {
298
			$error_field = str_replace( '_', ' ', $field );
299
300
			$org_str[]     = 'stripe';
301
			$replace_str[] = '';
302
303
			$org_str[]     = 'sepa';
304
			$replace_str[] = 'SEPA';
305
306
			$org_str[]     = 'iban';
307
			$replace_str[] = 'IBAN';
308
309
			$org_str[]     = 'sofort';
310
			$replace_str[] = 'SOFORT';
311
312
			$org_str[]     = 'owner';
313
			$replace_str[] = __( 'Owner', 'woocommerce-gateway-stripe' );
314
315
			$error_field   = str_replace( $org_str, $replace_str, $error_field );
316
		}
317
318
		return $error_field;
319
	}
320
321
	/**
322
	 * Validates the checkout before submitting checkout form.
323
	 *
324
	 * @since 4.0.0
325
	 * @version 4.0.0
326
	 */
327
	public function validate_checkout() {
328
		if ( ! wp_verify_nonce( $_POST['nonce'], '_wc_stripe_nonce' ) ) {
329
			wp_die( __( 'Cheatin&#8217; huh?', 'woocommerce-gateway-stripe' ) );
330
		}
331
332
		$errors = new WP_Error();
333
		parse_str( $_POST['required_fields'], $required_fields );
334
		parse_str( $_POST['all_fields'], $all_fields );
335
		$source_type = isset( $_POST['source_type'] ) ? wc_clean( $_POST['source_type'] ) : '';
336
		$validate_shipping_fields = false;
337
		$create_account = false;
338
339
		array_walk_recursive( $required_fields, 'wc_clean' );
340
		array_walk_recursive( $all_fields, 'wc_clean' );
341
342
		// Remove unneeded required fields depending on source type.
343
		if ( 'stripe_sepa' !== $source_type ) {
344
			unset( $required_fields['stripe_sepa_owner'] );
345
			unset( $required_fields['stripe_sepa_iban'] );
346
		}
347
348
		if ( 'stripe_sofort' !== $source_type ) {
349
			unset( $required_fields['stripe_sofort_bank_country'] );
350
		}
351
352
		/**
353
		 * If ship to different address checkbox is checked then we need
354
		 * to validate shipping fields too.
355
		 */
356
		if ( isset( $all_fields['ship_to_different_address'] ) ) {
357
			$validate_shipping_fields = true;
358
		}
359
360
		// Check if createaccount is checked.
361
		if ( isset( $all_fields['createaccount'] ) ) {
362
			$create_account = true;
363
		}
364
365
		// Check if required fields are empty.
366
		foreach ( $required_fields as $field => $field_value ) {
367
			// Check for shipping field.
368
			if ( preg_match( '/^shipping_/', $field ) && ! $validate_shipping_fields ) {
369
				continue;
370
			}
371
372
			// Check create account name.
373
			if ( 'account_username' === $field && ! $create_account ) {
374
				continue;
375
			}
376
377
			// Check create account password.
378
			if ( 'account_password' === $field && ! $create_account ) {
379
				continue;
380
			}
381
382
			// Check if is SEPA.
383
			if ( 'stripe_sepa' !== $source_type && 'stripe_sepa_owner' === $field ) {
384
				continue;
385
			}
386
387
			if ( 'stripe_sepa' !== $source_type && 'stripe_sepa_iban' === $field ) {
388
				$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...
389
			}
390
391
			if ( empty( $field_value ) || '-1' === $field_value ) {
392
				$error_field = $this->normalize_field( $field );
393
				/* translators: error field name */
394
				$errors->add( 'validation', sprintf( __( '%s cannot be empty', 'woocommerce-gateway-stripe' ), $error_field ) );
395
			}
396
		}
397
398
		// Check if email is valid format.
399
		if ( ! empty( $required_fields['billing_email'] ) && ! is_email( $required_fields['billing_email'] ) ) {
400
			$errors->add( 'validation', __( 'Email is not valid', 'woocommerce-gateway-stripe' ) );
401
		}
402
403
		// Check if phone number is valid format.
404
		if ( ! empty( $required_fields['billing_phone'] ) ) {
405
			$phone = wc_format_phone_number( $required_fields['billing_phone'] );
406
407
			if ( '' !== $phone && ! WC_Validation::is_phone( $phone ) ) {
408
				/* translators: %s: phone number */
409
				$errors->add( 'validation', __( 'Please enter a valid phone number.', 'woocommerce-gateway-stripe' ) );
410
			}
411
		}
412
413
		// Check if postal code is valid format.
414 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...
415
			$country = isset( $required_fields['billing_country'] ) ? $required_fields['billing_country'] : WC()->customer->get_billing_country();
416
			$postcode = wc_format_postcode( $required_fields['billing_postcode'], $country );
417
418
			if ( '' !== $required_fields['billing_postcode'] && ! WC_Validation::is_postcode( $postcode, $country ) ) {
419
				$errors->add( 'validation', __( 'Please enter a valid billing postcode / ZIP.', 'woocommerce-gateway-stripe' ) );
420
			}
421
		}
422
423
		// Don't check this on add payment method page.
424
		if ( ! $_POST['is_add_payment_page'] ) {
425
			if ( empty( $all_fields['woocommerce_checkout_update_totals'] ) && empty( $all_fields['terms'] ) && apply_filters( 'woocommerce_checkout_show_terms', wc_get_page_id( 'terms' ) > 0 ) ) {
426
				$errors->add( 'terms', __( 'You must accept our Terms &amp; Conditions.', 'woocommerce-gateway-stripe' ) );
427
			}
428
		}
429
430 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...
431
			// Check if postal code is valid format.
432
			if ( ! empty( $required_fields['shipping_postcode'] ) ) {
433
				$country = isset( $required_fields['shipping_country'] ) ? $required_fields['shipping_country'] : WC()->customer->get_shipping_country();
434
				$postcode = wc_format_postcode( $required_fields['shipping_postcode'], $country );
435
436
				if ( '' !== $required_fields['shipping_postcode'] && ! WC_Validation::is_postcode( $postcode, $country ) ) {
437
					$errors->add( 'validation', __( 'Please enter a valid shipping postcode / ZIP.', 'woocommerce-gateway-stripe' ) );
438
				}
439
			}
440
		}
441
442
		if ( WC()->cart->needs_shipping() ) {
443
			$shipping_country = WC()->customer->get_shipping_country();
444
445
			if ( empty( $shipping_country ) ) {
446
				$errors->add( 'shipping', __( 'Please enter an address to continue.', 'woocommerce-gateway-stripe' ) );
447
			} elseif ( ! in_array( WC()->customer->get_shipping_country(), array_keys( WC()->countries->get_shipping_countries() ) ) ) {
448
				/* translators: country name */
449
				$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() ) );
450
			} else {
451
				$chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods' );
452
453
				foreach ( WC()->shipping->get_packages() as $i => $package ) {
454
					if ( ! isset( $chosen_shipping_methods[ $i ], $package['rates'][ $chosen_shipping_methods[ $i ] ] ) ) {
455
						$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' ) );
456
					}
457
				}
458
			}
459
		}
460
461
		if ( WC()->cart->needs_payment() ) {
462
			$available_gateways = WC()->payment_gateways->get_available_payment_gateways();
463
464
			if ( ! isset( $available_gateways[ $all_fields['payment_method'] ] ) ) {
465
				$errors->add( 'payment', __( 'Invalid payment method.', 'woocommerce-gateway-stripe' ) );
466
			} else {
467
				$available_gateways[ $all_fields['payment_method'] ]->validate_fields();
468
			}
469
		}
470
471
		if ( 0 === count( $errors->errors ) ) {
472
			wp_send_json( 'success' );
473
		} else {
474
			foreach ( $errors->get_error_messages() as $message ) {
475
				wc_add_notice( $message, 'error' );
476
			}
477
478
			$this->send_ajax_failure_response();
479
		}
480
	}
481
482
	/**
483
	 * Preps the error messages to be displayed.
484
	 *
485
	 * @since 4.0.0
486
	 * @version 4.0.0
487
	 */
488
	public function send_ajax_failure_response() {
489
		if ( is_ajax() ) {
490
			// only print notices if not reloading the checkout, otherwise they're lost in the page reload.
491
			if ( ! isset( WC()->session->reload_checkout ) ) {
492
				ob_start();
493
				wc_print_notices();
494
				$messages = ob_get_clean();
495
			}
496
497
			$response = array(
498
				'result'   => 'failure',
499
				'messages' => isset( $messages ) ? $messages : '',
500
				'refresh'  => isset( WC()->session->refresh_totals ),
501
				'reload'   => isset( WC()->session->reload_checkout ),
502
			);
503
504
			unset( WC()->session->refresh_totals, WC()->session->reload_checkout );
505
506
			wp_send_json( $response );
507
		}
508
	}
509
}
510
511
new WC_Stripe_Order_Handler();
512