Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like WC_Stripe_Payment_Gateway often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use WC_Stripe_Payment_Gateway, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
15 | abstract class WC_Stripe_Payment_Gateway extends WC_Payment_Gateway_CC { |
||
16 | /** |
||
17 | * Displays the admin settings webhook description. |
||
18 | * |
||
19 | * @since 4.1.0 |
||
20 | * @return mixed |
||
21 | */ |
||
22 | public function display_admin_settings_webhook_description() { |
||
26 | |||
27 | /** |
||
28 | * Displays the save to account checkbox. |
||
29 | * |
||
30 | * @since 4.1.0 |
||
31 | */ |
||
32 | public function save_payment_method_checkbox() { |
||
42 | |||
43 | /** |
||
44 | * Checks to see if request is invalid and that |
||
45 | * they are worth retrying. |
||
46 | * |
||
47 | * @since 4.0.5 |
||
48 | * @param array $error |
||
49 | */ |
||
50 | public function is_retryable_error( $error ) { |
||
59 | |||
60 | /** |
||
61 | * Checks to see if error is of same idempotency key |
||
62 | * error due to retries with different parameters. |
||
63 | * |
||
64 | * @since 4.1.0 |
||
65 | * @param array $error |
||
66 | */ |
||
67 | public function is_same_idempotency_error( $error ) { |
||
74 | |||
75 | /** |
||
76 | * Checks to see if error is of invalid request |
||
77 | * error and it is no such customer. |
||
78 | * |
||
79 | * @since 4.1.0 |
||
80 | * @param array $error |
||
81 | */ |
||
82 | public function is_no_such_customer_error( $error ) { |
||
83 | return ( |
||
84 | $error && |
||
85 | 'invalid_request_error' === $error->type && |
||
86 | preg_match( '/No such customer/i', $error->message ) |
||
87 | ); |
||
88 | } |
||
89 | |||
90 | /** |
||
91 | * Checks to see if error is of invalid request |
||
92 | * error and it is no such token. |
||
93 | * |
||
94 | * @since 4.1.0 |
||
95 | * @param array $error |
||
96 | */ |
||
97 | public function is_no_such_token_error( $error ) { |
||
98 | return ( |
||
99 | $error && |
||
100 | 'invalid_request_error' === $error->type && |
||
101 | preg_match( '/No such token/i', $error->message ) |
||
102 | ); |
||
103 | } |
||
104 | |||
105 | /** |
||
106 | * Checks to see if error is of invalid request |
||
107 | * error and it is no such source. |
||
108 | * |
||
109 | * @since 4.1.0 |
||
110 | * @param array $error |
||
111 | */ |
||
112 | public function is_no_such_source_error( $error ) { |
||
113 | return ( |
||
114 | $error && |
||
115 | 'invalid_request_error' === $error->type && |
||
116 | preg_match( '/No such source/i', $error->message ) |
||
117 | ); |
||
118 | } |
||
119 | |||
120 | /** |
||
121 | * Checks to see if error is of invalid request |
||
122 | * error and it is no such source linked to customer. |
||
123 | * |
||
124 | * @since 4.1.0 |
||
125 | * @param array $error |
||
126 | */ |
||
127 | public function is_no_linked_source_error( $error ) { |
||
128 | return ( |
||
129 | $error && |
||
130 | 'invalid_request_error' === $error->type && |
||
131 | preg_match( '/does not have a linked source with ID/i', $error->message ) |
||
132 | ); |
||
133 | } |
||
134 | |||
135 | /** |
||
136 | * Check to see if we need to update the idempotency |
||
137 | * key to be different from previous charge request. |
||
138 | * |
||
139 | * @since 4.1.0 |
||
140 | * @param object $source_object |
||
141 | * @param object $error |
||
142 | * @return bool |
||
143 | */ |
||
144 | public function need_update_idempotency_key( $source_object, $error ) { |
||
145 | return ( |
||
146 | $error && |
||
147 | 1 < $this->retry_interval && |
||
148 | ! empty( $source_object ) && |
||
149 | 'chargeable' === $source_object->status && |
||
150 | self::is_same_idempotency_error( $error ) |
||
151 | ); |
||
152 | } |
||
153 | |||
154 | /** |
||
155 | * Checks if keys are set and valid. |
||
156 | * |
||
157 | * @since 4.0.6 |
||
158 | * @return bool True if the keys are set *and* valid, false otherwise (for example, if keys are empty or the secret key was pasted as publishable key). |
||
159 | */ |
||
160 | View Code Duplication | public function are_keys_set() { |
|
161 | // NOTE: updates to this function should be added to are_keys_set() |
||
162 | // in includes/payment-methods/class-wc-stripe-payment-request.php |
||
163 | |||
164 | if ( $this->testmode ) { |
||
165 | return preg_match( '/^pk_test_/', $this->publishable_key ) |
||
166 | && preg_match( '/^[rs]k_test_/', $this->secret_key ); |
||
167 | } else { |
||
168 | return preg_match( '/^pk_live_/', $this->publishable_key ) |
||
169 | && preg_match( '/^[rs]k_live_/', $this->secret_key ); |
||
170 | } |
||
171 | } |
||
172 | |||
173 | /** |
||
174 | * Check if we need to make gateways available. |
||
175 | * |
||
176 | * @since 4.1.3 |
||
177 | */ |
||
178 | public function is_available() { |
||
179 | if ( 'yes' === $this->enabled ) { |
||
180 | return $this->are_keys_set(); |
||
181 | } |
||
182 | |||
183 | return parent::is_available(); |
||
184 | } |
||
185 | |||
186 | /** |
||
187 | * Checks if we need to process pre orders when |
||
188 | * pre orders is in the cart. |
||
189 | * |
||
190 | * @since 4.1.0 |
||
191 | * @param int $order_id |
||
192 | * @return bool |
||
193 | */ |
||
194 | public function maybe_process_pre_orders( $order_id ) { |
||
195 | return ( |
||
196 | WC_Stripe_Helper::is_pre_orders_exists() && |
||
197 | $this->pre_orders->is_pre_order( $order_id ) && |
||
198 | WC_Pre_Orders_Order::order_requires_payment_tokenization( $order_id ) && |
||
199 | ! is_wc_endpoint_url( 'order-pay' ) |
||
200 | ); |
||
201 | } |
||
202 | |||
203 | /** |
||
204 | * All payment icons that work with Stripe. Some icons references |
||
205 | * WC core icons. |
||
206 | * |
||
207 | * @since 4.0.0 |
||
208 | * @since 4.1.0 Changed to using img with svg (colored) instead of fonts. |
||
209 | * @return array |
||
210 | */ |
||
211 | public function payment_icons() { |
||
212 | return apply_filters( |
||
213 | 'wc_stripe_payment_icons', |
||
214 | array( |
||
215 | 'visa' => '<img src="' . WC_STRIPE_PLUGIN_URL . '/assets/images/visa.svg" class="stripe-visa-icon stripe-icon" alt="Visa" />', |
||
216 | 'amex' => '<img src="' . WC_STRIPE_PLUGIN_URL . '/assets/images/amex.svg" class="stripe-amex-icon stripe-icon" alt="American Express" />', |
||
217 | 'mastercard' => '<img src="' . WC_STRIPE_PLUGIN_URL . '/assets/images/mastercard.svg" class="stripe-mastercard-icon stripe-icon" alt="Mastercard" />', |
||
218 | 'discover' => '<img src="' . WC_STRIPE_PLUGIN_URL . '/assets/images/discover.svg" class="stripe-discover-icon stripe-icon" alt="Discover" />', |
||
219 | 'diners' => '<img src="' . WC_STRIPE_PLUGIN_URL . '/assets/images/diners.svg" class="stripe-diners-icon stripe-icon" alt="Diners" />', |
||
220 | 'jcb' => '<img src="' . WC_STRIPE_PLUGIN_URL . '/assets/images/jcb.svg" class="stripe-jcb-icon stripe-icon" alt="JCB" />', |
||
221 | 'alipay' => '<img src="' . WC_STRIPE_PLUGIN_URL . '/assets/images/alipay.svg" class="stripe-alipay-icon stripe-icon" alt="Alipay" />', |
||
222 | 'wechat' => '<img src="' . WC_STRIPE_PLUGIN_URL . '/assets/images/wechat.svg" class="stripe-wechat-icon stripe-icon" alt="Wechat Pay" />', |
||
223 | 'bancontact' => '<img src="' . WC_STRIPE_PLUGIN_URL . '/assets/images/bancontact.svg" class="stripe-bancontact-icon stripe-icon" alt="Bancontact" />', |
||
224 | 'ideal' => '<img src="' . WC_STRIPE_PLUGIN_URL . '/assets/images/ideal.svg" class="stripe-ideal-icon stripe-icon" alt="iDeal" />', |
||
225 | 'p24' => '<img src="' . WC_STRIPE_PLUGIN_URL . '/assets/images/p24.svg" class="stripe-p24-icon stripe-icon" alt="P24" />', |
||
226 | 'giropay' => '<img src="' . WC_STRIPE_PLUGIN_URL . '/assets/images/giropay.svg" class="stripe-giropay-icon stripe-icon" alt="Giropay" />', |
||
227 | 'eps' => '<img src="' . WC_STRIPE_PLUGIN_URL . '/assets/images/eps.svg" class="stripe-eps-icon stripe-icon" alt="EPS" />', |
||
228 | 'multibanco' => '<img src="' . WC_STRIPE_PLUGIN_URL . '/assets/images/multibanco.svg" class="stripe-multibanco-icon stripe-icon" alt="Multibanco" />', |
||
229 | 'sofort' => '<img src="' . WC_STRIPE_PLUGIN_URL . '/assets/images/sofort.svg" class="stripe-sofort-icon stripe-icon" alt="SOFORT" />', |
||
230 | 'sepa' => '<img src="' . WC_STRIPE_PLUGIN_URL . '/assets/images/sepa.svg" class="stripe-sepa-icon stripe-icon" alt="SEPA" />', |
||
231 | ) |
||
232 | ); |
||
233 | } |
||
234 | |||
235 | /** |
||
236 | * Validates that the order meets the minimum order amount |
||
237 | * set by Stripe. |
||
238 | * |
||
239 | * @since 4.0.0 |
||
240 | * @version 4.0.0 |
||
241 | * @param object $order |
||
242 | */ |
||
243 | public function validate_minimum_order_amount( $order ) { |
||
244 | if ( $order->get_total() * 100 < WC_Stripe_Helper::get_minimum_amount() ) { |
||
245 | /* translators: 1) dollar amount */ |
||
246 | throw new WC_Stripe_Exception( 'Did not meet minimum amount', 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 ) ) ); |
||
247 | } |
||
248 | } |
||
249 | |||
250 | /** |
||
251 | * Gets the transaction URL linked to Stripe dashboard. |
||
252 | * |
||
253 | * @since 4.0.0 |
||
254 | * @version 4.0.0 |
||
255 | */ |
||
256 | public function get_transaction_url( $order ) { |
||
257 | if ( $this->testmode ) { |
||
258 | $this->view_transaction_url = 'https://dashboard.stripe.com/test/payments/%s'; |
||
259 | } else { |
||
260 | $this->view_transaction_url = 'https://dashboard.stripe.com/payments/%s'; |
||
261 | } |
||
262 | |||
263 | return parent::get_transaction_url( $order ); |
||
264 | } |
||
265 | |||
266 | /** |
||
267 | * Gets the saved customer id if exists. |
||
268 | * |
||
269 | * @since 4.0.0 |
||
270 | * @version 4.0.0 |
||
271 | */ |
||
272 | public function get_stripe_customer_id( $order ) { |
||
273 | $customer = get_user_option( '_stripe_customer_id', $order->get_customer_id() ); |
||
274 | |||
275 | if ( empty( $customer ) ) { |
||
276 | // Try to get it via the order. |
||
277 | return $order->get_meta( '_stripe_customer_id', true ); |
||
278 | } else { |
||
279 | return $customer; |
||
280 | } |
||
281 | |||
282 | return false; |
||
283 | } |
||
284 | |||
285 | /** |
||
286 | * Builds the return URL from redirects. |
||
287 | * |
||
288 | * @since 4.0.0 |
||
289 | * @version 4.0.0 |
||
290 | * @param object $order |
||
291 | * @param int $id Stripe session id. |
||
292 | */ |
||
293 | public function get_stripe_return_url( $order = null, $id = null ) { |
||
294 | if ( is_object( $order ) ) { |
||
295 | if ( empty( $id ) ) { |
||
296 | $id = uniqid(); |
||
297 | } |
||
298 | |||
299 | $order_id = $order->get_id(); |
||
300 | |||
301 | $args = array( |
||
302 | 'utm_nooverride' => '1', |
||
303 | 'order_id' => $order_id, |
||
304 | ); |
||
305 | |||
306 | return wp_sanitize_redirect( esc_url_raw( add_query_arg( $args, $this->get_return_url( $order ) ) ) ); |
||
307 | } |
||
308 | |||
309 | return wp_sanitize_redirect( esc_url_raw( add_query_arg( array( 'utm_nooverride' => '1' ), $this->get_return_url() ) ) ); |
||
310 | } |
||
311 | |||
312 | /** |
||
313 | * Is $order_id a subscription? |
||
314 | * @param int $order_id |
||
315 | * @return boolean |
||
316 | */ |
||
317 | public function has_subscription( $order_id ) { |
||
318 | return ( function_exists( 'wcs_order_contains_subscription' ) && ( wcs_order_contains_subscription( $order_id ) || wcs_is_subscription( $order_id ) || wcs_order_contains_renewal( $order_id ) ) ); |
||
319 | } |
||
320 | |||
321 | /** |
||
322 | * Generate the request for the payment. |
||
323 | * |
||
324 | * @since 3.1.0 |
||
325 | * @version 4.5.4 |
||
326 | * @param WC_Order $order |
||
327 | * @param object $prepared_source |
||
328 | * @return array() |
||
329 | */ |
||
330 | public function generate_payment_request( $order, $prepared_source ) { |
||
331 | $settings = get_option( 'woocommerce_stripe_settings', array() ); |
||
332 | $statement_descriptor = ! empty( $settings['statement_descriptor'] ) ? str_replace( "'", '', $settings['statement_descriptor'] ) : ''; |
||
333 | $capture = ! empty( $settings['capture'] ) && 'yes' === $settings['capture'] ? true : false; |
||
334 | $post_data = array(); |
||
335 | $post_data['currency'] = strtolower( $order->get_currency() ); |
||
336 | $post_data['amount'] = WC_Stripe_Helper::get_stripe_amount( $order->get_total(), $post_data['currency'] ); |
||
337 | /* translators: 1) blog name 2) order number */ |
||
338 | $post_data['description'] = sprintf( __( '%1$s - Order %2$s', 'woocommerce-gateway-stripe' ), wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ), $order->get_order_number() ); |
||
339 | $billing_email = $order->get_billing_email(); |
||
340 | $billing_first_name = $order->get_billing_first_name(); |
||
341 | $billing_last_name = $order->get_billing_last_name(); |
||
342 | |||
343 | if ( ! empty( $billing_email ) && apply_filters( 'wc_stripe_send_stripe_receipt', false ) ) { |
||
344 | $post_data['receipt_email'] = $billing_email; |
||
345 | } |
||
346 | |||
347 | switch ( $order->get_payment_method() ) { |
||
348 | case 'stripe': |
||
349 | if ( ! empty( $statement_descriptor ) ) { |
||
350 | $post_data['statement_descriptor'] = WC_Stripe_Helper::clean_statement_descriptor( $statement_descriptor ); |
||
351 | } |
||
352 | |||
353 | $post_data['capture'] = $capture ? 'true' : 'false'; |
||
354 | break; |
||
355 | case 'stripe_sepa': |
||
356 | if ( ! empty( $statement_descriptor ) ) { |
||
357 | $post_data['statement_descriptor'] = WC_Stripe_Helper::clean_statement_descriptor( $statement_descriptor ); |
||
358 | } |
||
359 | break; |
||
360 | } |
||
361 | |||
362 | if ( method_exists( $order, 'get_shipping_postcode' ) && ! empty( $order->get_shipping_postcode() ) ) { |
||
363 | $post_data['shipping'] = array( |
||
364 | 'name' => trim( $order->get_shipping_first_name() . ' ' . $order->get_shipping_last_name() ), |
||
365 | 'address' => array( |
||
366 | 'line1' => $order->get_shipping_address_1(), |
||
367 | 'line2' => $order->get_shipping_address_2(), |
||
368 | 'city' => $order->get_shipping_city(), |
||
369 | 'country' => $order->get_shipping_country(), |
||
370 | 'postal_code' => $order->get_shipping_postcode(), |
||
371 | 'state' => $order->get_shipping_state(), |
||
372 | ) |
||
373 | ); |
||
374 | } |
||
375 | |||
376 | $post_data['expand[]'] = 'balance_transaction'; |
||
377 | |||
378 | $metadata = array( |
||
379 | __( 'customer_name', 'woocommerce-gateway-stripe' ) => sanitize_text_field( $billing_first_name ) . ' ' . sanitize_text_field( $billing_last_name ), |
||
380 | __( 'customer_email', 'woocommerce-gateway-stripe' ) => sanitize_email( $billing_email ), |
||
381 | 'order_id' => $order->get_order_number(), |
||
382 | 'site_url' => esc_url( get_site_url() ), |
||
383 | ); |
||
384 | |||
385 | if ( $this->has_subscription( $order->get_id() ) ) { |
||
386 | $metadata += array( |
||
387 | 'payment_type' => 'recurring', |
||
388 | ); |
||
389 | } |
||
390 | |||
391 | $post_data['metadata'] = apply_filters( 'wc_stripe_payment_metadata', $metadata, $order, $prepared_source ); |
||
392 | |||
393 | if ( $prepared_source->customer ) { |
||
394 | $post_data['customer'] = $prepared_source->customer; |
||
395 | } |
||
396 | |||
397 | if ( $prepared_source->source ) { |
||
398 | $post_data['source'] = $prepared_source->source; |
||
399 | } |
||
400 | |||
401 | /** |
||
402 | * Filter the return value of the WC_Payment_Gateway_CC::generate_payment_request. |
||
403 | * |
||
404 | * @since 3.1.0 |
||
405 | * @param array $post_data |
||
406 | * @param WC_Order $order |
||
407 | * @param object $source |
||
408 | */ |
||
409 | return apply_filters( 'wc_stripe_generate_payment_request', $post_data, $order, $prepared_source ); |
||
410 | } |
||
411 | |||
412 | /** |
||
413 | * Store extra meta data for an order from a Stripe Response. |
||
414 | */ |
||
415 | public function process_response( $response, $order ) { |
||
416 | WC_Stripe_Logger::log( 'Processing response: ' . print_r( $response, true ) ); |
||
417 | |||
418 | $order_id = $order->get_id(); |
||
419 | $captured = ( isset( $response->captured ) && $response->captured ) ? 'yes' : 'no'; |
||
420 | |||
421 | // Store charge data. |
||
422 | $order->update_meta_data( '_stripe_charge_captured', $captured ); |
||
423 | |||
424 | if ( isset( $response->balance_transaction ) ) { |
||
425 | $this->update_fees( $order, is_string( $response->balance_transaction ) ? $response->balance_transaction : $response->balance_transaction->id ); |
||
426 | } |
||
427 | |||
428 | if ( 'yes' === $captured ) { |
||
429 | /** |
||
430 | * Charge can be captured but in a pending state. Payment methods |
||
431 | * that are asynchronous may take couple days to clear. Webhook will |
||
432 | * take care of the status changes. |
||
433 | */ |
||
434 | if ( 'pending' === $response->status ) { |
||
435 | $order_stock_reduced = $order->get_meta( '_order_stock_reduced', true ); |
||
436 | |||
437 | if ( ! $order_stock_reduced ) { |
||
438 | wc_reduce_stock_levels( $order_id ); |
||
439 | } |
||
440 | |||
441 | $order->set_transaction_id( $response->id ); |
||
442 | /* translators: transaction id */ |
||
443 | $order->update_status( 'on-hold', sprintf( __( 'Stripe charge awaiting payment: %s.', 'woocommerce-gateway-stripe' ), $response->id ) ); |
||
444 | } |
||
445 | |||
446 | if ( 'succeeded' === $response->status ) { |
||
447 | $order->payment_complete( $response->id ); |
||
448 | |||
449 | /* translators: transaction id */ |
||
450 | $message = sprintf( __( 'Stripe charge complete (Charge ID: %s)', 'woocommerce-gateway-stripe' ), $response->id ); |
||
451 | $order->add_order_note( $message ); |
||
452 | } |
||
453 | |||
454 | if ( 'failed' === $response->status ) { |
||
455 | $localized_message = __( 'Payment processing failed. Please retry.', 'woocommerce-gateway-stripe' ); |
||
456 | $order->add_order_note( $localized_message ); |
||
457 | throw new WC_Stripe_Exception( print_r( $response, true ), $localized_message ); |
||
458 | } |
||
459 | } else { |
||
460 | $order->set_transaction_id( $response->id ); |
||
461 | |||
462 | if ( $order->has_status( array( 'pending', 'failed' ) ) ) { |
||
463 | wc_reduce_stock_levels( $order_id ); |
||
464 | } |
||
465 | |||
466 | /* translators: transaction id */ |
||
467 | $order->update_status( 'on-hold', sprintf( __( 'Stripe charge authorized (Charge ID: %s). Process order to take payment, or cancel to remove the pre-authorization.', 'woocommerce-gateway-stripe' ), $response->id ) ); |
||
468 | } |
||
469 | |||
470 | if ( is_callable( array( $order, 'save' ) ) ) { |
||
471 | $order->save(); |
||
472 | } |
||
473 | |||
474 | do_action( 'wc_gateway_stripe_process_response', $response, $order ); |
||
475 | |||
476 | return $response; |
||
477 | } |
||
478 | |||
479 | /** |
||
480 | * Sends the failed order email to admin. |
||
481 | * |
||
482 | * @since 3.1.0 |
||
483 | * @version 4.0.0 |
||
484 | * @param int $order_id |
||
485 | * @return null |
||
486 | */ |
||
487 | public function send_failed_order_email( $order_id ) { |
||
493 | |||
494 | /** |
||
495 | * Get owner details. |
||
496 | * |
||
497 | * @since 4.0.0 |
||
498 | * @version 4.0.0 |
||
499 | * @param object $order |
||
500 | * @return object $details |
||
501 | */ |
||
502 | public function get_owner_details( $order ) { |
||
503 | $billing_first_name = $order->get_billing_first_name(); |
||
504 | $billing_last_name = $order->get_billing_last_name(); |
||
505 | |||
506 | $details = array(); |
||
507 | |||
508 | $name = $billing_first_name . ' ' . $billing_last_name; |
||
509 | $email = $order->get_billing_email(); |
||
510 | $phone = $order->get_billing_phone(); |
||
511 | |||
512 | if ( ! empty( $phone ) ) { |
||
513 | $details['phone'] = $phone; |
||
514 | } |
||
515 | |||
516 | if ( ! empty( $name ) ) { |
||
517 | $details['name'] = $name; |
||
518 | } |
||
519 | |||
520 | if ( ! empty( $email ) ) { |
||
521 | $details['email'] = $email; |
||
522 | } |
||
523 | |||
524 | $details['address']['line1'] = $order->get_billing_address_1(); |
||
525 | $details['address']['line2'] = $order->get_billing_address_2(); |
||
526 | $details['address']['state'] = $order->get_billing_state(); |
||
527 | $details['address']['city'] = $order->get_billing_city(); |
||
528 | $details['address']['postal_code'] = $order->get_billing_postcode(); |
||
529 | $details['address']['country'] = $order->get_billing_country(); |
||
530 | |||
531 | return (object) apply_filters( 'wc_stripe_owner_details', $details, $order ); |
||
532 | } |
||
533 | |||
534 | /** |
||
535 | * Get source object by source id. |
||
536 | * |
||
537 | * @since 4.0.3 |
||
538 | * @param string $source_id The source ID to get source object for. |
||
539 | */ |
||
540 | public function get_source_object( $source_id = '' ) { |
||
553 | |||
554 | /** |
||
555 | * Checks if card is a prepaid card. |
||
556 | * |
||
557 | * @since 4.0.6 |
||
558 | * @param object $source_object |
||
559 | * @return bool |
||
560 | */ |
||
561 | public function is_prepaid_card( $source_object ) { |
||
562 | return ( |
||
563 | $source_object |
||
564 | && ( 'token' === $source_object->object || 'source' === $source_object->object ) |
||
565 | && 'prepaid' === $source_object->card->funding |
||
566 | ); |
||
567 | } |
||
568 | |||
569 | /** |
||
570 | * Checks if source is of legacy type card. |
||
571 | * |
||
572 | * @since 4.0.8 |
||
573 | * @param string $source_id |
||
574 | * @return bool |
||
575 | */ |
||
576 | public function is_type_legacy_card( $source_id ) { |
||
577 | return ( preg_match( '/^card_/', $source_id ) ); |
||
578 | } |
||
579 | |||
580 | /** |
||
581 | * Checks if payment is via saved payment source. |
||
582 | * |
||
583 | * @since 4.1.0 |
||
584 | * @return bool |
||
585 | */ |
||
586 | public function is_using_saved_payment_method() { |
||
587 | $payment_method = isset( $_POST['payment_method'] ) ? wc_clean( $_POST['payment_method'] ) : 'stripe'; |
||
588 | |||
589 | return ( isset( $_POST[ 'wc-' . $payment_method . '-payment-token' ] ) && 'new' !== $_POST[ 'wc-' . $payment_method . '-payment-token' ] ); |
||
590 | } |
||
591 | |||
592 | /** |
||
593 | * Get payment source. This can be a new token/source or existing WC token. |
||
594 | * If user is logged in and/or has WC account, create an account on Stripe. |
||
595 | * This way we can attribute the payment to the user to better fight fraud. |
||
596 | * |
||
597 | * @since 3.1.0 |
||
598 | * @version 4.0.0 |
||
599 | * @param string $user_id |
||
600 | * @param bool $force_save_source Should we force save payment source. |
||
601 | * |
||
602 | * @throws Exception When card was not added or for and invalid card. |
||
603 | * @return object |
||
604 | */ |
||
605 | public function prepare_source( $user_id, $force_save_source = false, $existing_customer_id = null ) { |
||
606 | $customer = new WC_Stripe_Customer( $user_id ); |
||
607 | if ( ! empty( $existing_customer_id ) ) { |
||
608 | $customer->set_id( $existing_customer_id ); |
||
609 | } |
||
610 | |||
611 | $force_save_source = apply_filters( 'wc_stripe_force_save_source', $force_save_source, $customer ); |
||
612 | $source_object = ''; |
||
613 | $source_id = ''; |
||
614 | $wc_token_id = false; |
||
615 | $payment_method = isset( $_POST['payment_method'] ) ? wc_clean( $_POST['payment_method'] ) : 'stripe'; |
||
616 | $is_token = false; |
||
617 | |||
618 | // New CC info was entered and we have a new source to process. |
||
619 | if ( ! empty( $_POST['stripe_source'] ) ) { |
||
620 | $source_object = self::get_source_object( wc_clean( $_POST['stripe_source'] ) ); |
||
621 | $source_id = $source_object->id; |
||
622 | |||
623 | // This checks to see if customer opted to save the payment method to file. |
||
624 | $maybe_saved_card = isset( $_POST[ 'wc-' . $payment_method . '-new-payment-method' ] ) && ! empty( $_POST[ 'wc-' . $payment_method . '-new-payment-method' ] ); |
||
625 | |||
626 | /** |
||
627 | * This is true if the user wants to store the card to their account. |
||
628 | * Criteria to save to file is they are logged in, they opted to save or product requirements and the source is |
||
629 | * actually reusable. Either that or force_save_source is true. |
||
630 | */ |
||
631 | if ( ( $user_id && $this->saved_cards && $maybe_saved_card && 'reusable' === $source_object->usage ) || $force_save_source ) { |
||
632 | $response = $customer->add_source( $source_object->id ); |
||
633 | |||
634 | View Code Duplication | if ( ! empty( $response->error ) ) { |
|
635 | throw new WC_Stripe_Exception( print_r( $response, true ), $this->get_localized_error_message_from_response( $response ) ); |
||
636 | } |
||
637 | } |
||
638 | } elseif ( $this->is_using_saved_payment_method() ) { |
||
639 | // Use an existing token, and then process the payment. |
||
640 | $wc_token_id = wc_clean( $_POST[ 'wc-' . $payment_method . '-payment-token' ] ); |
||
641 | $wc_token = WC_Payment_Tokens::get( $wc_token_id ); |
||
642 | |||
643 | if ( ! $wc_token || $wc_token->get_user_id() !== get_current_user_id() ) { |
||
644 | WC()->session->set( 'refresh_totals', true ); |
||
645 | throw new WC_Stripe_Exception( 'Invalid payment method', __( 'Invalid payment method. Please input a new card number.', 'woocommerce-gateway-stripe' ) ); |
||
646 | } |
||
647 | |||
648 | $source_id = $wc_token->get_token(); |
||
649 | |||
650 | if ( $this->is_type_legacy_card( $source_id ) ) { |
||
651 | $is_token = true; |
||
652 | } |
||
653 | } elseif ( isset( $_POST['stripe_token'] ) && 'new' !== $_POST['stripe_token'] ) { |
||
654 | $stripe_token = wc_clean( $_POST['stripe_token'] ); |
||
655 | $maybe_saved_card = isset( $_POST[ 'wc-' . $payment_method . '-new-payment-method' ] ) && ! empty( $_POST[ 'wc-' . $payment_method . '-new-payment-method' ] ); |
||
656 | |||
657 | // This is true if the user wants to store the card to their account. |
||
658 | if ( ( $user_id && $this->saved_cards && $maybe_saved_card ) || $force_save_source ) { |
||
659 | $response = $customer->add_source( $stripe_token ); |
||
660 | |||
661 | View Code Duplication | if ( ! empty( $response->error ) ) { |
|
662 | throw new WC_Stripe_Exception( print_r( $response, true ), $response->error->message ); |
||
663 | } |
||
664 | $source_id = $response; |
||
665 | } else { |
||
666 | $source_id = $stripe_token; |
||
667 | $is_token = true; |
||
668 | } |
||
669 | } |
||
670 | |||
671 | $customer_id = $customer->get_id(); |
||
672 | if ( ! $customer_id ) { |
||
673 | $customer->set_id( $customer->create_customer() ); |
||
674 | $customer_id = $customer->get_id(); |
||
675 | } else { |
||
676 | $customer_id = $customer->update_customer(); |
||
677 | } |
||
678 | |||
679 | if ( empty( $source_object ) && ! $is_token ) { |
||
680 | $source_object = self::get_source_object( $source_id ); |
||
681 | } |
||
682 | |||
683 | return (object) array( |
||
684 | 'token_id' => $wc_token_id, |
||
685 | 'customer' => $customer_id, |
||
686 | 'source' => $source_id, |
||
687 | 'source_object' => $source_object, |
||
688 | ); |
||
689 | } |
||
690 | |||
691 | /** |
||
692 | * Get payment source from an order. This could be used in the future for |
||
693 | * a subscription as an example, therefore using the current user ID would |
||
694 | * not work - the customer won't be logged in :) |
||
695 | * |
||
696 | * Not using 2.6 tokens for this part since we need a customer AND a card |
||
697 | * token, and not just one. |
||
698 | * |
||
699 | * @since 3.1.0 |
||
700 | * @version 4.0.0 |
||
701 | * @param object $order |
||
702 | * @return object |
||
703 | */ |
||
704 | public function prepare_order_source( $order = null ) { |
||
705 | $stripe_customer = new WC_Stripe_Customer(); |
||
706 | $stripe_source = false; |
||
707 | $token_id = false; |
||
708 | $source_object = false; |
||
709 | |||
710 | if ( $order ) { |
||
711 | $order_id = $order->get_id(); |
||
712 | |||
713 | $stripe_customer_id = get_post_meta( $order_id, '_stripe_customer_id', true ); |
||
714 | |||
715 | if ( $stripe_customer_id ) { |
||
716 | $stripe_customer->set_id( $stripe_customer_id ); |
||
717 | } |
||
718 | |||
719 | $source_id = $order->get_meta( '_stripe_source_id', true ); |
||
720 | |||
721 | // Since 4.0.0, we changed card to source so we need to account for that. |
||
722 | if ( empty( $source_id ) ) { |
||
723 | $source_id = $order->get_meta( '_stripe_card_id', true ); |
||
724 | |||
725 | // Take this opportunity to update the key name. |
||
726 | $order->update_meta_data( '_stripe_source_id', $source_id ); |
||
727 | |||
728 | if ( is_callable( array( $order, 'save' ) ) ) { |
||
729 | $order->save(); |
||
730 | } |
||
731 | } |
||
732 | |||
733 | if ( $source_id ) { |
||
734 | $stripe_source = $source_id; |
||
735 | $source_object = WC_Stripe_API::retrieve( 'sources/' . $source_id ); |
||
736 | } elseif ( apply_filters( 'wc_stripe_use_default_customer_source', true ) ) { |
||
737 | /* |
||
738 | * We can attempt to charge the customer's default source |
||
739 | * by sending empty source id. |
||
740 | */ |
||
741 | $stripe_source = ''; |
||
742 | } |
||
743 | } |
||
744 | |||
745 | return (object) array( |
||
746 | 'token_id' => $token_id, |
||
747 | 'customer' => $stripe_customer ? $stripe_customer->get_id() : false, |
||
748 | 'source' => $stripe_source, |
||
749 | 'source_object' => $source_object, |
||
750 | ); |
||
751 | } |
||
752 | |||
753 | /** |
||
754 | * Save source to order. |
||
755 | * |
||
756 | * @since 3.1.0 |
||
757 | * @version 4.0.0 |
||
758 | * @param WC_Order $order For to which the source applies. |
||
759 | * @param stdClass $source Source information. |
||
760 | */ |
||
761 | public function save_source_to_order( $order, $source ) { |
||
762 | // Store source in the order. |
||
763 | if ( $source->customer ) { |
||
764 | $order->update_meta_data( '_stripe_customer_id', $source->customer ); |
||
765 | } |
||
766 | |||
767 | if ( $source->source ) { |
||
768 | $order->update_meta_data( '_stripe_source_id', $source->source ); |
||
769 | } |
||
770 | |||
771 | if ( is_callable( array( $order, 'save' ) ) ) { |
||
772 | $order->save(); |
||
773 | } |
||
774 | } |
||
775 | |||
776 | /** |
||
777 | * Updates Stripe fees/net. |
||
778 | * e.g usage would be after a refund. |
||
779 | * |
||
780 | * @since 4.0.0 |
||
781 | * @version 4.0.6 |
||
782 | * @param object $order The order object |
||
783 | * @param int $balance_transaction_id |
||
784 | */ |
||
785 | public function update_fees( $order, $balance_transaction_id ) { |
||
786 | $balance_transaction = WC_Stripe_API::retrieve( 'balance/history/' . $balance_transaction_id ); |
||
787 | |||
788 | if ( empty( $balance_transaction->error ) ) { |
||
789 | if ( isset( $balance_transaction ) && isset( $balance_transaction->fee ) ) { |
||
790 | // Fees and Net needs to both come from Stripe to be accurate as the returned |
||
791 | // values are in the local currency of the Stripe account, not from WC. |
||
792 | $fee_refund = ! empty( $balance_transaction->fee ) ? WC_Stripe_Helper::format_balance_fee( $balance_transaction, 'fee' ) : 0; |
||
793 | $net_refund = ! empty( $balance_transaction->net ) ? WC_Stripe_Helper::format_balance_fee( $balance_transaction, 'net' ) : 0; |
||
794 | |||
795 | // Current data fee & net. |
||
796 | $fee_current = WC_Stripe_Helper::get_stripe_fee( $order ); |
||
797 | $net_current = WC_Stripe_Helper::get_stripe_net( $order ); |
||
798 | |||
799 | // Calculation. |
||
800 | $fee = (float) $fee_current + (float) $fee_refund; |
||
801 | $net = (float) $net_current + (float) $net_refund; |
||
802 | |||
803 | WC_Stripe_Helper::update_stripe_fee( $order, $fee ); |
||
804 | WC_Stripe_Helper::update_stripe_net( $order, $net ); |
||
805 | |||
806 | $currency = ! empty( $balance_transaction->currency ) ? strtoupper( $balance_transaction->currency ) : null; |
||
807 | WC_Stripe_Helper::update_stripe_currency( $order, $currency ); |
||
808 | |||
809 | if ( is_callable( array( $order, 'save' ) ) ) { |
||
810 | $order->save(); |
||
811 | } |
||
812 | } |
||
813 | } else { |
||
814 | WC_Stripe_Logger::log( 'Unable to update fees/net meta for order: ' . $order->get_id() ); |
||
815 | } |
||
816 | } |
||
817 | |||
818 | /** |
||
819 | * Refund a charge. |
||
820 | * |
||
821 | * @since 3.1.0 |
||
822 | * @version 4.0.0 |
||
823 | * @param int $order_id |
||
824 | * @param float $amount |
||
825 | * @return bool |
||
826 | */ |
||
827 | public function process_refund( $order_id, $amount = null, $reason = '' ) { |
||
828 | $order = wc_get_order( $order_id ); |
||
829 | |||
830 | if ( ! $order ) { |
||
831 | return false; |
||
832 | } |
||
833 | |||
834 | $request = array(); |
||
835 | |||
836 | $order_currency = $order->get_currency(); |
||
837 | $captured = $order->get_meta( '_stripe_charge_captured', true ); |
||
838 | $charge_id = $order->get_transaction_id(); |
||
839 | |||
840 | if ( ! $charge_id ) { |
||
841 | return false; |
||
842 | } |
||
843 | |||
844 | if ( ! is_null( $amount ) ) { |
||
845 | $request['amount'] = WC_Stripe_Helper::get_stripe_amount( $amount, $order_currency ); |
||
846 | } |
||
847 | |||
848 | // If order is only authorized, don't pass amount. |
||
849 | if ( 'yes' !== $captured ) { |
||
850 | unset( $request['amount'] ); |
||
851 | } |
||
852 | |||
853 | if ( $reason ) { |
||
854 | $request['metadata'] = array( |
||
855 | 'reason' => $reason, |
||
856 | ); |
||
857 | } |
||
858 | |||
859 | $request['charge'] = $charge_id; |
||
860 | WC_Stripe_Logger::log( "Info: Beginning refund for order {$charge_id} for the amount of {$amount}" ); |
||
861 | |||
862 | $request = apply_filters( 'wc_stripe_refund_request', $request, $order ); |
||
863 | |||
864 | $intent = $this->get_intent_from_order( $order ); |
||
865 | $intent_cancelled = false; |
||
866 | if ( $intent ) { |
||
867 | // If the order has a Payment Intent pending capture, then the Intent itself must be refunded (cancelled), not the Charge |
||
868 | if ( ! empty( $intent->error ) ) { |
||
869 | $response = $intent; |
||
870 | $intent_cancelled = true; |
||
871 | } elseif ( 'requires_capture' === $intent->status ) { |
||
872 | $result = WC_Stripe_API::request( |
||
873 | array(), |
||
874 | 'payment_intents/' . $intent->id . '/cancel' |
||
875 | ); |
||
876 | $intent_cancelled = true; |
||
877 | |||
878 | if ( ! empty( $result->error ) ) { |
||
879 | $response = $result; |
||
880 | } else { |
||
881 | $charge = end( $result->charges->data ); |
||
882 | $response = end( $charge->refunds->data ); |
||
883 | } |
||
884 | } |
||
885 | } |
||
886 | |||
887 | if ( ! $intent_cancelled ) { |
||
888 | $response = WC_Stripe_API::request( $request, 'refunds' ); |
||
889 | } |
||
890 | |||
891 | if ( ! empty( $response->error ) ) { |
||
892 | WC_Stripe_Logger::log( 'Error: ' . $response->error->message ); |
||
893 | |||
894 | return $response; |
||
895 | |||
896 | } elseif ( ! empty( $response->id ) ) { |
||
897 | $order->update_meta_data( '_stripe_refund_id', $response->id ); |
||
898 | |||
899 | $amount = wc_price( $response->amount / 100 ); |
||
900 | |||
901 | if ( in_array( strtolower( $order->get_currency() ), WC_Stripe_Helper::no_decimal_currencies() ) ) { |
||
902 | $amount = wc_price( $response->amount ); |
||
903 | } |
||
904 | |||
905 | if ( isset( $response->balance_transaction ) ) { |
||
906 | $this->update_fees( $order, $response->balance_transaction ); |
||
907 | } |
||
908 | |||
909 | /* translators: 1) dollar amount 2) transaction id 3) refund message */ |
||
910 | $refund_message = ( isset( $captured ) && 'yes' === $captured ) ? sprintf( __( 'Refunded %1$s - Refund ID: %2$s - Reason: %3$s', 'woocommerce-gateway-stripe' ), $amount, $response->id, $reason ) : __( 'Pre-Authorization Released', 'woocommerce-gateway-stripe' ); |
||
911 | |||
912 | $order->add_order_note( $refund_message ); |
||
913 | WC_Stripe_Logger::log( 'Success: ' . html_entity_decode( wp_strip_all_tags( $refund_message ) ) ); |
||
914 | |||
915 | return true; |
||
916 | } |
||
917 | } |
||
918 | |||
919 | /** |
||
920 | * Add payment method via account screen. |
||
921 | * We don't store the token locally, but to the Stripe API. |
||
922 | * |
||
923 | * @since 3.0.0 |
||
924 | * @version 4.0.0 |
||
925 | */ |
||
926 | public function add_payment_method() { |
||
970 | |||
971 | /** |
||
972 | * Gets the locale with normalization that only Stripe accepts. |
||
973 | * |
||
974 | * @since 4.0.6 |
||
975 | * @return string $locale |
||
976 | */ |
||
977 | public function get_locale() { |
||
992 | |||
993 | /** |
||
994 | * Change the idempotency key so charge can |
||
995 | * process order as a different transaction. |
||
996 | * |
||
997 | * @since 4.0.6 |
||
998 | * @param string $idempotency_key |
||
999 | * @param array $request |
||
1000 | */ |
||
1001 | public function change_idempotency_key( $idempotency_key, $request ) { |
||
1008 | |||
1009 | /** |
||
1010 | * Checks if request is the original to prevent double processing |
||
1011 | * on WC side. The original-request header and request-id header |
||
1012 | * needs to be the same to mean its the original request. |
||
1013 | * |
||
1014 | * @since 4.0.6 |
||
1015 | * @param array $headers |
||
1016 | */ |
||
1017 | public function is_original_request( $headers ) { |
||
1024 | |||
1025 | /** |
||
1026 | * Generates the request when creating a new payment intent. |
||
1027 | * |
||
1028 | * @param WC_Order $order The order that is being paid for. |
||
1029 | * @param object $prepared_source The source that is used for the payment. |
||
1030 | * @return array The arguments for the request. |
||
1031 | */ |
||
1032 | public function generate_create_intent_request( $order, $prepared_source ) { |
||
1033 | // The request for a charge contains metadata for the intent. |
||
1034 | $full_request = $this->generate_payment_request( $order, $prepared_source ); |
||
1035 | |||
1036 | $request = array( |
||
1037 | 'source' => $prepared_source->source, |
||
1038 | 'amount' => WC_Stripe_Helper::get_stripe_amount( $order->get_total() ), |
||
1039 | 'currency' => strtolower( $order->get_currency() ), |
||
1040 | 'description' => $full_request['description'], |
||
1041 | 'metadata' => $full_request['metadata'], |
||
1042 | 'capture_method' => ( 'true' === $full_request['capture'] ) ? 'automatic' : 'manual', |
||
1043 | 'payment_method_types' => array( |
||
1070 | |||
1071 | /** |
||
1072 | * Create the level 3 data array to send to Stripe when making a purchase. |
||
1073 | * |
||
1074 | * @param WC_Order $order The order that is being paid for. |
||
1075 | * @return array The level 3 data to send to Stripe. |
||
1076 | */ |
||
1077 | public function get_level3_data_from_order( $order ) { |
||
1123 | |||
1124 | /** |
||
1125 | * Create a new PaymentIntent. |
||
1126 | * |
||
1127 | * @param WC_Order $order The order that is being paid for. |
||
1128 | * @param object $prepared_source The source that is used for the payment. |
||
1129 | * @return object An intent or an error. |
||
1130 | */ |
||
1131 | public function create_intent( $order, $prepared_source ) { |
||
1148 | |||
1149 | /** |
||
1150 | * Updates an existing intent with updated amount, source, and customer. |
||
1151 | * |
||
1152 | * @param object $intent The existing intent object. |
||
1153 | * @param WC_Order $order The order. |
||
1154 | * @param object $prepared_source Currently selected source. |
||
1155 | * @return object An updated intent. |
||
1156 | */ |
||
1157 | public function update_existing_intent( $intent, $order, $prepared_source ) { |
||
1158 | $request = array(); |
||
1159 | |||
1160 | if ( $prepared_source->source !== $intent->source ) { |
||
1161 | $request['source'] = $prepared_source->source; |
||
1162 | } |
||
1163 | |||
1164 | $new_amount = WC_Stripe_Helper::get_stripe_amount( $order->get_total() ); |
||
1165 | if ( $intent->amount !== $new_amount ) { |
||
1166 | $request['amount'] = $new_amount; |
||
1167 | } |
||
1168 | |||
1169 | if ( $prepared_source->customer && $intent->customer !== $prepared_source->customer ) { |
||
1170 | $request['customer'] = $prepared_source->customer; |
||
1171 | } |
||
1172 | |||
1173 | if ( $this->has_subscription( $order ) ) { |
||
1174 | // If this is a failed subscription order payment, the intent should be |
||
1175 | // prepared for future usage. |
||
1176 | $request['setup_future_usage'] = 'off_session'; |
||
1177 | } |
||
1178 | |||
1179 | if ( empty( $request ) ) { |
||
1180 | return $intent; |
||
1181 | } |
||
1182 | |||
1183 | $level3_data = $this->get_level3_data_from_order( $order ); |
||
1184 | return WC_Stripe_API::request_with_level3_data( |
||
1185 | $request, |
||
1186 | "payment_intents/$intent->id", |
||
1187 | $level3_data, |
||
1188 | $order |
||
1189 | ); |
||
1190 | } |
||
1191 | |||
1192 | /** |
||
1193 | * Confirms an intent if it is the `requires_confirmation` state. |
||
1194 | * |
||
1195 | * @since 4.2.1 |
||
1196 | * @param object $intent The intent to confirm. |
||
1197 | * @param WC_Order $order The order that the intent is associated with. |
||
1198 | * @param object $prepared_source The source that is being charged. |
||
1199 | * @return object Either an error or the updated intent. |
||
1200 | */ |
||
1201 | public function confirm_intent( $intent, $order, $prepared_source ) { |
||
1233 | |||
1234 | /** |
||
1235 | * Saves intent to order. |
||
1236 | * |
||
1237 | * @since 3.2.0 |
||
1238 | * @param WC_Order $order For to which the source applies. |
||
1239 | * @param stdClass $intent Payment intent information. |
||
1240 | */ |
||
1241 | public function save_intent_to_order( $order, $intent ) { |
||
1248 | |||
1249 | /** |
||
1250 | * Retrieves the payment intent, associated with an order. |
||
1251 | * |
||
1252 | * @since 4.2 |
||
1253 | * @param WC_Order $order The order to retrieve an intent for. |
||
1254 | * @return obect|bool Either the intent object or `false`. |
||
1255 | */ |
||
1256 | public function get_intent_from_order( $order ) { |
||
1272 | |||
1273 | /** |
||
1274 | * Retrieves intent from Stripe API by intent id. |
||
1275 | * |
||
1276 | * @param string $intent_type Either 'payment_intents' or 'setup_intents'. |
||
1277 | * @param string $intent_id Intent id. |
||
1278 | * @return object|bool Either the intent object or `false`. |
||
1279 | * @throws Exception Throws exception for unknown $intent_type. |
||
1280 | */ |
||
1281 | private function get_intent( $intent_type, $intent_id ) { |
||
1297 | |||
1298 | /** |
||
1299 | * Locks an order for payment intent processing for 5 minutes. |
||
1300 | * |
||
1301 | * @since 4.2 |
||
1302 | * @param WC_Order $order The order that is being paid. |
||
1303 | * @param stdClass $intent The intent that is being processed. |
||
1304 | * @return bool A flag that indicates whether the order is already locked. |
||
1305 | */ |
||
1306 | public function lock_order_payment( $order, $intent = null ) { |
||
1321 | |||
1322 | /** |
||
1323 | * Unlocks an order for processing by payment intents. |
||
1324 | * |
||
1325 | * @since 4.2 |
||
1326 | * @param WC_Order $order The order that is being unlocked. |
||
1327 | */ |
||
1328 | public function unlock_order_payment( $order ) { |
||
1332 | |||
1333 | /** |
||
1334 | * Given a response from Stripe, check if it's a card error where authentication is required |
||
1335 | * to complete the payment. |
||
1336 | * |
||
1337 | * @param object $response The response from Stripe. |
||
1338 | * @return boolean Whether or not it's a 'authentication_required' error |
||
1339 | */ |
||
1340 | public function is_authentication_required_for_payment( $response ) { |
||
1344 | |||
1345 | /** |
||
1346 | * Creates a SetupIntent for future payments, and saves it to the order. |
||
1347 | * |
||
1348 | * @param WC_Order $order The ID of the (free/pre- order). |
||
1349 | * @param object $prepared_source The source, entered/chosen by the customer. |
||
1350 | * @return string The client secret of the intent, used for confirmation in JS. |
||
1351 | */ |
||
1352 | public function setup_intent( $order, $prepared_source ) { |
||
1369 | |||
1370 | /** |
||
1371 | * Create and confirm a new PaymentIntent. |
||
1372 | * |
||
1373 | * @param WC_Order $order The order that is being paid for. |
||
1374 | * @param object $prepared_source The source that is used for the payment. |
||
1375 | * @param float $amount The amount to charge. If not specified, it will be read from the order. |
||
1376 | * @return object An intent or an error. |
||
1377 | */ |
||
1378 | public function create_and_confirm_intent_for_off_session( $order, $prepared_source, $amount = NULL ) { |
||
1451 | |||
1452 | /** |
||
1453 | * Checks if subscription has a Stripe customer ID and adds it if doesn't. |
||
1454 | * |
||
1455 | * Fix renewal for existing subscriptions affected by https://github.com/woocommerce/woocommerce-gateway-stripe/issues/1072. |
||
1456 | * @param int $order_id subscription renewal order id. |
||
1457 | */ |
||
1458 | public function ensure_subscription_has_customer_id( $order_id ) { |
||
1468 | |||
1469 | /** Verifies whether a certain ZIP code is valid for the US, incl. 4-digit extensions. |
||
1470 | * |
||
1471 | * @param string $zip The ZIP code to verify. |
||
1472 | * @return boolean |
||
1473 | */ |
||
1474 | public function is_valid_us_zip_code( $zip ) { |
||
1477 | } |
||
1478 |
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.