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

1,338 lines 45.3 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 use SpringDevs\Subscription\Illuminate\Gateways\Stripe\Stripe;
6 use SpringDevs\Subscription\Illuminate\Subscription\Subscription;
7
8 // HPOS: This file is compatible with WooCommerce High-Performance Order Storage (HPOS).
9 // All WooCommerce order data is accessed via WooCommerce CRUD methods (wc_get_order, wc_get_orders, etc.).
10 // All direct post meta access is for subscription data only, not WooCommerce order data.
11 // If you add new order data access, use WooCommerce CRUD for HPOS compatibility.
12
13 /**
14 * Class Helper || Some Helper Methods
15 *
16 * @package SpringDevs\Subscription\Illuminate
17 */
18 class Helper {
19 /**
20 * Get type's singular or plural from time_per.
21 *
22 * @param int $number timing_per.
23 * @param string $typo timing_option.
24 * @param bool $translate Whether to translate the output.
25 * @return string
26 */
27 public static function get_typos( $number, $typo, $translate = false ) {
28 switch ( strtolower( $typo ) ) {
29 case 'day':
30 case 'days':
31 return $translate
32 ? _n( 'day', 'days', $number, 'subscription' )
33 : ( (int) $number === 1 ? 'day' : 'days' );
34
35 case 'week':
36 case 'weeks':
37 return $translate
38 ? _n( 'week', 'weeks', $number, 'subscription' )
39 : ( (int) $number === 1 ? 'week' : 'weeks' );
40
41 case 'month':
42 case 'months':
43 return $translate
44 ? _n( 'month', 'months', $number, 'subscription' )
45 : ( (int) $number === 1 ? 'month' : 'months' );
46
47 case 'year':
48 case 'years':
49 return $translate
50 ? _n( 'year', 'years', $number, 'subscription' )
51 : ( (int) $number === 1 ? 'year' : 'years' );
52
53 default:
54 return $typo;
55 }
56 }
57
58 /**
59 * Get verbose status from status slug.
60 *
61 * @param string $status Status.
62 * @param bool $return_all Whether to return all statuses or a single status.
63 */
64 public static function get_verbose_status( $status, $return_all = false ): string|array {
65 $statuses = array(
66 'pending' => __( 'Pending', 'subscription' ),
67 'active' => __( 'Active', 'subscription' ),
68 'on-hold' => __( 'On Hold', 'subscription' ),
69 'expired' => __( 'Expired', 'subscription' ),
70 'pe_cancelled' => __( 'Pending Cancellation', 'subscription' ),
71 'cancelled' => __( 'Cancelled', 'subscription' ),
72 'draft' => __( 'Draft', 'subscription' ),
73 'trash' => __( 'Trash', 'subscription' ),
74 );
75
76 if ( $return_all ) {
77 return $statuses;
78 }
79
80 $status = strtolower( $status );
81 return isset( $statuses[ $status ] ) ? $statuses[ $status ] : '';
82 }
83
84 /**
85 * Generate start date
86 *
87 * @param null|string $trial Trial.
88 *
89 * @return string
90 */
91 public static function start_date( $trial = null ) {
92 if ( null === $trial ) {
93 $start_date = time();
94 } else {
95 $start_date = strtotime( $trial );
96 }
97 return wp_date( get_option( 'date_format' ), $start_date );
98 }
99
100 /**
101 * Generate next date
102 *
103 * @param string $time Time.
104 * @param null|string $trial Trial.
105 *
106 * @return string
107 */
108 public static function next_date( $time, $trial = null ) {
109 if ( null === $trial ) {
110 $start_date = time();
111 } else {
112 $start_date = strtotime( $trial );
113 }
114 return wp_date( get_option( 'date_format' ), strtotime( $time, $start_date ) );
115 }
116
117 /**
118 * Get Subscriptions
119 *
120 * Args:
121 * - status => [ any, active, pending, expired, pe_cancelled, cancelled, trash ]
122 * - user_id => user_id, -1 for all users.
123 * - posts_per_page => limit number of subscriptions.
124 * - return => return data: ids, post, subscription_data
125 *
126 * @param array $args Args.
127 */
128 public static function get_subscriptions( array $args = array() ) {
129 $default_args = array(
130 'post_type' => 'subscrpt_order',
131 'post_status' => 'active',
132 'author' => get_current_user_id(),
133 'posts_per_page' => -1,
134 'fields' => 'all',
135 'return' => 'post',
136 );
137
138 // Normalize some args.
139 if ( isset( $args['status'] ) ) {
140 $args['post_status'] = $args['status'];
141 unset( $args['status'] );
142 }
143 if ( isset( $args['user_id'] ) ) {
144 $args['author'] = $args['user_id'];
145 unset( $args['user_id'] );
146 }
147
148 // Merge default args with provided args.
149 $final_args = wp_parse_args( $args, $default_args );
150
151 if ( isset( $args['author'] ) ) {
152 if ( $args['author'] === -1 ) {
153 unset( $final_args['author'] );
154 } else {
155 $final_args['author'] = (int) $args['author'];
156 }
157 }
158
159 if ( isset( $args['product_id'] ) ) {
160 $final_args['meta_query'] = array(
161 array(
162 'key' => '_subscrpt_product_id',
163 'value' => (int) $args['product_id'],
164 ),
165 );
166 unset( $final_args['product_id'] );
167 }
168
169 // Fields check
170 $only_ids = false;
171 if ( $final_args['fields'] === 'ids' || $final_args['return'] === 'ids' ) {
172 $final_args['fields'] = 'all';
173 $only_ids = true;
174 }
175
176 // Status check
177 $statuses = $final_args['post_status'];
178 $final_args['post_status'] = 'any';
179
180 // Get all subscriptions.
181 $subscriptions = get_posts( $final_args );
182
183 // Fallback filtering.
184 // ? Sometime status filtering not works properly. So, we need to filter manually.
185 $filtered_subscriptions = [];
186
187 // Filter by status.
188 foreach ( $subscriptions as $subscription ) {
189 if ( ( is_array( $statuses ) && in_array( 'any', $statuses, true ) ) || $statuses === 'any' ) {
190 $filtered_subscriptions[] = $subscription;
191 continue;
192 }
193
194 if ( ( is_array( $statuses ) && in_array( $subscription->post_status, $statuses, true ) ) || $subscription->post_status === $statuses ) {
195 $filtered_subscriptions[] = $subscription;
196 }
197 }
198
199 // Final filtering (only ids, post, or full data)
200 $subscriptions = [];
201 foreach ( $filtered_subscriptions as $subscription ) {
202 if ( $only_ids ) {
203 $subscriptions[] = $subscription->ID;
204 } elseif ( $final_args['return'] === 'subscription_data' ) {
205 $subs_id = $subscription->ID;
206 $subscription_data = self::get_subscription_data( $subs_id );
207 $subscriptions[] = $subscription_data;
208 } else {
209 $subscriptions[] = $subscription;
210 }
211 }
212
213 return $subscriptions;
214 }
215
216 /**
217 * Check subscription exists by product ID.
218 *
219 * @param int $product_id Product ID.
220 * @param string|array $status Status.
221 *
222 * @return \WP_Post | false
223 */
224 public static function subscription_exists( int $product_id, $status ) {
225 if ( 0 === get_current_user_id() ) {
226 return false;
227 }
228
229 $args = array(
230 'post_status' => $status,
231 'fields' => 'ids',
232 'product_id' => $product_id,
233 );
234
235 $posts = self::get_subscriptions( $args );
236 return count( $posts ) > 0 ? $posts[0] : false;
237 }
238
239 /**
240 * Check if product trial exixts for an user.
241 *
242 * @param int $product_id Product ID.
243 *
244 * @return boolean
245 */
246 public static function check_trial( int $product_id ): bool {
247 return ! self::subscription_exists( $product_id, array( 'expired', 'pending', 'active', 'on-hold', 'pe_cancelled', 'cancelled' ) );
248 }
249
250 /**
251 * Rewew when expired.
252 *
253 * @param int $subscription_id Subscription ID.
254 */
255 public static function renew( int $subscription_id ) {
256 $trial = get_post_meta( $subscription_id, '_subscrpt_trial', true );
257 if ( null !== $trial ) {
258 update_post_meta( $subscription_id, '_subscrpt_trial', null );
259 }
260
261 do_action( 'subscrpt_when_product_expired', $subscription_id, true );
262 }
263
264 /**
265 * Get Subscriptions Histories
266 *
267 * @param int $order_id Order ID.
268 */
269 public static function get_subscriptions_from_order( $order_id ) {
270 global $wpdb;
271 $table_name = $wpdb->prefix . 'subscrpt_order_relation';
272 $histories = $wpdb->get_results(
273 $wpdb->prepare(
274 // @phpcs:ignore
275 'SELECT * FROM %i WHERE order_id=%d',
276 array( $table_name, $order_id )
277 )
278 );
279
280 return $histories;
281 }
282
283 /**
284 * Get Subscriptions Histories
285 *
286 * @param int $order_item_id Order item ID.
287 */
288 public static function get_subscription_from_order_item_id( $order_item_id ) {
289 global $wpdb;
290 $table_name = $wpdb->prefix . 'subscrpt_order_relation';
291 return $wpdb->get_row(
292 $wpdb->prepare(
293 // @phpcs:ignore
294 'SELECT * FROM %i WHERE order_item_id=%d',
295 array( $table_name, $order_item_id )
296 )
297 );
298 }
299
300 /**
301 * Format price with Subscription
302 *
303 * @param string $price Price.
304 * @param int $subscription_id Subscription ID.
305 * @param bool $display_trial True/False.
306 *
307 * @return string
308 */
309 public static function format_price_with_subscription( $price, $subscription_id, $display_trial = false ) {
310 $order_id = get_post_meta( $subscription_id, '_subscrpt_order_id', true );
311 $order_item_id = get_post_meta( $subscription_id, '_subscrpt_order_item_id', true );
312 $item_meta = wc_get_order_item_meta( $order_item_id, '_subscrpt_meta', true );
313
314 $order = wc_get_order( $order_id );
315 $time = '1' === $item_meta['time'] ? null : $item_meta['time'] . ' ';
316 $type = self::get_typos( $item_meta['time'], $item_meta['type'] );
317
318 $formatted_price = wc_price(
319 $price,
320 array(
321 'currency' => $order->get_currency(),
322 )
323 ) . ' / ' . $time . $type;
324
325 if ( $display_trial ) {
326 $trial = $item_meta['trial'];
327 $has_trial = isset( $item_meta['trial'] ) && strlen( $item_meta['trial'] ) > 2;
328
329 if ( $has_trial ) {
330 $trial_html = '<br/><small> + Got ' . $trial . ' free trial!</small>';
331 $formatted_price .= $trial_html;
332 }
333 }
334
335 return apply_filters( 'subscrpt_format_price_with_subscription', $formatted_price, $price, $subscription_id );
336 }
337
338 /**
339 * Format price with order item
340 *
341 * @param string $price Price.
342 * @param int $item_id Item Id.
343 * @param bool $display_trial display trial?.
344 *
345 * @return string
346 */
347 public static function format_price_with_order_item( $price, $item_id, $display_trial = false ) {
348 $order_id = wc_get_order_id_by_order_item_id( $item_id );
349 $order = wc_get_order( $order_id );
350
351 $item_meta = wc_get_order_item_meta( $item_id, '_subscrpt_meta', true );
352
353 if ( ! $item_meta || ! is_array( $item_meta ) ) {
354 return false;
355 }
356
357 $time = 1 === (int) $item_meta['time'] ? null : $item_meta['time'] . '-';
358 $type = self::get_typos( $item_meta['time'], $item_meta['type'], true );
359
360 $formatted_price = wc_price(
361 $price,
362 array(
363 'currency' => $order->get_currency(),
364 )
365 ) . ' / ' . $time . ucfirst( $type );
366
367 if ( $display_trial ) {
368 $has_trial = isset( $item_meta['trial'] ) && strlen( $item_meta['trial'] ) > 2;
369 $trial = $item_meta['trial'] ?? '';
370
371 if ( $has_trial ) {
372 // translators: %s: trial period.
373 $trial_html = '<br/><small> ' . sprintf( __( '+ %s free trial!', 'subscription' ), $trial ) . '</small>';
374 $formatted_price .= $trial_html;
375 }
376 }
377
378 return apply_filters( 'subscrpt_format_price_with_subscription', $formatted_price, $price, $item_id );
379 }
380
381 /**
382 * Get total subscriptions by product ID.
383 *
384 * @param int $product_id Product ID.
385 * @param string | array $status Status.
386 *
387 * @return \WP_Post | false
388 */
389 public static function get_total_subscriptions_from_product( int $product_id, $status = array( 'active', 'pending', 'expired', 'pe_cancelled', 'cancelled' ) ) {
390 $args = array(
391 'post_type' => 'subscrpt_order',
392 'post_status' => $status,
393 'fields' => 'ids',
394 'meta_query' => array(
395 array(
396 'key' => '_subscrpt_product_id',
397 'value' => $product_id,
398 ),
399 ),
400 );
401
402 $posts = get_posts( $args );
403
404 return count( $posts );
405 }
406
407 /**
408 * Process renewal on order.
409 *
410 * @param int $subscription_id Subscription Id.
411 * @param int $order_id Order Id.
412 * @param int $order_item_id Order Item Id.
413 *
414 * @return void
415 */
416 public static function process_order_renewal( $subscription_id, $order_id, $order_item_id ) {
417 global $wpdb;
418 $history_table = $wpdb->prefix . 'subscrpt_order_relation';
419
420 // Check if this is a split payment subscription
421 $payment_type = function_exists( 'subscrpt_get_payment_type' ) ? subscrpt_get_payment_type( $subscription_id ) : 'recurring';
422 $max_payments = function_exists( 'subscrpt_get_max_payments' ) ? subscrpt_get_max_payments( $subscription_id ) : 0;
423 $payments_made = function_exists( 'subscrpt_count_payments_made' ) ? subscrpt_count_payments_made( $subscription_id ) : 0;
424
425 $comment_content = '';
426 $activity_type = '';
427
428 if ( 'split_payment' === $payment_type && $max_payments ) {
429 $comment_content = sprintf(
430 /* translators: %1$s: order id, %2$d: payment number, %3$d: total payments */
431 __( 'Split payment installment %2$d of %3$d. Order %1$s created for subscription.', 'subscription' ),
432 $order_id,
433 $payments_made + 1, // +1 because this is a new renewal
434 $max_payments
435 );
436 $activity_type = __( 'Split Payment - Renewal', 'subscription' );
437 } else {
438 $comment_content = sprintf(
439 /* translators: order id. */
440 __( 'The order %s has been created for the subscription', 'subscription' ),
441 $order_id
442 );
443 $activity_type = __( 'Renewal Order', 'subscription' );
444 }
445
446 $comment_id = wp_insert_comment(
447 array(
448 'comment_author' => 'Subscription for WooCommerce',
449 'comment_content' => $comment_content,
450 'comment_post_ID' => $subscription_id,
451 'comment_type' => 'order_note',
452 )
453 );
454 update_comment_meta( $comment_id, '_subscrpt_activity', $activity_type );
455 update_comment_meta( $comment_id, '_subscrpt_activity_type', 'renewal_order' );
456
457 $wpdb->insert(
458 $history_table,
459 array(
460 'subscription_id' => $subscription_id,
461 'order_id' => $order_id,
462 'order_item_id' => $order_item_id,
463 'type' => 'renew',
464 )
465 );
466
467 // Fire action when split payment is renewed
468 do_action( 'subscrpt_split_payment_renewed', $subscription_id, $order_id, $order_item_id );
469 }
470
471 /**
472 * Process new subscription on order.
473 *
474 * @param \WC_Order_Item $order_item Order Item.
475 * @param string $post_status status.
476 * @param \WC_Product $product Product.
477 *
478 * @return int
479 */
480 public static function process_new_subscription_order( $order_item, $post_status, $product ) {
481 global $wpdb;
482 $history_table = $wpdb->prefix . 'subscrpt_order_relation';
483
484 // Prepare split payment arguments
485 $split_payment_args = array(
486 'product_id' => $product->get_id(),
487 'order_id' => $order_item->get_order_id(),
488 'order_item_id' => $order_item->get_id(),
489 'post_status' => $post_status,
490 'max_payments' => $product->get_meta( '_subscrpt_max_no_payment' ),
491 'timing_per' => $product->get_meta( '_subscrpt_timing_per' ),
492 'timing_option' => $product->get_meta( '_subscrpt_timing_option' ),
493 'price' => $product->get_price(),
494 );
495
496 // Allow modification of split payment arguments
497 $split_payment_args = apply_filters( 'subscrpt_split_payment_args', $split_payment_args, $order_item, $product );
498
499 $args = array(
500 'post_title' => 'Subscription',
501 'post_type' => 'subscrpt_order',
502 'post_status' => $split_payment_args['post_status'],
503 );
504 $subscription_id = wp_insert_post( $args );
505 wp_update_post(
506 array(
507 'ID' => $subscription_id,
508 'post_title' => "Subscription #{$subscription_id}",
509 )
510 );
511 // Check if this is a split payment subscription
512 $payment_type = $product->get_meta( '_subscrpt_payment_type' ) ?: 'recurring';
513 $max_payments = $product->get_meta( '_subscrpt_max_no_payment' );
514
515 $comment_content = '';
516 $activity_type = '';
517
518 if ( 'split_payment' === $payment_type && $max_payments ) {
519 $comment_content = sprintf(
520 /* translators: %1$s: order id, %2$d: max payments */
521 __( 'Split payment subscription created successfully. Order: %1$s. Total installments: %2$d.', 'subscription' ),
522 $order_item->get_order_id(),
523 $max_payments
524 );
525 $activity_type = __( 'Split Payment - New Subscription', 'subscription' );
526 } else {
527 $comment_content = sprintf(
528 /* translators: Order Id. */
529 __( 'Subscription successfully created. Order is %s', 'subscription' ),
530 $order_item->get_order_id()
531 );
532 $activity_type = __( 'New Subscription', 'subscription' );
533 }
534
535 $comment_id = wp_insert_comment(
536 array(
537 'comment_author' => 'Subscription for WooCommerce',
538 'comment_content' => $comment_content,
539 'comment_post_ID' => $subscription_id,
540 'comment_type' => 'order_note',
541 )
542 );
543 update_comment_meta( $comment_id, '_subscrpt_activity', $activity_type );
544 update_comment_meta( $comment_id, '_subscrpt_activity_type', 'subs_created' );
545
546 update_post_meta( $subscription_id, '_subscrpt_product_id', $product->get_id() );
547
548 $wpdb->insert(
549 $history_table,
550 array(
551 'subscription_id' => $subscription_id,
552 'order_id' => $order_item->get_order_id(),
553 'order_item_id' => $order_item->get_id(),
554 'type' => 'new',
555 )
556 );
557
558 // Fire action when split payment plan is created
559 do_action( 'subscrpt_split_payment_created', $subscription_id, $split_payment_args, $order_item );
560
561 return $subscription_id;
562 }
563
564 /**
565 * Get recurrings items from cart items.
566 *
567 * @param array $cart_items Cart items.
568 *
569 * @return array
570 */
571 public static function get_recurrs_from_cart( $cart_items ) {
572 $recurrs = array();
573 foreach ( $cart_items as $key => $cart_item ) {
574 $product = $cart_item['data'];
575 if ( $product->is_type( 'simple' ) && isset( $cart_item['subscription'] ) ) {
576 $cart_subscription = $cart_item['subscription'];
577 $type = ucfirst( $cart_subscription['type'] );
578
579 // Total amount with tax
580 $quantity = (int) $cart_item['quantity'];
581 $total_amount = wc_get_price_including_tax( $product, [ 'qty' => $quantity ] );
582 $timing_html = "<span class='wpsubs-subscription-timing'>&nbsp;/&nbsp;{$type}</span>";
583 $price_html = wc_price( (float) $total_amount ) . $timing_html;
584
585 $recurrs[ $key ] = array(
586 'trial_status' => ! is_null( $cart_subscription['trial'] ),
587 'price_html' => $price_html,
588 'start_date' => self::start_date( $cart_subscription['trial'] ),
589 'next_date' => self::next_date( ( $cart_subscription['time'] ?? 1 ) . ' ' . $cart_subscription['type'], $cart_subscription['trial'] ),
590 'can_user_cancel' => $cart_item['data']->get_meta( '_subscrpt_user_cancel' ),
591 'max_no_payment' => $cart_item['data']->get_meta( '_subscrpt_max_no_payment' ),
592 'price' => (float) $cart_subscription['per_cost'],
593 'quantity' => (int) $cart_item['quantity'],
594 );
595 }
596 }
597
598 return apply_filters( 'wpsubs_cart_recurring_items', $recurrs, $cart_items );
599 }
600
601 /**
602 * Check if the order has subscription item.
603 *
604 * @param \WC_Order|int $order Order object.
605 */
606 public static function order_has_subscription_item( $order ) {
607 if ( is_int( $order ) ) {
608 $order = wc_get_order( $order );
609 }
610
611 $is_subscription_order = false;
612 foreach ( $order->get_items() as $item ) {
613 $item_data = $item->get_data() ?? array();
614 $item_product_id = $item_data['product_id'] ?? 0;
615 $item_variation_id = $item_data['variation_id'] ?? 0;
616
617 $product_id = $item_variation_id ? $item_variation_id : $item_product_id;
618 $product = Subscription::get_subs_product( $product_id );
619
620 if ( $product && $product->is_enabled() ) {
621 $is_subscription_order = true;
622 break;
623 }
624 }
625 return $is_subscription_order;
626 }
627
628 /**
629 * Create renewal order when subscription expired. [wip]
630 *
631 * @param int $subscription_id Subscription ID.
632 * @throws \WC_Data_Exception Exception.
633 * @throws \Exception Exception.
634 */
635 public static function create_renewal_order( $subscription_id ) {
636 // Check if maximum payment limit has been reached
637 if ( subscrpt_is_max_payments_reached( $subscription_id ) ) {
638 // Mark subscription as expired due to limit reached
639 Action::status( 'expired', $subscription_id );
640
641 error_log( "WPS: Maximum payment limit reached for subscription #{$subscription_id}. No renewal order created." );
642 return false;
643 }
644
645 $order_item_id = get_post_meta( $subscription_id, '_subscrpt_order_item_id', true );
646 $order_id = wc_get_order_id_by_order_item_id( $order_item_id );
647 $old_order = self::check_order_for_renewal( $order_id );
648
649 if ( ! $old_order ) {
650 subscrpt_write_log( "Old order not found for renewal. Skipping creating renewal order. [ Subscription ID: {$subscription_id} ]" );
651 return;
652 }
653
654 $order_item = $old_order->get_item( $order_item_id );
655 $subscription_price = (float) get_post_meta( $subscription_id, '_subscrpt_price', true );
656 $qty = $order_item->get_quantity();
657
658 // Subtract tax from per-unit subscription price if prices include tax. WC_Order will calculate tax on line total.
659 if ( wc_prices_include_tax() ) {
660 $product = ( $order_item instanceof \WC_Order_Item_Product ) ? $order_item->get_product() : null;
661 $tax_class = $product ? $product->get_tax_class() : '';
662 $tax_rates = \WC_Tax::get_rates( $tax_class );
663 $taxes = \WC_Tax::calc_inclusive_tax( $subscription_price, $tax_rates );
664 $subscription_price = $subscription_price - array_sum( $taxes );
665 }
666
667 $line_total = $subscription_price * $qty;
668 $product_args = array(
669 'name' => $order_item->get_name(),
670 'subtotal' => $line_total,
671 'total' => $line_total,
672 );
673
674 // creating new order.
675 $new_order_data = self::create_new_order_for_renewal( $old_order, $order_item, $product_args );
676 if ( ! $new_order_data ) {
677 subscrpt_write_log( "Failed to create renewal order. [ Subscription ID: {$subscription_id} ]" );
678 return;
679 }
680 $new_order = $new_order_data['order'];
681 $new_order_item_id = $new_order_data['order_item_id'];
682
683 self::create_renewal_history( $subscription_id, $new_order->get_id(), $new_order_item_id );
684 update_post_meta( $subscription_id, '_subscrpt_order_id', $new_order->get_id() );
685 update_post_meta( $subscription_id, '_subscrpt_order_item_id', $new_order_item_id );
686
687 self::clone_order_metadata( $new_order, $old_order );
688
689 // Allow modification of the renewal order before saving.
690 $new_order = apply_filters( 'subscrpt_before_saving_renewal_order', $new_order, $old_order, $subscription_id );
691
692 // Save the new order.
693 $new_order->calculate_totals();
694 $new_order->save();
695
696 if ( ! is_admin() && function_exists( 'wc_add_notice' ) && WC()->session ) {
697 $message = 'Renewal Order(#' . $new_order->get_id() . ') Created.';
698 if ( $new_order->has_status( 'pending' ) ) {
699 $message .= 'Please <a href="' . $new_order->get_checkout_payment_url() . '">Pay now</a>';
700 }
701 wc_add_notice( $message, 'success' );
702 }
703
704 do_action( 'subscrpt_after_create_renew_order', $new_order, $old_order, $subscription_id, false );
705
706 return $new_order;
707 }
708
709 /**
710 * Get subscription total price.
711 *
712 * @param int $subscription_id Subscription ID.
713 * @return float
714 */
715 public static function get_subscription_total( $subscription_id ) {
716 return (float) get_post_meta( $subscription_id, '_subscrpt_price', true );
717 }
718
719 /**
720 * Get subscription status.
721 *
722 * @param int $subscription_id Subscription ID.
723 * @return string
724 */
725 public static function get_subscription_status( $subscription_id ) {
726 return get_post_status( $subscription_id );
727 }
728
729 /**
730 * Check if subscription has status.
731 *
732 * @param int $subscription_id Subscription ID.
733 * @param string $status Status to check.
734 * @return bool
735 */
736 public static function subscription_has_status( $subscription_id, $status ) {
737 return self::get_subscription_status( $subscription_id ) === $status;
738 }
739
740 /**
741 * Check if subscription needs payment.
742 *
743 * @param int $subscription_id Subscription ID.
744 * @return bool
745 */
746 public static function subscription_needs_payment( $subscription_id ) {
747 return true; // Always true for now
748 }
749
750 /**
751 * Get product period (timing option).
752 *
753 * @param int $product_id Product ID.
754 * @return string
755 */
756 public static function get_product_period( $product_id ) {
757 $product = wc_get_product( $product_id );
758 return $product ? $product->get_meta( '_subscrpt_timing_option' ) : '';
759 }
760
761 /**
762 * Get product interval (timing per).
763 *
764 * @param int $product_id Product ID.
765 * @return int
766 */
767 public static function get_product_interval( $product_id ) {
768 $product = wc_get_product( $product_id );
769 return $product ? (int) $product->get_meta( '_subscrpt_timing_per' ) : 1;
770 }
771
772 /**
773 * Get product length (max payments).
774 *
775 * @param int $product_id Product ID.
776 * @return int
777 */
778 public static function get_product_length( $product_id ) {
779 $product = wc_get_product( $product_id );
780 return $product ? (int) $product->get_meta( '_subscrpt_max_no_payment' ) : 0;
781 }
782
783 /**
784 * Get product trial length.
785 *
786 * @param int $product_id Product ID.
787 * @return int
788 */
789 public static function get_product_trial_length( $product_id ) {
790 $product = wc_get_product( $product_id );
791 return $product ? (int) $product->get_meta( '_subscrpt_trial_timing_per' ) : 0;
792 }
793
794 /**
795 * Get product signup fee.
796 *
797 * @param int $product_id Product ID.
798 * @return float
799 */
800 public static function get_product_signup_fee( $product_id ) {
801 $product = wc_get_product( $product_id );
802 return $product ? (float) $product->get_meta( '_subscrpt_signup_fee' ) : 0.0;
803 }
804
805 /**
806 * Get first renewal payment time.
807 *
808 * @param int $product_id Product ID.
809 * @return int Timestamp
810 */
811 public static function get_first_renewal_payment_time( $product_id ) {
812 $product = wc_get_product( $product_id );
813 if ( ! $product ) {
814 return 0;
815 }
816
817 $trial_period = $product->get_meta( '_subscrpt_trial_timing_per' );
818 $trial_option = $product->get_meta( '_subscrpt_trial_timing_option' );
819
820 if ( ! empty( $trial_period ) && ! empty( $trial_option ) ) {
821 return strtotime( "+{$trial_period} {$trial_option}" );
822 }
823
824 return 0;
825 }
826
827 /**
828 * Update subscription next payment date.
829 *
830 * @param int $subscription_id Subscription ID.
831 * @param string $new_date New Date string.
832 * @return void
833 */
834 public static function update_subscription_next_payment_date( $subscription_id, $new_date ) {
835 update_post_meta( $subscription_id, '_subscrpt_next_date', strtotime( $new_date ) );
836 }
837
838 /**
839 * Cancel subscription.
840 *
841 * @param int $subscription_id Subscription ID.
842 * @return void
843 */
844 public static function cancel_subscription( $subscription_id ) {
845 Action::status( 'cancelled', $subscription_id );
846 }
847
848 /**
849 * Pause subscription.
850 *
851 * @param int $subscription_id Subscription ID.
852 * @return void
853 */
854 public static function pause_subscription( $subscription_id ) {
855 Action::status( 'on-hold', $subscription_id );
856 }
857
858 /**
859 * Resume subscription.
860 *
861 * @param int $subscription_id Subscription ID.
862 * @return void
863 */
864 public static function resume_subscription( $subscription_id ) {
865 Action::status( 'active', $subscription_id );
866 }
867
868 /**
869 * Mark subscription payment as complete.
870 *
871 * @param int $subscription_id Subscription ID.
872 * @param string $payment_id Payment/Transaction ID.
873 * @return void
874 */
875 public static function subscription_payment_complete( $subscription_id, $payment_id ) {
876 if ( 'active' !== get_post_status( $subscription_id ) ) {
877 Action::status( 'active', $subscription_id );
878 }
879
880 // Allow payment gateways to add their own comments/notes
881 do_action( 'subscrpt_subscription_payment_completed', $subscription_id, $payment_id );
882 }
883
884 /**
885 * Clone stripe metadata from old order.
886 *
887 * @param int $subscription_id Subscription Id.
888 * @param \WC_Order $old_order Old Order Object.
889 * @param \WC_Order $new_order New Order Object.
890 *
891 * @return void
892 */
893 public static function clone_stripe_metadata_for_renewal( $subscription_id, $old_order, $new_order ) {
894 $is_auto_renew = get_post_meta( $subscription_id, '_subscrpt_auto_renew', true );
895 if ( empty( $is_auto_renew ) && subscrpt_is_auto_renew_enabled() ) {
896 $is_auto_renew = true;
897 update_post_meta( $subscription_id, '_subscrpt_auto_renew', true );
898 }
899
900 $is_auto_renew = get_post_meta( $subscription_id, '_subscrpt_auto_renew', true );
901 $is_auto_renew = in_array( $is_auto_renew, array( 1, '1' ), true );
902
903 $is_global_auto_renew = get_option( 'wp_subscription_stripe_auto_renew', '1' );
904 $is_global_auto_renew = in_array( $is_global_auto_renew, array( 1, '1' ), true );
905
906 $stripe_supported_methods = Stripe::WPSUBS_SUPPORTED_METHODS;
907 $old_method = $old_order->get_payment_method();
908 $is_stripe_pm = ! empty( $old_method ) && in_array( $old_method, $stripe_supported_methods, true );
909
910 $has_stripe_meta = ! empty( $old_order->get_meta( '_stripe_customer_id' ) ) || ! empty( $old_order->get_meta( '_stripe_source_id' ) );
911
912 $stripe_enabled = ( ( $is_stripe_pm || $has_stripe_meta ) && $is_auto_renew && $is_global_auto_renew && subscrpt_is_auto_renew_enabled() );
913
914 if ( $stripe_enabled ) {
915 $new_order->update_meta_data( '_stripe_customer_id', $old_order->get_meta( '_stripe_customer_id' ) );
916 $new_order->update_meta_data( '_stripe_source_id', $old_order->get_meta( '_stripe_source_id' ) );
917 $new_order->set_payment_method( $old_order->get_payment_method() );
918 $new_order->set_payment_method_title( $old_order->get_payment_method_title() );
919
920 // Add debug log.
921 subscrpt_write_debug_log( "Stripe metadata cloned for renewal order #{$new_order->get_id()} from old order #{$old_order->get_id()}" );
922 } else {
923 subscrpt_write_log( "Stripe metadata not processed. Auto renewal may fail. [ Renewal order #{$new_order->get_id()}, Old order #{$old_order->get_id()} ]" );
924 subscrpt_write_debug_log( "Stripe metadata did not clone for renewal order #{$new_order->get_id()} from old order #{$old_order->get_id()}" );
925 }
926 }
927
928 /**
929 * Create history for renewal.
930 *
931 * @param int $subscription_id Subscription Id.
932 * @param int $new_order_id New Order Id.
933 * @param int $new_order_item_id New Order Item Id.
934 *
935 * @return void
936 */
937 public static function create_renewal_history( $subscription_id, $new_order_id, $new_order_item_id ) {
938 global $wpdb;
939 $history_table = $wpdb->prefix . 'subscrpt_order_relation';
940 $wpdb->insert(
941 $history_table,
942 array(
943 'subscription_id' => $subscription_id,
944 'order_id' => $new_order_id,
945 'order_item_id' => $new_order_item_id,
946 'type' => 'renew',
947 )
948 );
949
950 $comment_id = wp_insert_comment(
951 array(
952 'comment_author' => 'Subscription for WooCommerce',
953 'comment_content' => sprintf( 'Subscription Renewal order successfully created. order is %s', $new_order_id ),
954 'comment_post_ID' => $subscription_id,
955 'comment_type' => 'order_note',
956 )
957 );
958 update_comment_meta( $comment_id, '_subscrpt_activity', 'Renewal Order' );
959 update_comment_meta( $comment_id, '_subscrpt_activity_type', 'renewal_order' );
960 }
961
962 /**
963 * Get a subscription data.
964 *
965 * @param int $subscription_id Subscription ID.
966 * @return array|null
967 */
968 public static function get_subscription_data( int $subscription_id ): ?array {
969 if ( empty( get_post_meta( $subscription_id ) ) ) {
970 return null;
971 }
972
973 $subs_post = get_post( $subscription_id );
974 $user_id = (int) $subs_post->post_author ?? 0;
975
976 $product_id = get_post_meta( $subscription_id, '_subscrpt_product_id', true );
977 $product_id = ! empty( $product_id ) ? (int) $product_id : 0;
978
979 $variation_id = get_post_meta( $subscription_id, '_subscrpt_variation_id', true );
980 $variation_id = ! empty( $variation_id ) ? (int) $variation_id : 0;
981
982 $chk_product_id = $variation_id ? $variation_id : $product_id;
983
984 $status = get_post_status( $subscription_id );
985 $price = get_post_meta( $subscription_id, '_subscrpt_price', true );
986
987 $signup_fee = get_post_meta( $subscription_id, '_subscrpt_signup_fee', true );
988 $signup_fee = ! empty( $signup_fee ) ? $signup_fee : 0;
989
990 $order_id = get_post_meta( $subscription_id, '_subscrpt_order_id', true );
991 $order_item_id = get_post_meta( $subscription_id, '_subscrpt_order_item_id', true );
992
993 $can_user_cancel = in_array( get_post_meta( $subscription_id, '_subscrpt_user_cancel', true ), array( 1, '1', 'true', 'yes' ), true );
994
995 $start_datetime = (int) get_post_meta( $subscription_id, '_subscrpt_start_date', true );
996 $start_date = ! empty( $start_datetime ) ? gmdate( DATE_RFC2822, $start_datetime ) : null;
997
998 $next_datetime = (int) get_post_meta( $subscription_id, '_subscrpt_next_date', true );
999 $next_date = ! empty( $next_datetime ) ? gmdate( DATE_RFC2822, $next_datetime ) : null;
1000
1001 $timing_per = get_post_meta( $subscription_id, '_subscrpt_timing_per', true );
1002 $timing_per = empty( $timing_per ) ? get_post_meta( $chk_product_id, '_subscrpt_timing_per', true ) : $timing_per;
1003
1004 $timing_option = get_post_meta( $subscription_id, '_subscrpt_timing_option', true );
1005 $timing_option = empty( $timing_option ) ? get_post_meta( $chk_product_id, '_subscrpt_timing_option', true ) : $timing_option;
1006
1007 $trial_timing_per = get_post_meta( $subscription_id, '_subscrpt_trial_timing_per', true );
1008 $trial_timing_per = empty( $trial_timing_per ) ? get_post_meta( $chk_product_id, '_subscrpt_trial_timing_per', true ) : $trial_timing_per;
1009
1010 $trial_timing_option = get_post_meta( $subscription_id, '_subscrpt_trial_timing_option', true );
1011 $trial_timing_option = empty( $trial_timing_option ) ? get_post_meta( $chk_product_id, '_subscrpt_trial_timing_option', true ) : $trial_timing_option;
1012
1013 $is_auto_renew = in_array( get_post_meta( $subscription_id, '_subscrpt_auto_renew', true ), array( 1, '1', 'true', 'yes' ), true );
1014 $is_auto_renew = ! empty( $is_auto_renew ) ? $is_auto_renew : subscrpt_is_auto_renew_enabled();
1015
1016 $default_grace_period = (int) get_option( 'subscrpt_default_payment_grace_period', '7' );
1017 $default_grace_period = subscrpt_pro_activated() ? $default_grace_period : 0;
1018 $grace_end_datetime = $next_datetime + ( $default_grace_period * DAY_IN_SECONDS );
1019 $grace_end_date = gmdate( DATE_RFC2822, $grace_end_datetime );
1020 $grace_remaining_days = ceil( max( 0, $grace_end_datetime - time() ) / DAY_IN_SECONDS );
1021
1022 $subscription_data = array(
1023 'id' => $subscription_id,
1024 'status' => $status,
1025 'schedule' => array(
1026 'timing_per' => $timing_per,
1027 'timing_option' => $timing_option,
1028 ),
1029 'price' => $price,
1030 'signup_fee' => $signup_fee,
1031 'start_date' => $start_date,
1032 'next_date' => $next_date,
1033 'product' => array(
1034 'product_id' => $product_id,
1035 'variation_id' => $variation_id,
1036 ),
1037 'order' => array(
1038 'order_id' => $order_id,
1039 'order_item_id' => $order_item_id,
1040 ),
1041 'can_user_cancel' => $can_user_cancel,
1042 'is_auto_renew' => (bool) $is_auto_renew,
1043 'user_id' => $user_id,
1044 );
1045
1046 if ( ! empty( $trial_timing_per ) ) {
1047 $subscription_data['trial'] = array(
1048 'timing_per' => $trial_timing_per,
1049 'timing_option' => $trial_timing_option,
1050 );
1051 }
1052
1053 if (
1054 ! in_array( strtolower( $status ), array( 'cancelled', 'pending' ), true )
1055 && $next_datetime - time() <= 0
1056 && (int) $default_grace_period > 0
1057 ) {
1058 $subscription_data['grace_period'] = array(
1059 'remaining_days' => $grace_remaining_days,
1060 'end_date' => $grace_end_date,
1061 );
1062 }
1063
1064 return $subscription_data;
1065 }
1066
1067 /**
1068 * Get related orders of a subscription.
1069 *
1070 * @param int $subscription_id Subscription ID.
1071 * @return array
1072 */
1073 public static function get_related_orders( int $subscription_id ): array {
1074 global $wpdb;
1075 $table_name = $wpdb->prefix . 'subscrpt_order_relation';
1076
1077 // @phpcs:ignore
1078 $order_histories = $wpdb->get_results(
1079 $wpdb->prepare(
1080 'SELECT order_id, order_item_id, type FROM %i WHERE subscription_id=%d ORDER BY id DESC',
1081 array(
1082 $table_name,
1083 $subscription_id,
1084 )
1085 )
1086 );
1087
1088 return $order_histories;
1089 }
1090
1091 /**
1092 * Get parent order from subscription.
1093 *
1094 * @param int $subscription_id Subscription ID.
1095 */
1096 public static function get_parent_order( int $subscription_id ) {
1097 $related_orders = self::get_related_orders( $subscription_id );
1098 $last_order = end( $related_orders );
1099
1100 if ( ! $last_order || strtolower( $last_order->type ?? '' ) !== 'new' ) {
1101 foreach ( $related_orders as $order ) {
1102 if ( strtolower( $order->type ?? '' ) === 'new' ) {
1103 $last_order = $order;
1104 break;
1105 }
1106 }
1107 }
1108
1109 $parent_order_id = $last_order->order_id ?? 0;
1110 $parent_order = wc_get_order( $parent_order_id );
1111 return $parent_order;
1112 }
1113
1114 /**
1115 * Create new order for renewal.
1116 *
1117 * @param \WC_Order $old_order Old Order Object.
1118 * @param \WC_Order_Item_Product $order_item Old Order Item Object.
1119 * @param array $product_args Product args for add product.
1120 *
1121 * @return array|false
1122 */
1123 public static function create_new_order_for_renewal( \WC_Order $old_order, \WC_Order_Item_Product $order_item, array $product_args ) {
1124 $product = $order_item->get_product();
1125 $user_id = $old_order->get_user_id();
1126 $new_order = wc_create_order(
1127 array(
1128 'customer_id' => $user_id,
1129 'status' => 'pending',
1130 )
1131 );
1132 $product_meta = apply_filters( 'subscrpt_renewal_item_meta', wc_get_order_item_meta( $order_item->get_id(), '_subscrpt_meta', true ), $product, $order_item );
1133 $product_args = apply_filters( 'subscrpt_renewal_product_args', $product_args, $product, $order_item );
1134 if ( ! $product_args ) {
1135 return false;
1136 }
1137
1138 $new_order_item_id = $new_order->add_product(
1139 $product,
1140 $order_item->get_quantity(),
1141 $product_args
1142 );
1143 wc_update_order_item_meta(
1144 $new_order_item_id,
1145 '_subscrpt_meta',
1146 array(
1147 'time' => $product_meta['time'],
1148 'type' => $product_meta['type'],
1149 'trial' => null,
1150 )
1151 );
1152
1153 // Add debug log.
1154 subscrpt_write_debug_log( "Renewal order #{$new_order->get_id()} created for old order #{$old_order->get_id()}" );
1155
1156 return array(
1157 'order' => $new_order,
1158 'order_item_id' => $new_order_item_id,
1159 );
1160 }
1161
1162 /**
1163 * Check if old order is completed or deleted!
1164 *
1165 * @param mixed $old_order_id Old Order Id.
1166 *
1167 * @return \WC_Order|false
1168 */
1169 public static function check_order_for_renewal( $old_order_id ) {
1170 $old_order = wc_get_order( $old_order_id );
1171 if ( ! $old_order || 'completed' !== $old_order->get_status() ) {
1172 if ( ! is_admin() && function_exists( 'wc_add_notice' ) && WC()->session ) {
1173 return wc_add_notice( __( 'Subscription renewal isn\'t possible due to previous order not completed or deletion.', 'subscription' ), 'error' );
1174 }
1175 return false;
1176 }
1177
1178 return $old_order;
1179 }
1180
1181 /**
1182 * Get delivery info from order.
1183 *
1184 * @param \WC_Order $order Order object.
1185 * @return array
1186 */
1187 public static function get_delivery_info_from_order( \WC_Order $order ) {
1188 $customer_id = $order->get_customer_id();
1189 $customer = new \WC_Customer( $customer_id );
1190 $email = $customer->get_email();
1191
1192 // Billing info (get from order first, if empty get from customer).
1193 $billing_first_name = ! empty( $order->get_billing_first_name() ) ? $order->get_billing_first_name() : $customer->get_billing_first_name();
1194 $billing_last_name = ! empty( $order->get_billing_last_name() ) ? $order->get_billing_last_name() : $customer->get_billing_last_name();
1195 $billing_email = ! empty( $order->get_billing_email() ) ? $order->get_billing_email() : $customer->get_billing_email();
1196 $billing_phone = ! empty( $order->get_billing_phone() ) ? $order->get_billing_phone() : $customer->get_billing_phone();
1197 $billing_company = ! empty( $order->get_billing_company() ) ? $order->get_billing_company() : $customer->get_billing_company();
1198
1199 $billing_city = ! empty( $order->get_billing_city() ) ? $order->get_billing_city() : $customer->get_billing_city();
1200 $billing_state = ! empty( $order->get_billing_state() ) ? $order->get_billing_state() : $customer->get_billing_state();
1201 $billing_country = ! empty( $order->get_billing_country() ) ? $order->get_billing_country() : $customer->get_billing_country();
1202 $billing_postcode = ! empty( $order->get_billing_postcode() ) ? $order->get_billing_postcode() : $customer->get_billing_postcode();
1203 $billing_address_1 = ! empty( $order->get_billing_address_1() ) ? $order->get_billing_address_1() : $customer->get_billing_address_1();
1204 $billing_address_2 = ! empty( $order->get_billing_address_2() ) ? $order->get_billing_address_2() : $customer->get_billing_address_2();
1205
1206 // Shipping info (get from order first, if empty get from customer).
1207 $shipping_first_name = ! empty( $order->get_shipping_first_name() ) ? $order->get_shipping_first_name() : $customer->get_shipping_first_name();
1208 $shipping_last_name = ! empty( $order->get_shipping_last_name() ) ? $order->get_shipping_last_name() : $customer->get_shipping_last_name();
1209 $shipping_phone = ! empty( $order->get_shipping_phone() ) ? $order->get_shipping_phone() : $customer->get_shipping_phone();
1210 $shipping_company = ! empty( $order->get_shipping_company() ) ? $order->get_shipping_company() : $customer->get_shipping_company();
1211
1212 $shipping_city = ! empty( $order->get_shipping_city() ) ? $order->get_shipping_city() : $customer->get_shipping_city();
1213 $shipping_state = ! empty( $order->get_shipping_state() ) ? $order->get_shipping_state() : $customer->get_shipping_state();
1214 $shipping_country = ! empty( $order->get_shipping_country() ) ? $order->get_shipping_country() : $customer->get_shipping_country();
1215 $shipping_postcode = ! empty( $order->get_shipping_postcode() ) ? $order->get_shipping_postcode() : $customer->get_shipping_postcode();
1216 $shipping_address_1 = ! empty( $order->get_shipping_address_1() ) ? $order->get_shipping_address_1() : $customer->get_shipping_address_1();
1217 $shipping_address_2 = ! empty( $order->get_shipping_address_2() ) ? $order->get_shipping_address_2() : $customer->get_shipping_address_2();
1218
1219 $order_meta = [
1220 'customer_id' => $order->get_customer_id(),
1221 'email' => $email,
1222 'billing' => [
1223 'first_name' => $billing_first_name,
1224 'last_name' => $billing_last_name,
1225 'email' => $billing_email,
1226 'phone' => $billing_phone,
1227 'company' => $billing_company,
1228 'city' => $billing_city,
1229 'state' => $billing_state,
1230 'country' => $billing_country,
1231 'postcode' => $billing_postcode,
1232 'address_1' => $billing_address_1,
1233 'address_2' => $billing_address_2,
1234 ],
1235 'shipping' => [
1236 'first_name' => $shipping_first_name,
1237 'last_name' => $shipping_last_name,
1238 'phone' => $shipping_phone,
1239 'company' => $shipping_company,
1240 'city' => $shipping_city,
1241 'state' => $shipping_state,
1242 'country' => $shipping_country,
1243 'postcode' => $shipping_postcode,
1244 'address_1' => $shipping_address_1,
1245 'address_2' => $shipping_address_2,
1246 ],
1247 ];
1248
1249 return $order_meta;
1250 }
1251
1252 /**
1253 * Set delivery info to order.
1254 *
1255 * @param \WC_Order $order Order object.
1256 * @param array $order_meta Order meta data.
1257 */
1258 public static function set_delivery_info_to_order( \WC_Order $order, array $order_meta ) {
1259 // Set Billing Info.
1260 $order->set_billing_first_name( $order_meta['billing']['first_name'] ?? '' );
1261 $order->set_billing_last_name( $order_meta['billing']['last_name'] ?? '' );
1262 $order->set_billing_email( $order_meta['billing']['email'] ?? '' );
1263 $order->set_billing_phone( $order_meta['billing']['phone'] ?? '' );
1264 $order->set_billing_company( $order_meta['billing']['company'] ?? '' );
1265 $order->set_billing_city( $order_meta['billing']['city'] ?? '' );
1266 $order->set_billing_state( $order_meta['billing']['state'] ?? '' );
1267 $order->set_billing_country( $order_meta['billing']['country'] ?? '' );
1268 $order->set_billing_postcode( $order_meta['billing']['postcode'] ?? '' );
1269 $order->set_billing_address_1( $order_meta['billing']['address_1'] ?? '' );
1270 $order->set_billing_address_2( $order_meta['billing']['address_2'] ?? '' );
1271
1272 // Set Shipping Info.
1273 $order->set_shipping_first_name( $order_meta['shipping']['first_name'] ?? '' );
1274 $order->set_shipping_last_name( $order_meta['shipping']['last_name'] ?? '' );
1275 $order->set_shipping_phone( $order_meta['shipping']['phone'] ?? '' );
1276 $order->set_shipping_company( $order_meta['shipping']['company'] ?? '' );
1277 $order->set_shipping_city( $order_meta['shipping']['city'] ?? '' );
1278 $order->set_shipping_state( $order_meta['shipping']['state'] ?? '' );
1279 $order->set_shipping_country( $order_meta['shipping']['country'] ?? '' );
1280 $order->set_shipping_postcode( $order_meta['shipping']['postcode'] ?? '' );
1281 $order->set_shipping_address_1( $order_meta['shipping']['address_1'] ?? '' );
1282 $order->set_shipping_address_2( $order_meta['shipping']['address_2'] ?? '' );
1283 }
1284
1285 /**
1286 * Save meta-data from old order
1287 *
1288 * @param \WC_Order $new_order new order object.
1289 * @param \WC_Order $old_order old order object.
1290 *
1291 * @return void
1292 */
1293 public static function clone_order_metadata( $new_order, $old_order ) {
1294 // Set customer and currency info.
1295 $new_order->set_customer_id( $old_order->get_customer_id() );
1296 $new_order->set_currency( $old_order->get_currency() );
1297
1298 // Get delivery info from old order.
1299 $order_meta = self::get_delivery_info_from_order( $old_order );
1300
1301 // Check for any missing information.
1302 $missing_billing_info = true;
1303 foreach ( $order_meta['billing'] as $key => $value ) {
1304 if ( ! empty( $value ) ) {
1305 $missing_billing_info = false;
1306 break;
1307 }
1308 }
1309 $missing_shipping_info = true;
1310 foreach ( $order_meta['shipping'] as $key => $value ) {
1311 if ( ! empty( $value ) ) {
1312 $missing_shipping_info = false;
1313 break;
1314 }
1315 }
1316 $missing_info = $missing_billing_info || $missing_shipping_info;
1317
1318 // Get info from the parent order if missing.
1319 if ( $missing_info ) {
1320 $subscription = self::get_subscriptions_from_order( $old_order->get_id() );
1321 $subscription = reset( $subscription );
1322 $subscription_id = ! empty( $subscription ) ? $subscription->subscription_id : 0;
1323
1324 subscrpt_write_log( "Missing delivery info in old order #{$old_order->get_id()} for subscription #{$subscription_id}. Trying to get from parent order." );
1325
1326 $parent_order = self::get_parent_order( $subscription_id );
1327 if ( ! empty( $parent_order ) ) {
1328 $order_meta = self::get_delivery_info_from_order( $parent_order );
1329 }
1330 }
1331
1332 // Set delivery info to new order.
1333 self::set_delivery_info_to_order( $new_order, $order_meta );
1334 }
1335 }
1336
1337 // HPOS: All order data access below uses WooCommerce CRUD and is HPOS compatible.
1338