PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.7.1
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.7.1
1.7.1 1.7.0 1.6.6 1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 All 51 releases
← All changes | app/Modules/StoreManagedRenewal/Services/RenewalService.php +40 -10 1.6.2 → 1.7.1 View file →
@@ -11,8 +11,9 @@
11 11 use FluentCart\App\Models\Subscription;
12 12 use FluentCart\App\Services\Payments\SubscriptionHelper;
13 13 use FluentCart\App\Modules\Subscriptions\Services\SubscriptionService;
14 14 use FluentCart\App\Modules\Subscriptions\Services\SystemChargeService;
15 +use FluentCart\Api\StoreSettings;
15 16 use FluentCart\Framework\Support\Arr;
16 17 use WP_Error;
17 18
18 19 class RenewalService
@@ -32,8 +33,17 @@
32 33 if (!$parentOrder) {
33 34 return new WP_Error('parent_order_not_found', __('Parent order not found for this subscription.', 'fluent-cart'));
34 35 }
35 36
37 + if (!SubscriptionHelper::canProcessInMode($parentOrder->mode)) {
38 + return new WP_Error('store_mode_mismatch', sprintf(
39 + /* translators: 1: the subscription's payment mode (live/test), 2: the store's current mode (live/test) */
40 + __('Renewal skipped — this subscription is in %1$s mode but the store is currently in %2$s mode.', 'fluent-cart'),
41 + $parentOrder->mode,
42 + (new StoreSettings())->get('order_mode')
43 + ));
44 + }
45 +
36 46 // Get original order item — use eager-loaded collection if available, otherwise query
37 47 if ($parentOrder->relationLoaded('order_items')) {
38 48 $parentOrderItem = $parentOrder->order_items->filter(function ($item) {
39 49 return $item->payment_type === Status::ORDER_TYPE_SUBSCRIPTION;
@@ -349,10 +359,19 @@
349 359 if (!self::hasStoreManagedSubscriptions()) {
350 360 return $results;
351 361 }
352 362
353 - $subscriptions = Subscription::query()
354 - ->with(['order.order_items', 'product', 'variation'])
363 + $query = Subscription::query()
364 + ->with(['order.order_items', 'product', 'variation']);
365 +
366 + if (SubscriptionHelper::isModeGuardEnabled()) {
367 + $storeMode = (new StoreSettings())->get('order_mode');
368 + $query->whereHas('order', function ($query) use ($storeMode) {
369 + $query->where('mode', $storeMode);
370 + });
371 + }
372 +
373 + $subscriptions = $query
355 374 ->whereIn('collection_method', ['manual', 'system'])
356 375 ->whereNotIn('status', [
357 376 Status::SUBSCRIPTION_COMPLETED,
358 377 Status::SUBSCRIPTION_CANCELED,
@@ -579,10 +598,16 @@
579 598 if (!self::hasStoreManagedSubscriptions()) {
580 599 return $results;
581 600 }
582 601
583 - $pendingInvoices = Order::query()
584 - ->where('type', Status::ORDER_TYPE_RENEWAL)
602 + $invoiceQuery = Order::query()
603 + ->where('type', Status::ORDER_TYPE_RENEWAL);
604 +
605 + if (SubscriptionHelper::isModeGuardEnabled()) {
606 + $invoiceQuery->where('mode', (new StoreSettings())->get('order_mode'));
607 + }
608 +
609 + $pendingInvoices = $invoiceQuery
585 610 ->whereIn('payment_status', [
586 611 Status::PAYMENT_PENDING,
587 612 Status::PAYMENT_SCHEDULED,
588 613 Status::PAYMENT_AUTHORIZED,
@@ -648,8 +673,19 @@
648 673 }
649 674
650 675 // Past the per-interval grace period: past_due → expired
651 676 if ($invoiceAge >= $graceDays && $subscription->status === Status::SUBSCRIPTION_PAST_DUE ) {
677 + // CAS the past_due → expired write inside syncSubscriptionStates; if a
678 + // concurrent renewal payment reactivated the row, it returns null and we skip.
679 + $expired = SubscriptionService::syncSubscriptionStates($subscription, [
680 + 'status' => Status::SUBSCRIPTION_EXPIRED,
681 + 'next_billing_date' => null,
682 + ], Status::SUBSCRIPTION_PAST_DUE);
683 +
684 + if (!$expired) {
685 + continue;
686 + }
687 +
652 688 $subscription->addLog(
653 689 'Subscription expired',
654 690 sprintf(
655 691 'Unpaid invoice #%s exceeded the %d-day grace period. Subscription expired.',
@@ -657,14 +693,8 @@
657 693 $graceDays
658 694 ),
659 695 'error'
660 696 );
661 -
662 - // Go through syncSubscriptionStates so all lifecycle hooks fire consistently
663 - SubscriptionService::syncSubscriptionStates($subscription, [
664 - 'status' => Status::SUBSCRIPTION_EXPIRED,
665 - 'next_billing_date' => null,
666 - ]);
667 697
668 698 $results['expired']++;
669 699 continue;
670 700 }