Completed
Push — master ( cdc1b1...f6cdcf )
by Roy
10s
created

WC_Stripe_Webhook_Handler   D

Complexity

Total Complexity 119

Size/Duplication

Total Lines 613
Duplicated Lines 18.6 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
dl 114
loc 613
rs 4.8198
c 0
b 0
f 0
wmc 119
lcom 1
cbo 5

17 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 3
B check_for_webhook() 0 22 5
B is_valid_request() 0 11 5
A get_request_headers() 0 14 4
D process_webhook_payment() 67 128 26
A process_webhook_dispute() 0 16 3
F process_webhook_capture() 0 40 15
C process_webhook_charge_succeeded() 0 33 12
A process_webhook_charge_failed() 18 18 4
B process_webhook_source_canceled() 18 18 5
D process_webhook_refund() 0 39 13
A process_review_opened() 6 17 3
A process_review_closed() 5 21 4
A is_partial_capture() 0 3 1
A get_refund_amount() 0 13 3
A get_partial_amount_to_charge() 0 13 3
D process_webhook() 0 42 10

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_Webhook_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_Webhook_Handler, and based on these observations, apply Extract Interface, too.

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 ) );
166
167 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...
168
				// If it is an API error such connection or server, let's retry.
169
				if ( 'api_connection_error' === $response->error->type || 'api_error' === $response->error->type ) {
170
					if ( $retry ) {
171
						sleep( 5 );
172
						return $this->process_webhook_payment( $notification, false );
173
					} else {
174
						$localized_message = 'API connection error and retries exhausted.';
175
						$order->add_order_note( $localized_message );
176
						throw new WC_Stripe_Exception( print_r( $response, true ), $localized_message );
177
					}
178
				}
179
180
				// We want to retry.
181
				if ( $this->is_retryable_error( $response->error ) ) {
182
					if ( $retry ) {
183
						// Don't do anymore retries after this.
184
						if ( 5 <= $this->retry_interval ) {
185
186
							return $this->process_webhook_payment( $notification, false );
187
						}
188
189
						sleep( $this->retry_interval );
190
191
						$this->retry_interval++;
192
						return $this->process_webhook_payment( $notification, true );
193
					} else {
194
						$localized_message = __( 'On going requests error and retries exhausted.', 'woocommerce-gateway-stripe' );
195
						$order->add_order_note( $localized_message );
196
						throw new WC_Stripe_Exception( print_r( $response, true ), $localized_message );
197
					}
198
				}
199
200
				// Customer param wrong? The user may have been deleted on stripe's end. Remove customer_id. Can be retried without.
201
				if ( preg_match( '/No such customer/i', $response->error->message ) && $retry ) {
202
					if ( WC_Stripe_Helper::is_pre_30() ) {
203
						delete_user_meta( $order->customer_user, '_stripe_customer_id' );
204
						delete_post_meta( $order_id, '_stripe_customer_id' );
205
					} else {
206
						delete_user_meta( $order->get_customer_id(), '_stripe_customer_id' );
207
						$order->delete_meta_data( '_stripe_customer_id' );
208
						$order->save();
209
					}
210
211
					return $this->process_webhook_payment( $notification, false );
212
213
				} elseif ( preg_match( '/No such token/i', $response->error->message ) && $source_object->token_id ) {
214
					// Source param wrong? The CARD may have been deleted on stripe's end. Remove token and show message.
215
					$wc_token = WC_Payment_Tokens::get( $source_object->token_id );
216
					$wc_token->delete();
217
					$message = __( 'This card is no longer available and has been removed.', 'woocommerce-gateway-stripe' );
218
					$order->add_order_note( $message );
219
					throw new WC_Stripe_Exception( print_r( $response, true ), $message );
220
				}
221
222
				$localized_messages = WC_Stripe_Helper::get_localized_messages();
223
224
				if ( 'card_error' === $response->error->type ) {
225
					$localized_message = isset( $localized_messages[ $response->error->code ] ) ? $localized_messages[ $response->error->code ] : $response->error->message;
226
				} else {
227
					$localized_message = isset( $localized_messages[ $response->error->type ] ) ? $localized_messages[ $response->error->type ] : $response->error->message;
228
				}
229
230
				$order->add_order_note( $localized_message );
231
232
				throw new WC_Stripe_Exception( print_r( $response, true ), $localized_message );
233
			}
234
235
			do_action( 'wc_gateway_stripe_process_webhook_payment', $response, $order );
236
237
			$this->process_response( $response, $order );
238
239
		} catch ( WC_Stripe_Exception $e ) {
240
			WC_Stripe_Logger::log( 'Error: ' . $e->getMessage() );
241
242
			do_action( 'wc_gateway_stripe_process_webhook_payment_error', $e, $order );
243
244
			$statuses = array( 'pending', 'failed' );
245
246
			if ( $order->has_status( $statuses ) ) {
247
				$this->send_failed_order_email( $order_id );
248
			}
249
		}
250
	}
251
252
	/**
253
	 * Process webhook disputes that is created.
254
	 * This is trigger when a fraud is detected or customer processes chargeback.
255
	 * We want to put the order into on-hold and add an order note.
256
	 *
257
	 * @since 4.0.0
258
	 * @param object $notification
259
	 */
260
	public function process_webhook_dispute( $notification ) {
261
		$order = WC_Stripe_Helper::get_order_by_charge_id( $notification->data->object->charge );
262
263
		if ( ! $order ) {
264
			WC_Stripe_Logger::log( 'Could not find order via charge ID: ' . $notification->data->object->charge );
265
			return;
266
		}
267
268
		/* translators: 1) The URL to the order. */
269
		$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 ) ) );
270
271
		do_action( 'wc_gateway_stripe_process_webhook_payment_error', $order, $notification );
272
273
		$order_id = WC_Stripe_Helper::is_pre_30() ? $order->id : $order->get_id();
274
		$this->send_failed_order_email( $order_id );
275
	}
276
277
	/**
278
	 * Process webhook capture. This is used for an authorized only
279
	 * transaction that is later captured via Stripe not WC.
280
	 *
281
	 * @since 4.0.0
282
	 * @version 4.0.0
283
	 * @param object $notification
284
	 */
285
	public function process_webhook_capture( $notification ) {
286
		$order = WC_Stripe_Helper::get_order_by_charge_id( $notification->data->object->id );
287
288
		if ( ! $order ) {
289
			WC_Stripe_Logger::log( 'Could not find order via charge ID: ' . $notification->data->object->id );
290
			return;
291
		}
292
293
		$order_id = WC_Stripe_Helper::is_pre_30() ? $order->id : $order->get_id();
294
295
		if ( 'stripe' === ( WC_Stripe_Helper::is_pre_30() ? $order->payment_method : $order->get_payment_method() ) ) {
296
			$charge   = WC_Stripe_Helper::is_pre_30() ? get_post_meta( $order_id, '_transaction_id', true ) : $order->get_transaction_id();
297
			$captured = WC_Stripe_Helper::is_pre_30() ? get_post_meta( $order_id, '_stripe_charge_captured', true ) : $order->get_meta( '_stripe_charge_captured', true );
298
299
			if ( $charge && 'no' === $captured ) {
300
				WC_Stripe_Helper::is_pre_30() ? update_post_meta( $order_id, '_stripe_charge_captured', 'yes' ) : $order->update_meta_data( '_stripe_charge_captured', 'yes' );
301
302
				// Store other data such as fees
303
				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 );
304
305
				if ( isset( $notification->data->object->balance_transaction ) ) {
306
					$this->update_fees( $order, $notification->data->object->balance_transaction );
307
				}
308
309
				if ( is_callable( array( $order, 'save' ) ) ) {
310
					$order->save();
311
				}
312
313
				/* translators: transaction id */
314
				$order->update_status( $order->needs_processing() ? 'processing' : 'completed', sprintf( __( 'Stripe charge complete (Charge ID: %s)', 'woocommerce-gateway-stripe' ), $notification->data->object->id ) );
315
316
				// Check and see if capture is partial.
317
				if ( $this->is_partial_capture( $notification ) ) {
318
					$order->set_total( $this->get_partial_amount_to_charge( $notification ) );
319
					$order->add_order_note( __( 'This charge was partially captured via Stripe Dashboard', 'woocommerce-gateway-stripe' ) );
320
					$order->save();
321
				}
322
			}
323
		}
324
	}
325
326
	/**
327
	 * Process webhook charge succeeded. This is used for payment methods
328
	 * that takes time to clear which is asynchronous. e.g. SEPA, SOFORT.
329
	 *
330
	 * @since 4.0.0
331
	 * @version 4.0.0
332
	 * @param object $notification
333
	 */
334
	public function process_webhook_charge_succeeded( $notification ) {
335
		// The following payment methods are synchronous so does not need to be handle via webhook.
336
		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 ) ) {
337
			return;
338
		}
339
340
		$order = WC_Stripe_Helper::get_order_by_charge_id( $notification->data->object->id );
341
342
		if ( ! $order ) {
343
			WC_Stripe_Logger::log( 'Could not find order via charge ID: ' . $notification->data->object->id );
344
			return;
345
		}
346
347
		$order_id = WC_Stripe_Helper::is_pre_30() ? $order->id : $order->get_id();
348
349
		if ( 'on-hold' !== $order->get_status() ) {
350
			return;
351
		}
352
353
		// Store other data such as fees
354
		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 );
355
356
		if ( isset( $notification->data->object->balance_transaction ) ) {
357
			$this->update_fees( $order, $notification->data->object->balance_transaction );
358
		}
359
360
		if ( is_callable( array( $order, 'save' ) ) ) {
361
			$order->save();
362
		}
363
364
		/* translators: transaction id */
365
		$order->update_status( $order->needs_processing() ? 'processing' : 'completed', sprintf( __( 'Stripe charge complete (Charge ID: %s)', 'woocommerce-gateway-stripe' ), $notification->data->object->id ) );
366
	}
367
368
	/**
369
	 * Process webhook charge failed. This is used for payment methods
370
	 * that takes time to clear which is asynchronous. e.g. SEPA, SOFORT.
371
	 *
372
	 * @since 4.0.0
373
	 * @version 4.0.0
374
	 * @param object $notification
375
	 */
376 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...
377
		$order = WC_Stripe_Helper::get_order_by_charge_id( $notification->data->object->id );
378
379
		if ( ! $order ) {
380
			WC_Stripe_Logger::log( 'Could not find order via charge ID: ' . $notification->data->object->id );
381
			return;
382
		}
383
384
		$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...
385
386
		if ( 'on-hold' !== $order->get_status() ) {
387
			return;
388
		}
389
390
		$order->update_status( 'failed', __( 'This payment failed to clear.', 'woocommerce-gateway-stripe' ) );
391
392
		do_action( 'wc_gateway_stripe_process_webhook_payment_error', $order, $notification );
393
	}
394
395
	/**
396
	 * Process webhook source canceled. This is used for payment methods
397
	 * that redirects and awaits payments from customer.
398
	 *
399
	 * @since 4.0.0
400
	 * @version 4.0.0
401
	 * @param object $notification
402
	 */
403 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...
404
		$order = WC_Stripe_Helper::get_order_by_charge_id( $notification->data->object->id );
405
406
		if ( ! $order ) {
407
			WC_Stripe_Logger::log( 'Could not find order via charge ID: ' . $notification->data->object->id );
408
			return;
409
		}
410
411
		$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...
412
413
		if ( 'on-hold' !== $order->get_status() || 'cancelled' !== $order->get_status() ) {
414
			return;
415
		}
416
417
		$order->update_status( 'cancelled', __( 'This payment has cancelled.', 'woocommerce-gateway-stripe' ) );
418
419
		do_action( 'wc_gateway_stripe_process_webhook_payment_error', $order, $notification );
420
	}
421
422
	/**
423
	 * Process webhook refund.
424
	 * Note currently only support 1 time refund.
425
	 *
426
	 * @since 4.0.0
427
	 * @version 4.0.0
428
	 * @param object $notification
429
	 */
430
	public function process_webhook_refund( $notification ) {
431
		$order = WC_Stripe_Helper::get_order_by_charge_id( $notification->data->object->id );
432
433
		if ( ! $order ) {
434
			WC_Stripe_Logger::log( 'Could not find order via charge ID: ' . $notification->data->object->id );
435
			return;
436
		}
437
438
		$order_id = WC_Stripe_Helper::is_pre_30() ? $order->id : $order->get_id();
439
440
		if ( 'stripe' === ( WC_Stripe_Helper::is_pre_30() ? $order->payment_method : $order->get_payment_method() ) ) {
441
			$charge    = WC_Stripe_Helper::is_pre_30() ? get_post_meta( $order_id, '_transaction_id', true ) : $order->get_transaction_id();
442
			$captured  = WC_Stripe_Helper::is_pre_30() ? get_post_meta( $order_id, '_stripe_charge_captured', true ) : $order->get_meta( '_stripe_charge_captured', true );
443
			$refund_id = WC_Stripe_Helper::is_pre_30() ? get_post_meta( $order_id, '_stripe_refund_id', true ) : $order->get_meta( '_stripe_refund_id', true );
444
445
			// If the refund ID matches, don't continue to prevent double refunding.
446
			if ( $notification->data->object->refunds->data[0]->id === $refund_id ) {
447
				return;
448
			}
449
450
			// Only refund captured charge.
451
			if ( $charge ) {
452
				$reason = ( isset( $captured ) && 'yes' === $captured ) ? __( 'Refunded via Stripe Dashboard', 'woocommerce-gateway-stripe' ) : __( 'Pre-Authorization Released via Stripe Dashboard', 'woocommerce-gateway-stripe' );
453
454
				// Create the refund.
455
				$refund = wc_create_refund( array(
456
					'order_id'       => $order_id,
457
					'amount'         => $this->get_refund_amount( $notification ),
458
					'reason'         => $reason,
459
				) );
460
461
				if ( is_wp_error( $refund ) ) {
462
					WC_Stripe_Logger::log( $refund->get_error_message() );
463
				}
464
465
				$order->add_order_note( $reason );
466
			}
467
		}
468
	}
469
470
	/**
471
	 * Process webhook reviews that are opened. i.e Radar.
472
	 *
473
	 * @since 4.0.6
474
	 * @param object $notification
475
	 */
476
	public function process_review_opened( $notification ) {
477
		$order = WC_Stripe_Helper::get_order_by_charge_id( $notification->data->object->charge );
478
479
		if ( ! $order ) {
480
			WC_Stripe_Logger::log( 'Could not find order via charge ID: ' . $notification->data->object->charge );
481
			return;
482
		}
483
484
		/* translators: 1) The URL to the order. 2) The reason type. */
485
		$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 );
486
487 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...
488
			$order->update_status( 'on-hold', $message );
489
		} else {
490
			$order->add_order_note( $message );
491
		}
492
	}
493
494
	/**
495
	 * Process webhook reviews that are closed. i.e Radar.
496
	 *
497
	 * @since 4.0.6
498
	 * @param object $notification
499
	 */
500
	public function process_review_closed( $notification ) {
501
		$order = WC_Stripe_Helper::get_order_by_charge_id( $notification->data->object->charge );
502
503
		if ( ! $order ) {
504
			WC_Stripe_Logger::log( 'Could not find order via charge ID: ' . $notification->data->object->charge );
505
			return;
506
		}
507
508
		/* translators: 1) The reason type. */
509
		$message = sprintf( __( 'The opened review for this order is now closed. Reason: (%s)', 'woocommerce-gateway-stripe' ), $notification->data->object->reason );
510
511
		if ( 'on-hold' === $order->get_status() ) {
512 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...
513
				$order->update_status( 'processing', $message );
514
			} else {
515
				$order->add_order_note( $message );
516
			}
517
		} else {
518
			$order->add_order_note( $message );
519
		}
520
	}
521
522
	/**
523
	 * Checks if capture is partial.
524
	 *
525
	 * @since 4.0.0
526
	 * @version 4.0.0
527
	 * @param object $notification
528
	 */
529
	public function is_partial_capture( $notification ) {
530
		return 0 < $notification->data->object->amount_refunded;
531
	}
532
533
	/**
534
	 * Gets the amount refunded.
535
	 *
536
	 * @since 4.0.0
537
	 * @version 4.0.0
538
	 * @param object $notification
539
	 */
540
	public function get_refund_amount( $notification ) {
541
		if ( $this->is_partial_capture( $notification ) ) {
542
			$amount = $notification->data->object->amount_refunded / 100;
543
544
			if ( in_array( strtolower( $notification->data->object->currency ), WC_Stripe_Helper::no_decimal_currencies() ) ) {
545
				$amount = $notification->data->object->amount_refunded;
546
			}
547
548
			return $amount;
549
		}
550
551
		return false;
552
	}
553
554
	/**
555
	 * Gets the amount we actually charge.
556
	 *
557
	 * @since 4.0.0
558
	 * @version 4.0.0
559
	 * @param object $notification
560
	 */
561
	public function get_partial_amount_to_charge( $notification ) {
562
		if ( $this->is_partial_capture( $notification ) ) {
563
			$amount = ( $notification->data->object->amount - $notification->data->object->amount_refunded ) / 100;
564
565
			if ( in_array( strtolower( $notification->data->object->currency ), WC_Stripe_Helper::no_decimal_currencies() ) ) {
566
				$amount = ( $notification->data->object->amount - $notification->data->object->amount_refunded );
567
			}
568
569
			return $amount;
570
		}
571
572
		return false;
573
	}
574
575
	/**
576
	 * Processes the incoming webhook.
577
	 *
578
	 * @since 4.0.0
579
	 * @version 4.0.0
580
	 * @param string $request_body
581
	 */
582
	public function process_webhook( $request_body ) {
583
		$notification = json_decode( $request_body );
584
585
		switch ( $notification->type ) {
586
			case 'source.chargeable':
587
				$this->process_webhook_payment( $notification );
588
				break;
589
590
			case 'source.canceled':
591
				$this->process_webhook_source_canceled( $notification );
592
				break;
593
594
			case 'charge.succeeded':
595
				$this->process_webhook_charge_succeeded( $notification );
596
				break;
597
598
			case 'charge.failed':
599
				$this->process_webhook_charge_failed( $notification );
600
				break;
601
602
			case 'charge.captured':
603
				$this->process_webhook_capture( $notification );
604
				break;
605
606
			case 'charge.dispute.created':
607
				$this->process_webhook_dispute( $notification );
608
				break;
609
610
			case 'charge.refunded':
611
				$this->process_webhook_refund( $notification );
612
				break;
613
614
			case 'review.opened':
615
				$this->process_review_opened( $notification );
616
				break;
617
618
			case 'review.closed':
619
				$this->process_review_closed( $notification );
620
				break;
621
622
		}
623
	}
624
}
625
626
new WC_Stripe_Webhook_Handler();
627