PluginProbe
Subscriptions for WooCommerce with Stripe Recurring Payments / 1.4.2
Subscriptions for WooCommerce with Stripe Recurring Payments v1.4.2
2.0.0 1.11.2 1.11.1 1.11.0 1.10.9 1.10.8 1.10.7 1.10.6 1.10.5 1.10.4 1.10.3 1.10.2 1.10.1 1.10.0 1.9.6 1.9.5 trunk 1.3.0 1.3.1 1.3.2 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 All 61 releases
subscription / includes / Illuminate / Helper.php

Helper.php in Subscriptions for WooCommerce with Stripe Recurring Payments 1.4.2, at includes/Illuminate/Helper.php

599 lines 18.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace SpringDevs\Subscription\Illuminate;
4
5 // HPOS: This file is compatible with WooCommerce High-Performance Order Storage (HPOS).
6 // All WooCommerce order data is accessed via WooCommerce CRUD methods (wc_get_order, wc_get_orders, etc.).
7 // All direct post meta access is for subscription data only, not WooCommerce order data.
8 // If you add new order data access, use WooCommerce CRUD for HPOS compatibility.
9
10 /**
11 * Class Helper || Some Helper Methods
12 *
13 * @package SpringDevs\Subscription\Illuminate
14 */
15 class Helper {
16
17 /**
18 * Get type's singular or plural from time_per.
19 *
20 * @param int $number timing_per.
21 * @param string $typo timing_option.
22 *
23 * @return string
24 */
25 public static function get_typos( $number, $typo ) {
26 if ( 1 === (int) $number && 'days' === $typo ) {
27 return __( 'day', 'sdevs_subscrpt' );
28 } elseif ( 1 === (int) $number && 'weeks' === $typo ) {
29 return __( 'week', 'sdevs_subscrpt' );
30 } elseif ( 1 === (int) $number && 'months' === $typo ) {
31 return __( 'month', 'sdevs_subscrpt' );
32 } elseif ( 1 === (int) $number && 'years' === $typo ) {
33 return __( 'year', 'sdevs_subscrpt' );
34 } else {
35 return $typo;
36 }
37 }
38
39 /**
40 * Generate start date
41 *
42 * @param null|string $trial Trial.
43 *
44 * @return string
45 */
46 public static function start_date( $trial = null ) {
47 if ( null === $trial ) {
48 $start_date = time();
49 } else {
50 $start_date = strtotime( $trial );
51 }
52 return wp_date( get_option( 'date_format' ), $start_date );
53 }
54
55 /**
56 * Generate next date
57 *
58 * @param string $time Time.
59 * @param null|string $trial Trial.
60 *
61 * @return string
62 */
63 public static function next_date( $time, $trial = null ) {
64 if ( null === $trial ) {
65 $start_date = time();
66 } else {
67 $start_date = strtotime( $trial );
68 }
69 return wp_date( get_option( 'date_format' ), strtotime( $time, $start_date ) );
70 }
71
72 /**
73 * Check subscription exists by product ID.
74 *
75 * @param int $product_id Product ID.
76 * @param string|array $status Status.
77 *
78 * @return \WP_Post | false
79 */
80 public static function subscription_exists( int $product_id, $status ) {
81 if ( 0 === get_current_user_id() ) {
82 return false;
83 }
84
85 $args = array(
86 'post_type' => 'subscrpt_order',
87 'post_status' => $status,
88 'fields' => 'ids',
89 'meta_query' => array(
90 array(
91 'key' => '_subscrpt_product_id',
92 'value' => $product_id,
93 ),
94 ),
95 'author' => get_current_user_id(),
96 );
97
98 $posts = get_posts( $args );
99 return count( $posts ) > 0 ? $posts[0] : false;
100 }
101
102 /**
103 * Check if product trial exixts for an user.
104 *
105 * @param int $product_id Product ID.
106 *
107 * @return boolean
108 */
109 public static function check_trial( int $product_id ): bool {
110 return ! self::subscription_exists( $product_id, array( 'expired', 'pending', 'active', 'on-hold', 'pe_cancelled', 'cancelled' ) );
111 }
112
113 /**
114 * Rewew when expired.
115 *
116 * @param int $subscription_id Subscription ID.
117 */
118 public static function renew( int $subscription_id ) {
119 $trial = get_post_meta( $subscription_id, '_subscrpt_trial', true );
120 if ( null !== $trial ) {
121 update_post_meta( $subscription_id, '_subscrpt_trial', null );
122 }
123
124 do_action( 'subscrpt_when_product_expired', $subscription_id, true );
125 }
126
127 /**
128 * Get Subscriptions Histories
129 *
130 * @param int $order_id Order ID.
131 */
132 public static function get_subscriptions_from_order( $order_id ) {
133 global $wpdb;
134 $table_name = $wpdb->prefix . 'subscrpt_order_relation';
135 $histories = $wpdb->get_results(
136 $wpdb->prepare(
137 // @phpcs:ignore
138 'SELECT * FROM %i WHERE order_id=%d',
139 array( $table_name, $order_id )
140 )
141 );
142
143 return $histories;
144 }
145
146 /**
147 * Get Subscriptions Histories
148 *
149 * @param int $order_item_id Order item ID.
150 */
151 public static function get_subscription_from_order_item_id( $order_item_id ) {
152 global $wpdb;
153 $table_name = $wpdb->prefix . 'subscrpt_order_relation';
154 return $wpdb->get_row(
155 $wpdb->prepare(
156 // @phpcs:ignore
157 'SELECT * FROM %i WHERE order_item_id=%d',
158 array( $table_name, $order_item_id )
159 )
160 );
161 }
162
163 /**
164 * Format price with Subscription
165 *
166 * @param string $price Price.
167 * @param int $subscription_id Subscription ID.
168 * @param bool $display_trial True/False.
169 *
170 * @return string
171 */
172 public static function format_price_with_subscription( $price, $subscription_id, $display_trial = false ) {
173 $order_id = get_post_meta( $subscription_id, '_subscrpt_order_id', true );
174 $order_item_id = get_post_meta( $subscription_id, '_subscrpt_order_item_id', true );
175 $item_meta = wc_get_order_item_meta( $order_item_id, '_subscrpt_meta', true );
176
177 $order = wc_get_order( $order_id );
178 $time = '1' === $item_meta['time'] ? null : $item_meta['time'] . ' ';
179 $type = self::get_typos( $item_meta['time'], $item_meta['type'] );
180
181 $formatted_price = wc_price(
182 $price,
183 array(
184 'currency' => $order->get_currency(),
185 )
186 ) . ' / ' . $time . $type;
187
188 if ( $display_trial ) {
189 $trial = $item_meta['trial'];
190 $has_trial = isset( $item_meta['trial'] ) && strlen( $item_meta['trial'] ) > 2;
191
192 if ( $has_trial ) {
193 $trial_html = '<br/><small> + Got ' . $trial . ' free trial!</small>';
194 $formatted_price .= $trial_html;
195 }
196 }
197
198 return apply_filters( 'subscrpt_format_price_with_subscription', $formatted_price, $price, $subscription_id );
199 }
200
201 /**
202 * Format price with order item
203 *
204 * @param string $price Price.
205 * @param int $item_id Item Id.
206 * @param bool $display_trial display trial?.
207 *
208 * @return string
209 */
210 public static function format_price_with_order_item( $price, $item_id, $display_trial = false ) {
211 $order_id = wc_get_order_id_by_order_item_id( $item_id );
212 $order = wc_get_order( $order_id );
213
214 $item_meta = wc_get_order_item_meta( $item_id, '_subscrpt_meta', true );
215
216 if ( ! $item_meta || ! is_array( $item_meta ) ) {
217 return false;
218 }
219
220 $time = 1 === (int) $item_meta['time'] ? null : $item_meta['time'] . '-';
221 $type = self::get_typos( $item_meta['time'], $item_meta['type'] );
222
223 $formatted_price = wc_price(
224 $price,
225 array(
226 'currency' => $order->get_currency(),
227 )
228 ) . ' / ' . $time . $type;
229
230 if ( $display_trial ) {
231 $trial = $item_meta['trial'];
232 $has_trial = isset( $item_meta['trial'] ) && strlen( $item_meta['trial'] ) > 2;
233
234 if ( $has_trial ) {
235 $trial_html = '<br/><small> + Got ' . $trial . ' free trial!</small>';
236 $formatted_price .= $trial_html;
237 }
238 }
239
240 return apply_filters( 'subscrpt_format_price_with_subscription', $formatted_price, $price, $item_id );
241 }
242
243 /**
244 * Get total subscriptions by product ID.
245 *
246 * @param int $product_id Product ID.
247 * @param string | array $status Status.
248 *
249 * @return \WP_Post | false
250 */
251 public static function get_total_subscriptions_from_product( int $product_id, $status = array( 'active', 'pending', 'expired', 'pe_cancelled', 'cancelled' ) ) {
252 $args = array(
253 'post_type' => 'subscrpt_order',
254 'post_status' => $status,
255 'fields' => 'ids',
256 'meta_query' => array(
257 array(
258 'key' => '_subscrpt_product_id',
259 'value' => $product_id,
260 ),
261 ),
262 );
263
264 $posts = get_posts( $args );
265
266 return count( $posts );
267 }
268
269 /**
270 * Process renewal on order.
271 *
272 * @param int $subscription_id Subscription Id.
273 * @param int $order_id Order Id.
274 * @param int $order_item_id Order Item Id.
275 *
276 * @return void
277 */
278 public static function process_order_renewal( $subscription_id, $order_id, $order_item_id ) {
279 global $wpdb;
280 $history_table = $wpdb->prefix . 'subscrpt_order_relation';
281
282 $comment_id = wp_insert_comment(
283 array(
284 'comment_author' => 'Subscription for WooCommerce',
285 'comment_content' => sprintf(
286 // translators: order id.
287 __( 'The order %s has been created for the subscription', 'sdevs_subscrpt' ),
288 $order_id
289 ),
290 'comment_post_ID' => $subscription_id,
291 'comment_type' => 'order_note',
292 )
293 );
294 update_comment_meta( $comment_id, '_subscrpt_activity', __( 'Renewal Order', 'sdevs_subscrpt' ) );
295
296 $wpdb->insert(
297 $history_table,
298 array(
299 'subscription_id' => $subscription_id,
300 'order_id' => $order_id,
301 'order_item_id' => $order_item_id,
302 'type' => 'renew',
303 )
304 );
305 }
306
307 /**
308 * Process new subscription on order.
309 *
310 * @param \WC_Order_Item $order_item Order Item.
311 * @param string $post_status status.
312 * @param \WC_Product $product Product.
313 *
314 * @return int
315 */
316 public static function process_new_subscription_order( $order_item, $post_status, $product ) {
317 global $wpdb;
318 $history_table = $wpdb->prefix . 'subscrpt_order_relation';
319
320 $args = array(
321 'post_title' => 'Subscription',
322 'post_type' => 'subscrpt_order',
323 'post_status' => $post_status,
324 );
325 $subscription_id = wp_insert_post( $args );
326 wp_update_post(
327 array(
328 'ID' => $subscription_id,
329 'post_title' => "Subscription #{$subscription_id}",
330 )
331 );
332 $comment_id = wp_insert_comment(
333 array(
334 'comment_author' => 'Subscription for WooCommerce',
335 'comment_content' => sprintf(
336 // translators: Order Id.
337 __( 'Subscription successfully created. order is %s', 'sdevs_subscrpt' ),
338 $order_item->get_order_id()
339 ),
340 'comment_post_ID' => $subscription_id,
341 'comment_type' => 'order_note',
342 )
343 );
344 update_comment_meta( $comment_id, '_subscrpt_activity', __( 'New Subscription', 'sdevs_subscrpt' ) );
345
346 update_post_meta( $subscription_id, '_subscrpt_product_id', $product->get_id() );
347
348 $wpdb->insert(
349 $history_table,
350 array(
351 'subscription_id' => $subscription_id,
352 'order_id' => $order_item->get_order_id(),
353 'order_item_id' => $order_item->get_id(),
354 'type' => 'new',
355 )
356 );
357
358 return $subscription_id;
359 }
360
361 /**
362 * Get recurrings items from cart items.
363 *
364 * @param array $cart_items Cart items.
365 *
366 * @return array
367 */
368 public static function get_recurrs_for_cart( $cart_items ) {
369 $recurrs = array();
370 foreach ( $cart_items as $key => $cart_item ) {
371 $product = $cart_item['data'];
372 if ( $product->is_type( 'simple' ) && isset( $cart_item['subscription'] ) ) {
373 $cart_subscription = $cart_item['subscription'];
374 $type = $cart_subscription['type'];
375
376 $price_html = wc_price( $cart_subscription['per_cost'] * $cart_item['quantity'] ) . '/ ' . $type;
377 $recurrs[ $key ] = array(
378 'trial_status' => ! is_null( $cart_subscription['trial'] ),
379 'price_html' => $price_html,
380 'start_date' => self::start_date( $cart_subscription['trial'] ),
381 'next_date' => self::next_date( ( $cart_subscription['time'] ?? 1 ) . ' ' . $cart_subscription['type'], $cart_subscription['trial'] ),
382 'can_user_cancel' => $cart_item['data']->get_meta( '_subscrpt_user_cancel' ),
383 );
384 }
385 }
386
387 return apply_filters( 'subscrpt_cart_recurring_items', $recurrs, $cart_items );
388 }
389
390 /**
391 * Create renewal order when subscription expired. [wip]
392 *
393 * @param int $subscription_id Subscription ID.
394 * @throws \WC_Data_Exception Exception.
395 * @throws \Exception Exception.
396 */
397 public static function create_renewal_order( $subscription_id ) {
398 $order_item_id = get_post_meta( $subscription_id, '_subscrpt_order_item_id', true );
399 $order_id = wc_get_order_id_by_order_item_id( $order_item_id );
400 $old_order = self::check_order_for_renewal( $order_id );
401
402 if ( ! $old_order ) {
403 return;
404 }
405
406 $order_item = $old_order->get_item( $order_item_id );
407 $subscription_price = get_post_meta( $subscription_id, '_subscrpt_price', true );
408 $product_args = array(
409 'name' => $order_item->get_name(),
410 'subtotal' => $subscription_price,
411 'total' => $subscription_price,
412 );
413
414 // creating new order.
415 $new_order_data = self::create_new_order_for_renewal( $old_order, $order_item, $product_args );
416 if ( ! $new_order_data ) {
417 return;
418 }
419 $new_order = $new_order_data['order'];
420 $new_order_item_id = $new_order_data['order_item_id'];
421
422 self::create_renewal_history( $subscription_id, $new_order->get_id(), $new_order_item_id );
423 update_post_meta( $subscription_id, '_subscrpt_order_id', $new_order->get_id() );
424 update_post_meta( $subscription_id, '_subscrpt_order_item_id', $new_order_item_id );
425
426 self::clone_order_metadata( $new_order, $old_order );
427 self::clone_stripe_metadata_for_renewal( $subscription_id, $old_order, $new_order );
428
429 $new_order->calculate_totals();
430 $new_order->save();
431 if ( ! is_admin() && function_exists( 'wc_add_notice' ) ) {
432 $message = 'Renewal Order(#' . $new_order->get_id() . ') Created.';
433 if ( $new_order->has_status( 'pending' ) ) {
434 $message .= 'Please <a href="' . $new_order->get_checkout_payment_url() . '">Pay now</a>';
435 }
436 wc_add_notice( $message, 'success' );
437 }
438
439 do_action( 'subscrpt_after_create_renew_order', $new_order, $old_order, $subscription_id, false );
440 }
441
442 /**
443 * Clone stripe metadata from old order.
444 *
445 * @param int $subscription_id Subscription Id.
446 * @param \WC_Order $old_order Old Order Object.
447 * @param \WC_Order $new_order New Order Object.
448 *
449 * @return void
450 */
451 public static function clone_stripe_metadata_for_renewal( $subscription_id, $old_order, $new_order ) {
452 $is_auto_renew = get_post_meta( $subscription_id, '_subscrpt_auto_renew', true );
453 $stripe_enabled = ( 'stripe' === $old_order->get_payment_method() && in_array( $is_auto_renew, array( 1, '1' ), true ) && subscrpt_is_auto_renew_enabled() && '1' === get_option( 'subscrpt_stripe_auto_renew', '1' ) );
454 if ( $stripe_enabled ) {
455 $new_order->update_meta_data( '_stripe_customer_id', $old_order->get_meta( '_stripe_customer_id' ) );
456 $new_order->update_meta_data( '_stripe_source_id', $old_order->get_meta( '_stripe_source_id' ) );
457 $new_order->set_payment_method( $old_order->get_payment_method() );
458 $new_order->set_payment_method_title( $old_order->get_payment_method_title() );
459 }
460 }
461
462 /**
463 * Create history for renewal.
464 *
465 * @param int $subscription_id Subscription Id.
466 * @param int $new_order_id New Order Id.
467 * @param int $new_order_item_id New Order Item Id.
468 *
469 * @return void
470 */
471 public static function create_renewal_history( $subscription_id, $new_order_id, $new_order_item_id ) {
472 global $wpdb;
473 $history_table = $wpdb->prefix . 'subscrpt_order_relation';
474 $wpdb->insert(
475 $history_table,
476 array(
477 'subscription_id' => $subscription_id,
478 'order_id' => $new_order_id,
479 'order_item_id' => $new_order_item_id,
480 'type' => 'renew',
481 )
482 );
483
484 $comment_id = wp_insert_comment(
485 array(
486 'comment_author' => 'Subscription for WooCommerce',
487 'comment_content' => sprintf( 'Subscription Renewal order successfully created. order is %s', $new_order_id ),
488 'comment_post_ID' => $subscription_id,
489 'comment_type' => 'order_note',
490 )
491 );
492 update_comment_meta( $comment_id, '_subscrpt_activity', 'Renewal Order' );
493 }
494
495 /**
496 * Create new order for renewal.
497 *
498 * @param \WC_Order $old_order Old Order Object.
499 * @param \WC_Order_Item $order_item Old Order Item Object.
500 * @param array $product_args Product args for add product.
501 *
502 * @return array|false
503 */
504 public static function create_new_order_for_renewal( \WC_Order $old_order, \WC_Order_Item $order_item, array $product_args ) {
505 $product = $order_item->get_product();
506 $user_id = $old_order->get_user_id();
507 $new_order = wc_create_order(
508 array(
509 'customer_id' => $user_id,
510 'status' => 'pending',
511 )
512 );
513 $product_meta = apply_filters( 'subscrpt_renewal_item_meta', wc_get_order_item_meta( $order_item->get_id(), '_subscrpt_meta', true ), $product, $order_item );
514 $product_args = apply_filters( 'subscrpt_renewal_product_args', $product_args, $product, $order_item );
515 if ( ! $product_args ) {
516 return false;
517 }
518
519 $new_order_item_id = $new_order->add_product(
520 $product,
521 $order_item->get_quantity(),
522 $product_args
523 );
524 wc_update_order_item_meta(
525 $new_order_item_id,
526 '_subscrpt_meta',
527 array(
528 'time' => $product_meta['time'],
529 'type' => $product_meta['type'],
530 'trial' => null,
531 )
532 );
533
534 return array(
535 'order' => $new_order,
536 'order_item_id' => $new_order_item_id,
537 );
538 }
539
540 /**
541 * Check if old order is completed or deleted!
542 *
543 * @param mixed $old_order_id Old Order Id.
544 *
545 * @return \WC_Order|false
546 */
547 public static function check_order_for_renewal( $old_order_id ) {
548 $old_order = wc_get_order( $old_order_id );
549 if ( ! $old_order || 'completed' !== $old_order->get_status() ) {
550 if ( ! is_admin() && function_exists( 'wc_add_notice' ) ) {
551 return wc_add_notice( __( 'Subscription renewal isn\'t possible due to previous order not completed or deletion.', 'sdevs_subscrpt' ), 'error' );
552 }
553 return false;
554 }
555
556 return $old_order;
557 }
558
559 /**
560 * Save meta-data from old order
561 *
562 * @param \WC_Order $new_order new order object.
563 * @param \WC_Order $old_order old order object.
564 *
565 * @return void
566 */
567 public static function clone_order_metadata( $new_order, $old_order ) {
568 $new_order->set_customer_id( $old_order->get_customer_id() );
569 $new_order->set_currency( $old_order->get_currency() );
570
571 // 3 Add Billing Fields
572 $customer = new \WC_Customer( $old_order->get_customer_id() );
573 $new_order->set_billing_city( $customer->get_billing_city() );
574 $new_order->set_billing_state( $customer->get_billing_state() );
575 $new_order->set_billing_postcode( $customer->get_billing_postcode() );
576 $new_order->set_billing_email( $customer->get_billing_email() );
577 $new_order->set_billing_phone( $customer->get_billing_phone() );
578 $new_order->set_billing_address_1( $customer->get_billing_address_1() );
579 $new_order->set_billing_address_2( $customer->get_billing_address_2() );
580 $new_order->set_billing_country( $customer->get_billing_country() );
581 $new_order->set_billing_first_name( $customer->get_billing_first_name() );
582 $new_order->set_billing_last_name( $customer->get_billing_last_name() );
583 $new_order->set_billing_company( $customer->get_billing_company() );
584
585 // 4 Add Shipping Fields
586 $new_order->set_shipping_country( $customer->get_shipping_country() );
587 $new_order->set_shipping_first_name( $customer->get_shipping_first_name() );
588 $new_order->set_shipping_last_name( $customer->get_shipping_last_name() );
589 $new_order->set_shipping_company( $customer->get_shipping_company() );
590 $new_order->set_shipping_address_1( $customer->get_shipping_address_1() );
591 $new_order->set_shipping_address_2( $customer->get_shipping_address_2() );
592 $new_order->set_shipping_city( $customer->get_shipping_city() );
593 $new_order->set_shipping_state( $customer->get_shipping_state() );
594 $new_order->set_shipping_postcode( $customer->get_shipping_postcode() );
595 }
596 }
597
598 // HPOS: All order data access below uses WooCommerce CRUD and is HPOS compatible.
599