← 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 | } |