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

WC_Stripe_Compat   D

Complexity

Total Complexity 101

Size/Duplication

Total Lines 484
Duplicated Lines 100 %

Coupling/Cohesion

Components 2
Dependencies 5

Importance

Changes 0
Metric Value
dl 484
loc 484
rs 4.8717
c 0
b 0
f 0
wmc 101
lcom 2
cbo 5

19 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 23 23 3
A maybe_hide_save_checkbox() 7 7 2
A has_subscription() 3 3 4
A is_pre_order() 3 3 2
A process_payment() 10 10 3
A add_subscription_meta_data() 10 10 2
B save_source() 20 20 8
B process_subscription_payment() 34 34 6
A delete_resubscribe_meta() 7 7 4
A delete_renewal_meta() 5 5 3
A scheduled_subscription_payment() 13 13 3
A remove_order_source_before_retry() 6 6 2
A remove_order_customer_before_retry() 4 4 2
A update_failing_payment_method() 10 10 2
B add_subscription_payment_meta() 26 26 6
B validate_subscription_payment_meta() 14 14 7
F maybe_render_subscription_payment_method() 80 80 30
B process_pre_order() 39 39 6
B process_pre_order_release_payment() 38 38 6

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

1
<?php
2
if ( ! defined( 'ABSPATH' ) ) {
3
	exit;
4
}
5
6
/**
7
 * Compatibility class for Subscriptions and Pre-Orders.
8
 *
9
 * @extends WC_Gateway_Stripe
10
 */
11 View Code Duplication
class WC_Stripe_Compat extends WC_Gateway_Stripe {
0 ignored issues
show
Duplication introduced by
This class 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...
12
	/**
13
	 * Constructor
14
	 */
15
	public function __construct() {
16
		parent::__construct();
17
18
		if ( class_exists( 'WC_Subscriptions_Order' ) ) {
19
			add_action( 'woocommerce_scheduled_subscription_payment_' . $this->id, array( $this, 'scheduled_subscription_payment' ), 10, 2 );
20
			add_action( 'wcs_resubscribe_order_created', array( $this, 'delete_resubscribe_meta' ), 10 );
21
			add_action( 'wcs_renewal_order_created', array( $this, 'delete_renewal_meta' ), 10 );
22
			add_action( 'woocommerce_subscription_failing_payment_method_updated_stripe', array( $this, 'update_failing_payment_method' ), 10, 2 );
23
24
			// display the credit card used for a subscription in the "My Subscriptions" table
25
			add_filter( 'woocommerce_my_subscriptions_payment_method', array( $this, 'maybe_render_subscription_payment_method' ), 10, 2 );
26
27
			// allow store managers to manually set Stripe as the payment method on a subscription
28
			add_filter( 'woocommerce_subscription_payment_meta', array( $this, 'add_subscription_payment_meta' ), 10, 2 );
29
			add_filter( 'woocommerce_subscription_validate_payment_meta', array( $this, 'validate_subscription_payment_meta' ), 10, 2 );
30
			add_filter( 'wc_stripe_display_save_payment_method_checkbox', array( $this, 'maybe_hide_save_checkbox' ) );
31
			add_filter( 'wc_stripe_payment_metadata', array( $this, 'add_subscription_meta_data' ), 10, 2 );
32
		}
33
34
		if ( class_exists( 'WC_Pre_Orders_Order' ) ) {
35
			add_action( 'wc_pre_orders_process_pre_order_completion_payment_' . $this->id, array( $this, 'process_pre_order_release_payment' ) );
36
		}
37
	}
38
39
	/**
40
	 * Checks to see if we need to hide the save checkbox field.
41
	 * Because when cart contains a subs product, it will save regardless.
42
	 *
43
	 * @since 4.0.0
44
	 * @version 4.0.0
45
	 */
46
	public function maybe_hide_save_checkbox( $display_tokenization ) {
47
		if ( WC_Subscriptions_Cart::cart_contains_subscription() ) {
48
			return false;
49
		}
50
51
		return $display_tokenization;
52
	}
53
54
	/**
55
	 * Is $order_id a subscription?
56
	 * @param  int  $order_id
57
	 * @return boolean
58
	 */
59
	public function has_subscription( $order_id ) {
60
		return ( function_exists( 'wcs_order_contains_subscription' ) && ( wcs_order_contains_subscription( $order_id ) || wcs_is_subscription( $order_id ) || wcs_order_contains_renewal( $order_id ) ) );
61
	}
62
63
	/**
64
	 * Is $order_id a pre-order?
65
	 * @param  int  $order_id
66
	 * @return boolean
67
	 */
68
	protected function is_pre_order( $order_id ) {
69
		return ( class_exists( 'WC_Pre_Orders_Order' ) && WC_Pre_Orders_Order::order_contains_pre_order( $order_id ) );
70
	}
71
72
	/**
73
	 * Process the payment based on type.
74
	 * @param  int $order_id
75
	 * @return array
76
	 */
77
	public function process_payment( $order_id, $retry = true, $force_save_source = false ) {
78
		if ( $this->has_subscription( $order_id ) ) {
79
			// Regular payment with force customer enabled
80
			return parent::process_payment( $order_id, true, true );
81
		} elseif ( $this->is_pre_order( $order_id ) ) {
82
			return $this->process_pre_order( $order_id, $retry, $force_save_source );
83
		} else {
84
			return parent::process_payment( $order_id, $retry, $force_save_source );
85
		}
86
	}
87
88
	/**
89
	 * Adds subscription related meta data on charge request.
90
	 *
91
	 * @since 4.0.0
92
	 * @param array $metadata
93
	 * @param object $order
94
	 */
95
	public function add_subscription_meta_data( $metadata, $order ) {
96
		if ( ! $this->has_subscription( $order->get_id() ) ) {
97
			return $metadata;
98
		}
99
100
		return $metadata += array(
101
			'payment_type'   => 'recurring',
102
			'site_url'       => esc_url( get_site_url() ),
103
		);
104
	}
105
106
	/**
107
	 * Updates other subscription sources.
108
	 *
109
	 * @since 3.1.0
110
	 * @version 4.0.0
111
	 */
112
	public function save_source( $order, $source ) {
113
		parent::save_source( $order, $source );
114
115
		$order_id  = WC_Stripe_Helper::is_pre_30() ? $order->id : $order->get_id();
116
117
		// Also store it on the subscriptions being purchased or paid for in the order
118
		if ( function_exists( 'wcs_order_contains_subscription' ) && wcs_order_contains_subscription( $order_id ) ) {
119
			$subscriptions = wcs_get_subscriptions_for_order( $order_id );
120
		} elseif ( function_exists( 'wcs_order_contains_renewal' ) && wcs_order_contains_renewal( $order_id ) ) {
121
			$subscriptions = wcs_get_subscriptions_for_renewal_order( $order_id );
122
		} else {
123
			$subscriptions = array();
124
		}
125
126
		foreach ( $subscriptions as $subscription ) {
127
			$subscription_id = WC_Stripe_Helper::is_pre_30() ? $subscription->id : $subscription->get_id();
128
			update_post_meta( $subscription_id, '_stripe_customer_id', $source->customer );
129
			update_post_meta( $subscription_id, '_stripe_source_id', $source->source );
130
		}
131
	}
132
133
	/**
134
	 * process_subscription_payment function.
135
	 * @param mixed $order
136
	 * @param int $amount (default: 0)
137
	 * @param string $stripe_token (default: '')
0 ignored issues
show
Bug introduced by
There is no parameter named $stripe_token. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
138
	 * @param  bool initial_payment
139
	 */
140
	public function process_subscription_payment( $order = '', $amount = 0 ) {
141
		if ( $amount * 100 < WC_Stripe_Helper::get_minimum_amount() ) {
142
			/* translators: minimum amount */
143
			return new WP_Error( 'stripe_error', sprintf( __( 'Sorry, the minimum allowed order total is %1$s to use this payment method.', 'woocommerce-gateway-stripe' ), wc_price( WC_Stripe_Helper::get_minimum_amount() / 100 ) ) );
144
		}
145
146
		$customer_id = WC_Stripe_Helper::is_pre_30() ? $order->customer_user : $order->get_customer_id();
0 ignored issues
show
Unused Code introduced by
$customer_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...
147
		$order_id    = WC_Stripe_Helper::is_pre_30() ? $order->id : $order->get_id();
148
149
		// Get source from order
150
		$prepared_source = $this->prepare_order_source( $order );
151
152
		// Or fail :(
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
153
		if ( ! $prepared_source->customer ) {
154
			return new WP_Error( 'stripe_error', __( 'Customer not found', 'woocommerce-gateway-stripe' ) );
155
		}
156
157
		WC_Stripe_Logger::log( "Info: Begin processing subscription payment for order {$order_id} for the amount of {$amount}" );
158
159
		// Make the request
160
		$request             = $this->generate_payment_request( $order, $prepared_source );
161
		$request['capture']  = 'true';
162
		$request['amount']   = WC_Stripe_Helper::get_stripe_amount( $amount, $request['currency'] );
163
		$response            = WC_Stripe_API::request( $request );
164
165
		// Process valid response
166
		if ( ! empty( $response->error ) ) {
167
			return $response; // Default catch all errors.
168
		}
169
170
		$this->process_response( $response, $order );
171
172
		return $response;
173
	}
174
175
	/**
176
	 * Don't transfer Stripe customer/token meta to resubscribe orders.
177
	 * @param int $resubscribe_order The order created for the customer to resubscribe to the old expired/cancelled subscription
178
	 */
179
	public function delete_resubscribe_meta( $resubscribe_order ) {
180
		delete_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $resubscribe_order->id : $resubscribe_order->get_id() ), '_stripe_customer_id' );
0 ignored issues
show
Bug introduced by
The method get_id cannot be called on $resubscribe_order (of type integer).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
181
		delete_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $resubscribe_order->id : $resubscribe_order->get_id() ), '_stripe_source_id' );
0 ignored issues
show
Bug introduced by
The method get_id cannot be called on $resubscribe_order (of type integer).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
182
		// For BW compat will remove in future
183
		delete_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $resubscribe_order->id : $resubscribe_order->get_id() ), '_stripe_card_id' );
0 ignored issues
show
Bug introduced by
The method get_id cannot be called on $resubscribe_order (of type integer).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
184
		$this->delete_renewal_meta( $resubscribe_order );
185
	}
186
187
	/**
188
	 * Don't transfer Stripe fee/ID meta to renewal orders.
189
	 * @param int $resubscribe_order The order created for the customer to resubscribe to the old expired/cancelled subscription
0 ignored issues
show
Bug introduced by
There is no parameter named $resubscribe_order. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
190
	 */
191
	public function delete_renewal_meta( $renewal_order ) {
192
		delete_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $renewal_order->id : $renewal_order->get_id() ), 'Stripe Fee' );
193
		delete_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $renewal_order->id : $renewal_order->get_id() ), 'Net Revenue From Stripe' );
194
		return $renewal_order;
195
	}
196
197
	/**
198
	 * scheduled_subscription_payment function.
199
	 *
200
	 * @param $amount_to_charge float The amount to charge.
201
	 * @param $renewal_order WC_Order A WC_Order object created to record the renewal payment.
202
	 */
203
	public function scheduled_subscription_payment( $amount_to_charge, $renewal_order ) {
204
		$response = $this->process_subscription_payment( $renewal_order, $amount_to_charge );
205
206
		if ( is_wp_error( $response ) ) {
207
			/* translators: error message */
208
			$renewal_order->update_status( 'failed', sprintf( __( 'Stripe Transaction Failed (%s)', 'woocommerce-gateway-stripe' ), $response->get_error_message() ) );
209
		}
210
211
		if ( ! empty( $response->error ) ) {
212
			/* translators: error message */
213
			$renewal_order->update_status( 'failed', sprintf( __( 'Stripe Transaction Failed (%s)', 'woocommerce-gateway-stripe' ), $response->error->message ) );
214
		}
215
	}
216
217
	/**
218
	 * Remove order meta
219
	 * @param  object $order
220
	 */
221
	public function remove_order_source_before_retry( $order ) {
222
		$order_id = WC_Stripe_Helper::is_pre_30() ? $order->id : $order->get_id();
223
		delete_post_meta( $order_id, '_stripe_source_id' );
224
		// For BW compat will remove in the future.
225
		delete_post_meta( $order_id, '_stripe_card_id' );
226
	}
227
228
	/**
229
	 * Remove order meta
230
	 * @param  object $order
231
	 */
232
	public function remove_order_customer_before_retry( $order ) {
233
		$order_id = WC_Stripe_Helper::is_pre_30() ? $order->id : $order->get_id();
234
		delete_post_meta( $order_id, '_stripe_customer_id' );
235
	}
236
237
	/**
238
	 * Update the customer_id for a subscription after using Stripe to complete a payment to make up for
239
	 * an automatic renewal payment which previously failed.
240
	 *
241
	 * @access public
242
	 * @param WC_Subscription $subscription The subscription for which the failing payment method relates.
243
	 * @param WC_Order $renewal_order The order which recorded the successful payment (to make up for the failed automatic payment).
244
	 * @return void
245
	 */
246
	public function update_failing_payment_method( $subscription, $renewal_order ) {
247
		if ( WC_Stripe_Helper::is_pre_30() ) {
248
			update_post_meta( $subscription->id, '_stripe_customer_id', $renewal_order->stripe_customer_id );
249
			update_post_meta( $subscription->id, '_stripe_source_id', $renewal_order->stripe_source_id );
250
251
		} else {
252
			update_post_meta( $subscription->get_id(), '_stripe_customer_id', $renewal_order->get_meta( '_stripe_customer_id', true ) );
253
			update_post_meta( $subscription->get_id(), '_stripe_source_id', $renewal_order->get_meta( '_stripe_source_id', true ) );
254
		}
255
	}
256
257
	/**
258
	 * Include the payment meta data required to process automatic recurring payments so that store managers can
259
	 * manually set up automatic recurring payments for a customer via the Edit Subscriptions screen in 2.0+.
260
	 *
261
	 * @since 2.5
262
	 * @param array $payment_meta associative array of meta data required for automatic payments
263
	 * @param WC_Subscription $subscription An instance of a subscription object
264
	 * @return array
265
	 */
266
	public function add_subscription_payment_meta( $payment_meta, $subscription ) {
267
		$source_id = get_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $subscription->id : $subscription->get_id() ), '_stripe_source_id', true );
268
269
		// For BW compat will remove in future.
270
		if ( empty( $source_id ) ) {
271
			$source_id = get_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $subscription->id : $subscription->get_id() ), '_stripe_card_id', true );
272
273
			// Take this opportunity to update the key name.
274
			WC_Stripe_Helper::is_pre_30() ? update_post_meta( $subscription->id, '_stripe_source_id', $source_id ) : update_post_meta( $subscription->get_id(), '_stripe_source_id', $source_id );
275
		}
276
277
		$payment_meta[ $this->id ] = array(
278
			'post_meta' => array(
279
				'_stripe_customer_id' => array(
280
					'value' => get_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $subscription->id : $subscription->get_id() ), '_stripe_customer_id', true ),
281
					'label' => 'Stripe Customer ID',
282
				),
283
				'_stripe_source_id' => array(
284
					'value' => $source_id,
285
					'label' => 'Stripe Source ID',
286
				),
287
			),
288
		);
289
290
		return $payment_meta;
291
	}
292
293
	/**
294
	 * Validate the payment meta data required to process automatic recurring payments so that store managers can
295
	 * manually set up automatic recurring payments for a customer via the Edit Subscriptions screen in 2.0+.
296
	 *
297
	 * @since 2.5
298
	 * @param string $payment_method_id The ID of the payment method to validate
299
	 * @param array $payment_meta associative array of meta data required for automatic payments
300
	 * @return array
301
	 */
302
	public function validate_subscription_payment_meta( $payment_method_id, $payment_meta ) {
303
		if ( $this->id === $payment_method_id ) {
304
305
			if ( ! isset( $payment_meta['post_meta']['_stripe_customer_id']['value'] ) || empty( $payment_meta['post_meta']['_stripe_customer_id']['value'] ) ) {
306
				throw new Exception( 'A "_stripe_customer_id" value is required.' );
307
			} elseif ( 0 !== strpos( $payment_meta['post_meta']['_stripe_customer_id']['value'], 'cus_' ) ) {
308
				throw new Exception( 'Invalid customer ID. A valid "_stripe_customer_id" must begin with "cus_".' );
309
			}
310
311
			if ( ! isset( $payment_meta['post_meta']['_stripe_source_id']['value'] ) || empty( $payment_meta['post_meta']['_stripe_source_id']['value'] ) ) {
312
				throw new Exception( 'A "_stripe_source_id" value is required.' );
313
			}
314
		}
315
	}
316
317
	/**
318
	 * Render the payment method used for a subscription in the "My Subscriptions" table
319
	 *
320
	 * @since 1.7.5
321
	 * @param string $payment_method_to_display the default payment method text to display
322
	 * @param WC_Subscription $subscription the subscription details
323
	 * @return string the subscription payment method
324
	 */
325
	public function maybe_render_subscription_payment_method( $payment_method_to_display, $subscription ) {
326
		$customer_user = WC_Stripe_Helper::is_pre_30() ? $subscription->customer_user : $subscription->get_customer_id();
327
328
		// bail for other payment methods
329
		if ( ( WC_Stripe_Helper::is_pre_30() ? $subscription->payment_method : $subscription->get_payment_method() ) !== $this->id || ! $customer_user ) {
330
			return $payment_method_to_display;
331
		}
332
333
		$stripe_source_id = get_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $subscription->id : $subscription->get_id() ), '_stripe_source_id', true );
334
335
		// For BW compat will remove in future.
336
		if ( empty( $stripe_source_id ) ) {
337
			$stripe_source_id = get_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $subscription->id : $subscription->get_id() ), '_stripe_card_id', true );
338
339
			// Take this opportunity to update the key name.
340
			WC_Stripe_Helper::is_pre_30() ? update_post_meta( $subscription->id, '_stripe_source_id', $stripe_source_id ) : update_post_meta( $subscription->get_id(), '_stripe_source_id', $stripe_source_id );
341
		}
342
343
		$stripe_customer    = new WC_Stripe_Customer();
344
		$stripe_customer_id = get_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $subscription->id : $subscription->get_id() ), '_stripe_customer_id', true );
345
346
		// If we couldn't find a Stripe customer linked to the subscription, fallback to the user meta data.
347
		if ( ! $stripe_customer_id || ! is_string( $stripe_customer_id ) ) {
348
			$user_id            = $customer_user;
349
			$stripe_customer_id = get_user_meta( $user_id, '_stripe_customer_id', true );
350
			$stripe_source_id   = get_user_meta( $user_id, '_stripe_source_id', true );
351
352
			// For BW compat will remove in future.
353
			if ( empty( $stripe_source_id ) ) {
354
				$stripe_source_id = get_user_meta( $user_id, '_stripe_card_id', true );
355
356
				// Take this opportunity to update the key name.
357
				update_user_meta( $user_id, '_stripe_source_id', $stripe_source_id );
358
			}
359
		}
360
361
		// If we couldn't find a Stripe customer linked to the account, fallback to the order meta data.
362
		if ( ( ! $stripe_customer_id || ! is_string( $stripe_customer_id ) ) && false !== $subscription->order ) {
363
			$stripe_customer_id = get_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $subscription->order->id : $subscription->get_parent_id() ), '_stripe_customer_id', true );
364
			$stripe_source_id   = get_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $subscription->order->id : $subscription->get_parent_id() ), '_stripe_source_id', true );
365
366
			// For BW compat will remove in future.
367
			if ( empty( $stripe_source_id ) ) {
368
				$stripe_source_id = get_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $subscription->order->id : $subscription->get_parent_id() ), '_stripe_card_id', true );
369
370
				// Take this opportunity to update the key name.
371
				WC_Stripe_Helper::is_pre_30() ? update_post_meta( $subscription->order->id, '_stripe_source_id', $stripe_source_id ) : update_post_meta( $subscription->get_parent_id(), '_stripe_source_id', $stripe_source_id );
372
			}
373
		}
374
375
		$stripe_customer->set_id( $stripe_customer_id );
376
		$sources = $stripe_customer->get_sources();
377
378
		if ( $sources ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $sources of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
379
			$found_source = false;
380
			foreach ( $sources as $source ) {
381
				if ( isset( $source->type ) && 'card' === $source->type ) {
382
					$card = $source->card;
383
				}
384
385
				if ( $source->id === $stripe_source_id ) {
386
					$found_source = true;
387
					/* translators: 1) card brand 2) last 4 digits */
388
					$payment_method_to_display = sprintf( __( 'Via %1$s card ending in %2$s', 'woocommerce-gateway-stripe' ), ( isset( $card->brand ) ? $card->brand : __( 'N/A', 'woocommerce-gateway-stripe' ) ), $card->last4 );
0 ignored issues
show
Bug introduced by
The variable $card 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...
389
					break;
390
				}
391
			}
392
393
			if ( ! $found_source ) {
394
				if ( 'card' === $sources[0]->type ) {
395
					$card = $sources[0]->card;
396
				}
397
398
				/* translators: 1) card brand 2) last 4 digits */
399
				$payment_method_to_display = sprintf( __( 'Via %1$s card ending in %2$s', 'woocommerce-gateway-stripe' ), ( isset( $card->brand ) ? $card->brand : __( 'N/A', 'woocommerce-gateway-stripe' ) ), $card->last4 );
400
			}
401
		}
402
403
		return $payment_method_to_display;
404
	}
405
406
	/**
407
	 * Process the pre-order
408
	 * @param int $order_id
409
	 * @return array
410
	 */
411
	public function process_pre_order( $order_id, $retry, $force_save_source ) {
412
		if ( WC_Pre_Orders_Order::order_requires_payment_tokenization( $order_id ) ) {
413
			try {
414
				$order = wc_get_order( $order_id );
415
416
				if ( $order->get_total() * 100 < WC_Stripe_Helper::get_minimum_amount() ) {
417
					/* translators: minimum amount */
418
					throw new Exception( sprintf( __( 'Sorry, the minimum allowed order total is %1$s to use this payment method.', 'woocommerce-gateway-stripe' ), wc_price( WC_Stripe_Helper::get_minimum_amount() / 100 ) ) );
419
				}
420
421
				$source = $this->prepare_source( get_current_user_id(), true );
422
423
				// We need a source on file to continue.
424
				if ( empty( $source->customer ) || empty( $source->source ) ) {
425
					throw new Exception( __( 'Unable to store payment details. Please try again.', 'woocommerce-gateway-stripe' ) );
426
				}
427
428
				// Store source to order meta
429
				$this->save_source( $order, $source );
430
431
				// Remove cart
432
				WC()->cart->empty_cart();
433
434
				// Is pre ordered!
435
				WC_Pre_Orders_Order::mark_order_as_pre_ordered( $order );
436
437
				// Return thank you page redirect
438
				return array(
439
					'result'   => 'success',
440
					'redirect' => $this->get_return_url( $order ),
441
				);
442
			} catch ( Exception $e ) {
443
				wc_add_notice( $e->getMessage(), 'error' );
444
				return;
445
			}
446
		} else {
447
			return parent::process_payment( $order_id, $retry, $force_save_source );
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (process_payment() instead of process_pre_order()). Are you sure this is correct? If so, you might want to change this to $this->process_payment().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
448
		}
449
	}
450
451
	/**
452
	 * Process a pre-order payment when the pre-order is released
453
	 * @param WC_Order $order
454
	 * @return void
455
	 */
456
	public function process_pre_order_release_payment( $order ) {
457
		try {
458
			// Define some callbacks if the first attempt fails.
459
			$retry_callbacks = array(
460
				'remove_order_source_before_retry',
461
				'remove_order_customer_before_retry',
462
			);
463
464
			while ( 1 ) {
465
				$source   = $this->prepare_order_source( $order );
466
				$response = WC_Stripe_API::request( $this->generate_payment_request( $order, $source ) );
467
468
				if ( ! empty( $response->error ) ) {
469
					if ( 0 === sizeof( $retry_callbacks ) ) {
470
						throw new Exception( $response->error->message );
471
					} else {
472
						$retry_callback = array_shift( $retry_callbacks );
473
						call_user_func( array( $this, $retry_callback ), $order );
474
					}
475
				} else {
476
					// Successful
477
					$this->process_response( $response, $order );
478
					break;
479
				}
480
			}
481
		} catch ( Exception $e ) {
482
			/* translators: error message */
483
			$order_note = sprintf( __( 'Stripe Transaction Failed (%s)', 'woocommerce-gateway-stripe' ), $e->getMessage() );
484
485
			// Mark order as failed if not already set,
486
			// otherwise, make sure we add the order note so we can detect when someone fails to check out multiple times
487
			if ( ! $order->has_status( 'failed' ) ) {
488
				$order->update_status( 'failed', $order_note );
489
			} else {
490
				$order->add_order_note( $order_note );
491
			}
492
		}
493
	}
494
}
495