This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | if ( ! defined( 'ABSPATH' ) ) { |
||
3 | exit; |
||
4 | } |
||
5 | |||
6 | /** |
||
7 | * WC_Stripe_Customer class. |
||
8 | * |
||
9 | * Represents a Stripe Customer. |
||
10 | */ |
||
11 | class WC_Stripe_Customer { |
||
12 | |||
13 | /** |
||
14 | * Stripe customer ID |
||
15 | * @var string |
||
16 | */ |
||
17 | private $id = ''; |
||
18 | |||
19 | /** |
||
20 | * WP User ID |
||
21 | * @var integer |
||
22 | */ |
||
23 | private $user_id = 0; |
||
24 | |||
25 | /** |
||
26 | * Data from API |
||
27 | * @var array |
||
28 | */ |
||
29 | private $customer_data = array(); |
||
30 | |||
31 | /** |
||
32 | * Constructor |
||
33 | * @param int $user_id The WP user ID |
||
34 | */ |
||
35 | public function __construct( $user_id = 0 ) { |
||
36 | if ( $user_id ) { |
||
37 | $this->set_user_id( $user_id ); |
||
38 | $this->set_id( $this->get_id_from_meta( $user_id ) ); |
||
39 | } |
||
40 | } |
||
41 | |||
42 | /** |
||
43 | * Get Stripe customer ID. |
||
44 | * @return string |
||
45 | */ |
||
46 | public function get_id() { |
||
47 | return $this->id; |
||
48 | } |
||
49 | |||
50 | /** |
||
51 | * Set Stripe customer ID. |
||
52 | * @param [type] $id [description] |
||
0 ignored issues
–
show
|
|||
53 | */ |
||
54 | public function set_id( $id ) { |
||
55 | // Backwards compat for customer ID stored in array format. (Pre 3.0) |
||
56 | if ( is_array( $id ) && isset( $id['customer_id'] ) ) { |
||
57 | $id = $id['customer_id']; |
||
58 | |||
59 | $this->update_id_in_meta( $id ); |
||
60 | } |
||
61 | |||
62 | $this->id = wc_clean( $id ); |
||
63 | } |
||
64 | |||
65 | /** |
||
66 | * User ID in WordPress. |
||
67 | * @return int |
||
68 | */ |
||
69 | public function get_user_id() { |
||
70 | return absint( $this->user_id ); |
||
71 | } |
||
72 | |||
73 | /** |
||
74 | * Set User ID used by WordPress. |
||
75 | * @param int $user_id |
||
76 | */ |
||
77 | public function set_user_id( $user_id ) { |
||
78 | $this->user_id = absint( $user_id ); |
||
79 | } |
||
80 | |||
81 | /** |
||
82 | * Get user object. |
||
83 | * @return WP_User |
||
84 | */ |
||
85 | protected function get_user() { |
||
86 | return $this->get_user_id() ? get_user_by( 'id', $this->get_user_id() ) : false; |
||
87 | } |
||
88 | |||
89 | /** |
||
90 | * Store data from the Stripe API about this customer |
||
91 | */ |
||
92 | public function set_customer_data( $data ) { |
||
93 | $this->customer_data = $data; |
||
94 | } |
||
95 | |||
96 | /** |
||
97 | * Generates the customer request, used for both creating and updating customers. |
||
98 | * |
||
99 | * @param array $args Additional arguments (optional). |
||
100 | * @return array |
||
101 | */ |
||
102 | protected function generate_customer_request( $args = array() ) { |
||
103 | $billing_email = isset( $_POST['billing_email'] ) ? filter_var( $_POST['billing_email'], FILTER_SANITIZE_EMAIL ) : ''; |
||
104 | $user = $this->get_user(); |
||
105 | |||
106 | if ( $user ) { |
||
107 | $billing_first_name = get_user_meta( $user->ID, 'billing_first_name', true ); |
||
108 | $billing_last_name = get_user_meta( $user->ID, 'billing_last_name', true ); |
||
109 | |||
110 | // If billing first name does not exists try the user first name. |
||
111 | if ( empty( $billing_first_name ) ) { |
||
112 | $billing_first_name = get_user_meta( $user->ID, 'first_name', true ); |
||
113 | } |
||
114 | |||
115 | // If billing last name does not exists try the user last name. |
||
116 | if ( empty( $billing_last_name ) ) { |
||
117 | $billing_last_name = get_user_meta( $user->ID, 'last_name', true ); |
||
118 | } |
||
119 | |||
120 | // translators: %1$s First name, %2$s Second name, %3$s Username. |
||
121 | $description = sprintf( __( 'Name: %1$s %2$s, Username: %s', 'woocommerce-gateway-stripe' ), $billing_first_name, $billing_last_name, $user->user_login ); |
||
122 | |||
123 | $defaults = array( |
||
124 | 'email' => $user->user_email, |
||
125 | 'description' => $description, |
||
126 | ); |
||
127 | |||
128 | $billing_full_name = trim( $billing_first_name . ' ' . $billing_last_name ); |
||
129 | if ( ! empty( $billing_full_name ) ) { |
||
130 | $defaults['name'] = $billing_full_name; |
||
131 | } |
||
132 | } else { |
||
133 | $billing_first_name = isset( $_POST['billing_first_name'] ) ? filter_var( wp_unslash( $_POST['billing_first_name'] ), FILTER_SANITIZE_STRING ) : ''; // phpcs:ignore WordPress.Security.NonceVerification |
||
134 | $billing_last_name = isset( $_POST['billing_last_name'] ) ? filter_var( wp_unslash( $_POST['billing_last_name'] ), FILTER_SANITIZE_STRING ) : ''; // phpcs:ignore WordPress.Security.NonceVerification |
||
135 | |||
136 | // translators: %1$s First name, %2$s Second name. |
||
137 | $description = sprintf( __( 'Name: %1$s %2$s, Guest', 'woocommerce-gateway-stripe' ), $billing_first_name, $billing_last_name ); |
||
138 | |||
139 | $defaults = array( |
||
140 | 'email' => $billing_email, |
||
141 | 'description' => $description, |
||
142 | ); |
||
143 | |||
144 | $billing_full_name = trim( $billing_first_name . ' ' . $billing_last_name ); |
||
145 | if ( ! empty( $billing_full_name ) ) { |
||
146 | $defaults['name'] = $billing_full_name; |
||
147 | } |
||
148 | } |
||
149 | |||
150 | $metadata = array(); |
||
151 | $defaults['metadata'] = apply_filters( 'wc_stripe_customer_metadata', $metadata, $user ); |
||
152 | |||
153 | return wp_parse_args( $args, $defaults ); |
||
154 | } |
||
155 | |||
156 | /** |
||
157 | * Create a customer via API. |
||
158 | * @param array $args |
||
159 | * @return WP_Error|int |
||
160 | */ |
||
161 | public function create_customer( $args = array() ) { |
||
162 | $args = $this->generate_customer_request( $args ); |
||
163 | $response = WC_Stripe_API::request( apply_filters( 'wc_stripe_create_customer_args', $args ), 'customers' ); |
||
164 | |||
165 | View Code Duplication | if ( ! empty( $response->error ) ) { |
|
0 ignored issues
–
show
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. ![]() |
|||
166 | throw new WC_Stripe_Exception( print_r( $response, true ), $response->error->message ); |
||
167 | } |
||
168 | |||
169 | $this->set_id( $response->id ); |
||
170 | $this->clear_cache(); |
||
171 | $this->set_customer_data( $response ); |
||
172 | |||
173 | if ( $this->get_user_id() ) { |
||
174 | $this->update_id_in_meta( $response->id ); |
||
175 | } |
||
176 | |||
177 | do_action( 'woocommerce_stripe_add_customer', $args, $response ); |
||
178 | |||
179 | return $response->id; |
||
180 | } |
||
181 | |||
182 | /** |
||
183 | * Updates the Stripe customer through the API. |
||
184 | * |
||
185 | * @param array $args Additional arguments for the request (optional). |
||
186 | * @param bool $is_retry Whether the current call is a retry (optional, defaults to false). If true, then an exception will be thrown instead of further retries on error. |
||
187 | * |
||
188 | * @return string Customer ID |
||
189 | * |
||
190 | * @throws WC_Stripe_Exception |
||
191 | */ |
||
192 | public function update_customer( $args = array(), $is_retry = false ) { |
||
193 | if ( empty( $this->get_id() ) ) { |
||
194 | throw new WC_Stripe_Exception( 'id_required_to_update_user', __( 'Attempting to update a Stripe customer without a customer ID.', 'woocommerce-gateway-stripe' ) ); |
||
195 | } |
||
196 | |||
197 | $args = $this->generate_customer_request( $args ); |
||
198 | $args = apply_filters( 'wc_stripe_update_customer_args', $args ); |
||
199 | $response = WC_Stripe_API::request( $args, 'customers/' . $this->get_id() ); |
||
200 | |||
201 | if ( ! empty( $response->error ) ) { |
||
202 | if ( $this->is_no_such_customer_error( $response->error ) && ! $is_retry ) { |
||
203 | // This can happen when switching the main Stripe account or importing users from another site. |
||
204 | // If not already retrying, recreate the customer and then try updating it again. |
||
205 | $this->recreate_customer(); |
||
206 | return $this->update_customer( $args, true ); |
||
207 | } |
||
208 | |||
209 | throw new WC_Stripe_Exception( print_r( $response, true ), $response->error->message ); |
||
210 | } |
||
211 | |||
212 | $this->clear_cache(); |
||
213 | $this->set_customer_data( $response ); |
||
214 | |||
215 | do_action( 'woocommerce_stripe_update_customer', $args, $response ); |
||
216 | |||
217 | return $this->get_id(); |
||
218 | } |
||
219 | |||
220 | /** |
||
221 | * Checks to see if error is of invalid request |
||
222 | * error and it is no such customer. |
||
223 | * |
||
224 | * @since 4.1.2 |
||
225 | * @param array $error |
||
226 | */ |
||
227 | public function is_no_such_customer_error( $error ) { |
||
228 | return ( |
||
229 | $error && |
||
0 ignored issues
–
show
The expression
$error 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 ![]() |
|||
230 | 'invalid_request_error' === $error->type && |
||
231 | preg_match( '/No such customer/i', $error->message ) |
||
232 | ); |
||
233 | } |
||
234 | |||
235 | /** |
||
236 | * Checks to see if error is of invalid request |
||
237 | * error and it is no such customer. |
||
238 | * |
||
239 | * @since 4.5.6 |
||
240 | * @param array $error |
||
241 | * @return bool |
||
242 | */ |
||
243 | public function is_source_already_attached_error( $error ) { |
||
244 | return ( |
||
245 | $error && |
||
0 ignored issues
–
show
The expression
$error 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 ![]() |
|||
246 | 'invalid_request_error' === $error->type && |
||
247 | preg_match( '/already been attached to a customer/i', $error->message ) |
||
248 | ); |
||
249 | } |
||
250 | |||
251 | /** |
||
252 | * Add a source for this stripe customer. |
||
253 | * @param string $source_id |
||
254 | * @return WP_Error|int |
||
255 | */ |
||
256 | public function add_source( $source_id ) { |
||
257 | $response = $this->attach_source( $source_id ); |
||
0 ignored issues
–
show
|
|||
258 | if ( is_wp_error( $response ) ) { |
||
259 | return $response; |
||
260 | } |
||
261 | |||
262 | // Add token to WooCommerce. |
||
263 | $wc_token = false; |
||
264 | |||
265 | if ( $this->get_user_id() && class_exists( 'WC_Payment_Token_CC' ) ) { |
||
266 | if ( ! empty( $response->type ) ) { |
||
267 | switch ( $response->type ) { |
||
268 | case 'alipay': |
||
269 | break; |
||
270 | case 'sepa_debit': |
||
271 | $wc_token = new WC_Payment_Token_SEPA(); |
||
272 | $wc_token->set_token( $response->id ); |
||
273 | $wc_token->set_gateway_id( 'stripe_sepa' ); |
||
274 | $wc_token->set_last4( $response->sepa_debit->last4 ); |
||
275 | break; |
||
276 | default: |
||
277 | if ( 'source' === $response->object && 'card' === $response->type ) { |
||
278 | $wc_token = new WC_Payment_Token_CC(); |
||
279 | $wc_token->set_token( $response->id ); |
||
280 | $wc_token->set_gateway_id( 'stripe' ); |
||
281 | $wc_token->set_card_type( strtolower( $response->card->brand ) ); |
||
282 | $wc_token->set_last4( $response->card->last4 ); |
||
283 | $wc_token->set_expiry_month( $response->card->exp_month ); |
||
284 | $wc_token->set_expiry_year( $response->card->exp_year ); |
||
285 | } |
||
286 | break; |
||
287 | } |
||
288 | } else { |
||
289 | // Legacy. |
||
290 | $wc_token = new WC_Payment_Token_CC(); |
||
291 | $wc_token->set_token( $response->id ); |
||
292 | $wc_token->set_gateway_id( 'stripe' ); |
||
293 | $wc_token->set_card_type( strtolower( $response->brand ) ); |
||
294 | $wc_token->set_last4( $response->last4 ); |
||
295 | $wc_token->set_expiry_month( $response->exp_month ); |
||
296 | $wc_token->set_expiry_year( $response->exp_year ); |
||
297 | } |
||
298 | |||
299 | $wc_token->set_user_id( $this->get_user_id() ); |
||
300 | $wc_token->save(); |
||
301 | } |
||
302 | |||
303 | $this->clear_cache(); |
||
304 | |||
305 | do_action( 'woocommerce_stripe_add_source', $this->get_id(), $wc_token, $response, $source_id ); |
||
306 | |||
307 | return $response->id; |
||
308 | } |
||
309 | |||
310 | /** |
||
311 | * Attaches a source to the Stripe customer. |
||
312 | * |
||
313 | * @param string $source_id The ID of the new source. |
||
314 | * @return object|WP_Error Either a source object, or a WP error. |
||
315 | */ |
||
316 | public function attach_source( $source_id ) { |
||
317 | if ( ! $this->get_id() ) { |
||
318 | $this->set_id( $this->create_customer() ); |
||
319 | } |
||
320 | |||
321 | $response = WC_Stripe_API::request( |
||
322 | array( |
||
323 | 'source' => $source_id, |
||
324 | ), |
||
325 | 'customers/' . $this->get_id() . '/sources' |
||
326 | ); |
||
327 | |||
328 | if ( ! empty( $response->error ) ) { |
||
329 | // It is possible the WC user once was linked to a customer on Stripe |
||
330 | // but no longer exists. Instead of failing, lets try to create a |
||
331 | // new customer. |
||
332 | if ( $this->is_no_such_customer_error( $response->error ) ) { |
||
333 | $this->recreate_customer(); |
||
334 | return $this->attach_source( $source_id ); |
||
335 | } elseif( $this->is_source_already_attached_error( $response->error ) ) { |
||
336 | return WC_Stripe_API::request( array(), 'sources/' . $source_id, 'GET' ); |
||
337 | } else { |
||
338 | return $response; |
||
339 | } |
||
340 | } elseif ( empty( $response->id ) ) { |
||
341 | return new WP_Error( 'error', __( 'Unable to add payment source.', 'woocommerce-gateway-stripe' ) ); |
||
342 | } else { |
||
343 | return $response; |
||
344 | } |
||
345 | } |
||
346 | |||
347 | /** |
||
348 | * Get a customers saved sources using their Stripe ID. |
||
349 | * |
||
350 | * @param string $customer_id |
||
0 ignored issues
–
show
There is no parameter named
$customer_id . 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 /**
* @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. ![]() |
|||
351 | * @return array |
||
352 | */ |
||
353 | public function get_sources() { |
||
354 | if ( ! $this->get_id() ) { |
||
355 | return array(); |
||
356 | } |
||
357 | |||
358 | $sources = get_transient( 'stripe_sources_' . $this->get_id() ); |
||
359 | |||
360 | if ( false === $sources ) { |
||
361 | $response = WC_Stripe_API::request( |
||
362 | array( |
||
363 | 'limit' => 100, |
||
364 | ), |
||
365 | 'customers/' . $this->get_id() . '/sources', |
||
366 | 'GET' |
||
367 | ); |
||
368 | |||
369 | if ( ! empty( $response->error ) ) { |
||
370 | return array(); |
||
371 | } |
||
372 | |||
373 | if ( is_array( $response->data ) ) { |
||
374 | $sources = $response->data; |
||
375 | } |
||
376 | |||
377 | set_transient( 'stripe_sources_' . $this->get_id(), $sources, DAY_IN_SECONDS ); |
||
378 | } |
||
379 | |||
380 | return empty( $sources ) ? array() : $sources; |
||
381 | } |
||
382 | |||
383 | /** |
||
384 | * Delete a source from stripe. |
||
385 | * @param string $source_id |
||
386 | */ |
||
387 | public function delete_source( $source_id ) { |
||
388 | if ( ! $this->get_id() ) { |
||
389 | return false; |
||
390 | } |
||
391 | |||
392 | $response = WC_Stripe_API::request( array(), 'customers/' . $this->get_id() . '/sources/' . sanitize_text_field( $source_id ), 'DELETE' ); |
||
393 | |||
394 | $this->clear_cache(); |
||
395 | |||
396 | if ( empty( $response->error ) ) { |
||
397 | do_action( 'wc_stripe_delete_source', $this->get_id(), $response ); |
||
398 | |||
399 | return true; |
||
400 | } |
||
401 | |||
402 | return false; |
||
403 | } |
||
404 | |||
405 | /** |
||
406 | * Set default source in Stripe |
||
407 | * @param string $source_id |
||
408 | */ |
||
409 | public function set_default_source( $source_id ) { |
||
410 | $response = WC_Stripe_API::request( |
||
411 | array( |
||
412 | 'default_source' => sanitize_text_field( $source_id ), |
||
413 | ), |
||
414 | 'customers/' . $this->get_id(), |
||
415 | 'POST' |
||
416 | ); |
||
417 | |||
418 | $this->clear_cache(); |
||
419 | |||
420 | if ( empty( $response->error ) ) { |
||
421 | do_action( 'wc_stripe_set_default_source', $this->get_id(), $response ); |
||
422 | |||
423 | return true; |
||
424 | } |
||
425 | |||
426 | return false; |
||
427 | } |
||
428 | |||
429 | /** |
||
430 | * Deletes caches for this users cards. |
||
431 | */ |
||
432 | public function clear_cache() { |
||
433 | delete_transient( 'stripe_sources_' . $this->get_id() ); |
||
434 | delete_transient( 'stripe_customer_' . $this->get_id() ); |
||
435 | $this->customer_data = array(); |
||
436 | } |
||
437 | |||
438 | /** |
||
439 | * Retrieves the Stripe Customer ID from the user meta. |
||
440 | * |
||
441 | * @param int $user_id The ID of the WordPress user. |
||
442 | * @return string|bool Either the Stripe ID or false. |
||
443 | */ |
||
444 | public function get_id_from_meta( $user_id ) { |
||
445 | return get_user_option( '_stripe_customer_id', $user_id ); |
||
446 | } |
||
447 | |||
448 | /** |
||
449 | * Updates the current user with the right Stripe ID in the meta table. |
||
450 | * |
||
451 | * @param string $id The Stripe customer ID. |
||
452 | */ |
||
453 | public function update_id_in_meta( $id ) { |
||
454 | update_user_option( $this->get_user_id(), '_stripe_customer_id', $id, false ); |
||
455 | } |
||
456 | |||
457 | /** |
||
458 | * Deletes the user ID from the meta table with the right key. |
||
459 | */ |
||
460 | public function delete_id_from_meta() { |
||
461 | delete_user_option( $this->get_user_id(), '_stripe_customer_id', false ); |
||
462 | } |
||
463 | |||
464 | /** |
||
465 | * Recreates the customer for this user. |
||
466 | * |
||
467 | * @return string ID of the new Customer object. |
||
468 | */ |
||
469 | private function recreate_customer() { |
||
470 | $this->delete_id_from_meta(); |
||
471 | return $this->create_customer(); |
||
472 | } |
||
473 | } |
||
474 |
This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.