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

WC_Stripe_Subs_Compat::delete_resubscribe_meta()   A

Complexity

Conditions 4
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 7
Ratio 100 %

Importance

Changes 0
Metric Value
cc 4
eloc 5
nc 1
nop 1
dl 7
loc 7
rs 9.2
c 0
b 0
f 0
1
<?php
2
if ( ! defined( 'ABSPATH' ) ) {
3
	exit;
4
}
5
6
/**
7
 * Compatibility class for Subscriptions.
8
 *
9
 * @extends WC_Gateway_Stripe
10
 */
11
class WC_Stripe_Subs_Compat extends WC_Gateway_Stripe {
12
	/**
13
	 * Constructor
14
	 */
15 View Code Duplication
	public function __construct() {
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...
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
		}
32
	}
33
34
	/**
35
	 * Handles the return from processing the payment for Stripe Checkout.
36
	 *
37
	 * @since 4.1.0
38
	 */
39
	public function stripe_checkout_return_handler() {
40
		return parent::stripe_checkout_return_handler();
41
	}
42
43
	/**
44
	 * Checks to see if we need to hide the save checkbox field.
45
	 * Because when cart contains a subs product, it will save regardless.
46
	 *
47
	 * @since 4.0.0
48
	 * @version 4.0.0
49
	 */
50
	public function maybe_hide_save_checkbox( $display_tokenization ) {
51
		if ( WC_Subscriptions_Cart::cart_contains_subscription() ) {
52
			return false;
53
		}
54
55
		return $display_tokenization;
56
	}
57
58
	/**
59
	 * Is $order_id a subscription?
60
	 * @param  int  $order_id
61
	 * @return boolean
62
	 */
63
	public function has_subscription( $order_id ) {
64
		return ( function_exists( 'wcs_order_contains_subscription' ) && ( wcs_order_contains_subscription( $order_id ) || wcs_is_subscription( $order_id ) || wcs_order_contains_renewal( $order_id ) ) );
65
	}
66
67
	/**
68
	 * Checks if page is pay for order and change subs payment page.
69
	 *
70
	 * @since 4.0.4
71
	 * @return bool
72
	 */
73
	public function is_subs_change_payment() {
74
		return ( isset( $_GET['pay_for_order'] ) && isset( $_GET['change_payment_method'] ) );
75
	}
76
77
	/**
78
	 * Process the payment method change for subscriptions.
79
	 *
80
	 * @since 4.0.4
81
	 * @param int $order_id
82
	 */
83
	public function change_subs_payment_method( $order_id ) {
84
		try {
85
			$subscription    = wc_get_order( $order_id );
86
			$prepared_source = $this->prepare_source( get_current_user_id(), true );
87
			$source_object   = $prepared_source->source_object;
88
89
			// Check if we don't allow prepaid credit cards.
90 View Code Duplication
			if ( ! apply_filters( 'wc_stripe_allow_prepaid_card', true ) && $this->is_prepaid_card( $source_object ) ) {
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...
91
				$localized_message = __( 'Sorry, we\'re not accepting prepaid cards at this time. Your credit card has not been charge. Please try with alternative payment method.', 'woocommerce-gateway-stripe' );
92
				throw new WC_Stripe_Exception( print_r( $source_object, true ), $localized_message );
93
			}
94
95 View Code Duplication
			if ( empty( $prepared_source->source ) ) {
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...
96
				$localized_message = __( 'Payment processing failed. Please retry.', 'woocommerce-gateway-stripe' );
97
				throw new WC_Stripe_Exception( print_r( $prepared_source, true ), $localized_message );
98
			}
99
100
			$this->save_source_to_order( $subscription, $prepared_source );
101
102
			/*
103
			 * Check if card 3DS is required or optional with 3DS setting.
104
			 * Will need to first create 3DS source and require redirection
105
			 * for customer to login to their credit card company.
106
			 * Note that if we need to save source, the original source must be first
107
			 * attached to a customer in Stripe before it can be charged.
108
			 */
109
			if ( $this->is_3ds_required( $source_object ) ) {
110
				$order    = $subscription->get_parent();
111
				$response = $this->create_3ds_source( $order, $source_object, $subscription->get_view_order_url() );
112
113
				if ( ! empty( $response->error ) ) {
114
					$localized_message = $response->error->message;
115
116
					$order->add_order_note( $localized_message );
117
118
					throw new WC_Stripe_Exception( print_r( $response, true ), $localized_message );
119
				}
120
121
				// Update order meta with 3DS source.
122
				if ( WC_Stripe_Helper::is_pre_30() ) {
123
					update_post_meta( $order_id, '_stripe_source_id', $response->id );
124
				} else {
125
					$subscription->update_meta_data( '_stripe_source_id', $response->id );
126
					$subscription->save();
127
				}
128
129
				WC_Stripe_Logger::log( 'Info: Redirecting to 3DS...' );
130
131
				return array(
132
					'result'   => 'success',
133
					'redirect' => esc_url_raw( $response->redirect->url ),
134
				);
135
			}
136
137
			return array(
138
				'result'   => 'success',
139
				'redirect' => $this->get_return_url( $subscription ),
140
			);
141
		} catch ( WC_Stripe_Exception $e ) {
142
			wc_add_notice( $e->getLocalizedMessage(), 'error' );
143
			WC_Stripe_Logger::log( 'Error: ' . $e->getMessage() );
144
		}
145
	}
146
147
	/**
148
	 * Process the payment based on type.
149
	 * @param  int $order_id
150
	 * @return array
151
	 */
152 View Code Duplication
	public function process_payment( $order_id, $retry = true, $force_save_source = false, $previous_error = 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...
153
		if ( $this->has_subscription( $order_id ) ) {
154
			if ( $this->is_subs_change_payment() ) {
155
				return $this->change_subs_payment_method( $order_id );
156
			}
157
158
			// Regular payment with force customer enabled
159
			return parent::process_payment( $order_id, $retry, true, $previous_error );
160
		} else {
161
			return parent::process_payment( $order_id, $retry, $force_save_source, $previous_error );
162
		}
163
	}
164
165
	/**
166
	 * Scheduled_subscription_payment function.
167
	 *
168
	 * @param $amount_to_charge float The amount to charge.
169
	 * @param $renewal_order WC_Order A WC_Order object created to record the renewal payment.
170
	 */
171
	public function scheduled_subscription_payment( $amount_to_charge, $renewal_order ) {
172
		$this->process_subscription_payment( $amount_to_charge, $renewal_order, true, false );
173
	}
174
175
	/**
176
	 * Process_subscription_payment function.
177
	 *
178
	 * @since 3.0
179
	 * @since 4.0.4 Add third parameter flag to retry.
180
	 * @since 4.1.0 Add fourth parameter to log previous errors.
181
	 * @param float $amount
182
	 * @param mixed $renewal_order
183
	 * @param bool $retry Should we retry the process?
184
	 * @param object $previous_error
185
	 */
186 View Code Duplication
	public function process_subscription_payment( $amount = 0.0, $renewal_order, $retry = true, $previous_error ) {
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...
187
		try {
188
			if ( $amount * 100 < WC_Stripe_Helper::get_minimum_amount() ) {
189
				/* translators: minimum amount */
190
				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 ) ) );
191
			}
192
193
			$order_id = WC_Stripe_Helper::is_pre_30() ? $renewal_order->id : $renewal_order->get_id();
194
195
			// Get source from order
196
			$prepared_source = $this->prepare_order_source( $renewal_order );
197
			$source_object   = $prepared_source->source_object;
198
199
			if ( ! $prepared_source->customer ) {
200
				return new WP_Error( 'stripe_error', __( 'Customer not found', 'woocommerce-gateway-stripe' ) );
201
			}
202
203
			WC_Stripe_Logger::log( "Info: Begin processing subscription payment for order {$order_id} for the amount of {$amount}" );
204
205
			/* If we're doing a retry and source is chargeable, we need to pass
206
			 * a different idempotency key and retry for success.
207
			 */
208
			if ( is_object( $source_object ) && empty( $source_object->error ) && $this->need_update_idempotency_key( $source_object, $previous_error ) ) {
209
				add_filter( 'wc_stripe_idempotency_key', array( $this, 'change_idempotency_key' ), 10, 2 );
210
			}
211
212
			if ( ( $this->is_no_such_source_error( $previous_error ) || $this->is_no_linked_source_error( $previous_error ) ) && apply_filters( 'wc_stripe_use_default_customer_source', true ) ) {
213
				// Passing empty source will charge customer default.
214
				$prepared_source->source = '';
215
			}
216
217
			$request            = $this->generate_payment_request( $renewal_order, $prepared_source );
218
			$request['capture'] = 'true';
219
			$request['amount']  = WC_Stripe_Helper::get_stripe_amount( $amount, $request['currency'] );
220
			$response           = WC_Stripe_API::request( $request );
221
222
			if ( ! empty( $response->error ) ) {
223
				// We want to retry.
224
				if ( $this->is_retryable_error( $response->error ) ) {
225
					if ( $retry ) {
226
						// Don't do anymore retries after this.
227
						if ( 5 <= $this->retry_interval ) {
228
							return $this->process_subscription_payment( $amount, $renewal_order, false, $response->error );
229
						}
230
231
						sleep( $this->retry_interval );
232
233
						$this->retry_interval++;
234
235
						return $this->process_subscription_payment( $amount, $renewal_order, true, $response->error );
236
					} else {
237
						$localized_message = __( 'Sorry, we are unable to process your payment at this time. Please retry later.', 'woocommerce-gateway-stripe' );
238
						$renewal_order->add_order_note( $localized_message );
239
						throw new WC_Stripe_Exception( print_r( $response, true ), $localized_message );
240
					}
241
				}
242
243
				$localized_messages = WC_Stripe_Helper::get_localized_messages();
244
245
				if ( 'card_error' === $response->error->type ) {
246
					$localized_message = isset( $localized_messages[ $response->error->code ] ) ? $localized_messages[ $response->error->code ] : $response->error->message;
247
				} else {
248
					$localized_message = isset( $localized_messages[ $response->error->type ] ) ? $localized_messages[ $response->error->type ] : $response->error->message;
249
				}
250
251
				$renewal_order->add_order_note( $localized_message );
252
253
				throw new WC_Stripe_Exception( print_r( $response, true ), $localized_message );
254
			}
255
256
			do_action( 'wc_gateway_stripe_process_payment', $response, $renewal_order );
257
258
			$this->process_response( $response, $renewal_order );
259
		} catch ( WC_Stripe_Exception $e ) {
260
			WC_Stripe_Logger::log( 'Error: ' . $e->getMessage() );
261
262
			do_action( 'wc_gateway_stripe_process_payment_error', $e, $renewal_order );
263
264
			/* translators: error message */
265
			$renewal_order->update_status( 'failed' );
266
267
			if ( $renewal_order->has_status( array( 'pending', 'failed' ) ) ) {
268
				$this->send_failed_order_email( $order_id );
0 ignored issues
show
Bug introduced by
The variable $order_id 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...
269
			}
270
		}
271
	}
272
273
	/**
274
	 * Updates other subscription sources.
275
	 *
276
	 * @since 3.1.0
277
	 * @version 4.0.0
278
	 */
279 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...
280
		parent::save_source_to_order( $order, $source );
281
282
		$order_id = WC_Stripe_Helper::is_pre_30() ? $order->id : $order->get_id();
283
284
		// Also store it on the subscriptions being purchased or paid for in the order
285
		if ( function_exists( 'wcs_order_contains_subscription' ) && wcs_order_contains_subscription( $order_id ) ) {
286
			$subscriptions = wcs_get_subscriptions_for_order( $order_id );
287
		} elseif ( function_exists( 'wcs_order_contains_renewal' ) && wcs_order_contains_renewal( $order_id ) ) {
288
			$subscriptions = wcs_get_subscriptions_for_renewal_order( $order_id );
289
		} else {
290
			$subscriptions = array();
291
		}
292
293
		foreach ( $subscriptions as $subscription ) {
294
			$subscription_id = WC_Stripe_Helper::is_pre_30() ? $subscription->id : $subscription->get_id();
295
			update_post_meta( $subscription_id, '_stripe_customer_id', $source->customer );
296
			update_post_meta( $subscription_id, '_stripe_source_id', $source->source );
297
		}
298
	}
299
300
	/**
301
	 * Don't transfer Stripe customer/token meta to resubscribe orders.
302
	 * @param int $resubscribe_order The order created for the customer to resubscribe to the old expired/cancelled subscription
303
	 */
304 View Code Duplication
	public function delete_resubscribe_meta( $resubscribe_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...
305
		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...
306
		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...
307
		// For BW compat will remove in future
308
		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...
309
		$this->delete_renewal_meta( $resubscribe_order );
310
	}
311
312
	/**
313
	 * Don't transfer Stripe fee/ID meta to renewal orders.
314
	 * @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...
315
	 */
316
	public function delete_renewal_meta( $renewal_order ) {
317
		WC_Stripe_Helper::delete_stripe_fee( $renewal_order );
318
		WC_Stripe_Helper::delete_stripe_net( $renewal_order );
319
320
		return $renewal_order;
321
	}
322
323
	/**
324
	 * Update the customer_id for a subscription after using Stripe to complete a payment to make up for
325
	 * an automatic renewal payment which previously failed.
326
	 *
327
	 * @access public
328
	 * @param WC_Subscription $subscription The subscription for which the failing payment method relates.
329
	 * @param WC_Order $renewal_order The order which recorded the successful payment (to make up for the failed automatic payment).
330
	 * @return void
331
	 */
332 View Code Duplication
	public function update_failing_payment_method( $subscription, $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...
333
		if ( WC_Stripe_Helper::is_pre_30() ) {
334
			update_post_meta( $subscription->id, '_stripe_customer_id', $renewal_order->stripe_customer_id );
335
			update_post_meta( $subscription->id, '_stripe_source_id', $renewal_order->stripe_source_id );
336
337
		} else {
338
			update_post_meta( $subscription->get_id(), '_stripe_customer_id', $renewal_order->get_meta( '_stripe_customer_id', true ) );
339
			update_post_meta( $subscription->get_id(), '_stripe_source_id', $renewal_order->get_meta( '_stripe_source_id', true ) );
340
		}
341
	}
342
343
	/**
344
	 * Include the payment meta data required to process automatic recurring payments so that store managers can
345
	 * manually set up automatic recurring payments for a customer via the Edit Subscriptions screen in 2.0+.
346
	 *
347
	 * @since 2.5
348
	 * @param array $payment_meta associative array of meta data required for automatic payments
349
	 * @param WC_Subscription $subscription An instance of a subscription object
350
	 * @return array
351
	 */
352 View Code Duplication
	public function add_subscription_payment_meta( $payment_meta, $subscription ) {
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...
353
		$source_id = get_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $subscription->id : $subscription->get_id() ), '_stripe_source_id', true );
354
355
		// For BW compat will remove in future.
356
		if ( empty( $source_id ) ) {
357
			$source_id = get_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $subscription->id : $subscription->get_id() ), '_stripe_card_id', true );
358
359
			// Take this opportunity to update the key name.
360
			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 );
361
		}
362
363
		$payment_meta[ $this->id ] = array(
364
			'post_meta' => array(
365
				'_stripe_customer_id' => array(
366
					'value' => get_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $subscription->id : $subscription->get_id() ), '_stripe_customer_id', true ),
367
					'label' => 'Stripe Customer ID',
368
				),
369
				'_stripe_source_id' => array(
370
					'value' => $source_id,
371
					'label' => 'Stripe Source ID',
372
				),
373
			),
374
		);
375
376
		return $payment_meta;
377
	}
378
379
	/**
380
	 * Validate the payment meta data required to process automatic recurring payments so that store managers can
381
	 * manually set up automatic recurring payments for a customer via the Edit Subscriptions screen in 2.0+.
382
	 *
383
	 * @since 2.5
384
	 * @since 4.0.4 Stripe sourd id field no longer needs to be required.
385
	 * @param string $payment_method_id The ID of the payment method to validate
386
	 * @param array $payment_meta associative array of meta data required for automatic payments
387
	 * @return array
388
	 */
389 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...
390
		if ( $this->id === $payment_method_id ) {
391
392
			if ( ! isset( $payment_meta['post_meta']['_stripe_customer_id']['value'] ) || empty( $payment_meta['post_meta']['_stripe_customer_id']['value'] ) ) {
393
				throw new Exception( __( 'A "Stripe Customer ID" value is required.', 'woocommerce-gateway-stripe' ) );
394
			} elseif ( 0 !== strpos( $payment_meta['post_meta']['_stripe_customer_id']['value'], 'cus_' ) ) {
395
				throw new Exception( __( 'Invalid customer ID. A valid "Stripe Customer ID" must begin with "cus_".', 'woocommerce-gateway-stripe' ) );
396
			}
397
398
			if (
399
				( ! empty( $payment_meta['post_meta']['_stripe_source_id']['value'] )
400
				&& 0 !== strpos( $payment_meta['post_meta']['_stripe_source_id']['value'], 'card_' ) )
401
				&& ( ! empty( $payment_meta['post_meta']['_stripe_source_id']['value'] )
402
				&& 0 !== strpos( $payment_meta['post_meta']['_stripe_source_id']['value'], 'src_' ) ) ) {
403
404
				throw new Exception( __( 'Invalid source ID. A valid source "Stripe Source ID" must begin with "src_" or "card_".', 'woocommerce-gateway-stripe' ) );
405
			}
406
		}
407
	}
408
409
	/**
410
	 * Render the payment method used for a subscription in the "My Subscriptions" table
411
	 *
412
	 * @since 1.7.5
413
	 * @param string $payment_method_to_display the default payment method text to display
414
	 * @param WC_Subscription $subscription the subscription details
415
	 * @return string the subscription payment method
416
	 */
417
	public function maybe_render_subscription_payment_method( $payment_method_to_display, $subscription ) {
418
		$customer_user = WC_Stripe_Helper::is_pre_30() ? $subscription->customer_user : $subscription->get_customer_id();
419
420
		// bail for other payment methods
421 View Code Duplication
		if ( ( WC_Stripe_Helper::is_pre_30() ? $subscription->payment_method : $subscription->get_payment_method() ) !== $this->id || ! $customer_user ) {
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...
422
			return $payment_method_to_display;
423
		}
424
425
		$stripe_source_id = get_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $subscription->id : $subscription->get_id() ), '_stripe_source_id', true );
426
427
		// For BW compat will remove in future.
428
		if ( empty( $stripe_source_id ) ) {
429
			$stripe_source_id = get_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $subscription->id : $subscription->get_id() ), '_stripe_card_id', true );
430
431
			// Take this opportunity to update the key name.
432
			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 );
433
		}
434
435
		$stripe_customer    = new WC_Stripe_Customer();
436
		$stripe_customer_id = get_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $subscription->id : $subscription->get_id() ), '_stripe_customer_id', true );
437
438
		// If we couldn't find a Stripe customer linked to the subscription, fallback to the user meta data.
439 View Code Duplication
		if ( ! $stripe_customer_id || ! is_string( $stripe_customer_id ) ) {
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...
440
			$user_id            = $customer_user;
441
			$stripe_customer_id = get_user_meta( $user_id, '_stripe_customer_id', true );
442
			$stripe_source_id   = get_user_meta( $user_id, '_stripe_source_id', true );
443
444
			// For BW compat will remove in future.
445
			if ( empty( $stripe_source_id ) ) {
446
				$stripe_source_id = get_user_meta( $user_id, '_stripe_card_id', true );
447
448
				// Take this opportunity to update the key name.
449
				update_user_meta( $user_id, '_stripe_source_id', $stripe_source_id );
450
			}
451
		}
452
453
		// If we couldn't find a Stripe customer linked to the account, fallback to the order meta data.
454 View Code Duplication
		if ( ( ! $stripe_customer_id || ! is_string( $stripe_customer_id ) ) && false !== $subscription->order ) {
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...
455
			$stripe_customer_id = get_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $subscription->order->id : $subscription->get_parent_id() ), '_stripe_customer_id', true );
456
			$stripe_source_id   = get_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $subscription->order->id : $subscription->get_parent_id() ), '_stripe_source_id', true );
457
458
			// For BW compat will remove in future.
459
			if ( empty( $stripe_source_id ) ) {
460
				$stripe_source_id = get_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $subscription->order->id : $subscription->get_parent_id() ), '_stripe_card_id', true );
461
462
				// Take this opportunity to update the key name.
463
				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 );
464
			}
465
		}
466
467
		$stripe_customer->set_id( $stripe_customer_id );
468
		$sources = $stripe_customer->get_sources();
469
470
		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...
471
			$card         = false;
472
			$found_source = false;
473
			foreach ( $sources as $source ) {
474
				if ( isset( $source->type ) && 'card' === $source->type ) {
475
					$card = $source->card;
476
				} elseif ( isset( $source->object ) && 'card' === $source->object ) {
477
					$card = $source;
478
				}
479
480
				if ( $source->id === $stripe_source_id ) {
481
					$found_source = true;
482
483 View Code Duplication
					if ( $card ) {
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...
484
						/* translators: 1) card brand 2) last 4 digits */
485
						$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 );
486
					} else {
487
						$payment_method_to_display = __( 'N/A', 'woocommerce-gateway-stripe' );
488
					}
489
					break;
490
				}
491
			}
492
493
			if ( ! $found_source ) {
494
				if ( 'card' === $sources[0]->type ) {
495
					$card = $sources[0]->card;
496
				}
497
498 View Code Duplication
				if ( $card ) {
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...
499
					/* translators: 1) card brand 2) last 4 digits */
500
					$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 );
501
				} else {
502
					$payment_method_to_display = __( 'N/A', 'woocommerce-gateway-stripe' );
503
				}
504
			}
505
		}
506
507
		return $payment_method_to_display;
508
	}
509
}
510