Completed
Push — master ( 394da4...31ec7c )
by Mike
10:43
created

WC_Addons_Gateway_Simplify_Commerce::process_subscription()   C

Complexity

Conditions 11
Paths 29

Size

Total Lines 58
Code Lines 34

Duplication

Lines 28
Ratio 48.28 %

Importance

Changes 0
Metric Value
cc 11
dl 28
loc 58
rs 6.4179
c 0
b 0
f 0
eloc 34
nc 29
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
if ( ! defined( 'ABSPATH' ) ) {
4
	exit; // Exit if accessed directly
5
}
6
7
/**
8
 * Simplify Commerce Gateway for subscriptions.
9
 *
10
 * @class 		WC_Addons_Gateway_Simplify_Commerce
11
 * @extends		WC_Gateway_Simplify_Commerce
12
 * @since       2.2.0
13
 * @version		1.0.0
14
 * @package		WooCommerce/Classes/Payment
15
 * @author 		WooThemes
16
 */
17
class WC_Addons_Gateway_Simplify_Commerce extends WC_Gateway_Simplify_Commerce {
18
19
	/**
20
	 * Constructor.
21
	 */
22
	public function __construct() {
23
		parent::__construct();
24
25
		if ( class_exists( 'WC_Subscriptions_Order' ) ) {
26
			add_action( 'woocommerce_scheduled_subscription_payment_' . $this->id, array( $this, 'scheduled_subscription_payment' ), 10, 2 );
27
			add_action( 'woocommerce_subscription_failing_payment_method_updated_' . $this->id, array( $this, 'update_failing_payment_method' ), 10, 2 );
28
29
			add_action( 'wcs_resubscribe_order_created', array( $this, 'delete_resubscribe_meta' ), 10 );
30
31
			// Allow store managers to manually set Simplify as the payment method on a subscription
32
			add_filter( 'woocommerce_subscription_payment_meta', array( $this, 'add_subscription_payment_meta' ), 10, 2 );
33
			add_filter( 'woocommerce_subscription_validate_payment_meta', array( $this, 'validate_subscription_payment_meta' ), 10, 2 );
34
		}
35
36
		if ( class_exists( 'WC_Pre_Orders_Order' ) ) {
37
			add_action( 'wc_pre_orders_process_pre_order_completion_payment_' . $this->id, array( $this, 'process_pre_order_release_payment' ) );
38
		}
39
40
		add_filter( 'woocommerce_simplify_commerce_hosted_args', array( $this, 'hosted_payment_args' ), 10, 2 );
41
		add_action( 'woocommerce_api_wc_addons_gateway_simplify_commerce', array( $this, 'return_handler' ) );
42
	}
43
44
	/**
45
	 * Hosted payment args.
46
	 *
47
	 * @param  array $args
48
	 * @param  int   $order_id
49
	 * @return array
50
	 */
51
	public function hosted_payment_args( $args, $order_id ) {
52
		if ( ( $this->order_contains_subscription( $order_id ) ) || ( $this->order_contains_pre_order( $order_id ) && WC_Pre_Orders_Order::order_requires_payment_tokenization( $order_id ) ) ) {
53
			$args['operation'] = 'create.token';
54
		}
55
56
		$args['redirect-url'] = WC()->api_request_url( 'WC_Addons_Gateway_Simplify_Commerce' );
57
58
		return $args;
59
	}
60
61
	/**
62
	 * Check if order contains subscriptions.
63
	 *
64
	 * @param  int $order_id
65
	 * @return bool
66
	 */
67
	protected function order_contains_subscription( $order_id ) {
68
		return function_exists( 'wcs_order_contains_subscription' ) && ( wcs_order_contains_subscription( $order_id ) || wcs_order_contains_renewal( $order_id ) );
69
	}
70
71
	/**
72
	 * Check if order contains pre-orders.
73
	 *
74
	 * @param  int $order_id
75
	 * @return bool
76
	 */
77
	protected function order_contains_pre_order( $order_id ) {
78
		return class_exists( 'WC_Pre_Orders_Order' ) && WC_Pre_Orders_Order::order_contains_pre_order( $order_id );
79
	}
80
81
	/**
82
	 * Process the subscription.
83
	 *
84
	 * @param  WC_Order $order
85
	 * @param  string   $cart_token
86
	 * @uses   Simplify_ApiException
87
	 * @uses   Simplify_BadRequestException
88
	 * @return array
89
	 * @throws Exception
90
	 */
91
	protected function process_subscription( $order, $cart_token = '' ) {
92
		try {
93
			if ( empty( $cart_token ) ) {
94
				$error_msg = __( 'Please make sure your card details have been entered correctly and that your browser supports JavaScript.', 'woocommerce' );
95
96
				if ( 'yes' == $this->sandbox ) {
97
					$error_msg .= ' ' . __( 'Developers: Please make sure that you\'re including jQuery and there are no JavaScript errors on the page.', 'woocommerce' );
98
				}
99
100
				throw new Simplify_ApiException( $error_msg );
101
			}
102
103
			// Create customer
104
			$customer = Simplify_Customer::createCustomer( array(
105
				'token'     => $cart_token,
106
				'email'     => $order->billing_email,
107
				'name'      => trim( $order->get_formatted_billing_full_name() ),
108
				'reference' => $order->id
109
			) );
110
111
			if ( is_object( $customer ) && '' != $customer->id ) {
112
				$this->save_subscription_meta( $order->id, $customer->id );
113
			} else {
114
				$error_msg = __( 'Error creating user in Simplify Commerce.', 'woocommerce' );
115
116
				throw new Simplify_ApiException( $error_msg );
117
			}
118
119
			$payment_response = $this->process_subscription_payment( $order, $order->get_total() );
120
121
			if ( is_wp_error( $payment_response ) ) {
122
				throw new Exception( $payment_response->get_error_message() );
123
			} else {
124
				// Remove cart
125
				WC()->cart->empty_cart();
126
127
				// Return thank you page redirect
128
				return array(
129
					'result'   => 'success',
130
					'redirect' => $this->get_return_url( $order )
131
				);
132
			}
133
134
		} catch ( Simplify_ApiException $e ) {
135
			if ( $e instanceof Simplify_BadRequestException && $e->hasFieldErrors() && $e->getFieldErrors() ) {
136
				foreach ( $e->getFieldErrors() as $error ) {
137
					wc_add_notice( $error->getFieldName() . ': "' . $error->getMessage() . '" (' . $error->getErrorCode() . ')', 'error' );
138
				}
139
			} else {
140
				wc_add_notice( $e->getMessage(), 'error' );
141
			}
142
143
			return array(
144
				'result'   => 'fail',
145
				'redirect' => ''
146
			);
147
		}
148
	}
149
150
	/**
151
	 * Store the customer and card IDs on the order and subscriptions in the order.
152
	 *
153
	 * @param int $order_id
154
	 * @param string $customer_id
155
	 */
156
	protected function save_subscription_meta( $order_id, $customer_id ) {
157
158
		$customer_id = wc_clean( $customer_id );
159
160
		update_post_meta( $order_id, '_simplify_customer_id', $customer_id );
161
162
		// Also store it on the subscriptions being purchased in the order
163
		foreach( wcs_get_subscriptions_for_order( $order_id ) as $subscription ) {
164
			update_post_meta( $subscription->id, '_simplify_customer_id', $customer_id );
165
		}
166
	}
167
168
	/**
169
	 * Process the pre-order.
170
	 *
171
	 * @param WC_Order $order
172
	 * @param string   $cart_token
173
	 * @uses  Simplify_ApiException
174
	 * @uses  Simplify_BadRequestException
175
	 * @return array
176
	 */
177
	protected function process_pre_order( $order, $cart_token = '' ) {
178
		if ( WC_Pre_Orders_Order::order_requires_payment_tokenization( $order->id ) ) {
179
180
			try {
181
				if ( $order->order_total * 100 < 50 ) {
182
					$error_msg = __( 'Sorry, the minimum allowed order total is 0.50 to use this payment method.', 'woocommerce' );
183
184
					throw new Simplify_ApiException( $error_msg );
185
				}
186
187
				if ( empty( $cart_token ) ) {
188
					$error_msg = __( 'Please make sure your card details have been entered correctly and that your browser supports JavaScript.', 'woocommerce' );
189
190
					if ( 'yes' == $this->sandbox ) {
191
						$error_msg .= ' ' . __( 'Developers: Please make sure that you\'re including jQuery and there are no JavaScript errors on the page.', 'woocommerce' );
192
					}
193
194
					throw new Simplify_ApiException( $error_msg );
195
				}
196
197
				// Create customer
198
				$customer = Simplify_Customer::createCustomer( array(
199
					'token'     => $cart_token,
200
					'email'     => $order->billing_email,
201
					'name'      => trim( $order->get_formatted_billing_full_name() ),
202
					'reference' => $order->id
203
				) );
204
205
				if ( is_object( $customer ) && '' != $customer->id ) {
206
					$customer_id = wc_clean( $customer->id );
207
208
					// Store the customer ID in the order
209
					update_post_meta( $order->id, '_simplify_customer_id', $customer_id );
210
				} else {
211
					$error_msg = __( 'Error creating user in Simplify Commerce.', 'woocommerce' );
212
213
					throw new Simplify_ApiException( $error_msg );
214
				}
215
216
				// Reduce stock levels
217
				$order->reduce_order_stock();
218
219
				// Remove cart
220
				WC()->cart->empty_cart();
221
222
				// Is pre ordered!
223
				WC_Pre_Orders_Order::mark_order_as_pre_ordered( $order );
224
225
				// Return thank you page redirect
226
				return array(
227
					'result'   => 'success',
228
					'redirect' => $this->get_return_url( $order )
229
				);
230
231
			} catch ( Simplify_ApiException $e ) {
232
				if ( $e instanceof Simplify_BadRequestException && $e->hasFieldErrors() && $e->getFieldErrors() ) {
233
					foreach ( $e->getFieldErrors() as $error ) {
234
						wc_add_notice( $error->getFieldName() . ': "' . $error->getMessage() . '" (' . $error->getErrorCode() . ')', 'error' );
235
					}
236
				} else {
237
					wc_add_notice( $e->getMessage(), 'error' );
238
				}
239
240
				return array(
241
					'result'   => 'fail',
242
					'redirect' => ''
243
				);
244
			}
245
246
		} else {
247
			return parent::process_standard_payments( $order, $cart_token );
248
		}
249
	}
250
251
	/**
252
	 * Process the payment.
253
	 *
254
	 * @param  int $order_id
255
	 * @return array
256
	 */
257
	public function process_payment( $order_id ) {
258
		$cart_token = isset( $_POST['simplify_token'] ) ? wc_clean( $_POST['simplify_token'] ) : '';
259
		$order      = wc_get_order( $order_id );
260
261
		// Processing subscription
262
		if ( 'standard' == $this->mode && ( $this->order_contains_subscription( $order->id ) || ( function_exists( 'wcs_is_subscription' ) && wcs_is_subscription( $order_id ) ) ) ) {
263
			return $this->process_subscription( $order, $cart_token );
264
265
		// Processing pre-order
266
		} elseif ( 'standard' == $this->mode && $this->order_contains_pre_order( $order->id ) ) {
267
			return $this->process_pre_order( $order, $cart_token );
268
269
		// Processing regular product
270
		} else {
271
			return parent::process_payment( $order_id );
272
		}
273
	}
274
275
	/**
276
	 * process_subscription_payment function.
277
	 *
278
	 * @param WC_order $order
279
	 * @param int $amount (default: 0)
280
	 * @uses  Simplify_BadRequestException
281
	 * @return bool|WP_Error
282
	 */
283
	public function process_subscription_payment( $order, $amount = 0 ) {
284
		if ( 0 == $amount ) {
285
			// Payment complete
286
			$order->payment_complete();
287
288
			return true;
289
		}
290
291
		if ( $amount * 100 < 50 ) {
292
			return new WP_Error( 'simplify_error', __( 'Sorry, the minimum allowed order total is 0.50 to use this payment method.', 'woocommerce' ) );
293
		}
294
295
		$customer_id = get_post_meta( $order->id, '_simplify_customer_id', true );
296
297
		if ( ! $customer_id ) {
298
			return new WP_Error( 'simplify_error', __( 'Customer not found', 'woocommerce' ) );
299
		}
300
301
		try {
302
			// Charge the customer
303
			$payment = Simplify_Payment::createPayment( array(
304
				'amount'              => $amount * 100, // In cents.
305
				'customer'            => $customer_id,
306
				'description'         => sprintf( __( '%s - Order #%s', 'woocommerce' ), esc_html( get_bloginfo( 'name', 'display' ) ), $order->get_order_number() ),
307
				'currency'            => strtoupper( get_woocommerce_currency() ),
308
				'reference'           => $order->id
309
			) );
310
311
		} catch ( Exception $e ) {
312
313
			$error_message = $e->getMessage();
314
315
			if ( $e instanceof Simplify_BadRequestException && $e->hasFieldErrors() && $e->getFieldErrors() ) {
316
				$error_message = '';
317
				foreach ( $e->getFieldErrors() as $error ) {
318
					$error_message .= ' ' . $error->getFieldName() . ': "' . $error->getMessage() . '" (' . $error->getErrorCode() . ')';
319
				}
320
			}
321
322
			$order->add_order_note( sprintf( __( 'Simplify payment error: %s', 'woocommerce' ), $error_message ) );
323
324
			return new WP_Error( 'simplify_payment_declined', $e->getMessage(), array( 'status' => $e->getCode() ) );
325
		}
326
327
		if ( 'APPROVED' == $payment->paymentStatus ) {
328
			// Payment complete
329
			$order->payment_complete( $payment->id );
330
331
			// Add order note
332
			$order->add_order_note( sprintf( __( 'Simplify payment approved (ID: %s, Auth Code: %s)', 'woocommerce' ), $payment->id, $payment->authCode ) );
333
334
			return true;
335
		} else {
336
			$order->add_order_note( __( 'Simplify payment declined', 'woocommerce' ) );
337
338
			return new WP_Error( 'simplify_payment_declined', __( 'Payment was declined - please try another card.', 'woocommerce' ) );
339
		}
340
	}
341
342
	/**
343
	 * scheduled_subscription_payment function.
344
	 *
345
	 * @param float $amount_to_charge The amount to charge.
346
	 * @param WC_Order $renewal_order A WC_Order object created to record the renewal payment.
347
	 */
348
	public function scheduled_subscription_payment( $amount_to_charge, $renewal_order ) {
349
		$result = $this->process_subscription_payment( $renewal_order, $amount_to_charge );
350
351
		if ( is_wp_error( $result ) ) {
352
			$renewal_order->update_status( 'failed', sprintf( __( 'Simplify Transaction Failed (%s)', 'woocommerce' ), $result->get_error_message() ) );
353
		}
354
	}
355
356
	/**
357
	 * Update the customer_id for a subscription after using Simplify to complete a payment to make up for.
358
	 * an automatic renewal payment which previously failed.
359
	 *
360
	 * @param WC_Subscription $subscription The subscription for which the failing payment method relates.
361
	 * @param WC_Order $renewal_order The order which recorded the successful payment (to make up for the failed automatic payment).
362
	 */
363
	public function update_failing_payment_method( $subscription, $renewal_order ) {
364
		update_post_meta( $subscription->id, '_simplify_customer_id', get_post_meta( $renewal_order->id, '_simplify_customer_id', true ) );
365
	}
366
367
	/**
368
	 * Include the payment meta data required to process automatic recurring payments so that store managers can.
369
	 * manually set up automatic recurring payments for a customer via the Edit Subscription screen in Subscriptions v2.0+.
370
	 *
371
	 * @since 2.4
372
	 * @param array $payment_meta associative array of meta data required for automatic payments
373
	 * @param WC_Subscription $subscription An instance of a subscription object
374
	 * @return array
375
	 */
376
	public function add_subscription_payment_meta( $payment_meta, $subscription ) {
377
378
		$payment_meta[ $this->id ] = array(
379
			'post_meta' => array(
380
				'_simplify_customer_id' => array(
381
					'value' => get_post_meta( $subscription->id, '_simplify_customer_id', true ),
382
					'label' => 'Simplify Customer ID',
383
				),
384
			),
385
		);
386
387
		return $payment_meta;
388
	}
389
390
	/**
391
	 * Validate the payment meta data required to process automatic recurring payments so that store managers can.
392
	 * manually set up automatic recurring payments for a customer via the Edit Subscription screen in Subscriptions 2.0+.
393
	 *
394
	 * @since  2.4
395
	 * @param  string $payment_method_id The ID of the payment method to validate
396
	 * @param  array $payment_meta associative array of meta data required for automatic payments
397
	 * @return array
398
	 * @throws Exception
399
	 */
400
	public function validate_subscription_payment_meta( $payment_method_id, $payment_meta ) {
401
		if ( $this->id === $payment_method_id ) {
402
			if ( ! isset( $payment_meta['post_meta']['_simplify_customer_id']['value'] ) || empty( $payment_meta['post_meta']['_simplify_customer_id']['value'] ) ) {
403
				throw new Exception( 'A "_simplify_customer_id" value is required.' );
404
			}
405
		}
406
	}
407
408
	/**
409
	 * Don't transfer customer meta to resubscribe orders.
410
	 *
411
	 * @access public
412
	 * @param int $resubscribe_order The order created for the customer to resubscribe to the old expired/cancelled subscription
413
	 * @return void
414
	 */
415
	public function delete_resubscribe_meta( $resubscribe_order ) {
416
		delete_post_meta( $resubscribe_order->id, '_simplify_customer_id' );
417
	}
418
419
	/**
420
	 * Process a pre-order payment when the pre-order is released.
421
	 *
422
	 * @param WC_Order $order
423
	 * @return WP_Error|null
424
	 */
425
	public function process_pre_order_release_payment( $order ) {
426
427
		try {
428
			$order_items    = $order->get_items();
429
			$order_item     = array_shift( $order_items );
430
			$pre_order_name = sprintf( __( '%s - Pre-order for "%s"', 'woocommerce' ), esc_html( get_bloginfo( 'name', 'display' ) ), $order_item['name'] ) . ' ' . sprintf( __( '(Order #%s)', 'woocommerce' ), $order->get_order_number() );
431
432
			$customer_id = get_post_meta( $order->id, '_simplify_customer_id', true );
433
434
			if ( ! $customer_id ) {
435
				return new WP_Error( 'simplify_error', __( 'Customer not found', 'woocommerce' ) );
436
			}
437
438
			// Charge the customer
439
			$payment = Simplify_Payment::createPayment( array(
440
				'amount'              => $order->order_total * 100, // In cents.
441
				'customer'            => $customer_id,
442
				'description'         => trim( substr( $pre_order_name, 0, 1024 ) ),
443
				'currency'            => strtoupper( get_woocommerce_currency() ),
444
				'reference'           => $order->id
445
			) );
446
447
			if ( 'APPROVED' == $payment->paymentStatus ) {
448
				// Payment complete
449
				$order->payment_complete( $payment->id );
450
451
				// Add order note
452
				$order->add_order_note( sprintf( __( 'Simplify payment approved (ID: %s, Auth Code: %s)', 'woocommerce' ), $payment->id, $payment->authCode ) );
453
			} else {
454
				return new WP_Error( 'simplify_payment_declined', __( 'Payment was declined - the customer need to try another card.', 'woocommerce' ) );
455
			}
456
		} catch ( Exception $e ) {
457
			$order_note = sprintf( __( 'Simplify Transaction Failed (%s)', 'woocommerce' ), $e->getMessage() );
458
459
			// Mark order as failed if not already set,
460
			// otherwise, make sure we add the order note so we can detect when someone fails to check out multiple times
461
			if ( 'failed' != $order->get_status() ) {
462
				$order->update_status( 'failed', $order_note );
463
			} else {
464
				$order->add_order_note( $order_note );
465
			}
466
		}
467
	}
468
469
	/**
470
	 * Return handler for Hosted Payments.
471
	 */
472
	public function return_handler() {
473
		if ( ! isset( $_REQUEST['cardToken'] ) ) {
474
			parent::return_handler();
475
		}
476
477
		@ob_clean();
478
		header( 'HTTP/1.1 200 OK' );
479
480
		$redirect_url = wc_get_page_permalink( 'cart' );
481
482
		if ( isset( $_REQUEST['reference'] ) && isset( $_REQUEST['amount'] ) ) {
483
			$cart_token  = $_REQUEST['cardToken'];
484
			$amount      = absint( $_REQUEST['amount'] );
485
			$order_id    = absint( $_REQUEST['reference'] );
486
			$order       = wc_get_order( $order_id );
487
			$order_total = absint( $order->order_total * 100 );
488
489
			if ( $amount === $order_total ) {
490
				if ( $this->order_contains_subscription( $order->id ) ) {
491
					$response = $this->process_subscription( $order, $cart_token );
492
				} elseif ( $this->order_contains_pre_order( $order->id ) ) {
493
					$response = $this->process_pre_order( $order, $cart_token );
494
				} else {
495
					$response = parent::process_standard_payments( $order, $cart_token );
496
				}
497
498
				if ( 'success' == $response['result'] ) {
499
					$redirect_url = $response['redirect'];
500
				} else {
501
					$order->update_status( 'failed', __( 'Payment was declined by Simplify Commerce.', 'woocommerce' ) );
502
				}
503
504
				wp_redirect( $redirect_url );
505
				exit();
506
			}
507
		}
508
509
		wp_redirect( $redirect_url );
510
		exit();
511
	}
512
}
513