Fix: Resolving box now by instance, not by type. Also, fixing bug where missing shipping driver would break checkout

This commit is contained in:
2026-09-30 17:48:59 +03:00
parent cd3245df7b
commit dbea4a8d69
11 changed files with 126 additions and 11 deletions
+2 -1
View File
@@ -29,6 +29,7 @@ use Modules\Core\Payment\Models\PaymentMethod;
use Modules\Core\Payment\Services\PaymentDriverRegistry;
use Modules\Core\Payment\Services\PaymentMethodCache;
use Modules\Core\Shipping\Contracts\SupportsCashCollection;
use Modules\Core\Shipping\Support\BoxNowShipping;
use Modules\Core\Shipping\Support\FulfillmentType;
/**
@@ -181,7 +182,7 @@ class CheckoutService
// switches BACK to Box Now and it resurfaces as if still chosen,
// possibly for a locker that no longer exists/fits. Cleared here,
// the one place that knows the method just changed.
if ($identifier !== 'box-now') {
if (! BoxNowShipping::isOption($identifier)) {
$address = $cart->shippingAddress;
if ($address && isset($address->meta['box_now_locker'])) {
@@ -19,6 +19,7 @@ use Modules\Core\Shipping\Enums\TrackingStatus;
use Modules\Core\Shipping\Events\ShipmentStatusUpdatedByCarrier;
use Modules\Core\Shipping\Filament\Resources\ShipmentResource;
use Modules\Core\Shipping\Models\Shipment;
use Modules\Core\Shipping\Support\BoxNowShipping;
/**
* Adds a "Shipments" section to the order page's main column — previously
@@ -224,7 +225,7 @@ class OrderShipmentsExtension extends ViewPageExtension
{
$parts = ['Created '.$record->created_at->format('Y-m-d H:i')];
if ($record->carrier === 'box-now' && $locker = $record->meta['locker_id'] ?? null) {
if (BoxNowShipping::isDriver($record->carrier) && $locker = $record->meta['locker_id'] ?? null) {
$parts[] = 'Locker '.$locker;
}
@@ -25,6 +25,7 @@ use Modules\Core\Shipping\Contracts\SupportsVoucherLookup;
use Modules\Core\Shipping\DTOs\ShipmentRequest;
use Modules\Core\Shipping\Enums\ExtraService;
use Modules\Core\Shipping\Models\Shipment;
use Modules\Core\Shipping\Support\BoxNowShipping;
use Modules\Core\Shipping\Support\WeightCalculator;
use Throwable;
@@ -122,7 +123,7 @@ class OrderViewExtension extends ViewPageExtension
->modalSubmitActionLabel('Create Shipment')
->schema(function (Order $record) {
$carrier = $this->service()->carrierFor($record);
$isBoxNow = $carrier === 'box-now';
$isBoxNow = BoxNowShipping::isDriver($carrier);
$lockerId = $record->shippingAddress?->meta['box_now_locker']['locationId'] ?? null;
if ($carrier === 'manual') {
+53
View File
@@ -0,0 +1,53 @@
<?php
namespace Modules\Core\Shipping\Support;
use Lunar\Shipping\Facades\Shipping;
use Lunar\Shipping\Models\ShippingMethod;
use Modules\Core\Shipping\Carriers\BoxNow\BoxNowRateDriver;
/**
* Whether something is Box Now — a checkout option, a shipping method, a
* driver key — by checking that the driver is a BoxNowRateDriver, never by
* comparing names: an option's identifier is the merchant-typed method
* code (may well not be "box-now"), and the driver may be registered under
* any key.
*/
class BoxNowShipping
{
public static function isOption(?string $identifier): bool
{
return $identifier !== null
&& self::isMethod(ShippingMethod::where('code', $identifier)->first());
}
public static function isMethod(?ShippingMethod $method): bool
{
return self::isDriver($method?->driver);
}
/**
* A shipping driver key (a method's `driver`, a shipment's `carrier`)
* that resolves to Box Now. getSupportedDrivers() rather than
* Shipping::driver(), which throws for a key no longer registered.
*/
public static function isDriver(?string $driver): bool
{
return $driver !== null
&& Shipping::getSupportedDrivers()->get($driver) instanceof BoxNowRateDriver;
}
/**
* Codes of every Box Now shipping method — for marking options in bulk.
*
* @return array<int, string>
*/
public static function codes(): array
{
return ShippingMethod::all()
->filter(fn (ShippingMethod $method) => self::isMethod($method))
->pluck('code')
->values()
->all();
}
}
@@ -0,0 +1,27 @@
<?php
namespace Modules\Core\Shipping\Support;
use Illuminate\Support\Collection;
use Lunar\Shipping\Facades\Shipping;
use Lunar\Shipping\Resolvers\ShippingRateResolver;
/**
* Lunar's rate resolver, minus rates whose shipping method points at a
* driver that isn't registered anymore (e.g. a carrier commented out of
* ShippingServiceProvider while its shipping method still exists). Lunar
* would go on to call that driver to price the option and throw "Driver
* [x] not supported", taking the whole checkout down; here the method is
* simply not offered. It stays in the admin as it was.
*/
class RegisteredDriverRateResolver extends ShippingRateResolver
{
public function get(): Collection
{
$registered = Shipping::getSupportedDrivers();
return parent::get()
->filter(fn ($rate) => $registered->has($rate->shippingMethod?->driver))
->values();
}
}
+11
View File
@@ -2,7 +2,9 @@
namespace Modules\Core\Shipping\Support;
use Lunar\Models\Contracts\Cart as CartContract;
use Lunar\Shipping\Managers\ShippingManager as VendorShippingManager;
use Lunar\Shipping\Resolvers\ShippingRateResolver;
/**
* Drops lunarphp/table-rate-shipping's own generic drivers (free-shipping,
@@ -28,6 +30,15 @@ use Lunar\Shipping\Managers\ShippingManager as VendorShippingManager;
*/
class ShippingManager extends VendorShippingManager
{
/**
* Rates for a cart, leaving out shipping methods whose driver is no
* longer registered (see RegisteredDriverRateResolver).
*/
public function shippingRates(?CartContract $cart = null): ShippingRateResolver
{
return new RegisteredDriverRateResolver($cart);
}
public function getSupportedDrivers(): \Illuminate\Support\Collection
{
return collect($this->customCreators)