Completed
Push — master ( e3a5fe...3adadd )
by Roy
05:10
created

process_webhook_payment()   F

Complexity

Conditions 23
Paths 410

Size

Total Lines 122
Code Lines 68

Duplication

Lines 54
Ratio 44.26 %

Importance

Changes 0
Metric Value
cc 23
eloc 68
nc 410
nop 2
dl 54
loc 122
rs 3.5316
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
if ( ! defined( 'ABSPATH' ) ) {
3
	exit;
4
}
5
6
/**
7
 * Class WC_Stripe_Webhook_Handler.
8
 *
9
 * Handles webhooks from Stripe on sources that are not immediately chargeable.
10
 * @since 4.0.0
11
 */
12
class WC_Stripe_Webhook_Handler extends WC_Stripe_Payment_Gateway {
13
	/**
14
	 * Delay of retries.
15
	 *
16
	 * @var int
17
	 */
18
	public $retry_interval;
19
20
	/**
21
	 * Is test mode active?
22
	 *
23
	 * @var bool
24
	 */
25
	public $testmode;
26
27
	/**
28
	 * Constructor.
29
	 *
30
	 * @since 4.0.0
31
	 * @version 4.0.0
32
	 */
33
	public function __construct() {
34
		$this->retry_interval = 2;
35
		$stripe_settings      = get_option( 'woocommerce_stripe_settings', array() );
36
		$this->testmode       = ( ! empty( $stripe_settings['testmode'] ) && 'yes' === $stripe_settings['testmode'] ) ? true : false;
37
		add_action( 'woocommerce_api_wc_stripe', array( $this, 'check_for_webhook' ) );
38
	}
39
40
	/**
41
	 * Check incoming requests for Stripe Webhook data and process them.
42
	 *
43
	 * @since 4.0.0
44
	 * @version 4.0.0
45
	 */
46
	public function check_for_webhook() {
47
		if ( ( 'POST' !== $_SERVER['REQUEST_METHOD'] )
48
			|| ! isset( $_GET['wc-api'] )
49
			|| ( 'wc_stripe' !== $_GET['wc-api'] )
50
		) {
51
			return;
52
		}
53
54
		$request_body    = file_get_contents( 'php://input' );
55
		$request_headers = array_change_key_case( $this->get_request_headers(), CASE_UPPER );
56
57
		// Validate it to make sure it is legit.
58
		if ( $this->is_valid_request( $request_headers, $request_body ) ) {
59
			$this->process_webhook( $request_body );
60
			status_header( 200 );
61
			exit;
62
		} else {
63
			WC_Stripe_Logger::log( 'Incoming webhook failed validation: ' . print_r( $request_body, true ) );
64
			status_header( 400 );
65
			exit;
66
		}
67
	}
68
69
	/**
70
	 * Verify the incoming webhook notification to make sure it is legit.
71
	 *
72
	 * @since 4.0.0
73
	 * @version 4.0.0
74
	 * @todo Implement proper webhook signature validation. Ref https://stripe.com/docs/webhooks#signatures
75
	 * @param string $request_headers The request headers from Stripe.
76
	 * @param string $request_body The request body from Stripe.
77
	 * @return bool
78
	 */
79
	public function is_valid_request( $request_headers = null, $request_body = null ) {
80
		if ( null === $request_headers || null === $request_body ) {
81
			return false;
82
		}
83
84
		if ( ! empty( $request_headers['USER-AGENT'] ) && ! preg_match( '/Stripe/', $request_headers['USER-AGENT'] ) ) {
85
			return false;
86
		}
87
88
		return true;
89
	}
90
91
	/**
92
	 * Gets the incoming request headers. Some servers are not using
93
	 * Apache and "getallheaders()" will not work so we may need to
94
	 * build our own headers.
95
	 *
96
	 * @since 4.0.0
97
	 * @version 4.0.0
98
	 */
99
	public function get_request_headers() {
100
		if ( ! function_exists( 'getallheaders' ) ) {
101
			$headers = [];
102
			foreach ( $_SERVER as $name => $value ) {
103
				if ( 'HTTP_' === substr( $name, 0, 5 ) ) {
104
					$headers[ str_replace( ' ', '-', ucwords( strtolower( str_replace( '_', ' ', substr( $name, 5 ) ) ) ) ) ] = $value;
105
				}
106
			}
107
108
			return $headers;
109
		} else {
110
			return getallheaders();
111
		}
112
	}
113
114
	/**
115
	 * Process webhook payments.
116
	 * This is where we charge the source.
117
	 *
118
	 * @since 4.0.0
119
	 * @version 4.0.0
120
	 * @param object $notification
121
	 * @param bool $retry
122
	 */
123
	public function process_webhook_payment( $notification, $retry = true ) {
124
		// The following 2 payment methods are synchronous so does not need to be handle via webhook.
125
		if ( 'card' === $notification->data->object->type || 'sepa_debit' === $notification->data->object->type ) {
126
			return;
127
		}
128
129
		$order = WC_Stripe_Helper::get_order_by_source_id( $notification->data->object->id );
130
131
		if ( ! $order ) {
132
			WC_Stripe_Logger::log( 'Could not find order via source ID: ' . $notification->data->object->id );
133
			return;
134
		}
135
136
		$order_id  = WC_Stripe_Helper::is_pre_30() ? $order->id : $order->get_id();
137
		$source_id = $notification->data->object->id;
138
139
		$is_pending_receiver = ( 'receiver' === $notification->data->object->flow );
140
141
		try {
142
			if ( 'processing' === $order->get_status() || 'completed' === $order->get_status() ) {
143
				return;
144
			}
145
146
			if ( 'on-hold' === $order->get_status() && ! $is_pending_receiver ) {
147
				return;
148
			}
149
150
			// Result from Stripe API request.
151
			$response = null;
152
153
			// This will throw exception if not valid.
154
			$this->validate_minimum_order_amount( $order );
155
156
			WC_Stripe_Logger::log( "Info: (Webhook) Begin processing payment for order $order_id for the amount of {$order->get_total()}" );
157
158
			// Prep source object.
159
			$source_object           = new stdClass();
160
			$source_object->token_id = '';
161
			$source_object->customer = $this->get_stripe_customer_id( $order );
162
			$source_object->source   = $source_id;
163
164
			// Make the request.
165
			$response = WC_Stripe_API::request( $this->generate_payment_request( $order, $source_object ), 'charges', 'POST', true );
166
			$headers  = $response['headers'];
167
			$response = $response['body'];
168
169 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...
170
				// Customer param wrong? The user may have been deleted on stripe's end. Remove customer_id. Can be retried without.
171
				if ( $this->is_no_such_customer_error( $response->error ) ) {
172
					if ( WC_Stripe_Helper::is_pre_30() ) {
173
						delete_user_meta( $order->customer_user, '_stripe_customer_id' );
174
						delete_post_meta( $order_id, '_stripe_customer_id' );
175
					} else {
176
						delete_user_meta( $order->get_customer_id(), '_stripe_customer_id' );
177
						$order->delete_meta_data( '_stripe_customer_id' );
178
						$order->save();
179
					}
180
				}
181
182
				if ( $this->is_no_such_token_error( $response->error ) && $prepared_source->token_id ) {
183
					// Source param wrong? The CARD may have been deleted on stripe's end. Remove token and show message.
184
					$wc_token = WC_Payment_Tokens::get( $prepared_source->token_id );
0 ignored issues
show
Bug introduced by
The variable $prepared_source 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...
185
					$wc_token->delete();
186
					$localized_message = __( 'This card is no longer available and has been removed.', 'woocommerce-gateway-stripe' );
187
					$order->add_order_note( $localized_message );
188
					throw new WC_Stripe_Exception( print_r( $response, true ), $localized_message );
189
				}
190
191
				// We want to retry.
192
				if ( $this->is_retryable_error( $response->error ) ) {
193
					if ( $retry ) {
194
						// Don't do anymore retries after this.
195
						if ( 5 <= $this->retry_interval ) {
196
197
							return $this->process_webhook_payment( $notification, false );
198
						}
199
200
						sleep( $this->retry_interval );
201
202
						$this->retry_interval++;
203
						return $this->process_webhook_payment( $notification, true );
204
					} else {
205
						$localized_message = __( 'Sorry, we are unable to process your payment at this time. Please retry later.', 'woocommerce-gateway-stripe' );
206
						$order->add_order_note( $localized_message );
207
						throw new WC_Stripe_Exception( print_r( $response, true ), $localized_message );
208
					}
209
				}
210
211
				$localized_messages = WC_Stripe_Helper::get_localized_messages();
212
213
				if ( 'card_error' === $response->error->type ) {
214
					$localized_message = isset( $localized_messages[ $response->error->code ] ) ? $localized_messages[ $response->error->code ] : $response->error->message;
215
				} else {
216
					$localized_message = isset( $localized_messages[ $response->error->type ] ) ? $localized_messages[ $response->error->type ] : $response->error->message;
217
				}
218
219
				$order->add_order_note( $localized_message );
220
221
				throw new WC_Stripe_Exception( print_r( $response, true ), $localized_message );
222
			}
223
224
			// To prevent double processing the order on WC side.
225
			if ( ! $this->is_original_request( $headers ) ) {
226
				return;
227
			}
228
229
			do_action( 'wc_gateway_stripe_process_webhook_payment', $response, $order );
230
231
			$this->process_response( $response, $order );
232
233
		} catch ( WC_Stripe_Exception $e ) {
234
			WC_Stripe_Logger::log( 'Error: ' . $e->getMessage() );
235
236
			do_action( 'wc_gateway_stripe_process_webhook_payment_error', $e, $order );
237
238
			$statuses = array( 'pending', 'failed' );
239
240
			if ( $order->has_status( $statuses ) ) {
241
				$this->send_failed_order_email( $order_id );
242
			}
243
		}
244
	}
245
246
	/**
247
	 * Process webhook disputes that is created.
248
	 * This is trigger when a fraud is detected or customer processes chargeback.
249
	 * We want to put the order into on-hold and add an order note.
250
	 *
251
	 * @since 4.0.0
252
	 * @param object $notification
253
	 */
254
	public function process_webhook_dispute( $notification ) {
255
		$order = WC_Stripe_Helper::get_order_by_charge_id( $notification->data->object->charge );
256
257
		if ( ! $order ) {
258
			WC_Stripe_Logger::log( 'Could not find order via charge ID: ' . $notification->data->object->charge );
259
			return;
260
		}
261
262
		/* translators: 1) The URL to the order. */
263
		$order->update_status( 'on-hold', sprintf( __( 'A dispute was created for this order. Response is needed. Please go to your <a href="%s" title="Stripe Dashboard" target="_blank">Stripe Dashboard</a> to review this dispute.', 'woocommerce-gateway-stripe' ), $this->get_transaction_url( $order ) ) );
264
265
		do_action( 'wc_gateway_stripe_process_webhook_payment_error', $order, $notification );
266
267
		$order_id = WC_Stripe_Helper::is_pre_30() ? $order->id : $order->get_id();
268
		$this->send_failed_order_email( $order_id );
269
	}
270
271
	/**
272
	 * Process webhook capture. This is used for an authorized only
273
	 * transaction that is later captured via Stripe not WC.
274
	 *
275
	 * @since 4.0.0
276
	 * @version 4.0.0
277
	 * @param object $notification
278
	 */
279
	public function process_webhook_capture( $notification ) {
280
		$order = WC_Stripe_Helper::get_order_by_charge_id( $notification->data->object->id );
281
282
		if ( ! $order ) {
283
			WC_Stripe_Logger::log( 'Could not find order via charge ID: ' . $notification->data->object->id );
284
			return;
285
		}
286
287
		$order_id = WC_Stripe_Helper::is_pre_30() ? $order->id : $order->get_id();
288
289
		if ( 'stripe' === ( WC_Stripe_Helper::is_pre_30() ? $order->payment_method : $order->get_payment_method() ) ) {
290
			$charge   = WC_Stripe_Helper::is_pre_30() ? get_post_meta( $order_id, '_transaction_id', true ) : $order->get_transaction_id();
291
			$captured = WC_Stripe_Helper::is_pre_30() ? get_post_meta( $order_id, '_stripe_charge_captured', true ) : $order->get_meta( '_stripe_charge_captured', true );
292
293
			if ( $charge && 'no' === $captured ) {
294
				WC_Stripe_Helper::is_pre_30() ? update_post_meta( $order_id, '_stripe_charge_captured', 'yes' ) : $order->update_meta_data( '_stripe_charge_captured', 'yes' );
295
296
				// Store other data such as fees
297
				WC_Stripe_Helper::is_pre_30() ? update_post_meta( $order_id, '_transaction_id', $notification->data->object->id ) : $order->set_transaction_id( $notification->data->object->id );
298
299
				if ( isset( $notification->data->object->balance_transaction ) ) {
300
					$this->update_fees( $order, $notification->data->object->balance_transaction );
301
				}
302
303
				if ( is_callable( array( $order, 'save' ) ) ) {
304
					$order->save();
305
				}
306
307
				/* translators: transaction id */
308
				$order->update_status( $order->needs_processing() ? 'processing' : 'completed', sprintf( __( 'Stripe charge complete (Charge ID: %s)', 'woocommerce-gateway-stripe' ), $notification->data->object->id ) );
309
310
				// Check and see if capture is partial.
311
				if ( $this->is_partial_capture( $notification ) ) {
312
					$order->set_total( $this->get_partial_amount_to_charge( $notification ) );
313
					$order->add_order_note( __( 'This charge was partially captured via Stripe Dashboard', 'woocommerce-gateway-stripe' ) );
314
					$order->save();
315
				}
316
			}
317
		}
318
	}
319
320
	/**
321
	 * Process webhook charge succeeded. This is used for payment methods
322
	 * that takes time to clear which is asynchronous. e.g. SEPA, SOFORT.
323
	 *
324
	 * @since 4.0.0
325
	 * @version 4.0.0
326
	 * @param object $notification
327
	 */
328
	public function process_webhook_charge_succeeded( $notification ) {
329
		// The following payment methods are synchronous so does not need to be handle via webhook.
330
		if ( ( isset( $notification->data->object->source->type ) && 'card' === $notification->data->object->source->type ) || ( isset( $notification->data->object->source->type ) && 'three_d_secure' === $notification->data->object->source->type ) ) {
331
			return;
332
		}
333
334
		$order = WC_Stripe_Helper::get_order_by_charge_id( $notification->data->object->id );
335
336
		if ( ! $order ) {
337
			WC_Stripe_Logger::log( 'Could not find order via charge ID: ' . $notification->data->object->id );
338
			return;
339
		}
340
341
		$order_id = WC_Stripe_Helper::is_pre_30() ? $order->id : $order->get_id();
342
343
		if ( 'on-hold' !== $order->get_status() ) {
344
			return;
345
		}
346
347
		// Store other data such as fees
348
		WC_Stripe_Helper::is_pre_30() ? update_post_meta( $order_id, '_transaction_id', $notification->data->object->id ) : $order->set_transaction_id( $notification->data->object->id );
349
350
		if ( isset( $notification->data->object->balance_transaction ) ) {
351
			$this->update_fees( $order, $notification->data->object->balance_transaction );
352
		}
353
354
		if ( is_callable( array( $order, 'save' ) ) ) {
355
			$order->save();
356
		}
357
358
		/* translators: transaction id */
359
		$order->update_status( $order->needs_processing() ? 'processing' : 'completed', sprintf( __( 'Stripe charge complete (Charge ID: %s)', 'woocommerce-gateway-stripe' ), $notification->data->object->id ) );
360
	}
361
362
	/**
363
	 * Process webhook charge failed. This is used for payment methods
364
	 * that takes time to clear which is asynchronous. e.g. SEPA, SOFORT.
365
	 *
366
	 * @since 4.0.0
367
	 * @version 4.0.0
368
	 * @param object $notification
369
	 */
370 View Code Duplication
	public function process_webhook_charge_failed( $notification ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
371
		$order = WC_Stripe_Helper::get_order_by_charge_id( $notification->data->object->id );
372
373
		if ( ! $order ) {
374
			WC_Stripe_Logger::log( 'Could not find order via charge ID: ' . $notification->data->object->id );
375
			return;
376
		}
377
378
		$order_id = WC_Stripe_Helper::is_pre_30() ? $order->id : $order->get_id();
0 ignored issues
show
Unused Code introduced by
$order_id 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...
379
380
		if ( 'on-hold' !== $order->get_status() ) {
381
			return;
382
		}
383
384
		$order->update_status( 'failed', __( 'This payment failed to clear.', 'woocommerce-gateway-stripe' ) );
385
386
		do_action( 'wc_gateway_stripe_process_webhook_payment_error', $order, $notification );
387
	}
388
389
	/**
390
	 * Process webhook source canceled. This is used for payment methods
391
	 * that redirects and awaits payments from customer.
392
	 *
393
	 * @since 4.0.0
394
	 * @version 4.0.0
395
	 * @param object $notification
396
	 */
397 View Code Duplication
	public function process_webhook_source_canceled( $notification ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
398
		$order = WC_Stripe_Helper::get_order_by_charge_id( $notification->data->object->id );
399
400
		if ( ! $order ) {
401
			WC_Stripe_Logger::log( 'Could not find order via charge ID: ' . $notification->data->object->id );
402
			return;
403
		}
404
405
		$order_id = WC_Stripe_Helper::is_pre_30() ? $order->id : $order->get_id();
0 ignored issues
show
Unused Code introduced by
$order_id 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...
406
407
		if ( 'on-hold' !== $order->get_status() || 'cancelled' !== $order->get_status() ) {
408
			return;
409
		}
410
411
		$order->update_status( 'cancelled', __( 'This payment has cancelled.', 'woocommerce-gateway-stripe' ) );
412
413
		do_action( 'wc_gateway_stripe_process_webhook_payment_error', $order, $notification );
414
	}
415
416
	/**
417
	 * Process webhook refund.
418
	 *
419
	 * @since 4.0.0
420
	 * @version 4.0.0
421
	 * @param object $notification
422
	 */
423
	public function process_webhook_refund( $notification ) {
424
		$order = WC_Stripe_Helper::get_order_by_charge_id( $notification->data->object->id );
425
426
		if ( ! $order ) {
427
			WC_Stripe_Logger::log( 'Could not find order via charge ID: ' . $notification->data->object->id );
428
			return;
429
		}
430
431
		$order_id = WC_Stripe_Helper::is_pre_30() ? $order->id : $order->get_id();
432
433
		if ( 'stripe' === ( WC_Stripe_Helper::is_pre_30() ? $order->payment_method : $order->get_payment_method() ) ) {
434
			$charge    = WC_Stripe_Helper::is_pre_30() ? get_post_meta( $order_id, '_transaction_id', true ) : $order->get_transaction_id();
435
			$captured  = WC_Stripe_Helper::is_pre_30() ? get_post_meta( $order_id, '_stripe_charge_captured', true ) : $order->get_meta( '_stripe_charge_captured', true );
436
			$refund_id = WC_Stripe_Helper::is_pre_30() ? get_post_meta( $order_id, '_stripe_refund_id', true ) : $order->get_meta( '_stripe_refund_id', true );
437
438
			// If the refund ID matches, don't continue to prevent double refunding.
439
			if ( $notification->data->object->refunds->data[0]->id === $refund_id ) {
440
				return;
441
			}
442
443
			// Only refund captured charge.
444
			if ( $charge ) {
445
				$reason = ( isset( $captured ) && 'yes' === $captured ) ? __( 'Refunded via Stripe Dashboard', 'woocommerce-gateway-stripe' ) : __( 'Pre-Authorization Released via Stripe Dashboard', 'woocommerce-gateway-stripe' );
446
447
				// Create the refund.
448
				$refund = wc_create_refund( array(
449
					'order_id' => $order_id,
450
					'amount'   => $this->get_refund_amount( $notification ),
451
					'reason'   => $reason,
452
				) );
453
454
				if ( is_wp_error( $refund ) ) {
455
					WC_Stripe_Logger::log( $refund->get_error_message() );
456
				}
457
458
				WC_Stripe_Helper::is_pre_30() ? update_post_meta( $order_id, '_stripe_refund_id', $notification->data->object->refunds->data[0]->id ) : $order->update_meta_data( '_stripe_refund_id', $notification->data->object->refunds->data[0]->id );
459
460
				$amount = wc_price( $notification->data->object->refunds->data[0]->amount / 100 );
461
462
				if ( in_array( strtolower( $order->get_currency() ), WC_Stripe_Helper::no_decimal_currencies() ) ) {
463
					$amount = wc_price( $notification->data->object->refunds->data[0]->amount );
464
				}
465
466
				if ( isset( $notification->data->object->refunds->data[0]->balance_transaction ) ) {
467
					$this->update_fees( $order, $notification->data->object->refunds->data[0]->balance_transaction );
468
				}
469
470
				/* translators: 1) dollar amount 2) transaction id 3) refund message */
471
				$refund_message = ( isset( $captured ) && 'yes' === $captured ) ? sprintf( __( 'Refunded %1$s - Refund ID: %2$s - %3$s', 'woocommerce-gateway-stripe' ), $amount, $notification->data->object->refunds->data[0]->id, $reason ) : __( 'Pre-Authorization Released via Stripe Dashboard', 'woocommerce-gateway-stripe' );
472
473
				$order->add_order_note( $refund_message );
474
			}
475
		}
476
	}
477
478
	/**
479
	 * Process webhook reviews that are opened. i.e Radar.
480
	 *
481
	 * @since 4.0.6
482
	 * @param object $notification
483
	 */
484
	public function process_review_opened( $notification ) {
485
		$order = WC_Stripe_Helper::get_order_by_charge_id( $notification->data->object->charge );
486
487
		if ( ! $order ) {
488
			WC_Stripe_Logger::log( 'Could not find order via charge ID: ' . $notification->data->object->charge );
489
			return;
490
		}
491
492
		/* translators: 1) The URL to the order. 2) The reason type. */
493
		$message = sprintf( __( 'A review has been opened for this order. Action is needed. Please go to your <a href="%1$s" title="Stripe Dashboard" target="_blank">Stripe Dashboard</a> to review the issue. Reason: (%2$s)', 'woocommerce-gateway-stripe' ), $this->get_transaction_url( $order ), $notification->data->object->reason );
494
495 View Code Duplication
		if ( apply_filters( 'wc_stripe_webhook_review_change_order_status', true, $order, $notification ) ) {
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...
496
			$order->update_status( 'on-hold', $message );
497
		} else {
498
			$order->add_order_note( $message );
499
		}
500
	}
501
502
	/**
503
	 * Process webhook reviews that are closed. i.e Radar.
504
	 *
505
	 * @since 4.0.6
506
	 * @param object $notification
507
	 */
508
	public function process_review_closed( $notification ) {
509
		$order = WC_Stripe_Helper::get_order_by_charge_id( $notification->data->object->charge );
510
511
		if ( ! $order ) {
512
			WC_Stripe_Logger::log( 'Could not find order via charge ID: ' . $notification->data->object->charge );
513
			return;
514
		}
515
516
		/* translators: 1) The reason type. */
517
		$message = sprintf( __( 'The opened review for this order is now closed. Reason: (%s)', 'woocommerce-gateway-stripe' ), $notification->data->object->reason );
518
519
		if ( 'on-hold' === $order->get_status() ) {
520 View Code Duplication
			if ( apply_filters( 'wc_stripe_webhook_review_change_order_status', true, $order, $notification ) ) {
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...
521
				$order->update_status( 'processing', $message );
522
			} else {
523
				$order->add_order_note( $message );
524
			}
525
		} else {
526
			$order->add_order_note( $message );
527
		}
528
	}
529
530
	/**
531
	 * Checks if capture is partial.
532
	 *
533
	 * @since 4.0.0
534
	 * @version 4.0.0
535
	 * @param object $notification
536
	 */
537
	public function is_partial_capture( $notification ) {
538
		return 0 < $notification->data->object->amount_refunded;
539
	}
540
541
	/**
542
	 * Gets the amount refunded.
543
	 *
544
	 * @since 4.0.0
545
	 * @version 4.0.0
546
	 * @param object $notification
547
	 */
548
	public function get_refund_amount( $notification ) {
549
		if ( $this->is_partial_capture( $notification ) ) {
550
			$amount = $notification->data->object->refunds->data[0]->amount / 100;
551
552 View Code Duplication
			if ( in_array( strtolower( $notification->data->object->currency ), WC_Stripe_Helper::no_decimal_currencies() ) ) {
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...
553
				$amount = $notification->data->object->refunds->data[0]->amount;
554
			}
555
556
			return $amount;
557
		}
558
559
		return false;
560
	}
561
562
	/**
563
	 * Gets the amount we actually charge.
564
	 *
565
	 * @since 4.0.0
566
	 * @version 4.0.0
567
	 * @param object $notification
568
	 */
569
	public function get_partial_amount_to_charge( $notification ) {
570
		if ( $this->is_partial_capture( $notification ) ) {
571
			$amount = ( $notification->data->object->amount - $notification->data->object->amount_refunded ) / 100;
572
573 View Code Duplication
			if ( in_array( strtolower( $notification->data->object->currency ), WC_Stripe_Helper::no_decimal_currencies() ) ) {
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...
574
				$amount = ( $notification->data->object->amount - $notification->data->object->amount_refunded );
575
			}
576
577
			return $amount;
578
		}
579
580
		return false;
581
	}
582
583
	/**
584
	 * Processes the incoming webhook.
585
	 *
586
	 * @since 4.0.0
587
	 * @version 4.0.0
588
	 * @param string $request_body
589
	 */
590
	public function process_webhook( $request_body ) {
591
		$notification = json_decode( $request_body );
592
593
		switch ( $notification->type ) {
594
			case 'source.chargeable':
595
				$this->process_webhook_payment( $notification );
596
				break;
597
598
			case 'source.canceled':
599
				$this->process_webhook_source_canceled( $notification );
600
				break;
601
602
			case 'charge.succeeded':
603
				$this->process_webhook_charge_succeeded( $notification );
604
				break;
605
606
			case 'charge.failed':
607
				$this->process_webhook_charge_failed( $notification );
608
				break;
609
610
			case 'charge.captured':
611
				$this->process_webhook_capture( $notification );
612
				break;
613
614
			case 'charge.dispute.created':
615
				$this->process_webhook_dispute( $notification );
616
				break;
617
618
			case 'charge.refunded':
619
				$this->process_webhook_refund( $notification );
620
				break;
621
622
			case 'review.opened':
623
				$this->process_review_opened( $notification );
624
				break;
625
626
			case 'review.closed':
627
				$this->process_review_closed( $notification );
628
				break;
629
630
		}
631
	}
632
}
633
634
new WC_Stripe_Webhook_Handler();
635