PluginProbe
Subscriptions for WooCommerce with Stripe Recurring Payments / 1.5.0
Subscriptions for WooCommerce with Stripe Recurring Payments v1.5.0
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.5.0, at includes/Illuminate/Helper.php

613 lines 19.1 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', 'wp_subscription' );
28 } elseif ( 1 === (int) $number && 'weeks' === $typo ) {
29 return __( 'week', 'wp_subscription' );
30 } elseif ( 1 === (int) $number && 'months' === $typo ) {
31 return __( 'month', 'wp_subscription' );
32 } elseif ( 1 === (int) $number && 'years' === $typo ) {
33 return __( 'year', 'wp_subscription' );
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', 'wp_subscription' ),
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', 'wp_subscription' ) );
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', 'wp_subscription' ),
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', 'wp_subscription' ) );
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 if ( empty( $is_auto_renew ) && subscrpt_is_auto_renew_enabled() ) {
454 $is_auto_renew = true;
455 update_post_meta( $subscription_id, '_subscrpt_auto_renew', true );
456 }
457
458 $stripe_enabled = ( 'stripe' === $old_order->get_payment_method() && in_array( $is_auto_renew, array( '1', 1, true ), true ) && subscrpt_is_auto_renew_enabled() && '1' === get_option( 'subscrpt_stripe_auto_renew', '1' ) );
459
460 if ( $stripe_enabled ) {
461 $new_order->update_meta_data( '_stripe_customer_id', $old_order->get_meta( '_stripe_customer_id' ) );
462 $new_order->update_meta_data( '_stripe_source_id', $old_order->get_meta( '_stripe_source_id' ) );
463 $new_order->set_payment_method( $old_order->get_payment_method() );
464 $new_order->set_payment_method_title( $old_order->get_payment_method_title() );
465
466 // Add debug log.
467 wp_subscrpt_write_debug_log( "Stripe metadata cloned for renewal order #{$new_order->get_id()} from old order #{$old_order->get_id()}" );
468 } else {
469 wp_subscrpt_write_debug_log( "Stripe metadata did not clone for renewal order #{$new_order->get_id()} from old order #{$old_order->get_id()}" );
470 }
471 }
472
473 /**
474 * Create history for renewal.
475 *
476 * @param int $subscription_id Subscription Id.
477 * @param int $new_order_id New Order Id.
478 * @param int $new_order_item_id New Order Item Id.
479 *
480 * @return void
481 */
482 public static function create_renewal_history( $subscription_id, $new_order_id, $new_order_item_id ) {
483 global $wpdb;
484 $history_table = $wpdb->prefix . 'subscrpt_order_relation';
485 $wpdb->insert(
486 $history_table,
487 array(
488 'subscription_id' => $subscription_id,
489 'order_id' => $new_order_id,
490 'order_item_id' => $new_order_item_id,
491 'type' => 'renew',
492 )
493 );
494
495 $comment_id = wp_insert_comment(
496 array(
497 'comment_author' => 'Subscription for WooCommerce',
498 'comment_content' => sprintf( 'Subscription Renewal order successfully created. order is %s', $new_order_id ),
499 'comment_post_ID' => $subscription_id,
500 'comment_type' => 'order_note',
501 )
502 );
503 update_comment_meta( $comment_id, '_subscrpt_activity', 'Renewal Order' );
504 }
505
506 /**
507 * Create new order for renewal.
508 *
509 * @param \WC_Order $old_order Old Order Object.
510 * @param \WC_Order_Item $order_item Old Order Item Object.
511 * @param array $product_args Product args for add product.
512 *
513 * @return array|false
514 */
515 public static function create_new_order_for_renewal( \WC_Order $old_order, \WC_Order_Item $order_item, array $product_args ) {
516 $product = $order_item->get_product();
517 $user_id = $old_order->get_user_id();
518 $new_order = wc_create_order(
519 array(
520 'customer_id' => $user_id,
521 'status' => 'pending',
522 )
523 );
524 $product_meta = apply_filters( 'subscrpt_renewal_item_meta', wc_get_order_item_meta( $order_item->get_id(), '_subscrpt_meta', true ), $product, $order_item );
525 $product_args = apply_filters( 'subscrpt_renewal_product_args', $product_args, $product, $order_item );
526 if ( ! $product_args ) {
527 return false;
528 }
529
530 $new_order_item_id = $new_order->add_product(
531 $product,
532 $order_item->get_quantity(),
533 $product_args
534 );
535 wc_update_order_item_meta(
536 $new_order_item_id,
537 '_subscrpt_meta',
538 array(
539 'time' => $product_meta['time'],
540 'type' => $product_meta['type'],
541 'trial' => null,
542 )
543 );
544
545 // Add debug log.
546 wp_subscrpt_write_debug_log( "Renewal order #{$new_order->get_id()} created for old order #{$old_order->get_id()}" );
547
548 return array(
549 'order' => $new_order,
550 'order_item_id' => $new_order_item_id,
551 );
552 }
553
554 /**
555 * Check if old order is completed or deleted!
556 *
557 * @param mixed $old_order_id Old Order Id.
558 *
559 * @return \WC_Order|false
560 */
561 public static function check_order_for_renewal( $old_order_id ) {
562 $old_order = wc_get_order( $old_order_id );
563 if ( ! $old_order || 'completed' !== $old_order->get_status() ) {
564 if ( ! is_admin() && function_exists( 'wc_add_notice' ) ) {
565 return wc_add_notice( __( 'Subscription renewal isn\'t possible due to previous order not completed or deletion.', 'wp_subscription' ), 'error' );
566 }
567 return false;
568 }
569
570 return $old_order;
571 }
572
573 /**
574 * Save meta-data from old order
575 *
576 * @param \WC_Order $new_order new order object.
577 * @param \WC_Order $old_order old order object.
578 *
579 * @return void
580 */
581 public static function clone_order_metadata( $new_order, $old_order ) {
582 $new_order->set_customer_id( $old_order->get_customer_id() );
583 $new_order->set_currency( $old_order->get_currency() );
584
585 // 3 Add Billing Fields
586 $customer = new \WC_Customer( $old_order->get_customer_id() );
587 $new_order->set_billing_city( $customer->get_billing_city() );
588 $new_order->set_billing_state( $customer->get_billing_state() );
589 $new_order->set_billing_postcode( $customer->get_billing_postcode() );
590 $new_order->set_billing_email( $customer->get_billing_email() );
591 $new_order->set_billing_phone( $customer->get_billing_phone() );
592 $new_order->set_billing_address_1( $customer->get_billing_address_1() );
593 $new_order->set_billing_address_2( $customer->get_billing_address_2() );
594 $new_order->set_billing_country( $customer->get_billing_country() );
595 $new_order->set_billing_first_name( $customer->get_billing_first_name() );
596 $new_order->set_billing_last_name( $customer->get_billing_last_name() );
597 $new_order->set_billing_company( $customer->get_billing_company() );
598
599 // 4 Add Shipping Fields
600 $new_order->set_shipping_country( $customer->get_shipping_country() );
601 $new_order->set_shipping_first_name( $customer->get_shipping_first_name() );
602 $new_order->set_shipping_last_name( $customer->get_shipping_last_name() );
603 $new_order->set_shipping_company( $customer->get_shipping_company() );
604 $new_order->set_shipping_address_1( $customer->get_shipping_address_1() );
605 $new_order->set_shipping_address_2( $customer->get_shipping_address_2() );
606 $new_order->set_shipping_city( $customer->get_shipping_city() );
607 $new_order->set_shipping_state( $customer->get_shipping_state() );
608 $new_order->set_shipping_postcode( $customer->get_shipping_postcode() );
609 }
610 }
611
612 // HPOS: All order data access below uses WooCommerce CRUD and is HPOS compatible.
613