Completed
Push — master ( 820036...352120 )
by Roy
02:31
created

WC_Stripe_Payment_Request   B

Complexity

Total Complexity 47

Size/Duplication

Total Lines 341
Duplicated Lines 9.38 %

Coupling/Cohesion

Components 0
Dependencies 0

Importance

Changes 0
Metric Value
wmc 47
lcom 0
cbo 0
dl 32
loc 341
rs 8.439
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
B is_activated() 0 8 7
A get_publishable_key() 0 9 3
B scripts() 0 39 6
B ajax_get_cart_details() 0 29 2
C calculate_shipping() 14 49 7
C ajax_get_shipping_options() 0 48 7
B ajax_update_shipping_method() 0 28 4
A ajax_create_order() 0 13 3
B compute_display_items() 18 54 7

How to fix   Duplicated Code    Complexity   

Duplicated Code

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 Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like WC_Stripe_Payment_Request 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_Request, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Stripe Payment Request API
4
 *
5
 * @package WooCommerce_Stripe/Classes/Payment_Request
6
 * @since   3.1.0
7
 * @version 3.1.0
8
 */
9
10
if ( ! defined( 'ABSPATH' ) ) {
11
	exit;
12
}
13
14
/**
15
 * WC_Stripe_Payment_Request class.
16
 */
17
class WC_Stripe_Payment_Request {
18
19
	/**
20
	 * Initialize class actions.
21
	 */
22
	public function __construct() {
23
		add_action( 'wp_enqueue_scripts', array( $this, 'scripts' ) );
24
25
		add_action( 'wc_ajax_wc_stripe_get_cart_details', array( $this, 'ajax_get_cart_details' ) );
26
		add_action( 'wc_ajax_wc_stripe_get_shipping_options', array( $this, 'ajax_get_shipping_options' ) );
27
		add_action( 'wc_ajax_wc_stripe_update_shipping_method', array( $this, 'ajax_update_shipping_method' ) );
28
		add_action( 'wc_ajax_wc_stripe_create_order', array( $this, 'ajax_create_order' ) );
29
	}
30
31
	/**
32
	 * Check if Stripe gateway is enabled.
33
	 *
34
	 * @return bool
35
	 */
36
	protected function is_activated() {
37
		$options             = get_option( 'woocommerce_stripe_settings', array() );
38
		$enabled             = isset( $options['enabled'] ) && 'yes' === $options['enabled'];
39
		$stripe_checkout     = isset( $options['stripe_checkout'] ) && 'yes' !== $options['stripe_checkout'];
40
		$request_payment_api = isset( $options['request_payment_api'] ) && 'yes' === $options['request_payment_api'];
41
42
		return $enabled && $stripe_checkout && $request_payment_api && is_ssl();
43
	}
44
45
	/**
46
	 * Get publishable key.
47
	 *
48
	 * @return string
49
	 */
50
	protected function get_publishable_key() {
51
		$options = get_option( 'woocommerce_stripe_settings', array() );
52
53
		if ( empty( $options ) ) {
54
			return '';
55
		}
56
57
		return 'yes' === $options['testmode'] ? $options['test_publishable_key'] : $options['publishable_key'];
58
	}
59
60
	/**
61
	 * Load public scripts.
62
	 */
63
	public function scripts() {
64
		// Load PaymentRequest only on cart for now.
65
		if ( ! is_cart() ) {
66
			return;
67
		}
68
69
		if ( ! $this->is_activated() ) {
70
			return;
71
		}
72
73
		$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
74
75
		wp_enqueue_script( 'stripe', 'https://js.stripe.com/v2/', '', '1.0', true );
76
		wp_enqueue_script( 'google-payment-request-shim', 'https://storage.googleapis.com/prshim/v1/payment-shim.js', '', '1.0', false );
77
		wp_enqueue_script( 'wc-stripe-payment-request', plugins_url( 'assets/js/payment-request' . $suffix . '.js', WC_STRIPE_MAIN_FILE ), array( 'jquery', 'stripe' ), WC_STRIPE_VERSION, true );
78
79
		wp_localize_script(
80
			'wc-stripe-payment-request',
81
			'wcStripePaymentRequestParams',
82
			array(
83
				'ajax_url' => WC_AJAX::get_endpoint( '%%endpoint%%' ),
84
				'stripe'   => array(
85
					'key'                => $this->get_publishable_key(),
86
					'allow_prepaid_card' => apply_filters( 'wc_stripe_allow_prepaid_card', true ) ? 'yes' : 'no',
87
				),
88
				'nonce'    => array(
89
					'payment'         => wp_create_nonce( 'wc-stripe-payment-request' ),
90
					'shipping'        => wp_create_nonce( 'wc-stripe-payment-request-shipping' ),
91
					'update_shipping' => wp_create_nonce( 'wc-stripe-update-shipping-method' ),
92
					'checkout'        => wp_create_nonce( 'woocommerce-process_checkout' ),
93
				),
94
				'i18n'     => array(
95
					'no_prepaid_card'  => __( 'Sorry, we\'re not accepting prepaid cards at this time.', 'woocommerce-gateway-stripe' ),
96
					/* translators: Do not translate the [option] placeholder */
97
					'unknown_shipping' => __( 'Unknown shipping option "[option]".', 'woocommerce-gateway-stripe' ),
98
				),
99
			)
100
		);
101
	}
102
103
	/**
104
	 * Get cart details.
105
	 */
106
	public function ajax_get_cart_details() {
107
		check_ajax_referer( 'wc-stripe-payment-request', 'security' );
108
109
		if ( ! defined( 'WOOCOMMERCE_CART' ) ) {
110
			define( 'WOOCOMMERCE_CART', true );
111
		}
112
113
		WC()->cart->calculate_totals();
114
115
		$currency = get_woocommerce_currency();
116
117
		// Set mandatory payment details.
118
		$data = array(
119
			'shipping_required' => WC()->cart->needs_shipping(),
120
			'order_data'        => array(
121
				'total' => array(
122
					'label'  => __( 'Total', 'woocommerce-gateway-stripe' ),
123
					'amount' => array(
124
						'value'    => max( 0, apply_filters( 'woocommerce_calculated_total', round( WC()->cart->cart_contents_total + WC()->cart->fee_total + WC()->cart->tax_total, WC()->cart->dp ), WC()->cart ) ),
125
						'currency' => $currency,
126
					),
127
				),
128
				// Include line items such as subtotal, fees and taxes. No shipping option is provided here because it is not chosen yet.
129
				'displayItems' => $this->compute_display_items( null ),
130
			),
131
		);
132
133
		wp_send_json( $data );
134
	}
135
136
	/**
137
	 * Calculate and set shipping method.
138
	 *
139
	 * @since 3.1.0
140
	 * @version 3.1.0
141
	 * @param array $address
142
	 */
143
	public function calculate_shipping( $address = array() ) {
144
		$country   = $address['country'];
145
		$state     = $address['state'];
146
		$postcode  = $address['postcode'];
147
		$city      = $address['city'];
148
		$address_1 = $address['address'];
149
		$address_2 = $address['address_2'];
150
151
		WC()->shipping->reset_shipping();
152
153
		if ( $postcode && WC_Validation::is_postcode( $postcode, $country ) ) {
154
			$postcode = wc_format_postcode( $postcode, $country );
155
		}
156
157 View Code Duplication
		if ( $country ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
158
			WC()->customer->set_location( $country, $state, $postcode, $city );
159
			WC()->customer->set_shipping_location( $country, $state, $postcode, $city );
160
		} else {
161
			WC()->customer->set_to_base();
162
			WC()->customer->set_shipping_to_base();
163
		}
164
165
		WC()->customer->calculated_shipping( true );
166
167
		$packages = array();
168
169
		$packages[0]['contents']                 = WC()->cart->get_cart();
170
		$packages[0]['contents_cost']            = 0;
171
		$packages[0]['applied_coupons']          = WC()->cart->applied_coupons;
172
		$packages[0]['user']['ID']               = get_current_user_id();
173
		$packages[0]['destination']['country']   = $country;
174
		$packages[0]['destination']['state']     = $state;
175
		$packages[0]['destination']['postcode']  = $postcode;
176
		$packages[0]['destination']['city']      = $city;
177
		$packages[0]['destination']['address']   = $address_1;
178
		$packages[0]['destination']['address_2'] = $address_2;
179
180 View Code Duplication
		foreach ( WC()->cart->get_cart() as $item ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
181
			if ( $item['data']->needs_shipping() ) {
182
				if ( isset( $item['line_total'] ) ) {
183
					$packages[0]['contents_cost'] += $item['line_total'];
184
				}
185
			}
186
		}
187
188
		$packages = apply_filters( 'woocommerce_cart_shipping_packages', $packages );
189
190
		WC()->shipping->calculate_shipping( $packages );
191
	}
192
193
	/**
194
	 * Get shipping options.
195
	 *
196
	 * @see WC_Cart::get_shipping_packages().
197
	 * @see WC_Shipping::calculate_shipping().
198
	 * @see WC_Shipping::get_packages().
199
	 */
200
	public function ajax_get_shipping_options() {
201
		check_ajax_referer( 'wc-stripe-payment-request-shipping', 'security' );
202
203
		// Set the shipping package.
204
		$posted   = filter_input_array( INPUT_POST, array(
205
			'country'   => FILTER_SANITIZE_STRING,
206
			'state'     => FILTER_SANITIZE_STRING,
207
			'postcode'  => FILTER_SANITIZE_STRING,
208
			'city'      => FILTER_SANITIZE_STRING,
209
			'address'   => FILTER_SANITIZE_STRING,
210
			'address_2' => FILTER_SANITIZE_STRING,
211
		) );
212
213
		$this->calculate_shipping( $posted );
214
215
		// Set the shipping options.
216
		$currency = get_woocommerce_currency();
217
		$data     = array();
218
219
		$packages = WC()->shipping->get_packages();
220
221
		if ( ! empty( $packages ) && WC()->customer->has_calculated_shipping() ) {
222
			foreach ( $packages as $package_key => $package ) {
223
				if ( empty( $package['rates'] ) ) {
224
					break;
225
				}
226
227
				foreach ( $package['rates'] as $key => $rate ) {
228
					$data[] = array(
229
						'id'       => $rate->id,
230
						'label'    => $rate->label,
231
						'amount'   => array(
232
							'currency' => $currency,
233
							'value'    => $rate->cost,
234
						),
235
						'selected' => false,
236
					);
237
				}
238
			}
239
		}
240
241
		// Auto select when have only one shipping method available.
242
		if ( 1 === count( $data ) ) {
243
			$data[0]['selected'] = true;
244
		}
245
246
		wp_send_json( $data );
247
	}
248
249
	/**
250
	 * Update shipping method.
251
	 */
252
	public function ajax_update_shipping_method() {
253
		check_ajax_referer( 'wc-stripe-update-shipping-method', 'security' );
254
255
		if ( ! defined( 'WOOCOMMERCE_CART' ) ) {
256
			define( 'WOOCOMMERCE_CART', true );
257
		}
258
259
		$chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods' );
260
		$shipping_method         = filter_input( INPUT_POST, 'shipping_method', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
261
262
		if ( is_array( $shipping_method ) ) {
263
			foreach ( $shipping_method as $i => $value ) {
264
				$chosen_shipping_methods[ $i ] = wc_clean( $value );
265
			}
266
		}
267
268
		WC()->session->set( 'chosen_shipping_methods', $chosen_shipping_methods );
269
270
		WC()->cart->calculate_totals();
271
272
		// Send back the new cart total and line items to be displayed, such as subtotal, shipping rate(s), fees and taxes.
273
		$data      = array(
274
			'total' => WC()->cart->total,
275
			'items' => $this->compute_display_items( $shipping_method[0] ),
276
		);
277
278
		wp_send_json( $data );
279
	}
280
281
	/**
282
	 * Create order.
283
	 */
284
	public function ajax_create_order() {
285
		if ( WC()->cart->is_empty() ) {
286
			wp_send_json_error( __( 'Empty cart', 'woocommerce-gateway-stripe' ) );
287
		}
288
289
		if ( ! defined( 'WOOCOMMERCE_CHECKOUT' ) ) {
290
			define( 'WOOCOMMERCE_CHECKOUT', true );
291
		}
292
293
		WC()->checkout()->process_checkout();
294
295
		die( 0 );
296
	}
297
298
	/**
299
	 * Compute display items to be included in the 'displayItems' key of the PaymentDetails.
300
	 *
301
	 * @param string shipping_method_id If shipping method ID is provided, will include display items about shipping.
302
	 */
303
	protected function compute_display_items( $shipping_method_id ) {
304
		$currency = get_woocommerce_currency();
305
		$items = array(
306
			// Subtotal excluding tax, because taxes is a separate item, below.
307
			array(
308
				'label' => __( 'Subtotal', 'woocommerce-gateway-stripe' ),
309
				'amount' => array(
310
					'value'    => max( 0, round( WC()->cart->subtotal_ex_tax, WC()->cart->dp ) ),
311
					'currency' => $currency,
312
				),
313
			),
314
		);
315
		// If a chosen shipping option was provided, add line item(s) for it and include the shipping tax.
316
		$tax_total = max( 0, round( WC()->cart->tax_total, WC()->cart->dp ) );
317
		if ( $shipping_method_id ) {
318
			$tax_total = max( 0, round( WC()->cart->tax_total + WC()->cart->shipping_tax_total, WC()->cart->dp ) );
319
			// Look through the package rates for $shipping_method_id, and when found, add a line item.
320
			foreach ( WC()->shipping->get_packages() as $package_key => $package ) {
321
				foreach ( $package['rates'] as $key => $rate ) {
322
					if ( $rate->id  == $shipping_method_id ) {
323
						$items[] = array(
324
							'label' => $rate->label,
325
							'amount' => array(
326
								'value' => $rate->cost,
327
								'currency' => $currency,
328
							),
329
						);
330
						break;
331
					}
332
				}
333
			}
334
		}
335
		// Include fees and taxes as display items.
336 View Code Duplication
		foreach ( WC()->cart->fees as $key => $fee ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
337
			$items[] = array(
338
				'label'  => $fee->name,
339
				'amount' => array(
340
					'currency' => $currency,
341
					'value'    => $fee->amount,
342
				),
343
			);
344
		}
345
		// The tax total may include the shipping taxes if a shipping option is provided.
346 View Code Duplication
		if ( 0 < $tax_total ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
347
			$items[] = array(
348
				'label'  => __( 'Tax', 'woocommerce-gateway-stripe' ),
349
				'amount' => array(
350
					'currency' => $currency,
351
					'value'    => $tax_total,
352
				),
353
			);
354
		}
355
		return $items;
356
	}
357
}
358
359
new WC_Stripe_Payment_Request();
360