Completed
Push — master ( f6cdcf...cb7011 )
by Roy
11s
created

WC_Stripe_Sepa_Compat::process_pre_order()   C

Complexity

Conditions 7
Paths 19

Size

Total Lines 39
Code Lines 21

Duplication

Lines 39
Ratio 100 %

Importance

Changes 0
Metric Value
cc 7
eloc 21
nc 19
nop 3
dl 39
loc 39
rs 6.7272
c 0
b 0
f 0
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_Sepa
10
 */
11
class WC_Stripe_Sepa_Compat extends WC_Gateway_Stripe_Sepa {
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( WC_Stripe_Helper::is_pre_30() ? $order->id : $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 View Code Duplication
	public function save_source_to_order( $order, $source ) {
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...
113
		parent::save_source_to_order( $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
	 *
136
	 * @since 3.0
137
	 * @since 4.0.4 Add third parameter flag to retry.
138
	 * @param float $amount
139
	 * @param mixed $renewal_order
140
	 * @param bool $is_retry Is this a retry process.
141
	 */
142 View Code Duplication
	public function process_subscription_payment( $amount = 0.0, $renewal_order, $is_retry = false ) {
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...
143
		if ( $amount * 100 < WC_Stripe_Helper::get_minimum_amount() ) {
144
			/* translators: minimum amount */
145
			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 ) ) );
146
		}
147
148
		$order_id = WC_Stripe_Helper::is_pre_30() ? $renewal_order->id : $renewal_order->get_id();
149
150
		// Get source from order.
151
		$prepared_source = $this->prepare_order_source( $renewal_order );
152
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
		if ( $is_retry ) {
160
			// Passing empty source with charge customer default.
161
			$prepared_source->source = '';
162
		}
163
164
		$request            = $this->generate_payment_request( $renewal_order, $prepared_source );
165
		$request['capture'] = 'true';
166
		$request['amount']  = WC_Stripe_Helper::get_stripe_amount( $amount, $request['currency'] );
167
		$response           = WC_Stripe_API::request( $request );
168
169
		if ( ! empty( $response->error ) || is_wp_error( $response ) ) {
170
			if ( $is_retry ) {
171
				/* translators: error message */
172
				$renewal_order->update_status( 'failed', sprintf( __( 'Stripe Transaction Failed (%s)', 'woocommerce-gateway-stripe' ), $response->error->message ) );
173
			}
174
175
			return $response; // Default catch all errors.
176
		}
177
178
		$this->process_response( $response, $renewal_order );
179
180
		if ( ! $is_retry ) {
181
			return $response;
182
		}
183
	}
184
185
	/**
186
	 * Don't transfer Stripe customer/token meta to resubscribe orders.
187
	 * @param int $resubscribe_order The order created for the customer to resubscribe to the old expired/cancelled subscription
188
	 */
189
	public function delete_resubscribe_meta( $resubscribe_order ) {
190
		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...
191
		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...
192
		// For BW compat will remove in future
193
		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...
194
		$this->delete_renewal_meta( $resubscribe_order );
195
	}
196
197
	/**
198
	 * Don't transfer Stripe fee/ID meta to renewal orders.
199
	 * @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...
200
	 */
201
	public function delete_renewal_meta( $renewal_order ) {
202
		delete_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $renewal_order->id : $renewal_order->get_id() ), 'Stripe Fee' );
203
		delete_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $renewal_order->id : $renewal_order->get_id() ), 'Net Revenue From Stripe' );
204
		return $renewal_order;
205
	}
206
207
	/**
208
	 * Scheduled_subscription_payment function.
209
	 *
210
	 * @param $amount_to_charge float The amount to charge.
211
	 * @param $renewal_order WC_Order A WC_Order object created to record the renewal payment.
212
	 */
213 View Code Duplication
	public function scheduled_subscription_payment( $amount_to_charge, $renewal_order ) {
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...
214
		$response = $this->process_subscription_payment( $amount_to_charge, $renewal_order );
215
216
		if ( is_wp_error( $response ) ) {
217
			/* translators: error message */
218
			$renewal_order->update_status( 'failed', sprintf( __( 'Stripe Transaction Failed (%s)', 'woocommerce-gateway-stripe' ), $response->get_error_message() ) );
219
		}
220
221
		if ( ! empty( $response->error ) ) {
222
			// This is a very generic error to listen for but worth a retry before total fail.
223
			if ( isset( $response->error->type ) && 'invalid_request_error' === $response->error->type && apply_filters( 'wc_stripe_use_default_customer_source', true ) ) {
224
				$this->process_subscription_payment( $amount_to_charge, $renewal_order, true );
225
			} else {
226
				/* translators: error message */
227
				$renewal_order->update_status( 'failed', sprintf( __( 'Stripe Transaction Failed (%s)', 'woocommerce-gateway-stripe' ), $response->error->message ) );
228
			}
229
		}
230
	}
231
232
	/**
233
	 * Remove order meta
234
	 * @param  object $order
235
	 */
236
	public function remove_order_source_before_retry( $order ) {
237
		$order_id = WC_Stripe_Helper::is_pre_30() ? $order->id : $order->get_id();
238
		delete_post_meta( $order_id, '_stripe_source_id' );
239
		// For BW compat will remove in the future.
240
		delete_post_meta( $order_id, '_stripe_card_id' );
241
	}
242
243
	/**
244
	 * Remove order meta
245
	 * @param object $order
246
	 */
247
	public function remove_order_customer_before_retry( $order ) {
248
		$order_id = WC_Stripe_Helper::is_pre_30() ? $order->id : $order->get_id();
249
		delete_post_meta( $order_id, '_stripe_customer_id' );
250
	}
251
252
	/**
253
	 * Update the customer_id for a subscription after using Stripe to complete a payment to make up for
254
	 * an automatic renewal payment which previously failed.
255
	 *
256
	 * @access public
257
	 * @param WC_Subscription $subscription The subscription for which the failing payment method relates.
258
	 * @param WC_Order $renewal_order The order which recorded the successful payment (to make up for the failed automatic payment).
259
	 * @return void
260
	 */
261
	public function update_failing_payment_method( $subscription, $renewal_order ) {
262
		if ( WC_Stripe_Helper::is_pre_30() ) {
263
			update_post_meta( $subscription->id, '_stripe_customer_id', $renewal_order->stripe_customer_id );
264
			update_post_meta( $subscription->id, '_stripe_source_id', $renewal_order->stripe_source_id );
265
266
		} else {
267
			update_post_meta( $subscription->get_id(), '_stripe_customer_id', $renewal_order->get_meta( '_stripe_customer_id', true ) );
268
			update_post_meta( $subscription->get_id(), '_stripe_source_id', $renewal_order->get_meta( '_stripe_source_id', true ) );
269
		}
270
	}
271
272
	/**
273
	 * Include the payment meta data required to process automatic recurring payments so that store managers can
274
	 * manually set up automatic recurring payments for a customer via the Edit Subscriptions screen in 2.0+.
275
	 *
276
	 * @since 2.5
277
	 * @param array $payment_meta associative array of meta data required for automatic payments
278
	 * @param WC_Subscription $subscription An instance of a subscription object
279
	 * @return array
280
	 */
281
	public function add_subscription_payment_meta( $payment_meta, $subscription ) {
282
		$source_id = get_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $subscription->id : $subscription->get_id() ), '_stripe_source_id', true );
283
284
		// For BW compat will remove in future.
285
		if ( empty( $source_id ) ) {
286
			$source_id = get_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $subscription->id : $subscription->get_id() ), '_stripe_card_id', true );
287
288
			// Take this opportunity to update the key name.
289
			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 );
290
		}
291
292
		$payment_meta[ $this->id ] = array(
293
			'post_meta' => array(
294
				'_stripe_customer_id' => array(
295
					'value' => get_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $subscription->id : $subscription->get_id() ), '_stripe_customer_id', true ),
296
					'label' => 'Stripe Customer ID',
297
				),
298
				'_stripe_source_id' => array(
299
					'value' => $source_id,
300
					'label' => 'Stripe Source ID',
301
				),
302
			),
303
		);
304
		return $payment_meta;
305
	}
306
307
	/**
308
	 * Validate the payment meta data required to process automatic recurring payments so that store managers can
309
	 * manually set up automatic recurring payments for a customer via the Edit Subscriptions screen in 2.0+.
310
	 *
311
	 * @since 2.5
312
	 * @param string $payment_method_id The ID of the payment method to validate
313
	 * @param array $payment_meta associative array of meta data required for automatic payments
314
	 * @return array
315
	 */
316 View Code Duplication
	public function validate_subscription_payment_meta( $payment_method_id, $payment_meta ) {
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...
317
		if ( $this->id === $payment_method_id ) {
318
319
			if ( ! isset( $payment_meta['post_meta']['_stripe_customer_id']['value'] ) || empty( $payment_meta['post_meta']['_stripe_customer_id']['value'] ) ) {
320
				throw new Exception( __( 'A "Stripe Customer ID" value is required.', 'woocommerce-gateway-stripe' ) );
321
			} elseif ( 0 !== strpos( $payment_meta['post_meta']['_stripe_customer_id']['value'], 'cus_' ) ) {
322
				throw new Exception( __( 'Invalid customer ID. A valid "Stripe Customer ID" must begin with "cus_".', 'woocommerce-gateway-stripe' ) );
323
			}
324
325
			if (
326
				( ! empty( $payment_meta['post_meta']['_stripe_source_id']['value'] )
327
				&& 0 !== strpos( $payment_meta['post_meta']['_stripe_source_id']['value'], 'card_' ) )
328
				&& ( ! empty( $payment_meta['post_meta']['_stripe_source_id']['value'] )
329
				&& 0 !== strpos( $payment_meta['post_meta']['_stripe_source_id']['value'], 'src_' ) ) ) {
330
331
				throw new Exception( __( 'Invalid source ID. A valid source "Stripe Source ID" must begin with "src_" or "card_".', 'woocommerce-gateway-stripe' ) );
332
			}
333
		}
334
	}
335
336
	/**
337
	 * Render the payment method used for a subscription in the "My Subscriptions" table
338
	 *
339
	 * @since 1.7.5
340
	 * @param string $payment_method_to_display the default payment method text to display
341
	 * @param WC_Subscription $subscription the subscription details
342
	 * @return string the subscription payment method
343
	 */
344
	public function maybe_render_subscription_payment_method( $payment_method_to_display, $subscription ) {
345
		$customer_user = WC_Stripe_Helper::is_pre_30() ? $subscription->customer_user : $subscription->get_customer_id();
346
347
		// bail for other payment methods
348
		if ( ( WC_Stripe_Helper::is_pre_30() ? $subscription->payment_method : $subscription->get_payment_method() ) !== $this->id || ! $customer_user ) {
349
			return $payment_method_to_display;
350
		}
351
352
		$stripe_source_id = get_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $subscription->id : $subscription->get_id() ), '_stripe_source_id', true );
353
354
		// For BW compat will remove in future.
355
		if ( empty( $stripe_source_id ) ) {
356
			$stripe_source_id = get_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $subscription->id : $subscription->get_id() ), '_stripe_card_id', true );
357
358
			// Take this opportunity to update the key name.
359
			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 );
360
		}
361
362
		$stripe_customer    = new WC_Stripe_Customer();
363
		$stripe_customer_id = get_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $subscription->id : $subscription->get_id() ), '_stripe_customer_id', true );
364
365
		// If we couldn't find a Stripe customer linked to the subscription, fallback to the user meta data.
366
		if ( ! $stripe_customer_id || ! is_string( $stripe_customer_id ) ) {
367
			$user_id            = $customer_user;
368
			$stripe_customer_id = get_user_meta( $user_id, '_stripe_customer_id', true );
369
			$stripe_source_id   = get_user_meta( $user_id, '_stripe_source_id', true );
370
371
			// For BW compat will remove in future.
372
			if ( empty( $stripe_source_id ) ) {
373
				$stripe_source_id = get_user_meta( $user_id, '_stripe_card_id', true );
374
375
				// Take this opportunity to update the key name.
376
				update_user_meta( $user_id, '_stripe_source_id', $stripe_source_id );
377
			}
378
		}
379
380
		// If we couldn't find a Stripe customer linked to the account, fallback to the order meta data.
381
		if ( ( ! $stripe_customer_id || ! is_string( $stripe_customer_id ) ) && false !== $subscription->order ) {
382
			$stripe_customer_id = get_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $subscription->order->id : $subscription->get_parent_id() ), '_stripe_customer_id', true );
383
			$stripe_source_id   = get_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $subscription->order->id : $subscription->get_parent_id() ), '_stripe_source_id', true );
384
385
			// For BW compat will remove in future.
386
			if ( empty( $stripe_source_id ) ) {
387
				$stripe_source_id = get_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $subscription->order->id : $subscription->get_parent_id() ), '_stripe_card_id', true );
388
389
				// Take this opportunity to update the key name.
390
				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 );
391
			}
392
		}
393
394
		$stripe_customer->set_id( $stripe_customer_id );
395
		$sources = $stripe_customer->get_sources();
396
397
		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...
398
			$found_source = false;
399
			foreach ( $sources as $source ) {
400
				if ( isset( $source->type ) && 'card' === $source->type ) {
401
					$card = $source->card;
402
				}
403
404
				if ( $source->id === $stripe_source_id ) {
405
					$found_source = true;
406
407
					if ( $card ) {
408
						/* translators: 1) card brand 2) last 4 digits */
409
						$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...
410
					} else {
411
						$payment_method_to_display = __( 'N/A', 'woocommerce-gateway-stripe' );
412
					}
413
					break;
414
				}
415
			}
416
417
			if ( ! $found_source ) {
418
				if ( 'card' === $sources[0]->type ) {
419
					$card = $sources[0]->card;
420
				}
421
422
				if ( $card ) {
423
					/* translators: 1) card brand 2) last 4 digits */
424
					$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 );
425
				} else {
426
					$payment_method_to_display = __( 'N/A', 'woocommerce-gateway-stripe' );
427
				}
428
			}
429
		}
430
431
		return $payment_method_to_display;
432
	}
433
434
	/**
435
	 * Process the pre-order
436
	 * @param int $order_id
437
	 * @return array
438
	 */
439 View Code Duplication
	public function process_pre_order( $order_id, $retry, $force_save_source ) {
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...
440
		if ( WC_Pre_Orders_Order::order_requires_payment_tokenization( $order_id ) ) {
441
			try {
442
				$order = wc_get_order( $order_id );
443
444
				if ( $order->get_total() * 100 < WC_Stripe_Helper::get_minimum_amount() ) {
445
					/* translators: minimum amount */
446
					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 ) ) );
447
				}
448
449
				$source_id = ! empty( $_POST['stripe_source'] ) ? wc_clean( $_POST['stripe_source'] ) : '';
450
				$source    = $this->prepare_source( $this->get_source_object( $source_id ), get_current_user_id(), true );
451
452
				// We need a source on file to continue.
453
				if ( empty( $source->customer ) || empty( $source->source ) ) {
454
					throw new Exception( __( 'Unable to store payment details. Please try again.', 'woocommerce-gateway-stripe' ) );
455
				}
456
457
				$this->save_source_to_order( $order, $source );
458
459
				// Remove cart
460
				WC()->cart->empty_cart();
461
462
				// Is pre ordered!
463
				WC_Pre_Orders_Order::mark_order_as_pre_ordered( $order );
464
465
				// Return thank you page redirect
466
				return array(
467
					'result'   => 'success',
468
					'redirect' => $this->get_return_url( $order ),
469
				);
470
			} catch ( Exception $e ) {
471
				wc_add_notice( $e->getMessage(), 'error' );
472
				return;
473
			}
474
		} else {
475
			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...
476
		}
477
	}
478
479
	/**
480
	 * Process a pre-order payment when the pre-order is released
481
	 * @param WC_Order $order
482
	 * @return void
483
	 */
484
	public function process_pre_order_release_payment( $order ) {
485
		try {
486
			// Define some callbacks if the first attempt fails.
487
			$retry_callbacks = array(
488
				'remove_order_source_before_retry',
489
				'remove_order_customer_before_retry',
490
			);
491
492
			while ( 1 ) {
493
				$source   = $this->prepare_order_source( $order );
494
				$response = WC_Stripe_API::request( $this->generate_payment_request( $order, $source ) );
495
496
				if ( ! empty( $response->error ) ) {
497
					if ( 0 === sizeof( $retry_callbacks ) ) {
498
						throw new Exception( $response->error->message );
499
					} else {
500
						$retry_callback = array_shift( $retry_callbacks );
501
						call_user_func( array( $this, $retry_callback ), $order );
502
					}
503
				} else {
504
					// Successful
505
					$this->process_response( $response, $order );
506
					break;
507
				}
508
			}
509
		} catch ( Exception $e ) {
510
			/* translators: error message */
511
			$order_note = sprintf( __( 'Stripe Transaction Failed (%s)', 'woocommerce-gateway-stripe' ), $e->getMessage() );
512
513
			// Mark order as failed if not already set,
514
			// otherwise, make sure we add the order note so we can detect when someone fails to check out multiple times
515
			if ( ! $order->has_status( 'failed' ) ) {
516
				$order->update_status( 'failed', $order_note );
517
			} else {
518
				$order->add_order_note( $order_note );
519
			}
520
		}
521
	}
522
}
523