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() { |
|
|
|
|
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 ) ) { |
|
|
|
|
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 ) ) { |
|
|
|
|
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 ) { |
|
|
|
|
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 ) { |
|
|
|
|
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
|
|
|
} |
268
|
|
|
|
269
|
|
|
/** |
270
|
|
|
* Updates other subscription sources. |
271
|
|
|
* |
272
|
|
|
* @since 3.1.0 |
273
|
|
|
* @version 4.0.0 |
274
|
|
|
*/ |
275
|
|
View Code Duplication |
public function save_source_to_order( $order, $source ) { |
|
|
|
|
276
|
|
|
parent::save_source_to_order( $order, $source ); |
277
|
|
|
|
278
|
|
|
$order_id = WC_Stripe_Helper::is_pre_30() ? $order->id : $order->get_id(); |
279
|
|
|
|
280
|
|
|
// Also store it on the subscriptions being purchased or paid for in the order |
281
|
|
|
if ( function_exists( 'wcs_order_contains_subscription' ) && wcs_order_contains_subscription( $order_id ) ) { |
282
|
|
|
$subscriptions = wcs_get_subscriptions_for_order( $order_id ); |
283
|
|
|
} elseif ( function_exists( 'wcs_order_contains_renewal' ) && wcs_order_contains_renewal( $order_id ) ) { |
284
|
|
|
$subscriptions = wcs_get_subscriptions_for_renewal_order( $order_id ); |
285
|
|
|
} else { |
286
|
|
|
$subscriptions = array(); |
287
|
|
|
} |
288
|
|
|
|
289
|
|
|
foreach ( $subscriptions as $subscription ) { |
290
|
|
|
$subscription_id = WC_Stripe_Helper::is_pre_30() ? $subscription->id : $subscription->get_id(); |
291
|
|
|
update_post_meta( $subscription_id, '_stripe_customer_id', $source->customer ); |
292
|
|
|
update_post_meta( $subscription_id, '_stripe_source_id', $source->source ); |
293
|
|
|
} |
294
|
|
|
} |
295
|
|
|
|
296
|
|
|
/** |
297
|
|
|
* Don't transfer Stripe customer/token meta to resubscribe orders. |
298
|
|
|
* @param int $resubscribe_order The order created for the customer to resubscribe to the old expired/cancelled subscription |
299
|
|
|
*/ |
300
|
|
View Code Duplication |
public function delete_resubscribe_meta( $resubscribe_order ) { |
|
|
|
|
301
|
|
|
delete_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $resubscribe_order->id : $resubscribe_order->get_id() ), '_stripe_customer_id' ); |
|
|
|
|
302
|
|
|
delete_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $resubscribe_order->id : $resubscribe_order->get_id() ), '_stripe_source_id' ); |
|
|
|
|
303
|
|
|
// For BW compat will remove in future |
304
|
|
|
delete_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $resubscribe_order->id : $resubscribe_order->get_id() ), '_stripe_card_id' ); |
|
|
|
|
305
|
|
|
$this->delete_renewal_meta( $resubscribe_order ); |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
/** |
309
|
|
|
* Don't transfer Stripe fee/ID meta to renewal orders. |
310
|
|
|
* @param int $resubscribe_order The order created for the customer to resubscribe to the old expired/cancelled subscription |
|
|
|
|
311
|
|
|
*/ |
312
|
|
|
public function delete_renewal_meta( $renewal_order ) { |
313
|
|
|
WC_Stripe_Helper::delete_stripe_fee( $renewal_order ); |
314
|
|
|
WC_Stripe_Helper::delete_stripe_net( $renewal_order ); |
315
|
|
|
|
316
|
|
|
return $renewal_order; |
317
|
|
|
} |
318
|
|
|
|
319
|
|
|
/** |
320
|
|
|
* Update the customer_id for a subscription after using Stripe to complete a payment to make up for |
321
|
|
|
* an automatic renewal payment which previously failed. |
322
|
|
|
* |
323
|
|
|
* @access public |
324
|
|
|
* @param WC_Subscription $subscription The subscription for which the failing payment method relates. |
325
|
|
|
* @param WC_Order $renewal_order The order which recorded the successful payment (to make up for the failed automatic payment). |
326
|
|
|
* @return void |
327
|
|
|
*/ |
328
|
|
View Code Duplication |
public function update_failing_payment_method( $subscription, $renewal_order ) { |
|
|
|
|
329
|
|
|
if ( WC_Stripe_Helper::is_pre_30() ) { |
330
|
|
|
update_post_meta( $subscription->id, '_stripe_customer_id', $renewal_order->stripe_customer_id ); |
331
|
|
|
update_post_meta( $subscription->id, '_stripe_source_id', $renewal_order->stripe_source_id ); |
332
|
|
|
|
333
|
|
|
} else { |
334
|
|
|
update_post_meta( $subscription->get_id(), '_stripe_customer_id', $renewal_order->get_meta( '_stripe_customer_id', true ) ); |
335
|
|
|
update_post_meta( $subscription->get_id(), '_stripe_source_id', $renewal_order->get_meta( '_stripe_source_id', true ) ); |
336
|
|
|
} |
337
|
|
|
} |
338
|
|
|
|
339
|
|
|
/** |
340
|
|
|
* Include the payment meta data required to process automatic recurring payments so that store managers can |
341
|
|
|
* manually set up automatic recurring payments for a customer via the Edit Subscriptions screen in 2.0+. |
342
|
|
|
* |
343
|
|
|
* @since 2.5 |
344
|
|
|
* @param array $payment_meta associative array of meta data required for automatic payments |
345
|
|
|
* @param WC_Subscription $subscription An instance of a subscription object |
346
|
|
|
* @return array |
347
|
|
|
*/ |
348
|
|
|
public function add_subscription_payment_meta( $payment_meta, $subscription ) { |
349
|
|
|
$subscription_id = WC_Stripe_Helper::is_pre_30() ? $subscription->id : $subscription->get_id(); |
350
|
|
|
$source_id = get_post_meta( $subscription_id, '_stripe_source_id', true ); |
351
|
|
|
|
352
|
|
|
// For BW compat will remove in future. |
353
|
|
|
if ( empty( $source_id ) ) { |
354
|
|
|
$source_id = get_post_meta( $subscription_id, '_stripe_card_id', true ); |
355
|
|
|
|
356
|
|
|
// Take this opportunity to update the key name. |
357
|
|
|
update_post_meta( $subscription_id, '_stripe_source_id', $source_id ); |
358
|
|
|
delete_post_meta( $subscription_id, '_stripe_card_id', $source_id ); |
359
|
|
|
} |
360
|
|
|
|
361
|
|
|
$payment_meta[ $this->id ] = array( |
362
|
|
|
'post_meta' => array( |
363
|
|
|
'_stripe_customer_id' => array( |
364
|
|
|
'value' => get_post_meta( $subscription_id, '_stripe_customer_id', true ), |
365
|
|
|
'label' => 'Stripe Customer ID', |
366
|
|
|
), |
367
|
|
|
'_stripe_source_id' => array( |
368
|
|
|
'value' => $source_id, |
369
|
|
|
'label' => 'Stripe Source ID', |
370
|
|
|
), |
371
|
|
|
), |
372
|
|
|
); |
373
|
|
|
|
374
|
|
|
return $payment_meta; |
375
|
|
|
} |
376
|
|
|
|
377
|
|
|
/** |
378
|
|
|
* Validate the payment meta data required to process automatic recurring payments so that store managers can |
379
|
|
|
* manually set up automatic recurring payments for a customer via the Edit Subscriptions screen in 2.0+. |
380
|
|
|
* |
381
|
|
|
* @since 2.5 |
382
|
|
|
* @since 4.0.4 Stripe sourd id field no longer needs to be required. |
383
|
|
|
* @param string $payment_method_id The ID of the payment method to validate |
384
|
|
|
* @param array $payment_meta associative array of meta data required for automatic payments |
385
|
|
|
* @return array |
386
|
|
|
*/ |
387
|
|
View Code Duplication |
public function validate_subscription_payment_meta( $payment_method_id, $payment_meta ) { |
|
|
|
|
388
|
|
|
if ( $this->id === $payment_method_id ) { |
389
|
|
|
|
390
|
|
|
if ( ! isset( $payment_meta['post_meta']['_stripe_customer_id']['value'] ) || empty( $payment_meta['post_meta']['_stripe_customer_id']['value'] ) ) { |
391
|
|
|
throw new Exception( __( 'A "Stripe Customer ID" value is required.', 'woocommerce-gateway-stripe' ) ); |
392
|
|
|
} elseif ( 0 !== strpos( $payment_meta['post_meta']['_stripe_customer_id']['value'], 'cus_' ) ) { |
393
|
|
|
throw new Exception( __( 'Invalid customer ID. A valid "Stripe Customer ID" must begin with "cus_".', 'woocommerce-gateway-stripe' ) ); |
394
|
|
|
} |
395
|
|
|
|
396
|
|
|
if ( |
397
|
|
|
( ! empty( $payment_meta['post_meta']['_stripe_source_id']['value'] ) |
398
|
|
|
&& 0 !== strpos( $payment_meta['post_meta']['_stripe_source_id']['value'], 'card_' ) ) |
399
|
|
|
&& ( ! empty( $payment_meta['post_meta']['_stripe_source_id']['value'] ) |
400
|
|
|
&& 0 !== strpos( $payment_meta['post_meta']['_stripe_source_id']['value'], 'src_' ) ) ) { |
401
|
|
|
|
402
|
|
|
throw new Exception( __( 'Invalid source ID. A valid source "Stripe Source ID" must begin with "src_" or "card_".', 'woocommerce-gateway-stripe' ) ); |
403
|
|
|
} |
404
|
|
|
} |
405
|
|
|
} |
406
|
|
|
|
407
|
|
|
/** |
408
|
|
|
* Render the payment method used for a subscription in the "My Subscriptions" table |
409
|
|
|
* |
410
|
|
|
* @since 1.7.5 |
411
|
|
|
* @param string $payment_method_to_display the default payment method text to display |
412
|
|
|
* @param WC_Subscription $subscription the subscription details |
413
|
|
|
* @return string the subscription payment method |
414
|
|
|
*/ |
415
|
|
|
public function maybe_render_subscription_payment_method( $payment_method_to_display, $subscription ) { |
416
|
|
|
$customer_user = WC_Stripe_Helper::is_pre_30() ? $subscription->customer_user : $subscription->get_customer_id(); |
417
|
|
|
|
418
|
|
|
// bail for other payment methods |
419
|
|
View Code Duplication |
if ( ( WC_Stripe_Helper::is_pre_30() ? $subscription->payment_method : $subscription->get_payment_method() ) !== $this->id || ! $customer_user ) { |
|
|
|
|
420
|
|
|
return $payment_method_to_display; |
421
|
|
|
} |
422
|
|
|
|
423
|
|
|
$stripe_source_id = get_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $subscription->id : $subscription->get_id() ), '_stripe_source_id', true ); |
424
|
|
|
|
425
|
|
|
// For BW compat will remove in future. |
426
|
|
|
if ( empty( $stripe_source_id ) ) { |
427
|
|
|
$stripe_source_id = get_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $subscription->id : $subscription->get_id() ), '_stripe_card_id', true ); |
428
|
|
|
|
429
|
|
|
// Take this opportunity to update the key name. |
430
|
|
|
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 ); |
431
|
|
|
} |
432
|
|
|
|
433
|
|
|
$stripe_customer = new WC_Stripe_Customer(); |
434
|
|
|
$stripe_customer_id = get_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $subscription->id : $subscription->get_id() ), '_stripe_customer_id', true ); |
435
|
|
|
|
436
|
|
|
// If we couldn't find a Stripe customer linked to the subscription, fallback to the user meta data. |
437
|
|
View Code Duplication |
if ( ! $stripe_customer_id || ! is_string( $stripe_customer_id ) ) { |
|
|
|
|
438
|
|
|
$user_id = $customer_user; |
439
|
|
|
$stripe_customer_id = get_user_meta( $user_id, '_stripe_customer_id', true ); |
440
|
|
|
$stripe_source_id = get_user_meta( $user_id, '_stripe_source_id', true ); |
441
|
|
|
|
442
|
|
|
// For BW compat will remove in future. |
443
|
|
|
if ( empty( $stripe_source_id ) ) { |
444
|
|
|
$stripe_source_id = get_user_meta( $user_id, '_stripe_card_id', true ); |
445
|
|
|
|
446
|
|
|
// Take this opportunity to update the key name. |
447
|
|
|
update_user_meta( $user_id, '_stripe_source_id', $stripe_source_id ); |
448
|
|
|
} |
449
|
|
|
} |
450
|
|
|
|
451
|
|
|
// If we couldn't find a Stripe customer linked to the account, fallback to the order meta data. |
452
|
|
View Code Duplication |
if ( ( ! $stripe_customer_id || ! is_string( $stripe_customer_id ) ) && false !== $subscription->order ) { |
|
|
|
|
453
|
|
|
$stripe_customer_id = get_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $subscription->order->id : $subscription->get_parent_id() ), '_stripe_customer_id', true ); |
454
|
|
|
$stripe_source_id = get_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $subscription->order->id : $subscription->get_parent_id() ), '_stripe_source_id', true ); |
455
|
|
|
|
456
|
|
|
// For BW compat will remove in future. |
457
|
|
|
if ( empty( $stripe_source_id ) ) { |
458
|
|
|
$stripe_source_id = get_post_meta( ( WC_Stripe_Helper::is_pre_30() ? $subscription->order->id : $subscription->get_parent_id() ), '_stripe_card_id', true ); |
459
|
|
|
|
460
|
|
|
// Take this opportunity to update the key name. |
461
|
|
|
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 ); |
462
|
|
|
} |
463
|
|
|
} |
464
|
|
|
|
465
|
|
|
$stripe_customer->set_id( $stripe_customer_id ); |
466
|
|
|
$sources = $stripe_customer->get_sources(); |
467
|
|
|
|
468
|
|
|
if ( $sources ) { |
|
|
|
|
469
|
|
|
$card = false; |
470
|
|
|
$found_source = false; |
471
|
|
|
|
472
|
|
|
foreach ( $sources as $source ) { |
473
|
|
|
if ( isset( $source->type ) && 'card' === $source->type ) { |
474
|
|
|
$card = $source->card; |
475
|
|
|
} elseif ( isset( $source->object ) && 'card' === $source->object ) { |
476
|
|
|
$card = $source; |
477
|
|
|
} |
478
|
|
|
|
479
|
|
|
if ( $source->id === $stripe_source_id ) { |
480
|
|
|
$found_source = true; |
481
|
|
|
|
482
|
|
View Code Duplication |
if ( $card ) { |
|
|
|
|
483
|
|
|
/* translators: 1) card brand 2) last 4 digits */ |
484
|
|
|
$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 ); |
485
|
|
|
} else { |
486
|
|
|
$payment_method_to_display = __( 'N/A', 'woocommerce-gateway-stripe' ); |
487
|
|
|
} |
488
|
|
|
break; |
489
|
|
|
} |
490
|
|
|
} |
491
|
|
|
|
492
|
|
|
if ( ! $found_source ) { |
493
|
|
|
if ( isset( $sources[0]->type ) && 'card' === $sources[0]->type ) { |
494
|
|
|
$card = $sources[0]->card; |
495
|
|
|
} |
496
|
|
|
|
497
|
|
View Code Duplication |
if ( $card ) { |
|
|
|
|
498
|
|
|
/* translators: 1) card brand 2) last 4 digits */ |
499
|
|
|
$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 ); |
500
|
|
|
} else { |
501
|
|
|
$payment_method_to_display = __( 'N/A', 'woocommerce-gateway-stripe' ); |
502
|
|
|
} |
503
|
|
|
} |
504
|
|
|
} |
505
|
|
|
|
506
|
|
|
return $payment_method_to_display; |
507
|
|
|
} |
508
|
|
|
} |
509
|
|
|
|
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.