PluginProbe
Subscriptions for WooCommerce with Stripe Recurring Payments / 1.10.7
Subscriptions for WooCommerce with Stripe Recurring Payments v1.10.7
2.4.0 2.3.0 2.2.0 2.1.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 All 65 releases
subscription / includes / Illuminate / Order.php

Order.php in Subscriptions for WooCommerce with Stripe Recurring Payments 1.10.7, at includes/Illuminate/Order.php

478 lines 15.9 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 /**
6 * Class Order
7 *
8 * @package SpringDevs\Subscription\Illuminate
9 */
10 class Order {
11
12 /**
13 * Initialize the class.
14 */
15 public function __construct() {
16 add_action( 'woocommerce_admin_order_item_headers', array( $this, 'register_custom_column' ) );
17 add_action( 'woocommerce_admin_order_item_values', array( $this, 'add_column_value' ), 10, 2 );
18 add_action( 'woocommerce_before_order_itemmeta', array( $this, 'add_order_item_data' ), 10, 3 );
19 add_action( 'woocommerce_order_status_changed', array( $this, 'order_status_changed' ) );
20 add_action( 'woocommerce_before_delete_order', array( $this, 'delete_the_subscription' ) );
21 add_action( 'subscrpt_subscription_activated', array( $this, 'generate_dates_for_subscription' ) );
22
23 add_action( 'subscrpt_queue_trial_order_autocomplete', array( $this, 'auto_complete_subscription_trial_order' ) );
24 }
25
26 /**
27 * Generate start, next and trial dates.
28 *
29 * @param int $subscription_id Subscription Id.
30 *
31 * @return void
32 */
33 public function generate_dates_for_subscription( $subscription_id ) {
34 $order_item_id = get_post_meta( $subscription_id, '_subscrpt_order_item_id', true );
35 $subscription_history = Helper::get_subscription_from_order_item_id( $order_item_id );
36
37 $order_item_meta = wc_get_order_item_meta( $order_item_id, '_subscrpt_meta' );
38 $type = Helper::get_typos( 1, $order_item_meta['type'] );
39 $trial = get_post_meta( $subscription_id, '_subscrpt_trial', true );
40 $recurr_timing = ( $order_item_meta['time'] ?? 1 ) . ' ' . $type;
41
42 if ( 'new' === $subscription_history->type ) {
43 $start_date = time();
44 $next_date = sdevs_wp_strtotime( $recurr_timing, $start_date );
45
46 if ( $trial && ! empty( $trial ) ) {
47 $trial_started = get_post_meta( $subscription_id, '_subscrpt_trial_started', true );
48 $trial_ended = get_post_meta( $subscription_id, '_subscrpt_trial_ended', true );
49
50 if ( empty( $trial_started ) && empty( $trial_ended ) ) {
51 $start_date = sdevs_wp_strtotime( $trial );
52 $next_date = $start_date;
53
54 update_post_meta( $subscription_id, '_subscrpt_trial_started', time() );
55 update_post_meta( $subscription_id, '_subscrpt_trial_ended', $start_date );
56 update_post_meta( $subscription_id, '_subscrpt_trial_mode', 'on' );
57 }
58
59 if ( ! empty( $trial_ended ) ) {
60 $start_date = $trial_ended;
61 $next_date = $start_date;
62 }
63 }
64
65 update_post_meta( $subscription_id, '_subscrpt_start_date', $start_date );
66
67 } elseif ( 'renew' === $subscription_history->type ) {
68 if ( $trial ) {
69 delete_post_meta( $subscription_id, '_subscrpt_trial' );
70 delete_post_meta( $subscription_id, '_subscrpt_trial_mode' );
71 delete_post_meta( $subscription_id, '_subscrpt_trial_started' );
72 delete_post_meta( $subscription_id, '_subscrpt_trial_ended' );
73 }
74
75 $next_date = sdevs_wp_strtotime( $recurr_timing, time() );
76
77 } elseif ( 'early-renew' === $subscription_history->type ) {
78 if ( $trial ) {
79 delete_post_meta( $subscription_id, '_subscrpt_trial' );
80 delete_post_meta( $subscription_id, '_subscrpt_trial_mode' );
81 delete_post_meta( $subscription_id, '_subscrpt_trial_started' );
82 delete_post_meta( $subscription_id, '_subscrpt_trial_ended' );
83 }
84
85 $next_date = sdevs_wp_strtotime( $recurr_timing, time() );
86 }
87
88 /**
89 * Filter the subscription next payment date before it is saved.
90 *
91 * General-purpose hook (not scoped to split payment) for adjusting the
92 * computed next renewal date. Note `$next_date` may be null for history
93 * types other than 'new'/'renew'/'early-renew' (e.g. switch orders).
94 *
95 * The deprecated `subscrpt_split_payment_next_due_date` filter is bridged
96 * onto this hook in LegacyCompat.php for backward compatibility.
97 *
98 * @param int|null $next_date Computed next payment timestamp, or null.
99 * @param int $subscription_id Subscription ID.
100 * @param string $recurr_timing Recurring timing string (e.g. "1 month").
101 * @param string $type Subscription history type.
102 */
103 $next_date = apply_filters( 'subscrpt_subscription_next_date', $next_date, $subscription_id, $recurr_timing, $subscription_history->type );
104
105 update_post_meta( $subscription_id, '_subscrpt_next_date', $next_date );
106 }
107
108 /**
109 * Add custom column on order item.
110 *
111 * @return void
112 */
113 public function register_custom_column() {
114 ?>
115 <th class="item_recurring sortable" data-sort="float"><?php esc_html_e( 'Recurring', 'subscription' ); ?></th>
116 <?php
117 }
118
119 /**
120 * Display data for custom column.
121 *
122 * @param \WC_Product $product Product Object.
123 * @param \WC_Order_Item $item Order Item.
124 *
125 * @return void
126 */
127 public function add_column_value( $product, $item ) {
128 if ( ! method_exists( $item, 'get_id' ) || ! method_exists( $item, 'get_subtotal' ) ) {
129 return;
130 }
131
132 $subtotal = '-';
133 $item_id = $item->get_id();
134 $subscription_id = Helper::get_subscription_from_order_item_id( $item->get_id() );
135
136 if ( ! $subscription_id ) {
137 echo "<td class='item_recurring' width='15%'>-</td>";
138 return;
139 }
140 $subscription_id = $subscription_id->subscription_id;
141
142 $price = get_post_meta( $subscription_id, '_subscrpt_price', true );
143 $subtotal = Helper::format_price_with_order_item( $price, $item_id );
144 ?>
145 <td class="item_recurring" width="15%">
146 <div class="view">
147 <?php echo wp_kses_post( $subtotal ); ?>
148 </div>
149 </td>
150 <?php
151 }
152
153 public function add_order_item_data( $item_id, $item, $product ) {
154 if ( ! $product ) {
155 return;
156 }
157
158 $item_meta = wc_get_order_item_meta( $item_id, '_subscrpt_meta', true );
159
160 if ( ! $item_meta || ! is_array( $item_meta ) ) {
161 return false;
162 }
163
164 $trial = $item_meta['trial'];
165 $has_trial = isset( $item_meta['trial'] ) && strlen( $item_meta['trial'] ) > 2;
166
167 if ( $has_trial ) {
168 echo '<br/><small> + Got ' . esc_html( $trial ) . ' free trial!</small>';
169 }
170 }
171
172 /**
173 * Take some actions based on order status changed.
174 *
175 * @param int $order_id Order Id.
176 */
177 public function order_status_changed( $order_id ) {
178 $order = wc_get_order( $order_id );
179 $post_status = 'active';
180
181 switch ( $order->get_status() ) {
182 case 'on-hold':
183 case 'pending':
184 $post_status = 'pending';
185 break;
186
187 case 'refunded':
188 case 'failed':
189 case 'cancelled':
190 $post_status = 'cancelled';
191 break;
192
193 default:
194 $post_status = 'active';
195 break;
196 }
197 $post_status = apply_filters( 'subscript_order_status_to_post_status', $post_status, $order );
198
199 global $wpdb;
200 $table_name = $wpdb->prefix . 'subscrpt_order_relation';
201 // @phpcs:ignore
202 $histories = $wpdb->get_results( $wpdb->prepare( 'SELECT * FROM %i WHERE order_id=%d', array( $table_name, $order_id ) ) );
203
204 foreach ( $histories as $history ) {
205 if ( 'new' === $history->type || 'renew' === $history->type ) {
206 $subscription_id = $history->subscription_id;
207 wp_update_post(
208 array(
209 'ID' => $subscription_id,
210 'post_status' => $post_status,
211 )
212 );
213
214 // If possible change order status to completed if it has a trial subscription.
215 $this->maybe_trigger_auto_complete_trial_order( $order_id, $subscription_id );
216
217 // Increment renewal count for completed renewal orders (wps-pro)
218 if ( 'renew' === $history->type && 'active' === $post_status && function_exists( 'subscrpt_pro_activated' ) && subscrpt_pro_activated() ) {
219 if ( class_exists( '\\SpringDevs\\SubscriptionPro\\Illuminate\\LimitChecker' ) ) {
220 \SpringDevs\SubscriptionPro\Illuminate\LimitChecker::increment_renewal_count( $history->subscription_id );
221 }
222 }
223
224 // Add enhanced split payment activity logging
225 $this->add_split_payment_activity_note( $history->subscription_id, $history->type, $post_status, $order );
226
227 Action::write_comment( $post_status, $history->subscription_id );
228 } else {
229 do_action( 'subscrpt_order_status_changed', $order, $history );
230 }
231 }
232 }
233
234 /**
235 * Delete the subscription.
236 *
237 * @param int $order_id Order ID.
238 *
239 * @return void
240 */
241 public function delete_the_subscription( $order_id ) {
242 global $wpdb;
243 $table_name = $wpdb->prefix . 'subscrpt_order_relation';
244
245 $histories = Helper::get_subscriptions_from_order( $order_id );
246 foreach ( (array) $histories as $history ) {
247 $subscription_order_id = get_post_meta( $history->subscription_id, '_subscrpt_order_id', true );
248 if ( (int) $subscription_order_id === $order_id ) {
249 wp_delete_post( $history->subscription_id, true );
250 }
251 }
252
253 // phpcs:ignore
254 $wpdb->delete( $table_name, array( 'order_id' => $order_id ), array( '%d' ) );
255 }
256
257 /**
258 * Add enhanced split payment activity note with payment progress and access information.
259 *
260 * @param int $subscription_id Subscription ID.
261 * @param string $history_type History type (new, renew, etc.).
262 * @param string $post_status Post status.
263 * @param \WC_Order $order WooCommerce order object.
264 */
265 private function add_split_payment_activity_note( $subscription_id, $history_type, $post_status, $order ) {
266 // Only add enhanced notes for active subscriptions
267 if ( 'active' !== $post_status ) {
268 return;
269 }
270
271 // Check if this is a split payment subscription
272 if ( ! function_exists( 'subscrpt_get_payment_type' ) ) {
273 return;
274 }
275
276 $payment_type = subscrpt_get_payment_type( $subscription_id );
277 if ( 'split_payment' !== $payment_type ) {
278 return;
279 }
280
281 // Get payment progress information
282 $max_payments = function_exists( 'subscrpt_get_max_payments' ) ? subscrpt_get_max_payments( $subscription_id ) : 0;
283 $payments_made = function_exists( 'subscrpt_count_payments_made' ) ? subscrpt_count_payments_made( $subscription_id ) : 0;
284 $remaining_payments = function_exists( 'subscrpt_get_remaining_payments' ) ? subscrpt_get_remaining_payments( $subscription_id ) : 0;
285
286 // Determine payment number for this order
287 $payment_number = $payments_made;
288 $order_total = $order->get_total();
289 $order_currency = $order->get_currency();
290
291 // Create enhanced activity note
292 $comment_content = '';
293 $activity_type = '';
294
295 if ( 'new' === $history_type ) {
296 $comment_content = sprintf(
297 /* translators: %1$d: payment number, %2$d: total payments, %3$s: amount, %4$s: currency */
298 __( 'Split payment %1$d of %2$d received (%3$s %4$s). Initial access granted.', 'subscription' ),
299 $payment_number,
300 $max_payments,
301 $order_total,
302 $order_currency
303 );
304 $activity_type = __( 'Split Payment - Initial', 'subscription' );
305 } elseif ( 'renew' === $history_type ) {
306 $comment_content = sprintf(
307 /* translators: %1$d: payment number, %2$d: total payments, %3$s: amount, %4$s: currency, %5$d: remaining */
308 __( 'Split payment %1$d of %2$d received (%3$s %4$s). %5$d payments remaining.', 'subscription' ),
309 $payment_number,
310 $max_payments,
311 $order_total,
312 $order_currency,
313 $remaining_payments
314 );
315 $activity_type = __( 'Split Payment - Installment', 'subscription' );
316 }
317
318 // Add the enhanced activity note
319 if ( $comment_content ) {
320 $comment_id = wp_insert_comment(
321 array(
322 'comment_author' => 'Subscription for WooCommerce',
323 'comment_content' => $comment_content,
324 'comment_post_ID' => $subscription_id,
325 'comment_type' => 'order_note',
326 )
327 );
328 update_comment_meta( $comment_id, '_subscrpt_activity', $activity_type );
329 update_comment_meta( $comment_id, '_subscrpt_activity_type', 'split_payment' );
330
331 // Add order note with split payment context
332 $order_note = sprintf(
333 /* translators: %1$d: payment number, %2$d: total payments, %3$d: subscription id */
334 __( 'Split payment %1$d of %2$d received for subscription #%3$d', 'subscription' ),
335 $payment_number,
336 $max_payments,
337 $subscription_id
338 );
339 $order->add_order_note( $order_note );
340 }
341
342 // Add payment milestone notes for significant progress
343 $this->add_payment_milestone_notes( $subscription_id, $payments_made, $max_payments );
344 }
345
346 /**
347 * Add payment milestone notes for significant progress (25%, 50%, 75%, 100%).
348 *
349 * @param int $subscription_id Subscription ID.
350 * @param int $payments_made Number of payments made.
351 * @param int $max_payments Maximum number of payments.
352 */
353 private function add_payment_milestone_notes( $subscription_id, $payments_made, $max_payments ) {
354 if ( ! $max_payments || $max_payments <= 0 ) {
355 return;
356 }
357
358 $percentage = round( ( $payments_made / $max_payments ) * 100 );
359 $milestone_key = '_subscrpt_milestone_' . $percentage . '_logged';
360
361 // Check if this milestone has already been logged
362 if ( get_post_meta( $subscription_id, $milestone_key, true ) ) {
363 return;
364 }
365
366 $milestone_note = '';
367 $activity_type = '';
368
369 switch ( $percentage ) {
370 case 25:
371 $milestone_note = sprintf(
372 /* translators: %1$d: payments made, %2$d: total payments */
373 __( 'Payment milestone reached: %1$d of %2$d payments completed (25%%).', 'subscription' ),
374 $payments_made,
375 $max_payments
376 );
377 $activity_type = __( 'Payment Milestone - 25%', 'subscription' );
378 break;
379
380 case 50:
381 $milestone_note = sprintf(
382 /* translators: %1$d: payments made, %2$d: total payments */
383 __( 'Payment milestone reached: %1$d of %2$d payments completed (50%%). Halfway there!', 'subscription' ),
384 $payments_made,
385 $max_payments
386 );
387 $activity_type = __( 'Payment Milestone - 50%', 'subscription' );
388 break;
389
390 case 75:
391 $milestone_note = sprintf(
392 /* translators: %1$d: payments made, %2$d: total payments */
393 __( 'Payment milestone reached: %1$d of %2$d payments completed (75%%). Almost complete!', 'subscription' ),
394 $payments_made,
395 $max_payments
396 );
397 $activity_type = __( 'Payment Milestone - 75%', 'subscription' );
398 break;
399
400 case 100:
401 $milestone_note = sprintf(
402 /* translators: %1$d: payments made, %2$d: total payments */
403 __( 'Payment milestone reached: %1$d of %2$d payments completed (100%%). Split payment plan complete!', 'subscription' ),
404 $payments_made,
405 $max_payments
406 );
407 $activity_type = __( 'Payment Milestone - 100%', 'subscription' );
408 break;
409 }
410
411 // Add milestone note if applicable
412 if ( $milestone_note ) {
413 $comment_id = wp_insert_comment(
414 array(
415 'comment_author' => 'Subscription for WooCommerce',
416 'comment_content' => $milestone_note,
417 'comment_post_ID' => $subscription_id,
418 'comment_type' => 'order_note',
419 )
420 );
421 update_comment_meta( $comment_id, '_subscrpt_activity', $activity_type );
422 update_comment_meta( $comment_id, '_subscrpt_activity_type', 'split_payment' );
423
424 // Mark milestone as logged
425 update_post_meta( $subscription_id, $milestone_key, current_time( 'timestamp' ) );
426 }
427 }
428
429 /**
430 * Maybe complete the order if it has a trial subscription and is still in processing status.
431 *
432 * @param int $order_id Order ID.
433 * @param int $subscription_id Subscription ID.
434 */
435 public function maybe_trigger_auto_complete_trial_order( $order_id, $subscription_id ) {
436 $order = wc_get_order( $order_id );
437 $order_items = $order->get_items();
438
439 // Only attempt to complete if order is still in processing status.
440 if ( 'processing' !== $order->get_status() ) {
441 return;
442 }
443
444 $is_subs_trial_order = false;
445 foreach ( $order_items as $order_item ) {
446 $subscrpt_meta = $order_item->get_meta( '_subscrpt_meta', true );
447
448 if ( ! empty( $subscrpt_meta ) && isset( $subscrpt_meta['trial'] ) ) {
449 $is_subs_trial_order = true;
450 }
451 }
452
453 if ( $is_subs_trial_order ) {
454 as_enqueue_async_action( 'subscrpt_queue_trial_order_autocomplete', [ 'order_id' => $order_id ] );
455
456 $log_message = "Queued auto complete task for free trial order [ID: {$order_id}]";
457 subscrpt_write_log( $log_message );
458 subscrpt_write_debug_log( $log_message );
459 }
460 }
461
462 /**
463 * Autocomplete subscription trial order.
464 *
465 * @param int $order_id Order ID.
466 */
467 public function auto_complete_subscription_trial_order( $order_id ) {
468 $order = wc_get_order( $order_id );
469 if ( ! $order ) {
470 return;
471 }
472
473 sleep( 3 ); // Adding a small delay to ensure order status is updated before we check it.
474
475 $order->update_status( 'completed', __( 'Subscription order with trial.', 'subscription' ) );
476 }
477 }
478