diff --git a/src/Providers/ShippingServiceProvider.php b/src/Providers/ShippingServiceProvider.php index 2b77e83..4ad386c 100644 --- a/src/Providers/ShippingServiceProvider.php +++ b/src/Providers/ShippingServiceProvider.php @@ -26,7 +26,7 @@ use Modules\Core\Shipping\Carriers\BoxNow\BoxNowRateDriver; use Modules\Core\Shipping\Contracts\CarrierFulfillmentInterface; use Modules\Core\Shipping\Filament\Pages\ManageShippingRates; use Modules\Core\Shipping\Jobs\PollShipmentTrackingJob; -use Modules\Core\Shipping\Listeners\FlushLivePricingCache; +use Modules\Core\Shipping\Listeners\InvalidateShippingOptions; use Modules\Core\Shipping\Models\Shipment; class ShippingServiceProvider extends ServiceProvider @@ -70,7 +70,7 @@ class ShippingServiceProvider extends ServiceProvider }); foreach ([CartLineAdded::class, CartLineUpdated::class, CartLineRemoved::class, CartCleared::class, ShippingAddressSet::class] as $event) { - Event::listen($event, [FlushLivePricingCache::class, 'handle']); + Event::listen($event, [InvalidateShippingOptions::class, 'handle']); } // Deferred: the Shipping facade resolves a binding registered in diff --git a/src/Shipping/Concerns/CachesLivePricing.php b/src/Shipping/Concerns/CachesLivePricing.php index 5df6887..a069ab5 100644 --- a/src/Shipping/Concerns/CachesLivePricing.php +++ b/src/Shipping/Concerns/CachesLivePricing.php @@ -17,7 +17,7 @@ use Lunar\Shipping\Models\ShippingRate; * across carts: the quote depends on cart-specific weight/quantity/ * destination (see docs/checkout.md). * - * Invalidated by Modules\Core\Shipping\Listeners\FlushLivePricingCache on + * Invalidated by Modules\Core\Shipping\Listeners\InvalidateShippingOptions on * the only two things that can change what this cart's quote should be: a * cart line changing (add/update/remove/clear) or the shipping address * changing. Deliberately NOT invalidated on order placement — the price diff --git a/src/Shipping/Listeners/FlushLivePricingCache.php b/src/Shipping/Listeners/InvalidateShippingOptions.php similarity index 53% rename from src/Shipping/Listeners/FlushLivePricingCache.php rename to src/Shipping/Listeners/InvalidateShippingOptions.php index b772b07..dc7d893 100644 --- a/src/Shipping/Listeners/FlushLivePricingCache.php +++ b/src/Shipping/Listeners/InvalidateShippingOptions.php @@ -3,6 +3,7 @@ namespace Modules\Core\Shipping\Listeners; use Illuminate\Support\Facades\Cache; +use Lunar\Facades\ShippingManifest; use Lunar\Shipping\Facades\Shipping; use Lunar\Shipping\Models\ShippingRate; use Modules\Core\Cart\Events\CartCleared; @@ -13,17 +14,29 @@ use Modules\Core\Checkout\Events\ShippingAddressSet; use Modules\Core\Shipping\Contracts\SupportsLivePricing; /** - * Flushes Modules\Core\Shipping\Concerns\CachesLivePricing's cached quotes - * for a cart on the only two things that can change what they should be: a + * Invalidates everything that caches or memoises resolved shipping options + * for a cart, on the only two things that can change what they should be: a * cart line changing (weight/quantity) or the shipping address changing - * (destination). See that trait's docblock for why order placement is - * deliberately not a trigger here. + * (destination). * - * Only rates whose method's driver implements SupportsLivePricing are ever - * cached by CachesLivePricing, so only their ids need a forget() call — - * no need to touch every ShippingRate row on every cart change. + * Two things need clearing here, both stale for the same reason: + * + * - Modules\Core\Shipping\Concerns\CachesLivePricing's per-rate cache (see + * that trait's docblock for why order placement is deliberately not a + * trigger). Only rates whose method's driver implements + * SupportsLivePricing are ever cached by it, so only their ids need a + * forget() call — no need to touch every ShippingRate row on every cart + * change. + * - Lunar\Base\ShippingManifest's $options collection, which is a + * request-lifetime singleton: ShippingManifest::getOptions() re-runs the + * modifier pipeline on every call but never clears $options first, and + * addOption() keeps the first entry per identifier and silently drops any + * later one — so an option resolved for an earlier address/cart state + * shadows the correct one after that state changes, within the same + * request. clearOptions() forces the next getOptions() call to resolve + * fresh. */ -class FlushLivePricingCache +class InvalidateShippingOptions { public function handle(CartLineAdded|CartLineUpdated|CartLineRemoved|CartCleared|ShippingAddressSet $event): void { @@ -32,6 +45,8 @@ class FlushLivePricingCache foreach ($this->livePricingRateIds() as $rateId) { Cache::forget("shipping.live_price.{$rateId}.{$cart->id}"); } + + ShippingManifest::clearOptions(); } /**