command: rg "Shipping" database/seeders app/Services -n

type: search

cmd: rg Shipping database/seeders app/Services -n

query: Shipping

path: seeders

aggregated_output:
database/seeders/DatabaseSeeder.php:32:            ShippingZoneSeeder::class,
database/seeders/DatabaseSeeder.php:33:            ShippingRateSeeder::class,
app/Services/OrderService.php:394:                'status' => CheckoutStatus::ShippingSelected,
app/Services/PricingEngine.php:8:use App\Models\ShippingRate;
app/Services/PricingEngine.php:18:        private readonly ShippingCalculator $shipping,
app/Services/PricingEngine.php:39:            $freeShipping = false;
app/Services/PricingEngine.php:45:                $freeShipping = $freeShipping || $discountResult->freeShipping;
app/Services/PricingEngine.php:51:                $freeShipping = $freeShipping || $discountResult->freeShipping;
app/Services/PricingEngine.php:58:            $discountResult = new DiscountResult($discountAmount, [], $freeShipping);
app/Services/PricingEngine.php:109:        if (! $this->shipping->requiresShipping($checkout->cart)) {
app/Services/PricingEngine.php:117:        $rate = ShippingRate::withoutGlobalScopes()->find($checkout->shipping_method_id);
app/Services/PricingEngine.php:119:        if (! $rate instanceof ShippingRate) {
app/Services/PricingEngine.php:120:            throw InvalidCheckoutTransitionException::because('Shipping rate is not available.');
app/Services/PricingEngine.php:126:            throw InvalidCheckoutTransitionException::because('Shipping rate is not available for this cart.');
app/Services/PricingEngine.php:129:        return $discountResult->freeShipping ? 0 : $amount;
app/Services/DiscountService.php:103:        if ($discount->value_type === DiscountValueType::FreeShipping) {
app/Services/DiscountService.php:117:            DiscountValueType::FreeShipping => 0,
app/Services/ShippingCalculator.php:5:use App\Enums\ShippingRateType;
app/Services/ShippingCalculator.php:9:use App\Models\ShippingRate;
app/Services/ShippingCalculator.php:10:use App\Models\ShippingZone;
app/Services/ShippingCalculator.php:14:class ShippingCalculator
app/Services/ShippingCalculator.php:18:     * @return Collection<int, ShippingRate>
app/Services/ShippingCalculator.php:24:        if (! $zone instanceof ShippingZone) {
app/Services/ShippingCalculator.php:28:        return ShippingRate::withoutGlobalScopes()
app/Services/ShippingCalculator.php:38:    public function matchingZone(Store $store, array $address): ?ShippingZone
app/Services/ShippingCalculator.php:45:        ShippingZone::withoutGlobalScopes()
app/Services/ShippingCalculator.php:49:            ->each(function (ShippingZone $zone) use ($country, $region, &$bestZone, &$bestSpecificity): void {
app/Services/ShippingCalculator.php:68:    public function calculate(ShippingRate $rate, Cart $cart): ?int
app/Services/ShippingCalculator.php:70:        if (! $this->requiresShipping($cart)) {
app/Services/ShippingCalculator.php:75:            ShippingRateType::Flat => (int) data_get($rate->config_json, 'amount', 0),
app/Services/ShippingCalculator.php:76:            ShippingRateType::Weight => $this->weightRate($rate, $cart),
app/Services/ShippingCalculator.php:77:            ShippingRateType::Price => $this->priceRate($rate, $cart),
app/Services/ShippingCalculator.php:78:            ShippingRateType::Carrier => (int) data_get($rate->config_json, 'amount', 1299),
app/Services/ShippingCalculator.php:82:    public function requiresShipping(Cart $cart): bool
app/Services/ShippingCalculator.php:87:    private function weightRate(ShippingRate $rate, Cart $cart): ?int
app/Services/ShippingCalculator.php:108:    private function priceRate(ShippingRate $rate, Cart $cart): ?int
database/seeders/ShippingZoneSeeder.php:5:use App\Models\ShippingZone;
database/seeders/ShippingZoneSeeder.php:9:class ShippingZoneSeeder extends Seeder
database/seeders/ShippingZoneSeeder.php:17:            ShippingZone::withoutGlobalScopes()->updateOrCreate(
database/seeders/PageSeeder.php:48:                'body_html' => '
Shipping, payment, returns, and account flows are implemented progressively according to the project roadmap.
',
database/seeders/PageSeeder.php:52:                'title' => 'Shipping',
database/seeders/PageSeeder.php:53:                'body_html' => '
Shipping zones, rates, and tax-aware checkout calculations are seeded in the checkout phase.
',
database/seeders/ShippingRateSeeder.php:5:use App\Models\ShippingRate;
database/seeders/ShippingRateSeeder.php:6:use App\Models\ShippingZone;
database/seeders/ShippingRateSeeder.php:9:class ShippingRateSeeder extends Seeder
database/seeders/ShippingRateSeeder.php:16:        ShippingZone::withoutGlobalScopes()->get()->each(function (ShippingZone $zone): void {
database/seeders/ShippingRateSeeder.php:18:                ShippingRate::withoutGlobalScopes()->updateOrCreate(
database/seeders/ShippingRateSeeder.php:39:            ['name' => 'Standard Shipping', 'type' => 'flat', 'config_json' => ['amount' => 799]],
database/seeders/ShippingRateSeeder.php:40:            ['name' => 'Express Shipping', 'type' => 'flat', 'config_json' => ['amount' => 1499]],
database/seeders/ShippingRateSeeder.php:42:                'name' => 'Free Shipping Over 75',
app/Services/CheckoutService.php:8:use App\Exceptions\UnserviceableShippingAddressException;
app/Services/CheckoutService.php:15:use App\Models\ShippingRate;
app/Services/CheckoutService.php:23:        private readonly ShippingCalculator $shipping,
app/Services/CheckoutService.php:86:    public function setShippingMethod(Checkout $checkout, ?int $shippingRateId): Checkout
app/Services/CheckoutService.php:90:            $this->assertStatus($checkout, [CheckoutStatus::Addressed, CheckoutStatus::ShippingSelected]);
app/Services/CheckoutService.php:92:            if (! $this->shipping->requiresShipping($checkout->cart)) {
app/Services/CheckoutService.php:95:                    'status' => CheckoutStatus::ShippingSelected,
app/Services/CheckoutService.php:106:                throw UnserviceableShippingAddressException::forAddress();
app/Services/CheckoutService.php:111:            if (! $rate instanceof ShippingRate) {
app/Services/CheckoutService.php:117:                'status' => CheckoutStatus::ShippingSelected,
app/Services/CheckoutService.php:135:            $this->assertStatus($checkout, [CheckoutStatus::ShippingSelected]);


output:
Chunk ID: 04b1a5
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1564
Output:
database/seeders/DatabaseSeeder.php:32:            ShippingZoneSeeder::class,
database/seeders/DatabaseSeeder.php:33:            ShippingRateSeeder::class,
app/Services/OrderService.php:394:                'status' => CheckoutStatus::ShippingSelected,
app/Services/PricingEngine.php:8:use App\Models\ShippingRate;
app/Services/PricingEngine.php:18:        private readonly ShippingCalculator $shipping,
app/Services/PricingEngine.php:39:            $freeShipping = false;
app/Services/PricingEngine.php:45:                $freeShipping = $freeShipping || $discountResult->freeShipping;
app/Services/PricingEngine.php:51:                $freeShipping = $freeShipping || $discountResult->freeShipping;
app/Services/PricingEngine.php:58:            $discountResult = new DiscountResult($discountAmount, [], $freeShipping);
app/Services/PricingEngine.php:109:        if (! $this->shipping->requiresShipping($checkout->cart)) {
app/Services/PricingEngine.php:117:        $rate = ShippingRate::withoutGlobalScopes()->find($checkout->shipping_method_id);
app/Services/PricingEngine.php:119:        if (! $rate instanceof ShippingRate) {
app/Services/PricingEngine.php:120:            throw InvalidCheckoutTransitionException::because('Shipping rate is not available.');
app/Services/PricingEngine.php:126:            throw InvalidCheckoutTransitionException::because('Shipping rate is not available for this cart.');
app/Services/PricingEngine.php:129:        return $discountResult->freeShipping ? 0 : $amount;
app/Services/DiscountService.php:103:        if ($discount->value_type === DiscountValueType::FreeShipping) {
app/Services/DiscountService.php:117:            DiscountValueType::FreeShipping => 0,
app/Services/ShippingCalculator.php:5:use App\Enums\ShippingRateType;
app/Services/ShippingCalculator.php:9:use App\Models\ShippingRate;
app/Services/ShippingCalculator.php:10:use App\Models\ShippingZone;
app/Services/ShippingCalculator.php:14:class ShippingCalculator
app/Services/ShippingCalculator.php:18:     * @return Collection<int, ShippingRate>
app/Services/ShippingCalculator.php:24:        if (! $zone instanceof ShippingZone) {
app/Services/ShippingCalculator.php:28:        return ShippingRate::withoutGlobalScopes()
app/Services/ShippingCalculator.php:38:    public function matchingZone(Store $store, array $address): ?ShippingZone
app/Services/ShippingCalculator.php:45:        ShippingZone::withoutGlobalScopes()
app/Services/ShippingCalculator.php:49:            ->each(function (ShippingZone $zone) use ($country, $region, &$bestZone, &$bestSpecificity): void {
app/Services/ShippingCalculator.php:68:    public function calculate(ShippingRate $rate, Cart $cart): ?int
app/Services/ShippingCalculator.php:70:        if (! $this->requiresShipping($cart)) {
app/Services/ShippingCalculator.php:75:            ShippingRateType::Flat => (int) data_get($rate->config_json, 'amount', 0),
app/Services/ShippingCalculator.php:76:            ShippingRateType::Weight => $this->weightRate($rate, $cart),
app/Services/ShippingCalculator.php:77:            ShippingRateType::Price => $this->priceRate($rate, $cart),
app/Services/ShippingCalculator.php:78:            ShippingRateType::Carrier => (int) data_get($rate->config_json, 'amount', 1299),
app/Services/ShippingCalculator.php:82:    public function requiresShipping(Cart $cart): bool
app/Services/ShippingCalculator.php:87:    private function weightRate(ShippingRate $rate, Cart $cart): ?int
app/Services/ShippingCalculator.php:108:    private function priceRate(ShippingRate $rate, Cart $cart): ?int
database/seeders/ShippingZoneSeeder.php:5:use App\Models\ShippingZone;
database/seeders/ShippingZoneSeeder.php:9:class ShippingZoneSeeder extends Seeder
database/seeders/ShippingZoneSeeder.php:17:            ShippingZone::withoutGlobalScopes()->updateOrCreate(
database/seeders/PageSeeder.php:48:                'body_html' => '
Shipping, payment, returns, and account flows are implemented progressively according to the project roadmap.
',
database/seeders/PageSeeder.php:52:                'title' => 'Shipping',
database/seeders/PageSeeder.php:53:                'body_html' => '
Shipping zones, rates, and tax-aware checkout calculations are seeded in the checkout phase.
',
database/seeders/ShippingRateSeeder.php:5:use App\Models\ShippingRate;
database/seeders/ShippingRateSeeder.php:6:use App\Models\ShippingZone;
database/seeders/ShippingRateSeeder.php:9:class ShippingRateSeeder extends Seeder
database/seeders/ShippingRateSeeder.php:16:        ShippingZone::withoutGlobalScopes()->get()->each(function (ShippingZone $zone): void {
database/seeders/ShippingRateSeeder.php:18:                ShippingRate::withoutGlobalScopes()->updateOrCreate(
database/seeders/ShippingRateSeeder.php:39:            ['name' => 'Standard Shipping', 'type' => 'flat', 'config_json' => ['amount' => 799]],
database/seeders/ShippingRateSeeder.php:40:            ['name' => 'Express Shipping', 'type' => 'flat', 'config_json' => ['amount' => 1499]],
database/seeders/ShippingRateSeeder.php:42:                'name' => 'Free Shipping Over 75',
app/Services/CheckoutService.php:8:use App\Exceptions\UnserviceableShippingAddressException;
app/Services/CheckoutService.php:15:use App\Models\ShippingRate;
app/Services/CheckoutService.php:23:        private readonly ShippingCalculator $shipping,
app/Services/CheckoutService.php:86:    public function setShippingMethod(Checkout $checkout, ?int $shippingRateId): Checkout
app/Services/CheckoutService.php:90:            $this->assertStatus($checkout, [CheckoutStatus::Addressed, CheckoutStatus::ShippingSelected]);
app/Services/CheckoutService.php:92:            if (! $this->shipping->requiresShipping($checkout->cart)) {
app/Services/CheckoutService.php:95:                    'status' => CheckoutStatus::ShippingSelected,
app/Services/CheckoutService.php:106:                throw UnserviceableShippingAddressException::forAddress();
app/Services/CheckoutService.php:111:            if (! $rate instanceof ShippingRate) {
app/Services/CheckoutService.php:117:                'status' => CheckoutStatus::ShippingSelected,
app/Services/CheckoutService.php:135:            $this->assertStatus($checkout, [CheckoutStatus::ShippingSelected]);


cmd: sed -n '1,140p' database/seeders/ShippingZoneSeeder.php && sed -n '1,120p' database/seeders/ShippingRateSeeder.php && sed -n '1,150p' app/Services/ShippingCalculator.php


command: sed -n '1,140p' database/seeders/ShippingZoneSeeder.php && sed -n '1,120p' database/seeders/ShippingRateSeeder.php && sed -n '1,150p' app/Services/ShippingCalculator.php

ShippingZoneSeeder.php

type: read

ShippingCalculator.php

aggregated_output:
<?php

namespace Database\Seeders;

use App\Models\ShippingZone;
use App\Models\Store;
use Illuminate\Database\Seeder;

class ShippingZoneSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        Store::query()->get()->each(function (Store $store): void {
            ShippingZone::withoutGlobalScopes()->updateOrCreate(
                [
                    'store_id' => $store->getKey(),
                    'name' => 'DACH',
                ],
                [
                    'countries_json' => ['DE', 'AT', 'CH'],
                    'regions_json' => [],
                ],
            );
        });
    }
}
<?php

namespace Database\Seeders;

use App\Models\ShippingRate;
use App\Models\ShippingZone;
use Illuminate\Database\Seeder;

class ShippingRateSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        ShippingZone::withoutGlobalScopes()->get()->each(function (ShippingZone $zone): void {
            foreach ($this->rates() as $rate) {
                ShippingRate::withoutGlobalScopes()->updateOrCreate(
                    [
                        'zone_id' => $zone->getKey(),
                        'name' => $rate['name'],
                    ],
                    [
                        'type' => $rate['type'],
                        'config_json' => $rate['config_json'],
                        'is_active' => true,
                    ],
                );
            }
        });
    }

    /**
     * @return array<int, array{name: string, type: string, config_json: array<string, mixed>}>
     */
    private function rates(): array
    {
        return [
            ['name' => 'Standard Shipping', 'type' => 'flat', 'config_json' => ['amount' => 799]],
            ['name' => 'Express Shipping', 'type' => 'flat', 'config_json' => ['amount' => 1499]],
            [
                'name' => 'Free Shipping Over 75',
                'type' => 'price',
                'config_json' => [
                    'ranges' => [
                        ['min_amount' => 0, 'max_amount' => 7499, 'amount' => 799],
                        ['min_amount' => 7500, 'amount' => 0],
                    ],
                ],
            ],
        ];
    }
}
<?php

namespace App\Services;

use App\Enums\ShippingRateType;
use App\Models\Cart;
use App\Models\CartLine;
use App\Models\ProductVariant;
use App\Models\ShippingRate;
use App\Models\ShippingZone;
use App\Models\Store;
use Illuminate\Support\Collection;

class ShippingCalculator
{
    /**
     * @param  array<string, mixed>  $address
     * @return Collection<int, ShippingRate>
     */
    public function getAvailableRates(Store $store, array $address): Collection
    {
        $zone = $this->matchingZone($store, $address);

        if (! $zone instanceof ShippingZone) {
            return collect();
        }

        return ShippingRate::withoutGlobalScopes()
            ->where('zone_id', $zone->getKey())
            ->where('is_active', true)
            ->orderBy('id')
            ->get();
    }

    /**
     * @param  array<string, mixed>  $address
     */
    public function matchingZone(Store $store, array $address): ?ShippingZone
    {
        $country = strtoupper((string) (data_get($address, 'country_code') ?: data_get($address, 'country')));
        $region = strtoupper((string) data_get($address, 'province_code'));
        $bestZone = null;
        $bestSpecificity = -1;

        ShippingZone::withoutGlobalScopes()
            ->where('store_id', $store->getKey())
            ->orderBy('id')
            ->get()
            ->each(function (ShippingZone $zone) use ($country, $region, &$bestZone, &$bestSpecificity): void {
                $countries = collect($zone->countries_json)->map(fn (string $code): string => strtoupper($code));
                $regions = collect($zone->regions_json)->map(fn (string $code): string => strtoupper($code));

                if (! $countries->contains($country)) {
                    return;
                }

                $specificity = $regions->contains($region) ? 2 : 1;

                if ($specificity > $bestSpecificity) {
                    $bestZone = $zone;
                    $bestSpecificity = $specificity;
                }
            });

        return $bestZone;
    }

    public function calculate(ShippingRate $rate, Cart $cart): ?int
    {
        if (! $this->requiresShipping($cart)) {
            return 0;
        }

        return match ($rate->type) {
            ShippingRateType::Flat => (int) data_get($rate->config_json, 'amount', 0),
            ShippingRateType::Weight => $this->weightRate($rate, $cart),
            ShippingRateType::Price => $this->priceRate($rate, $cart),
            ShippingRateType::Carrier => (int) data_get($rate->config_json, 'amount', 1299),
        };
    }

    public function requiresShipping(Cart $cart): bool
    {
        return $this->physicalLines($cart)->isNotEmpty();
    }

    private function weightRate(ShippingRate $rate, Cart $cart): ?int
    {
        $totalWeight = $this->physicalLines($cart)
            ->sum(function (CartLine $line): int {
                $variant = $this->variant($line);

                return ($variant?->weight_g ?? 0) * $line->quantity;
            });

        foreach (data_get($rate->config_json, 'ranges', []) as $range) {
            $minimum = (int) data_get($range, 'min_g', 0);
            $maximum = data_get($range, 'max_g');

            if ($totalWeight >= $minimum && ($maximum === null || $totalWeight <= (int) $maximum)) {
                return (int) data_get($range, 'amount', 0);
            }
        }

        return null;
    }

    private function priceRate(ShippingRate $rate, Cart $cart): ?int
    {
        $subtotal = CartLine::withoutGlobalScopes()
            ->where('cart_id', $cart->getKey())
            ->sum('line_subtotal_amount');

        foreach (data_get($rate->config_json, 'ranges', []) as $range) {
            $minimum = (int) data_get($range, 'min_amount', 0);
            $maximum = data_get($range, 'max_amount');

            if ($subtotal >= $minimum && ($maximum === null || $subtotal <= (int) $maximum)) {
                return (int) data_get($range, 'amount', 0);
            }
        }

        return null;
    }

    /**
     * @return Collection<int, CartLine>
     */
    private function physicalLines(Cart $cart): Collection
    {
        return CartLine::withoutGlobalScopes()
            ->where('cart_id', $cart->getKey())
            ->get()
            ->filter(function (CartLine $line): bool {
                return (bool) $this->variant($line)?->requires_shipping;
            });
    }

    private function variant(CartLine $line): ?ProductVariant
    {
        return ProductVariant::withoutGlobalScopes()
            ->whereKey($line->variant_id)
            ->first();
    }
}

status: completed


output:
Chunk ID: 7aa0a4
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1739
Output:
<?php

namespace Database\Seeders;

use App\Models\ShippingZone;
use App\Models\Store;
use Illuminate\Database\Seeder;

class ShippingZoneSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        Store::query()->get()->each(function (Store $store): void {
            ShippingZone::withoutGlobalScopes()->updateOrCreate(
                [
                    'store_id' => $store->getKey(),
                    'name' => 'DACH',
                ],
                [
                    'countries_json' => ['DE', 'AT', 'CH'],
                    'regions_json' => [],
                ],
            );
        });
    }
}
<?php

namespace Database\Seeders;

use App\Models\ShippingRate;
use App\Models\ShippingZone;
use Illuminate\Database\Seeder;

class ShippingRateSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        ShippingZone::withoutGlobalScopes()->get()->each(function (ShippingZone $zone): void {
            foreach ($this->rates() as $rate) {
                ShippingRate::withoutGlobalScopes()->updateOrCreate(
                    [
                        'zone_id' => $zone->getKey(),
                        'name' => $rate['name'],
                    ],
                    [
                        'type' => $rate['type'],
                        'config_json' => $rate['config_json'],
                        'is_active' => true,
                    ],
                );
            }
        });
    }

    /**
     * @return array<int, array{name: string, type: string, config_json: array<string, mixed>}>
     */
    private function rates(): array
    {
        return [
            ['name' => 'Standard Shipping', 'type' => 'flat', 'config_json' => ['amount' => 799]],
            ['name' => 'Express Shipping', 'type' => 'flat', 'config_json' => ['amount' => 1499]],
            [
                'name' => 'Free Shipping Over 75',
                'type' => 'price',
                'config_json' => [
                    'ranges' => [
                        ['min_amount' => 0, 'max_amount' => 7499, 'amount' => 799],
                        ['min_amount' => 7500, 'amount' => 0],
                    ],
                ],
            ],
        ];
    }
}
<?php

namespace App\Services;

use App\Enums\ShippingRateType;
use App\Models\Cart;
use App\Models\CartLine;
use App\Models\ProductVariant;
use App\Models\ShippingRate;
use App\Models\ShippingZone;
use App\Models\Store;
use Illuminate\Support\Collection;

class ShippingCalculator
{
    /**
     * @param  array<string, mixed>  $address
     * @return Collection<int, ShippingRate>
     */
    public function getAvailableRates(Store $store, array $address): Collection
    {
        $zone = $this->matchingZone($store, $address);

        if (! $zone instanceof ShippingZone) {
            return collect();
        }

        return ShippingRate::withoutGlobalScopes()
            ->where('zone_id', $zone->getKey())
            ->where('is_active', true)
            ->orderBy('id')
            ->get();
    }

    /**
     * @param  array<string, mixed>  $address
     */
    public function matchingZone(Store $store, array $address): ?ShippingZone
    {
        $country = strtoupper((string) (data_get($address, 'country_code') ?: data_get($address, 'country')));
        $region = strtoupper((string) data_get($address, 'province_code'));
        $bestZone = null;
        $bestSpecificity = -1;

        ShippingZone::withoutGlobalScopes()
            ->where('store_id', $store->getKey())
            ->orderBy('id')
            ->get()
            ->each(function (ShippingZone $zone) use ($country, $region, &$bestZone, &$bestSpecificity): void {
                $countries = collect($zone->countries_json)->map(fn (string $code): string => strtoupper($code));
                $regions = collect($zone->regions_json)->map(fn (string $code): string => strtoupper($code));

                if (! $countries->contains($country)) {
                    return;
                }

                $specificity = $regions->contains($region) ? 2 : 1;

                if ($specificity > $bestSpecificity) {
                    $bestZone = $zone;
                    $bestSpecificity = $specificity;
                }
            });

        return $bestZone;
    }

    public function calculate(ShippingRate $rate, Cart $cart): ?int
    {
        if (! $this->requiresShipping($cart)) {
            return 0;
        }

        return match ($rate->type) {
            ShippingRateType::Flat => (int) data_get($rate->config_json, 'amount', 0),
            ShippingRateType::Weight => $this->weightRate($rate, $cart),
            ShippingRateType::Price => $this->priceRate($rate, $cart),
            ShippingRateType::Carrier => (int) data_get($rate->config_json, 'amount', 1299),
        };
    }

    public function requiresShipping(Cart $cart): bool
    {
        return $this->physicalLines($cart)->isNotEmpty();
    }

    private function weightRate(ShippingRate $rate, Cart $cart): ?int
    {
        $totalWeight = $this->physicalLines($cart)
            ->sum(function (CartLine $line): int {
                $variant = $this->variant($line);

                return ($variant?->weight_g ?? 0) * $line->quantity;
            });

        foreach (data_get($rate->config_json, 'ranges', []) as $range) {
            $minimum = (int) data_get($range, 'min_g', 0);
            $maximum = data_get($range, 'max_g');

            if ($totalWeight >= $minimum && ($maximum === null || $totalWeight <= (int) $maximum)) {
                return (int) data_get($range, 'amount', 0);
            }
        }

        return null;
    }

    private function priceRate(ShippingRate $rate, Cart $cart): ?int
    {
        $subtotal = CartLine::withoutGlobalScopes()
            ->where('cart_id', $cart->getKey())
            ->sum('line_subtotal_amount');

        foreach (data_get($rate->config_json, 'ranges', []) as $range) {
            $minimum = (int) data_get($range, 'min_amount', 0);
            $maximum = data_get($range, 'max_amount');

            if ($subtotal >= $minimum && ($maximum === null || $subtotal <= (int) $maximum)) {
                return (int) data_get($range, 'amount', 0);
            }
        }

        return null;
    }

    /**
     * @return Collection<int, CartLine>
     */
    private function physicalLines(Cart $cart): Collection
    {
        return CartLine::withoutGlobalScopes()
            ->where('cart_id', $cart->getKey())
            ->get()
            ->filter(function (CartLine $line): bool {
                return (bool) $this->variant($line)?->requires_shipping;
            });
    }

    private function variant(CartLine $line): ?ProductVariant
    {
        return ProductVariant::withoutGlobalScopes()
            ->whereKey($line->variant_id)
            ->first();
    }
}


cmd: rg "799|7\.99|4\.99|Standard Shipping|International" tests specs database app resources -n


command: rg "799|7\.99|4\.99|Standard Shipping|International" tests specs database app resources -n

type: search

cmd: rg "799|7\\.99|4\\.99|Standard Shipping|International" tests specs database app resources -n

query: 799|7\.99|4\.99|Standard Shipping|International

path: tests


output:
Chunk ID: 6fd95f
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 5773
Output:
database/factories/ShippingRateFactory.php:23:            'name' => 'Standard Shipping',
database/factories/ShippingRateFactory.php:25:            'config_json' => ['amount' => 799],
database/factories/ShippingRateFactory.php:49:                    ['min_amount' => 0, 'max_amount' => 7500, 'amount' => 799],
specs/01-DATABASE-SCHEMA.md:608:**Notes:** `totals_json` contains a snapshot: `{"subtotal": 5000, "discount": 500, "shipping": 799, "tax": 430, "total": 5729}`. `tax_provider_snapshot_json` stores the full tax calculation response for audit purposes.
specs/01-DATABASE-SCHEMA.md:651:- Flat: `{"amount": 799}`
specs/01-DATABASE-SCHEMA.md:653:- Price: `{"ranges": [{"min_amount": 0, "max_amount": 5000, "amount": 799}]}`
tests/Feature/Api/StorefrontCheckoutApiTest.php:115:        ->assertJsonPath('data.totals.shipping', 799);
resources/views/welcome.blade.php:126:                        <path d="M110.256 41.6337C108.061 38.1275 104.945 35.3731 100.905 33.3681C96.8667 31.3647 92.8016 30.3618 88.7131 30.3618C83.4247 30.3618 78.5885 31.3389 74.201 33.2923C69.8111 35.2456 66.0474 37.928 62.9059 41.3333C59.7643 44.7401 57.3198 48.6726 55.5754 53.1293C53.8287 57.589 52.9572 62.274 52.9572 67.1813C52.9572 72.1925 53.8287 76.8995 55.5754 81.3069C57.3191 85.7173 59.7636 89.6241 62.9059 93.0293C66.0474 96.4361 69.8119 99.1155 74.201 101.069C78.5885 103.022 83.4247 103.999 88.7131 103.999C92.8016 103.999 96.8667 102.997 100.905 100.994C104.945 98.9911 108.061 96.2359 110.256 92.7282V102.195H126.563V32.1642H110.256V41.6337ZM108.76 75.7472C107.762 78.4531 106.366 80.8078 104.572 82.8112C102.776 84.8161 100.606 86.4183 98.0637 87.6206C95.5202 88.823 92.7004 89.4238 89.6103 89.4238C86.5178 89.4238 83.7252 88.823 81.2324 87.6206C78.7388 86.4183 76.5949 84.8161 74.7998 82.8112C73.004 80.8078 71.6319 78.4531 70.6856 75.7472C69.7356 73.0421 69.2644 70.1868 69.2644 67.1821C69.2644 64.1758 69.7356 61.3205 70.6856 58.6154C71.6319 55.9102 73.004 53.5571 74.7998 51.5522C76.5949 49.5495 78.738 47.9451 81.2324 46.7427C83.7252 45.5404 86.5178 44.9396 89.6103 44.9396C92.7012 44.9396 95.5202 45.5404 98.0637 46.7427C100.606 47.9451 102.776 49.5487 104.572 51.5522C106.367 53.5571 107.762 55.9102 108.76 58.6154C109.756 61.3205 110.256 64.1758 110.256 67.1821C110.256 70.1868 109.756 73.0421 108.76 75.7472Z" fill="currentColor" />
resources/views/welcome.blade.php:147:                            <path d="M217.342 305.363L217.673 305.363C224.519 298.478 234.844 289.393 248.647 278.11C262.008 266.826 272.056 257.837 278.792 251.144C285.418 244.259 289.945 236.991 292.374 229.341C294.803 221.691 293.643 213.753 288.895 205.529C283.594 196.349 276.14 188.985 266.532 183.438C257.035 177.7 246.986 174.831 236.385 174.83C226.005 174.83 219.214 177.603 216.012 183.149C212.699 188.504 213.804 195.963 219.325 205.527L154.725 205.524C145.449 189.458 141.473 175.114 142.798 162.491C144.343 149.868 150.637 140.114 161.68 133.229C172.833 126.153 187.685 122.615 206.237 122.616C225.672 122.617 244.942 126.156 264.046 133.233C283.039 140.119 300.377 149.874 316.058 162.497C331.739 175.121 344.218 189.466 353.495 205.532C362.108 220.45 365.919 233.551 364.925 244.835C364.153 256.12 360.454 265.969 353.828 274.384C347.424 282.799 337.486 293.127 324.014 305.367L406.172 305.37L434.996 355.289L246.165 355.282L217.342 305.363Z" fill="#F0ACB8" />
resources/views/welcome.blade.php:149:                            <path d="M217.342 305.363L217.673 305.363C224.519 298.478 234.844 289.393 248.647 278.11C262.008 266.826 272.056 257.837 278.792 251.144C285.418 244.259 289.945 236.991 292.374 229.341C294.803 221.691 293.643 213.753 288.895 205.529C283.594 196.349 276.14 188.985 266.532 183.438C257.035 177.7 246.986 174.831 236.385 174.83C226.005 174.83 219.214 177.603 216.012 183.149C212.699 188.504 213.804 195.963 219.325 205.527L154.725 205.524C145.449 189.458 141.473 175.114 142.798 162.491C144.343 149.868 150.637 140.114 161.68 133.229C172.833 126.153 187.685 122.615 206.237 122.616C225.672 122.617 244.942 126.156 264.046 133.233C283.039 140.119 300.377 149.874 316.058 162.497C331.739 175.121 344.218 189.466 353.495 205.532C362.108 220.45 365.919 233.551 364.925 244.835C364.153 256.12 360.454 265.969 353.828 274.384C347.424 282.799 337.486 293.127 324.014 305.367L406.172 305.37L434.996 355.289L246.165 355.282L217.342 305.363Z" stroke="#1B1B18" stroke-width="1" />
resources/views/welcome.blade.php:159:                            <path d="M188.467 355.363L188.798 355.363C195.644 348.478 205.969 339.393 219.772 328.11C233.133 316.826 243.181 307.837 249.917 301.144C253.696 297.217 256.792 293.166 259.205 288.991C261.024 285.845 262.455 282.628 263.499 279.341C265.928 271.691 264.768 263.753 260.02 255.529C254.719 246.349 247.265 238.985 237.657 233.438C228.16 227.7 218.111 224.831 207.51 224.83C197.13 224.83 190.339 227.603 187.137 233.149C183.824 238.504 184.929 245.963 190.45 255.527L125.851 255.524C116.574 239.458 112.598 225.114 113.923 212.491C114.615 206.836 116.261 201.756 118.859 197.253C122.061 191.704 126.709 187.03 132.805 183.229C143.958 176.153 158.81 172.615 177.362 172.616C196.797 172.617 216.067 176.156 235.171 183.233C254.164 190.119 271.502 199.874 287.183 212.497C302.864 225.121 315.343 239.466 324.62 255.532C333.233 270.45 337.044 283.551 336.05 294.835C335.46 303.459 333.16 311.245 329.151 318.194C327.915 320.337 326.515 322.4 324.953 324.384C318.549 332.799 308.611 343.127 295.139 355.367L377.297 355.37L406.121 405.289L217.29 405.282L188.467 355.363Z" stroke="#1B1B18" stroke-width="1" stroke-linejoin="bevel" />
resources/views/welcome.blade.php:194:                            <path d="M246.544 254.79L246.875 254.79C253.722 247.905 264.046 238.82 277.849 227.537C291.21 216.253 301.259 207.264 307.995 200.57C314.62 193.685 319.147 186.418 321.577 178.768C324.006 171.117 322.846 163.18 318.097 154.956C312.796 145.775 305.342 138.412 295.735 132.865C286.238 127.127 276.189 124.258 265.588 124.257C255.208 124.257 248.416 127.03 245.214 132.576C241.902 137.931 243.006 145.39 248.528 154.953L183.928 154.951C174.652 138.885 170.676 124.541 172 111.918C173.546 99.2946 179.84 89.5408 190.882 82.6559C202.035 75.5798 216.887 72.0421 235.439 72.0428C254.874 72.0435 274.144 75.5825 293.248 82.6598C312.242 89.5457 329.579 99.3005 345.261 111.924C360.942 124.548 373.421 138.892 382.697 154.958C391.311 169.877 395.121 182.978 394.128 194.262C393.355 205.546 389.656 215.396 383.031 223.811C376.627 232.226 366.688 242.554 353.217 254.794L435.375 254.797L464.198 304.716L275.367 304.709L246.544 254.79Z" fill="#F0ACB8" />
resources/views/welcome.blade.php:195:                            <path d="M246.544 254.79L246.875 254.79C253.722 247.905 264.046 238.82 277.849 227.537C291.21 216.253 301.259 207.264 307.995 200.57C314.62 193.685 319.147 186.418 321.577 178.768C324.006 171.117 322.846 163.18 318.097 154.956C312.796 145.775 305.342 138.412 295.735 132.865C286.238 127.127 276.189 124.258 265.588 124.257C255.208 124.257 248.416 127.03 245.214 132.576C241.902 137.931 243.006 145.39 248.528 154.953L183.928 154.951C174.652 138.885 170.676 124.541 172 111.918C173.546 99.2946 179.84 89.5408 190.882 82.6559C202.035 75.5798 216.887 72.0421 235.439 72.0428C254.874 72.0435 274.144 75.5825 293.248 82.6598C312.242 89.5457 329.579 99.3005 345.261 111.924C360.942 124.548 373.421 138.892 382.697 154.958C391.311 169.877 395.121 182.978 394.128 194.262C393.355 205.546 389.656 215.396 383.031 223.811C376.627 232.226 366.688 242.554 353.217 254.794L435.375 254.797L464.198 304.716L275.367 304.709L246.544 254.79Z" stroke="#1B1B18" stroke-width="1" stroke-linejoin="round" />
resources/views/welcome.blade.php:216:                            <path d="M217.342 305.363L217.673 305.363C224.519 298.478 234.844 289.393 248.647 278.11C262.008 266.826 272.056 257.837 278.792 251.144C285.418 244.259 289.945 236.991 292.374 229.341C294.803 221.691 293.643 213.753 288.895 205.529C283.594 196.349 276.14 188.985 266.532 183.438C257.035 177.7 246.986 174.831 236.385 174.83C226.005 174.83 219.214 177.603 216.012 183.149C212.699 188.504 213.804 195.963 219.325 205.527L154.725 205.524C145.449 189.458 141.473 175.114 142.798 162.491C144.343 149.868 150.637 140.114 161.68 133.229C172.833 126.153 187.685 122.615 206.237 122.616C225.672 122.617 244.942 126.156 264.046 133.233C283.039 140.119 300.377 149.874 316.058 162.497C331.739 175.121 344.218 189.466 353.495 205.532C362.108 220.45 365.919 233.551 364.925 244.835C364.153 256.12 360.454 265.969 353.828 274.384C347.424 282.799 337.486 293.127 324.014 305.367L406.172 305.37L434.996 355.289L246.165 355.282L217.342 305.363Z" fill="#733000"/>
resources/views/welcome.blade.php:218:                            <path d="M217.342 305.363L217.673 305.363C224.519 298.478 234.844 289.393 248.647 278.11C262.008 266.826 272.056 257.837 278.792 251.144C285.418 244.259 289.945 236.991 292.374 229.341C294.803 221.691 293.643 213.753 288.895 205.529C283.594 196.349 276.14 188.985 266.532 183.438C257.035 177.7 246.986 174.831 236.385 174.83C226.005 174.83 219.214 177.603 216.012 183.149C212.699 188.504 213.804 195.963 219.325 205.527L154.725 205.524C145.449 189.458 141.473 175.114 142.798 162.491C144.343 149.868 150.637 140.114 161.68 133.229C172.833 126.153 187.685 122.615 206.237 122.616C225.672 122.617 244.942 126.156 264.046 133.233C283.039 140.119 300.377 149.874 316.058 162.497C331.739 175.121 344.218 189.466 353.495 205.532C362.108 220.45 365.919 233.551 364.925 244.835C364.153 256.12 360.454 265.969 353.828 274.384C347.424 282.799 337.486 293.127 324.014 305.367L406.172 305.37L434.996 355.289L246.165 355.282L217.342 305.363Z" stroke="#FF750F" stroke-width="1"/>
resources/views/welcome.blade.php:222:                            <path d="M217.342 305.363L217.673 305.363C224.519 298.478 234.844 289.393 248.647 278.11C262.008 266.826 272.056 257.837 278.792 251.144C285.418 244.259 289.945 236.991 292.374 229.341C294.803 221.691 293.643 213.753 288.895 205.529C283.594 196.349 276.14 188.985 266.532 183.438C257.035 177.7 246.986 174.831 236.385 174.83C226.005 174.83 219.214 177.603 216.012 183.149C212.699 188.504 213.804 195.963 219.325 205.527L154.726 205.524C145.449 189.458 141.473 175.114 142.798 162.491C144.343 149.868 150.637 140.114 161.68 133.229C172.833 126.153 187.685 122.615 206.237 122.616C225.672 122.617 244.942 126.156 264.046 133.233C283.039 140.119 300.377 149.874 316.058 162.497C331.739 175.121 344.218 189.466 353.495 205.532C362.108 220.45 365.919 233.551 364.925 244.835C364.153 256.12 360.454 265.969 353.828 274.384C347.424 282.799 337.486 293.127 324.014 305.367L406.172 305.37L434.996 355.289L246.165 355.282L217.342 305.363Z" stroke="#FF750F" stroke-width="1"/>
resources/views/welcome.blade.php:226:                            <path d="M188.467 355.363L188.798 355.363C195.644 348.478 205.969 339.393 219.772 328.11C233.133 316.826 243.181 307.837 249.917 301.144C253.696 297.217 256.792 293.166 259.205 288.991C261.024 285.845 262.455 282.628 263.499 279.341C265.928 271.691 264.768 263.753 260.02 255.529C254.719 246.349 247.265 238.985 237.657 233.438C228.16 227.7 218.111 224.831 207.51 224.83C197.13 224.83 190.339 227.603 187.137 233.149C183.824 238.504 184.929 245.963 190.45 255.527L125.851 255.524C116.574 239.458 112.598 225.114 113.923 212.491C114.615 206.836 116.261 201.756 118.859 197.253C122.061 191.704 126.709 187.03 132.805 183.229C143.958 176.153 158.81 172.615 177.362 172.616C196.797 172.617 216.067 176.156 235.171 183.233C254.164 190.119 271.502 199.874 287.183 212.497C302.864 225.121 315.343 239.466 324.62 255.532C333.233 270.45 337.044 283.551 336.05 294.835C335.46 303.459 333.16 311.245 329.151 318.194C327.915 320.337 326.515 322.4 324.953 324.384C318.549 332.799 308.611 343.127 295.139 355.367L377.297 355.37L406.121 405.289L217.29 405.282L188.467 355.363Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
resources/views/welcome.blade.php:261:                            <path d="M246.544 254.79L246.875 254.79C253.722 247.905 264.046 238.82 277.849 227.537C291.21 216.253 301.259 207.264 307.995 200.57C314.62 193.685 319.147 186.418 321.577 178.768C324.006 171.117 322.846 163.18 318.097 154.956C312.796 145.775 305.342 138.412 295.735 132.865C286.238 127.127 276.189 124.258 265.588 124.257C255.208 124.257 248.416 127.03 245.214 132.576C241.902 137.931 243.006 145.39 248.528 154.953L183.928 154.951C174.652 138.885 170.676 124.541 172 111.918C173.546 99.2946 179.84 89.5408 190.882 82.6559C202.035 75.5798 216.887 72.0421 235.439 72.0428C254.874 72.0435 274.144 75.5825 293.248 82.6598C312.242 89.5457 329.579 99.3005 345.261 111.924C360.942 124.548 373.421 138.892 382.697 154.958C391.311 169.877 395.121 182.978 394.128 194.262C393.355 205.546 389.656 215.396 383.031 223.811C376.627 232.226 366.688 242.554 353.217 254.794L435.375 254.797L464.198 304.716L275.367 304.709L246.544 254.79Z" fill="#4B0600"/>
resources/views/welcome.blade.php:262:                            <path d="M246.544 254.79L246.875 254.79C253.722 247.905 264.046 238.82 277.849 227.537C291.21 216.253 301.259 207.264 307.995 200.57C314.62 193.685 319.147 186.418 321.577 178.768C324.006 171.117 322.846 163.18 318.097 154.956C312.796 145.775 305.342 138.412 295.735 132.865C286.238 127.127 276.189 124.258 265.588 124.257C255.208 124.257 248.416 127.03 245.214 132.576C241.902 137.931 243.006 145.39 248.528 154.953L183.928 154.951C174.652 138.885 170.676 124.541 172 111.918C173.546 99.2946 179.84 89.5408 190.882 82.6559C202.035 75.5798 216.887 72.0421 235.439 72.0428C254.874 72.0435 274.144 75.5825 293.248 82.6598C312.242 89.5457 329.579 99.3005 345.261 111.924C360.942 124.548 373.421 138.892 382.697 154.958C391.311 169.877 395.121 182.978 394.128 194.262C393.355 205.546 389.656 215.396 383.031 223.811C376.627 232.226 366.688 242.554 353.217 254.794L435.375 254.797L464.198 304.716L275.367 304.709L246.544 254.79Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="round"/>
database/factories/OrderFactory.php:27:        $shipping = fake()->randomElement([0, 499, 799]);
specs/02-API-ROUTES.md:626:      "name": "Standard Shipping",
specs/02-API-ROUTES.md:2307:          "name": "Standard Shipping",
specs/09-IMPLEMENTATION-ROADMAP.md:1017:| calculates subtotal from line items | 2 lines: variant A 2499 cents x 2, variant B 7999 cents x 1 | subtotal = 12997 | happy path |
specs/09-IMPLEMENTATION-ROADMAP.md:1083:| calculates a price-based rate | rate type: price, config: ranges 0-5000=799, 5001-999999=399; cart subtotal: 7500 | shipping = 399 | happy path |
database/seeders/ProductSeeder.php:274:            $this->product('Premium Slim Fit Jeans', 'premium-slim-fit-jeans', 'Acme Denim', 'Pants', ['new', 'sale'], ['new-arrivals', 'pants-jeans', 'sale'], '
Slim fit jeans crafted from premium stretch denim. Comfortable all-day wear with a modern silhouette.
', [['Size' => ['28', '30', '32', '34', '36']], ['Color' => ['Blue', 'Black']]], ['price_amount' => 7999, 'compare_at_amount' => 9999, 'weight_g' => 800, 'quantity_on_hand' => 8], 'ACME-JNS'),
database/seeders/ProductSeeder.php:280:            $this->product('Striped Polo Shirt', 'striped-polo-shirt', 'Acme Basics', 'T-Shirts', ['sale'], ['t-shirts', 'sale'], '
Classic striped polo with a modern relaxed fit. Knitted collar and two-button placket.
', [['Size' => ['S', 'M', 'L', 'XL']]], ['price_amount' => 2799, 'compare_at_amount' => 3999, 'weight_g' => 250, 'quantity_on_hand' => 10], 'ACME-POLO'),
database/seeders/ShippingRateSeeder.php:39:            ['name' => 'Standard Shipping', 'type' => 'flat', 'config_json' => ['amount' => 799]],
database/seeders/ShippingRateSeeder.php:46:                        ['min_amount' => 0, 'max_amount' => 7499, 'amount' => 799],
specs/07-SEEDERS-AND-TEST-DATA.md:880:| Standard Shipping | flat | amount: 499 (4.99 EUR) | 1 |
specs/07-SEEDERS-AND-TEST-DATA.md:897:| International | flat | amount: 1499 (14.99 EUR) | 1 |
specs/07-SEEDERS-AND-TEST-DATA.md:966:| price_amount | 2499 (24.99 EUR) |
specs/07-SEEDERS-AND-TEST-DATA.md:1001:| price_amount | 7999 (79.99 EUR) |
specs/07-SEEDERS-AND-TEST-DATA.md:1126:Variants (4 total): price_amount = 2799, compare_at_amount = 3999 (on sale), weight_g = 250, inventory = 10 each (deny)
specs/07-SEEDERS-AND-TEST-DATA.md:1393:| 2 | Premium Slim Fit Jeans | premium-slim-fit-jeans | active | 7999 | 9999 | 10 | 8 ea | On sale |
specs/07-SEEDERS-AND-TEST-DATA.md:1399:| 8 | Striped Polo Shirt | striped-polo-shirt | active | 2799 | 3999 | 4 | 10 ea | On sale |
specs/07-SEEDERS-AND-TEST-DATA.md:1567:| Premium Slim Fit Jeans | 32 / Blue | 1 | 7999 | 7999 |
specs/07-SEEDERS-AND-TEST-DATA.md:1751:| Striped Polo Shirt | XL | 1 | 2799 | 2799 |
specs/07-SEEDERS-AND-TEST-DATA.md:1753:Totals: subtotal 2799, discount 0, shipping 499, tax 447, total **3298**
specs/07-SEEDERS-AND-TEST-DATA.md:1774:| Chino Shorts | 34 / Navy | 2 | 3999 | 7998 |
specs/07-SEEDERS-AND-TEST-DATA.md:1776:Totals: subtotal 7998, discount 0, shipping 499, tax 1277, total **8497**
specs/07-SEEDERS-AND-TEST-DATA.md:1798:Totals: subtotal 7998, discount 0, shipping 499, tax 1277, total **8497**
specs/07-SEEDERS-AND-TEST-DATA.md:1954:  3. International shipping (EU + US, UK, CA, AU)
specs/07-SEEDERS-AND-TEST-DATA.md:1959:- body_html content: Shipping rates table (Germany standard 4.99/express 9.99, EU 8.99, International 14.99) using h3 and ul/li elements, plus returns policy paragraph (30 days, customer pays return shipping unless defective).
tests/Feature/Catalog/CatalogUiTest.php:28:        ->assertSee('24.99 EUR');
specs/03-ADMIN-UI.md:1554:- Zone name input (placeholder: "Domestic, Europe, International...")
specs/08-PLAYWRIGHT-E2E-PLAN.md:92:| **Product #1** | Title: "Classic Cotton T-Shirt", handle: `classic-cotton-t-shirt`, price: 24.99 EUR, status: active, options: Size (S/M/L/XL), Color (Black/White/Navy), inventory: in stock | 1, 3, 7, 8, 9, 11, 13, 14 |
specs/08-PLAYWRIGHT-E2E-PLAN.md:101:| **Shipping zones** | "Domestic" zone (countries: DE) with rate "Standard Shipping" at 4.99 EUR flat; "International" zone with its own rates | 6, 9 |
specs/08-PLAYWRIGHT-E2E-PLAN.md:215:- Page displays "24.99"
specs/08-PLAYWRIGHT-E2E-PLAN.md:1106:- Page displays "Standard Shipping"
specs/08-PLAYWRIGHT-E2E-PLAN.md:1107:- Page displays "4.99"
specs/08-PLAYWRIGHT-E2E-PLAN.md:1123:7. Enter price: `14.99`
specs/08-PLAYWRIGHT-E2E-PLAN.md:1128:- Page displays "Overnight Shipping" and "14.99" in the zone
specs/08-PLAYWRIGHT-E2E-PLAN.md:1217:- Page displays "24.99"
specs/08-PLAYWRIGHT-E2E-PLAN.md:1246:- Page displays "24.99"
specs/08-PLAYWRIGHT-E2E-PLAN.md:1261:- Page displays "24.99"
specs/08-PLAYWRIGHT-E2E-PLAN.md:1468:- Page displays "24.99"
specs/08-PLAYWRIGHT-E2E-PLAN.md:1487:- Page displays "24.99"
specs/08-PLAYWRIGHT-E2E-PLAN.md:1507:- Line total updates to 49.98 (2 x 24.99)
specs/08-PLAYWRIGHT-E2E-PLAN.md:1554:**Preconditions:** Product #1 in cart (24.99 EUR, above 20 EUR minimum), WELCOME10 discount exists
specs/08-PLAYWRIGHT-E2E-PLAN.md:1562:6. Verify "24.99" is displayed
specs/08-PLAYWRIGHT-E2E-PLAN.md:1569:- 10% discount applied (approximately 2.50 off 24.99)
specs/08-PLAYWRIGHT-E2E-PLAN.md:1684:- Page displays "24.99"
specs/08-PLAYWRIGHT-E2E-PLAN.md:1714:    Step2 -->|Country = DE| DomesticRates[Domestic: Standard 4.99 EUR]
specs/08-PLAYWRIGHT-E2E-PLAN.md:1715:    Step2 -->|Country = US| InternationalRates[International rates]
specs/08-PLAYWRIGHT-E2E-PLAN.md:1732:For the happy path (Classic Cotton T-Shirt, DE address, Standard Shipping):
specs/08-PLAYWRIGHT-E2E-PLAN.md:1736:| Subtotal | 24.99 EUR |
specs/08-PLAYWRIGHT-E2E-PLAN.md:1737:| Shipping (Standard, Domestic DE) | 4.99 EUR |
specs/08-PLAYWRIGHT-E2E-PLAN.md:1745:| Subtotal | 24.99 EUR |
specs/08-PLAYWRIGHT-E2E-PLAN.md:1747:| Shipping | 4.99 EUR |
specs/08-PLAYWRIGHT-E2E-PLAN.md:1754:**Preconditions:** Product #1, Domestic shipping zone with Standard Shipping at 4.99 EUR
specs/08-PLAYWRIGHT-E2E-PLAN.md:1772:16. Verify "Standard Shipping" and "4.99" are displayed
specs/08-PLAYWRIGHT-E2E-PLAN.md:1773:17. Click "Standard Shipping" to select it
specs/08-PLAYWRIGHT-E2E-PLAN.md:1780:24. Verify total is 29.98 (24.99 + 4.99 shipping)
specs/08-PLAYWRIGHT-E2E-PLAN.md:1812:- Page displays "Standard Shipping"
specs/08-PLAYWRIGHT-E2E-PLAN.md:1813:- Page displays "4.99" (Domestic zone rate)
specs/08-PLAYWRIGHT-E2E-PLAN.md:1820:**Preconditions:** Product #1 in cart, International shipping zone
specs/08-PLAYWRIGHT-E2E-PLAN.md:1840:- Page displays "International" shipping rates (not Domestic rates)
specs/08-PLAYWRIGHT-E2E-PLAN.md:1868:19. Click "Standard Shipping" to select it
specs/08-PLAYWRIGHT-E2E-PLAN.md:1874:- Totals reflect: 24.99 - 5.00 + 4.99 = 24.98
specs/08-PLAYWRIGHT-E2E-PLAN.md:1971:7. Select "Standard Shipping", click "Continue"
specs/08-PLAYWRIGHT-E2E-PLAN.md:1993:7. Select "Standard Shipping", click "Continue"
specs/08-PLAYWRIGHT-E2E-PLAN.md:2016:7. Select "Standard Shipping", click "Continue"
specs/08-PLAYWRIGHT-E2E-PLAN.md:2042:7. Select "Standard Shipping", click "Continue"
specs/08-PLAYWRIGHT-E2E-PLAN.md:2555:- Page displays "Classic Cotton T-Shirt", "24.99", "Add to cart"
specs/08-PLAYWRIGHT-E2E-PLAN.md:2620:- Page displays "Standard Shipping"
tests/Feature/Storefront/OrderViewsTest.php:55:        ->where('name', 'Standard Shipping')
specs/04-STOREFRONT-UI.md:29:| Currency symbol | After amount with space (e.g., `24.99 EUR`) |
specs/04-STOREFRONT-UI.md:37:- 2499 cents -> `24.99 EUR`
specs/04-STOREFRONT-UI.md:901:|  [img] | Title        | $24.99  | [-][2][+]| $49.98  | [X]   |
specs/04-STOREFRONT-UI.md:903:|  [img] | Title        | $34.99  | [-][1][+]| $34.99  | [X]   |
specs/04-STOREFRONT-UI.md:1027:| (o) Standard Shipping          5-7 business days       $7.99 |
specs/04-STOREFRONT-UI.md:1028:| ( ) Express Shipping           2-3 business days      $14.99 |
specs/04-STOREFRONT-UI.md:1126:|  [Thumb] Title x1     $34.99 |
specs/04-STOREFRONT-UI.md:1132:|  Shipping               $7.99|
specs/04-STOREFRONT-UI.md:1182:|  Shipping         $7.99                                       |
specs/04-STOREFRONT-UI.md:1358:|  Shipping         $7.99                                       |
specs/04-STOREFRONT-UI.md:1421:|  [Thumb] Product Title            $24.99                      |
specs/04-STOREFRONT-UI.md:1422:|  [Thumb] Product Title            $34.99                      |
tests/Browser/Storefront/BrowsingTest.php:28:        ->assertSee('24.99')
tests/Browser/Storefront/BrowsingTest.php:45:        ->assertSee('24.99')
tests/Browser/Storefront/BrowsingTest.php:53:        ->assertSee('24.99')
tests/Feature/Storefront/CartCheckoutUiTest.php:102:        ->assertSee('Standard Shipping');
tests/Feature/Storefront/CartCheckoutUiTest.php:106:        ->and($component->instance()->estimatedShippingAmount())->toBe(799)
tests/Feature/Storefront/CartCheckoutUiTest.php:151:        ->where('name', 'Standard Shipping')
tests/Browser/Storefront/CartTest.php:57:        ->assertSee('24.99')
tests/Browser/Storefront/CartTest.php:63:        ->assertSee('24.99')
tests/Browser/Storefront/CartTest.php:141:        ->assertSee('24.99')


cmd: rg "create_shipping|shipping_zones|shipping_rates" database/migrations -n


command: rg "create_shipping|shipping_zones|shipping_rates" database/migrations -n

cmd: rg 'create_shipping|shipping_zones|shipping_rates' database/migrations -n

query: create_shipping|shipping_zones|shipping_rates

path: migrations

aggregated_output:
database/migrations/2026_05_03_225413_create_shipping_zones_table.php:14:        Schema::create('shipping_zones', function (Blueprint $table) {
database/migrations/2026_05_03_225413_create_shipping_zones_table.php:30:        Schema::dropIfExists('shipping_zones');
database/migrations/2026_05_03_225418_create_shipping_rates_table.php:14:        Schema::create('shipping_rates', function (Blueprint $table) {
database/migrations/2026_05_03_225418_create_shipping_rates_table.php:16:            $table->foreignId('zone_id')->constrained('shipping_zones')->cascadeOnDelete();
database/migrations/2026_05_03_225418_create_shipping_rates_table.php:32:        Schema::dropIfExists('shipping_rates');


output:
Chunk ID: fee5ed
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 174
Output:
database/migrations/2026_05_03_225413_create_shipping_zones_table.php:14:        Schema::create('shipping_zones', function (Blueprint $table) {
database/migrations/2026_05_03_225413_create_shipping_zones_table.php:30:        Schema::dropIfExists('shipping_zones');
database/migrations/2026_05_03_225418_create_shipping_rates_table.php:14:        Schema::create('shipping_rates', function (Blueprint $table) {
database/migrations/2026_05_03_225418_create_shipping_rates_table.php:16:            $table->foreignId('zone_id')->constrained('shipping_zones')->cascadeOnDelete();
database/migrations/2026_05_03_225418_create_shipping_rates_table.php:32:        Schema::dropIfExists('shipping_rates');


cmd: sed -n '1,80p' database/migrations/2026_05_03_225413_create_shipping_zones_table.php && sed -n '1,80p' database/migrations/2026_05_03_225418_create_shipping_rates_table.php


command: sed -n '1,80p' database/migrations/2026_05_03_225413_create_shipping_zones_table.php && sed -n '1,80p' database/migrations/2026_05_03_225418_create_shipping_rates_table.php

aggregated_output:
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('shipping_zones', function (Blueprint $table) {
            $table->id();
            $table->foreignId('store_id')->constrained()->cascadeOnDelete();
            $table->string('name');
            $table->text('countries_json')->default('[]');
            $table->text('regions_json')->default('[]');

            $table->index('store_id');
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('shipping_zones');
    }
};
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('shipping_rates', function (Blueprint $table) {
            $table->id();
            $table->foreignId('zone_id')->constrained('shipping_zones')->cascadeOnDelete();
            $table->string('name');
            $table->enum('type', ['flat', 'weight', 'price', 'carrier'])->default('flat');
            $table->text('config_json')->default('{}');
            $table->boolean('is_active')->default(true);

            $table->index('zone_id');
            $table->index(['zone_id', 'is_active']);
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('shipping_rates');
    }
};


output:
Chunk ID: 392299
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 429
Output:
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('shipping_zones', function (Blueprint $table) {
            $table->id();
            $table->foreignId('store_id')->constrained()->cascadeOnDelete();
            $table->string('name');
            $table->text('countries_json')->default('[]');
            $table->text('regions_json')->default('[]');

            $table->index('store_id');
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('shipping_zones');
    }
};
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('shipping_rates', function (Blueprint $table) {
            $table->id();
            $table->foreignId('zone_id')->constrained('shipping_zones')->cascadeOnDelete();
            $table->string('name');
            $table->enum('type', ['flat', 'weight', 'price', 'carrier'])->default('flat');
            $table->text('config_json')->default('{}');
            $table->boolean('is_active')->default(true);

            $table->index('zone_id');
            $table->index(['zone_id', 'is_active']);
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('shipping_rates');
    }
};


cmd: rg "Order placed|Thank you|checkout.confirmation|Confirmation sent|payment_method|PayPal|bank transfer" tests resources app specs -n


command: rg "Order placed|Thank you|checkout.confirmation|Confirmation sent|payment_method|PayPal|bank transfer" tests resources app specs -n

type: search

cmd: rg 'Order placed|Thank you|checkout.confirmation|Confirmation sent|payment_method|PayPal|bank transfer' tests resources app specs -n

query: Order placed|Thank you|checkout.confirmation|Confirmation sent|payment_method|PayPal|bank transfer

path: tests

aggregated_output:
specs/01-DATABASE-SCHEMA.md:588:| payment_method | TEXT | Yes | NULL | CHECK IN ('credit_card', 'paypal', 'bank_transfer') | Selected payment method |
specs/01-DATABASE-SCHEMA.md:761:| payment_method | TEXT | No | - | CHECK IN ('credit_card', 'paypal', 'bank_transfer') | Payment method used for this order |
specs/01-DATABASE-SCHEMA.md:1406:        TEXT payment_method
specs/06-AUTH-AND-SECURITY.md:937:> **Not applicable.** All payment processing happens in-process via the Mock PSP. There are no inbound payment webhooks from external providers (no Stripe, no PayPal). This section is preserved for reference only -- if a real PSP is integrated in the future, inbound webhook verification would follow the patterns described in the outbound webhook section below (HMAC signature verification).
tests/Feature/Orders/OrderServiceTest.php:268:test('bank transfer completion creates a pending order and keeps inventory reserved', function () {
tests/Feature/Orders/OrderServiceTest.php:304:        ->and($checkout->payment_method)->toBeNull();
tests/Feature/Orders/BankTransferOrderTest.php:55:        'payment_method' => PaymentMethod::BankTransfer,
tests/Feature/Orders/BankTransferOrderTest.php:80:test('confirming bank transfer payment captures payment and commits reserved inventory', function () {
tests/Feature/Orders/BankTransferOrderTest.php:97:test('confirming digital bank transfer payment auto-fulfills the order', function () {
tests/Feature/Orders/BankTransferOrderTest.php:108:test('cancel job voids stale bank transfer orders and releases reservations', function () {
specs/02-API-ROUTES.md:439:    C->>API: POST /checkouts/{id}/pay (payment_method + details)
specs/02-API-ROUTES.md:443:    alt Credit Card / PayPal (instant capture)
specs/02-API-ROUTES.md:454:        API-->>C: 200 (order confirmation + bank transfer instructions)
specs/02-API-ROUTES.md:732:  "payment_method": "credit_card"
specs/02-API-ROUTES.md:738:| `payment_method` | string | Yes | Must be one of: `credit_card`, `paypal`, `bank_transfer`. |
specs/02-API-ROUTES.md:748:  "payment_method": "credit_card",
specs/02-API-ROUTES.md:859:  "payment_method": "credit_card",
specs/02-API-ROUTES.md:867:**Request body (PayPal):**
specs/02-API-ROUTES.md:871:  "payment_method": "paypal"
specs/02-API-ROUTES.md:879:  "payment_method": "bank_transfer"
specs/02-API-ROUTES.md:885:| `payment_method` | string | Yes | Must be one of: `credit_card`, `paypal`, `bank_transfer`. |
specs/02-API-ROUTES.md:886:| `card_number` | string | Conditional | Required when `payment_method = credit_card`. Digits only (spaces stripped). |
specs/02-API-ROUTES.md:887:| `card_expiry` | string | Conditional | Required when `payment_method = credit_card`. Format: MM/YY. |
specs/02-API-ROUTES.md:888:| `card_cvc` | string | Conditional | Required when `payment_method = credit_card`. 3-4 digits. |
specs/02-API-ROUTES.md:889:| `card_holder` | string | Conditional | Required when `payment_method = credit_card`. Max 255 chars. |
specs/02-API-ROUTES.md:891:**Success response (Credit Card / PayPal - instant capture):** `200 OK`
specs/02-API-ROUTES.md:902:    "payment_method": "credit_card",
specs/02-API-ROUTES.md:920:    "payment_method": "bank_transfer",
tests/Feature/Api/StorefrontCheckoutApiTest.php:131:            'payment_method' => 'credit_card',
tests/Feature/Api/StorefrontCheckoutApiTest.php:135:        ->assertJsonPath('data.payment_method', 'credit_card');
specs/09-IMPLEMENTATION-ROADMAP.md:25:| Payments | Stripe + PayPal | Mock PSP (in-process) | No external API calls. MockPaymentProvider handles credit card, PayPal, bank transfer with magic card numbers for testing. |
specs/09-IMPLEMENTATION-ROADMAP.md:674:- Credit card / PayPal: instant capture, returns PaymentResult with status=captured
specs/09-IMPLEMENTATION-ROADMAP.md:689:1. Validate order has `payment_method = bank_transfer` and `financial_status = pending`
specs/09-IMPLEMENTATION-ROADMAP.md:732:**Fulfillment guard:** Before creating a fulfillment, the service checks that the order's `financial_status` is `paid` or `partially_refunded`. If the guard fails, a `FulfillmentGuardException` is thrown. This prevents shipping items before payment is confirmed (especially important for bank transfer orders).
specs/09-IMPLEMENTATION-ROADMAP.md:1429:| fulfillment guard blocks fulfillment when financial_status is pending | order with financial_status=pending (bank transfer) | FulfillmentGuardException thrown | validation |
specs/09-IMPLEMENTATION-ROADMAP.md:1444:| charges PayPal successfully | checkout with payment_method=paypal | PaymentResult success=true, status=captured | happy path |
specs/09-IMPLEMENTATION-ROADMAP.md:1445:| creates pending payment for bank transfer | checkout with payment_method=bank_transfer | PaymentResult success=true, status=pending | happy path |
specs/09-IMPLEMENTATION-ROADMAP.md:1455:| processes PayPal payment and creates order as paid | checkout with PayPal | order created with financial_status=paid, inventory committe…14675 chars truncated…eckout->payment_method);
app/Services/OrderService.php:395:                'payment_method' => null,
app/Services/OrderService.php:417:        if ($order->payment_method !== PaymentMethod::BankTransfer || $order->financial_status !== FinancialStatus::Pending) {
app/Services/OrderService.php:418:            throw InvalidOrderOperationException::because('Order is not a pending bank transfer order.');
resources/views/livewire/storefront/checkout/confirmation.blade.php:11:                <flux:badge color="green">Order placed</flux:badge>
resources/views/livewire/storefront/checkout/confirmation.blade.php:16:                    Confirmation sent to {{ $order->email }}.
resources/views/livewire/storefront/checkout/confirmation.blade.php:20:            @if ($order->payment_method === \App\Enums\PaymentMethod::BankTransfer)
app/Http/Controllers/Api/Storefront/V1/CheckoutController.php:115:                (string) $request->validated('payment_method'),
app/Http/Controllers/Api/Storefront/V1/CheckoutController.php:125:            if ($checkout->payment_method !== $request->validated('payment_method')) {
app/Http/Controllers/Api/Storefront/V1/CheckoutController.php:126:                $checkout = $checkouts->selectPaymentMethod($checkout, (string) $request->validated('payment_method'));
specs/08-PLAYWRIGHT-E2E-PLAN.md:19:| 9 - Checkout Flow | `tests/Browser/Storefront/CheckoutTest.php` | 13 | Full checkout: contact, address, shipping, payment method selection, payment, confirmation, magic card decline, bank transfer flow | Products, Shipping zones, Discounts |
specs/08-PLAYWRIGHT-E2E-PLAN.md:139:| Phase 5 | Suite 9 (Checkout) | Depends on cart flow working. Tests payment method selection (credit card, PayPal, bank transfer), magic card declines, and bank transfer instructions. |
specs/08-PLAYWRIGHT-E2E-PLAN.md:830:### Test 4.8: Can confirm bank transfer payment
specs/08-PLAYWRIGHT-E2E-PLAN.md:832:**Preconditions:** Logged in as admin, Order #1005 exists with payment_method=bank_transfer and financial_status=pending
specs/08-PLAYWRIGHT-E2E-PLAN.md:1706:    Step4 -->|select PayPal, click 'Pay with PayPal'| Confirmation
specs/08-PLAYWRIGHT-E2E-PLAN.md:1727:| 4. Payment | Payment section | Select payment method (Credit Card / PayPal / Bank Transfer), fill card details if credit card | Redirect to confirmation |
specs/08-PLAYWRIGHT-E2E-PLAN.md:1728:| 5. Confirmation | Confirmation page | n/a (read only) | "Thank you", order number, line items, totals. Bank transfer: also shows transfer instructions. |
specs/08-PLAYWRIGHT-E2E-PLAN.md:1784:- Confirmation page displays "Thank you"
specs/08-PLAYWRIGHT-E2E-PLAN.md:1960:### Test 9.9: Completes checkout with PayPal
specs/08-PLAYWRIGHT-E2E-PLAN.md:1972:8. Click "PayPal" radio button to select PayPal as payment method
specs/08-PLAYWRIGHT-E2E-PLAN.md:1973:9. Click "Pay with PayPal"
specs/08-PLAYWRIGHT-E2E-PLAN.md:1976:- Confirmation page displays "Thank you"
specs/08-PLAYWRIGHT-E2E-PLAN.md:1977:- Confirmation page displays "PayPal" in payment method section
specs/08-PLAYWRIGHT-E2E-PLAN.md:1982:### Test 9.10: Completes checkout with bank transfer
specs/08-PLAYWRIGHT-E2E-PLAN.md:1998:- Confirmation page displays "Thank you"
specs/08-PLAYWRIGHT-E2E-PLAN.md:1999:- Confirmation page displays bank transfer instructions (IBAN, BIC, reference)
specs/08-PLAYWRIGHT-E2E-PLAN.md:2063:3. Click "PayPal" radio button
specs/08-PLAYWRIGHT-E2E-PLAN.md:2065:5. Verify "Pay with PayPal" button is visible
specs/08-PLAYWRIGHT-E2E-PLAN.md:2068:8. Verify bank transfer information text is displayed
app/Services/CheckoutService.php:157:                'payment_method' => $paymentMethod,
app/Models/Order.php:28:        'payment_method',
app/Models/Order.php:121:            'payment_method' => PaymentMethod::class,
tests/Feature/Storefront/OrderViewsTest.php:84:    $component->assertRedirect(route('checkout.confirmation', $order));
app/Jobs/CancelUnpaidBankTransferOrders.php:23:            ->where('payment_method', PaymentMethod::BankTransfer->value)
app/Http/Resources/Storefront/V1/OrderResource.php:27:            'payment_method' => $this->payment_method?->value,
app/Http/Resources/Storefront/V1/OrderResource.php:61:            'bank_transfer_instructions' => $this->payment_method === PaymentMethod::BankTransfer ? [
tests/Feature/Payments/MockPaymentProviderTest.php:31:test('mock provider captures paypal and leaves bank transfer pending', function () {
app/Models/Checkout.php:24:        'payment_method',
app/Http/Resources/Storefront/V1/CheckoutResource.php:23:            'payment_method' => $this->payment_method,
app/Livewire/Storefront/Checkout/Confirmation.php:46:        return view('livewire.storefront.checkout.confirmation', [
app/Livewire/Storefront/Checkout/Show.php:243:            $this->redirectRoute('checkout.confirmation', ['order' => $order], navigate: true);
app/Livewire/Storefront/Checkout/Show.php:408:        $this->paymentMethod = (string) ($checkout->payment_method ?? $this->paymentMethod);


output:
Chunk ID: 605c40
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 6169
Output:
specs/01-DATABASE-SCHEMA.md:588:| payment_method | TEXT | Yes | NULL | CHECK IN ('credit_card', 'paypal', 'bank_transfer') | Selected payment method |
specs/01-DATABASE-SCHEMA.md:761:| payment_method | TEXT | No | - | CHECK IN ('credit_card', 'paypal', 'bank_transfer') | Payment method used for this order |
specs/01-DATABASE-SCHEMA.md:1406:        TEXT payment_method
specs/06-AUTH-AND-SECURITY.md:937:> **Not applicable.** All payment processing happens in-process via the Mock PSP. There are no inbound payment webhooks from external providers (no Stripe, no PayPal). This section is preserved for reference only -- if a real PSP is integrated in the future, inbound webhook verification would follow the patterns described in the outbound webhook section below (HMAC signature verification).
tests/Feature/Orders/OrderServiceTest.php:268:test('bank transfer completion creates a pending order and keeps inventory reserved', function () {
tests/Feature/Orders/OrderServiceTest.php:304:        ->and($checkout->payment_method)->toBeNull();
tests/Feature/Orders/BankTransferOrderTest.php:55:        'payment_method' => PaymentMethod::BankTransfer,
tests/Feature/Orders/BankTransferOrderTest.php:80:test('confirming bank transfer payment captures payment and commits reserved inventory', function () {
tests/Feature/Orders/BankTransferOrderTest.php:97:test('confirming digital bank transfer payment auto-fulfills the order', function () {
tests/Feature/Orders/BankTransferOrderTest.php:108:test('cancel job voids stale bank transfer orders and releases reservations', function () {
specs/02-API-ROUTES.md:439:    C->>API: POST /checkouts/{id}/pay (payment_method + details)
specs/02-API-ROUTES.md:443:    alt Credit Card / PayPal (instant capture)
specs/02-API-ROUTES.md:454:        API-->>C: 200 (order confirmation + bank transfer instructions)
specs/02-API-ROUTES.md:732:  "payment_method": "credit_card"
specs/02-API-ROUTES.md:738:| `payment_method` | string | Yes | Must be one of: `credit_card`, `paypal`, `bank_transfer`. |
specs/02-API-ROUTES.md:748:  "payment_method": "credit_card",
specs/02-API-ROUTES.md:859:  "payment_method": "credit_card",
specs/02-API-ROUTES.md:867:**Request body (PayPal):**
specs/02-API-ROUTES.md:871:  "payment_method": "paypal"
specs/02-API-ROUTES.md:879:  "payment_method": "bank_transfer"
specs/02-API-ROUTES.md:885:| `payment_method` | string | Yes | Must be one of: `credit_card`, `paypal`, `bank_transfer`. |
specs/02-API-ROUTES.md:886:| `card_number` | string | Conditional | Required when `payment_method = credit_card`. Digits only (spaces stripped). |
specs/02-API-ROUTES.md:887:| `card_expiry` | string | Conditional | Required when `payment_method = credit_card`. Format: MM/YY. |
specs/02-API-ROUTES.md:888:| `card_cvc` | string | Conditional | Required when `payment_method = credit_card`. 3-4 digits. |
specs/02-API-ROUTES.md:889:| `card_holder` | string | Conditional | Required when `payment_method = credit_card`. Max 255 chars. |
specs/02-API-ROUTES.md:891:**Success response (Credit Card / PayPal - instant capture):** `200 OK`
specs/02-API-ROUTES.md:902:    "payment_method": "credit_card",
specs/02-API-ROUTES.md:920:    "payment_method": "bank_transfer",
tests/Feature/Api/StorefrontCheckoutApiTest.php:131:            'payment_method' => 'credit_card',
tests/Feature/Api/StorefrontCheckoutApiTest.php:135:        ->assertJsonPath('data.payment_method', 'credit_card');
specs/09-IMPLEMENTATION-ROADMAP.md:25:| Payments | Stripe + PayPal | Mock PSP (in-process) | No external API calls. MockPaymentProvider handles credit card, PayPal, bank transfer with magic card numbers for testing. |
specs/09-IMPLEMENTATION-ROADMAP.md:674:- Credit card / PayPal: instant capture, returns PaymentResult with status=captured
specs/09-IMPLEMENTATION-ROADMAP.md:689:1. Validate order has `payment_method = bank_transfer` and `financial_status = pending`
specs/09-IMPLEMENTATION-ROADMAP.md:732:**Fulfillment guard:** Before creating a fulfillment, the service checks that the order's `financial_status` is `paid` or `partially_refunded`. If the guard fails, a `FulfillmentGuardException` is thrown. This prevents shipping items before payment is confirmed (especially important for bank transfer orders).
specs/09-IMPLEMENTATION-ROADMAP.md:1429:| fulfillment guard blocks fulfillment when financial_status is pending | order with financial_status=pending (bank transfer) | FulfillmentGuardException thrown | validation |
specs/09-IMPLEMENTATION-ROADMAP.md:1444:| charges PayPal successfully | checkout with payment_method=paypal | PaymentResult success=true, status=captured | happy path |
specs/09-IMPLEMENTATION-ROADMAP.md:1445:| creates pending payment for bank transfer | checkout with payment_method=bank_transfer | PaymentResult success=true, status=pending | happy path |
specs/09-IMPLEMENTATION-ROADMAP.md:1455:| processes PayPal payment and creates order as paid | checkout with PayPal | order created with financial_status=paid, inventory committed | happy path |
specs/09-IMPLEMENTATION-ROADMAP.md:1456:| processes bank transfer and creates order as pending | checkout with bank transfer | order created with financial_status=pending, inventory reserved but not committed | happy path |
specs/09-IMPLEMENTATION-ROADMAP.md:1466:| admin can confirm bank transfer payment | order with payment_method=bank_transfer, financial_status=pending | financial_status transitions to paid, inventory committed | happy path |
specs/09-IMPLEMENTATION-ROADMAP.md:1467:| cannot confirm payment for non-bank-transfer orders | order with payment_method=credit_card | error thrown | validation |
specs/09-IMPLEMENTATION-ROADMAP.md:1469:| auto-cancel job cancels unpaid bank transfer orders after config days | order with payment_method=bank_transfer, placed_at > 7 days ago | order cancelled, inventory released | happy path |
specs/09-IMPLEMENTATION-ROADMAP.md:1470:| auto-cancel job does not cancel orders within config days | order with payment_method=bank_transfer, placed_at = 2 days ago | order remains pending | edge case |
specs/09-IMPLEMENTATION-ROADMAP.md:1657:| selects a payment method | PUT /api/storefront/v1/checkouts/{id}/payment-method with method=credit_card | 200 response, status=payment_selected, payment_method stored | happy path |
specs/09-IMPLEMENTATION-ROADMAP.md:1983:| `Payments\MockPaymentProvider` | In-process mock PSP: credit card (magic numbers), PayPal, bank transfer |
specs/07-SEEDERS-AND-TEST-DATA.md:334:| payment_method | null |
specs/07-SEEDERS-AND-TEST-DATA.md:346:| `withCreditCard` | payment_method = "credit_card" |
specs/07-SEEDERS-AND-TEST-DATA.md:347:| `withPaypal` | payment_method = "paypal" |
specs/07-SEEDERS-AND-TEST-DATA.md:348:| `withBankTransfer` | payment_method = "bank_transfer" |
specs/07-SEEDERS-AND-TEST-DATA.md:363:| payment_method | "credit_card" |
specs/07-SEEDERS-AND-TEST-DATA.md:383:| `pendingBankTransfer` | status = "pending", financial_status = "pending", payment_method = "bank_transfer" |
specs/07-SEEDERS-AND-TEST-DATA.md:388:| `creditCard` | payment_method = "credit_card" |
specs/07-SEEDERS-AND-TEST-DATA.md:389:| `paypal` | payment_method = "paypal" |
specs/07-SEEDERS-AND-TEST-DATA.md:390:| `bankTransfer` | payment_method = "bank_transfer" |
specs/07-SEEDERS-AND-TEST-DATA.md:1512:| payment_method | credit_card |
specs/07-SEEDERS-AND-TEST-DATA.md:1535:| payment_method | credit_card |
specs/07-SEEDERS-AND-TEST-DATA.md:1559:| payment_method | credit_card |
specs/07-SEEDERS-AND-TEST-DATA.md:1583:| payment_method | credit_card |
specs/07-SEEDERS-AND-TEST-DATA.md:1606:| payment_method | bank_transfer |
specs/07-SEEDERS-AND-TEST-DATA.md:1620:**Test purpose:** Tests the bank transfer flow - admin "Confirm Payment" button, fulfillment guard (cannot fulfill until payment confirmed), and auto-cancel job scenario.
specs/07-SEEDERS-AND-TEST-DATA.md:1629:| payment_method | credit_card |
specs/07-SEEDERS-AND-TEST-DATA.md:1645:**Order #1007 - Multi-item delivered (PayPal)**
specs/07-SEEDERS-AND-TEST-DATA.md:1650:| payment_method | paypal |
specs/07-SEEDERS-AND-TEST-DATA.md:1674:| payment_method | credit_card |
specs/07-SEEDERS-AND-TEST-DATA.md:1700:| payment_method | credit_card |
specs/07-SEEDERS-AND-TEST-DATA.md:1717:**Order #1010 - High-value order (PayPal)**
specs/07-SEEDERS-AND-TEST-DATA.md:1722:| payment_method | paypal |
specs/07-SEEDERS-AND-TEST-DATA.md:1743:| payment_method | credit_card |
specs/07-SEEDERS-AND-TEST-DATA.md:1766:| payment_method | credit_card |
specs/07-SEEDERS-AND-TEST-DATA.md:1787:| payment_method | credit_card |
specs/07-SEEDERS-AND-TEST-DATA.md:1809:| payment_method | credit_card |
specs/07-SEEDERS-AND-TEST-DATA.md:1832:| payment_method | bank_transfer |
specs/07-SEEDERS-AND-TEST-DATA.md:1849:**Test purpose:** Tests a bank transfer order that has been confirmed by the admin (payment status = captured). Contrasts with #1005 which is still pending.
specs/07-SEEDERS-AND-TEST-DATA.md:1863:| #1007 | Sarah Wilson | paypal | fulfilled | paid | fulfilled | 10496 | Multi-item delivered (PayPal) |
specs/07-SEEDERS-AND-TEST-DATA.md:1866:| #1010 | John Doe | paypal | paid | paid | unfulfilled | 50498 | High-value order (PayPal) |
specs/07-SEEDERS-AND-TEST-DATA.md:1871:| #1015 | John Doe | bank_transfer | paid | paid | unfulfilled | 5447 | Order with discount (bank transfer, confirmed) |
specs/07-SEEDERS-AND-TEST-DATA.md:1881:| #5003 | techfan@example.com | bank_transfer | pending | pending | unfulfilled | 4999 | Monitor Stand (4999), shipping 0, bank transfer pending |
resources/views/livewire/admin/orders/show.blade.php:15:            @if ($order->payment_method === \App\Enums\PaymentMethod::BankTransfer && $order->financial_status === \App\Enums\FinancialStatus::Pending)
specs/03-ADMIN-UI.md:960:| `confirmPayment()` | Confirms bank transfer payment received (transitions financial_status to `paid`, commits inventory, dispatches `OrderPaid`) |
specs/03-ADMIN-UI.md:980:| o  Order placed - Jan 15, 2:30 PM           | 123 Main St        |
specs/03-ADMIN-UI.md:1017:- "Confirm payment" button (primary, shown only when `payment_method = bank_transfer` AND `financial_status = pending`). On click, calls `confirmPayment()` which transitions the order to `paid`, commits inventory, and dispatches `OrderPaid`.
specs/03-ADMIN-UI.md:1032:- Events include: Order placed, Payment received, Fulfillment created, Shipped, Delivered, Refunded (as applicable)
specs/03-ADMIN-UI.md:1051:- Payment method as text (Credit Card / PayPal / Bank Transfer), derived from `order.payment_method` enum
specs/03-ADMIN-UI.md:1055:- **"Confirm payment" button** (primary variant): Shown only when `payment_method = bank_transfer` AND `financial_status = pending`. Clicking calls `confirmPayment()`.
app/Http/Requests/Api/Storefront/V1/SelectCheckoutPaymentRequest.php:25:            'payment_method' => ['required', 'in:credit_card,paypal,bank_transfer'],
specs/progress.md:30:| Routes/API | `specs/02-API-ROUTES.md` | partial | Phase 1 auth routes plus catalog/cart/order/customer/discount/content/settings/search/analytics/apps/developers UI routes exist: `/`, `/collections`, `/collections/{handle}`, `/products/{handle}`, `/cart`, `/checkout`, `/checkout/confirmation/{order}`, `/search`, `/pages/{handle}`, `/account/login`, `/account/register`, `/forgot-password`, `/reset-password/{token}`, `/account/forgot-password`, `/account/reset-password/{token}`, `/account`, `/account/orders/{order}`, `/admin/login`, `/admin/forgot-password`, `/admin/reset-password/{token}`, `/admin/products`, `/admin/products/create`, `/admin/products/{product}/edit`, `/admin/collections`, `/admin/collections/create`, `/admin/collections/{collection}/edit`, `/admin/inventory`, `/admin/orders`, `/admin/orders/{order}`, `/admin/customers`, `/admin/customers/{customer}`, `/admin/discounts`, `/admin/discounts/create`, `/admin/discounts/{discount}/edit`, `/admin/pages`, `/admin/pages/create`, `/admin/pages/{page}/edit`, `/admin/navigation`, `/admin/themes`, `/admin/themes/{theme}/editor`, `/admin/settings`, `/admin/settings/shipping`, `/admin/settings/taxes`, `/admin/settings/checkout`, `/admin/settings/notifications`, `/admin/search/settings`, `/admin/analytics`, `/admin/apps`, `/admin/apps/{installation}`, and `/admin/developers`. Storefront REST API now includes cart line CRUD, checkout address/shipping/discount/payment-method selection, checkout payment completion, token-gated order lookup, product search, search suggestions, and analytics event ingestion under `/api/storefront/v1`. Admin REST API now includes store-scoped product/customer/order list/detail, collection CRUD, discount CRUD, content page CRUD, store settings read/update, search status/reindex endpoints, analytics summary, order export creation/status, shipping zone/rate settings, tax settings read/update, theme upload/publish/settings update, fulfillment creation, and refund creation under `/api/admin/v1/stores/{store}` with session or scoped bearer-token authentication. `/oauth/authorize`, `/oauth/token`, and `/api/apps/v1/stores/{store}` app routes are registered as explicit `501 Not Implemented` stubs per the deferred OAuth/app ecosystem scope note. |
specs/progress.md:125:- 2026-05-04: `npm run build` passed after checkout confirmation and customer account Blade changes.
specs/progress.md:128:- 2026-05-04: Playwright MCP verified product add-to-cart through checkout order completion, `http://shop.test/checkout/confirmation/1`, `http://shop.test/account`, and `http://shop.test/account/orders/1`; order number, line item, totals, payment/fulfillment badges, and account order history rendered with no console warnings/errors.
app/Http/Requests/Api/Storefront/V1/CompleteCheckoutPaymentRequest.php:20:            'payment_method' => ['required', 'in:credit_card,paypal,bank_transfer'],
app/Http/Requests/Api/Storefront/V1/CompleteCheckoutPaymentRequest.php:21:            'card_number' => ['required_if:payment_method,credit_card', 'string'],
app/Http/Requests/Api/Storefront/V1/CompleteCheckoutPaymentRequest.php:22:            'card_holder' => ['required_if:payment_method,credit_card', 'string', 'max:255'],
app/Http/Requests/Api/Storefront/V1/CompleteCheckoutPaymentRequest.php:23:            'card_expiry' => ['required_if:payment_method,credit_card', 'string', 'max:20'],
app/Http/Requests/Api/Storefront/V1/CompleteCheckoutPaymentRequest.php:24:            'card_cvc' => ['required_if:payment_method,credit_card', 'string', 'max:10'],
tests/Feature/Api/StorefrontOrderApiTest.php:101:            'payment_method' => 'credit_card',
tests/Feature/Api/StorefrontOrderApiTest.php:133:            'payment_method' => 'credit_card',
specs/05-BUSINESS-LOGIC.md:497:1. Store `payment_method` on the checkout record.
specs/05-BUSINESS-LOGIC.md:527:       IF checkout.payment_method IN ('credit_card', 'paypal'):
specs/05-BUSINESS-LOGIC.md:532:       ELSE IF checkout.payment_method = 'bank_transfer':
specs/05-BUSINESS-LOGIC.md:540:       - payment_method = checkout.payment_method
specs/05-BUSINESS-LOGIC.md:553:       - Link to order, provider = 'mock', method = checkout.payment_method
specs/05-BUSINESS-LOGIC.md:952:| `paypal` | Simulates PayPal approval and immediate capture. Always succeeds (no magic numbers). | Auto-capture on checkout (instant) |
specs/05-BUSINESS-LOGIC.md:957:When payment_method is `credit_card`, the mock provider inspects the card number submitted in the checkout form to determine the outcome:
specs/05-BUSINESS-LOGIC.md:978:    Storefront->>Backend: POST /checkouts/{id}/pay (payment_method, card details if applicable)
specs/05-BUSINESS-LOGIC.md:982:    alt Credit Card / PayPal (instant capture)
specs/05-BUSINESS-LOGIC.md:996:        Backend-->>Storefront: 200 OK (order confirmation with bank transfer instructions)
specs/05-BUSINESS-LOGIC.md:1006:    [*] --> captured : Credit card / PayPal (success)
specs/05-BUSINESS-LOGIC.md:1016:When `payment_method = bank_transfer`, the order confirmation page and email display mock bank transfer details:
specs/05-BUSINESS-LOGIC.md:1030:Admin users can confirm that a bank transfer payment has been received via a "Confirm Payment" button on the order detail page (visible only when `payment_method = bank_transfer` AND `financial_status = pending`).
specs/05-BUSINESS-LOGIC.md:1046:- Finds orders where `payment_method = bank_transfer` AND `financial_status = pending` AND `placed_at < (now - configurable days)`.
specs/05-BUSINESS-LOGIC.md:1069:- The `payment_method` from the checkout is copied to the order record.
specs/05-BUSINESS-LOGIC.md:1177:| `pending` | **No** | Payment not yet received (e.g., bank transfer awaiting confirmation) |
specs/05-BUSINESS-LOGIC.md:1262:For bank transfer orders, inventory is held in a reserved state until the admin confirms payment. If the order is auto-cancelled (see Section 10.8), the reserved inventory is released back.
specs/05-BUSINESS-LOGIC.md:1604:| `OrderPaid` | Update order status, commit inventory (bank transfer confirmation), auto-fulfill digital products |
specs/05-BUSINESS-LOGIC.md:1621:| `App\Jobs\CancelUnpaidBankTransferOrders` | Daily | Cancel bank transfer orders that remain unpaid after configurable days (default: 7) |
specs/05-BUSINESS-LOGIC.md:1650:| MockPaymentProvider | `App\Services\Payment` | Mock PSP - simulates card/PayPal/bank transfer payments in-process |
app/Http/Resources/Admin/V1/OrderResource.php:30:            'payment_method' => $this->payment_method?->value,
resources/views/livewire/storefront/checkout/show.blade.php:162:                                <flux:select.option value="paypal">PayPal</flux:select.option>
specs/04-STOREFRONT-UI.md:206:| Payment method icons | Row of SVG icons for accepted payment methods (Visa, Mastercard, Amex, PayPal, etc.). Slightly reduced opacity |
specs/04-STOREFRONT-UI.md:1053:| ( ) PayPal                                                    |
specs/04-STOREFRONT-UI.md:1083:**PayPal form (shown when PayPal is selected):**
specs/04-STOREFRONT-UI.md:1087:| Your PayPal payment will be processed securely.               |
specs/04-STOREFRONT-UI.md:1089:| [Pay with PayPal - 29.98 EUR]                                |
specs/04-STOREFRONT-UI.md:1093:No additional form fields. The button text changes to "Pay with PayPal - {total}".
specs/04-STOREFRONT-UI.md:1099:| After placing your order, you will receive bank transfer      |
specs/04-STOREFRONT-UI.md:1114:| Success (bank_transfer) | On successful order placement, redirect to confirmation page (which shows bank transfer instructions). |
specs/04-STOREFRONT-UI.md:1169:|  "Thank you for your order!"                                  |
specs/04-STOREFRONT-UI.md:1178:|  123 Main St               |  (or: PayPal / Bank Transfer)    |
specs/04-STOREFRONT-UI.md:1204:| Payment method display | Shows "Credit Card ending in {last4}", "PayPal", or "Bank Transfer" depending on `order.payment_method` |
specs/04-STOREFRONT-UI.md:1207:#### Bank Transfer Instructions (shown only when `payment_method = bank_transfer`)
specs/04-STOREFRONT-UI.md:1922:| Order placed | "Thank you for your order!" | success |
tests/Feature/Admin/OrderManagementTest.php:85:        'method' => $order->payment_method,
tests/Feature/Admin/OrderManagementTest.php:136:        'payment_method' => PaymentMethod::BankTransfer,
tests/Feature/Admin/OrderManagementTest.php:152:test('admin order detail confirms bank transfer payments', function () {
tests/Feature/Admin/OrderManagementTest.php:156:        'payment_method' => PaymentMethod::BankTransfer,
app/Services/OrderService.php:84:                    'payment_method' => $method,
app/Services/OrderService.php:221:            return PaymentMethod::from((string) $checkout->payment_method);
app/Services/OrderService.php:395:                'payment_method' => null,
app/Services/OrderService.php:417:        if ($order->payment_method !== PaymentMethod::BankTransfer || $order->financial_status !== FinancialStatus::Pending) {
app/Services/OrderService.php:418:            throw InvalidOrderOperationException::because('Order is not a pending bank transfer order.');
resources/views/livewire/storefront/checkout/confirmation.blade.php:11:                <flux:badge color="green">Order placed</flux:badge>
resources/views/livewire/storefront/checkout/confirmation.blade.php:16:                    Confirmation sent to {{ $order->email }}.
resources/views/livewire/storefront/checkout/confirmation.blade.php:20:            @if ($order->payment_method === \App\Enums\PaymentMethod::BankTransfer)
app/Http/Controllers/Api/Storefront/V1/CheckoutController.php:115:                (string) $request->validated('payment_method'),
app/Http/Controllers/Api/Storefront/V1/CheckoutController.php:125:            if ($checkout->payment_method !== $request->validated('payment_method')) {
app/Http/Controllers/Api/Storefront/V1/CheckoutController.php:126:                $checkout = $checkouts->selectPaymentMethod($checkout, (string) $request->validated('payment_method'));
specs/08-PLAYWRIGHT-E2E-PLAN.md:19:| 9 - Checkout Flow | `tests/Browser/Storefront/CheckoutTest.php` | 13 | Full checkout: contact, address, shipping, payment method selection, payment, confirmation, magic card decline, bank transfer flow | Products, Shipping zones, Discounts |
specs/08-PLAYWRIGHT-E2E-PLAN.md:139:| Phase 5 | Suite 9 (Checkout) | Depends on cart flow working. Tests payment method selection (credit card, PayPal, bank transfer), magic card declines, and bank transfer instructions. |
specs/08-PLAYWRIGHT-E2E-PLAN.md:830:### Test 4.8: Can confirm bank transfer payment
specs/08-PLAYWRIGHT-E2E-PLAN.md:832:**Preconditions:** Logged in as admin, Order #1005 exists with payment_method=bank_transfer and financial_status=pending
specs/08-PLAYWRIGHT-E2E-PLAN.md:1706:    Step4 -->|select PayPal, click 'Pay with PayPal'| Confirmation
specs/08-PLAYWRIGHT-E2E-PLAN.md:1727:| 4. Payment | Payment section | Select payment method (Credit Card / PayPal / Bank Transfer), fill card details if credit card | Redirect to confirmation |
specs/08-PLAYWRIGHT-E2E-PLAN.md:1728:| 5. Confirmation | Confirmation page | n/a (read only) | "Thank you", order number, line items, totals. Bank transfer: also shows transfer instructions. |
specs/08-PLAYWRIGHT-E2E-PLAN.md:1784:- Confirmation page displays "Thank you"
specs/08-PLAYWRIGHT-E2E-PLAN.md:1960:### Test 9.9: Completes checkout with PayPal
specs/08-PLAYWRIGHT-E2E-PLAN.md:1972:8. Click "PayPal" radio button to select PayPal as payment method
specs/08-PLAYWRIGHT-E2E-PLAN.md:1973:9. Click "Pay with PayPal"
specs/08-PLAYWRIGHT-E2E-PLAN.md:1976:- Confirmation page displays "Thank you"
specs/08-PLAYWRIGHT-E2E-PLAN.md:1977:- Confirmation page displays "PayPal" in payment method section
specs/08-PLAYWRIGHT-E2E-PLAN.md:1982:### Test 9.10: Completes checkout with bank transfer
specs/08-PLAYWRIGHT-E2E-PLAN.md:1998:- Confirmation page displays "Thank you"
specs/08-PLAYWRIGHT-E2E-PLAN.md:1999:- Confirmation page displays bank transfer instructions (IBAN, BIC, reference)
specs/08-PLAYWRIGHT-E2E-PLAN.md:2063:3. Click "PayPal" radio button
specs/08-PLAYWRIGHT-E2E-PLAN.md:2065:5. Verify "Pay with PayPal" button is visible
specs/08-PLAYWRIGHT-E2E-PLAN.md:2068:8. Verify bank transfer information text is displayed
app/Services/CheckoutService.php:157:                'payment_method' => $paymentMethod,
app/Models/Order.php:28:        'payment_method',
app/Models/Order.php:121:            'payment_method' => PaymentMethod::class,
tests/Feature/Storefront/OrderViewsTest.php:84:    $component->assertRedirect(route('checkout.confirmation', $order));
app/Jobs/CancelUnpaidBankTransferOrders.php:23:            ->where('payment_method', PaymentMethod::BankTransfer->value)
app/Http/Resources/Storefront/V1/OrderResource.php:27:            'payment_method' => $this->payment_method?->value,
app/Http/Resources/Storefront/V1/OrderResource.php:61:            'bank_transfer_instructions' => $this->payment_method === PaymentMethod::BankTransfer ? [
tests/Feature/Payments/MockPaymentProviderTest.php:31:test('mock provider captures paypal and leaves bank transfer pending', function () {
app/Models/Checkout.php:24:        'payment_method',
app/Http/Resources/Storefront/V1/CheckoutResource.php:23:            'payment_method' => $this->payment_method,
app/Livewire/Storefront/Checkout/Confirmation.php:46:        return view('livewire.storefront.checkout.confirmation', [
app/Livewire/Storefront/Checkout/Show.php:243:            $this->redirectRoute('checkout.confirmation', ['order' => $order], navigate: true);
app/Livewire/Storefront/Checkout/Show.php:408:        $this->paymentMethod = (string) ($checkout->payment_method ?? $this->paymentMethod);


cmd: rg "order_number" app/Services app/Models database -n


command: rg "order_number" app/Services app/Models database -n

cmd: rg order_number app/Services app/Models database -n

path: Services

aggregated_output:
app/Services/OrderExportService.php:70:            'order_number',
app/Services/OrderExportService.php:94:                    $order->order_number,
app/Services/OrderExportService.php:137:                        ->where('order_number', 'like', $like)
database/factories/OrderFactory.php:34:            'order_number' => '#'.fake()->unique()->numberBetween(1001, 9999),
app/Services/OrderService.php:83:                    'order_number' => $this->nextOrderNumber($checkout->store),
app/Services/OrderService.php:233:        $prefix = (string) data_get($settings, 'order_number_prefix', '#');
app/Services/OrderService.php:234:        $start = max(1, (int) data_get($settings, 'order_number_start', 1001));
app/Services/OrderService.php:237:            ->pluck('order_number')
app/Models/Order.php:27:        'order_number',
database/seeders/AnalyticsSeeder.php:136:                'order_number' => 'SEED-'.$index,
database/migrations/2026_05_04_000408_create_orders_table.php:19:            $table->string('order_number');
database/migrations/2026_05_04_000408_create_orders_table.php:36:            $table->unique(['store_id', 'order_number']);


output:
Chunk ID: 69a435
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 287
Output:
app/Services/OrderExportService.php:70:            'order_number',
app/Services/OrderExportService.php:94:                    $order->order_number,
app/Services/OrderExportService.php:137:                        ->where('order_number', 'like', $like)
database/factories/OrderFactory.php:34:            'order_number' => '#'.fake()->unique()->numberBetween(1001, 9999),
app/Services/OrderService.php:83:                    'order_number' => $this->nextOrderNumber($checkout->store),
app/Services/OrderService.php:233:        $prefix = (string) data_get($settings, 'order_number_prefix', '#');
app/Services/OrderService.php:234:        $start = max(1, (int) data_get($settings, 'order_number_start', 1001));
app/Services/OrderService.php:237:            ->pluck('order_number')
app/Models/Order.php:27:        'order_number',
database/seeders/AnalyticsSeeder.php:136:                'order_number' => 'SEED-'.$index,
database/migrations/2026_05_04_000408_create_orders_table.php:19:            $table->string('order_number');
database/migrations/2026_05_04_000408_create_orders_table.php:36:            $table->unique(['store_id', 'order_number']);


cmd: sed -n '1,180p' app/Services/Payment/MockPaymentProvider.php && sed -n '130,190p' app/Services/CheckoutService.php


command: sed -n '1,180p' app/Services/Payment/MockPaymentProvider.php && sed -n '130,190p' app/Services/CheckoutService.php

CheckoutService.php

aggregated_output: sed: app/Services/Payment/MockPaymentProvider.php: No such file or directory

status: failed


output:
Chunk ID: 3aac6d
Wall time: 0.0000 seconds
Process exited with code 1
Original token count: 20
Output:
sed: app/Services/Payment/MockPaymentProvider.php: No such file or directory


cmd: rg "class Mock|PaymentProvider|PaymentFailed" app -n


command: rg "class Mock|PaymentProvider|PaymentFailed" app -n

type: search

cmd: rg 'class Mock|PaymentProvider|PaymentFailed' app -n

query: class Mock|PaymentProvider|PaymentFailed

path: app

aggregated_output:
app/Services/PaymentService.php:5:use App\Contracts\PaymentProvider;
app/Services/PaymentService.php:15:        private readonly PaymentProvider $provider,
app/Services/Payments/MockPaymentProvider.php:5:use App\Contracts\PaymentProvider;
app/Services/Payments/MockPaymentProvider.php:15:class MockPaymentProvider implements PaymentProvider
app/Services/OrderService.php:17:use App\Exceptions\PaymentFailedException;
app/Services/OrderService.php:73:                    throw PaymentFailedException::fromResult($paymentResult);
app/Services/OrderService.php:141:        } catch (PaymentFailedException $exception) {
app/Contracts/PaymentProvider.php:11:interface PaymentProvider
app/Providers/AppServiceProvider.php:6:use App\Contracts\PaymentProvider;
app/Providers/AppServiceProvider.php:24:use App\Services\Payments\MockPaymentProvider;
app/Providers/AppServiceProvider.php:50:        $this->app->bind(PaymentProvider::class, MockPaymentProvider::class);
app/Http/Controllers/Api/Storefront/V1/CheckoutController.php:8:use App\Exceptions\PaymentFailedException;
app/Http/Controllers/Api/Storefront/V1/CheckoutController.php:139:        } catch (PaymentFailedException $exception) {
app/Exceptions/PaymentFailedException.php:8:class PaymentFailedException extends RuntimeException
app/Livewire/Storefront/Checkout/Show.php:8:use App\Exceptions\PaymentFailedException;
app/Livewire/Storefront/Checkout/Show.php:244:        } catch (InvalidCheckoutTransitionException|PaymentFailedException $exception) {


output:
Chunk ID: b23292
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 377
Output:
app/Services/PaymentService.php:5:use App\Contracts\PaymentProvider;
app/Services/PaymentService.php:15:        private readonly PaymentProvider $provider,
app/Services/Payments/MockPaymentProvider.php:5:use App\Contracts\PaymentProvider;
app/Services/Payments/MockPaymentProvider.php:15:class MockPaymentProvider implements PaymentProvider
app/Services/OrderService.php:17:use App\Exceptions\PaymentFailedException;
app/Services/OrderService.php:73:                    throw PaymentFailedException::fromResult($paymentResult);
app/Services/OrderService.php:141:        } catch (PaymentFailedException $exception) {
app/Contracts/PaymentProvider.php:11:interface PaymentProvider
app/Providers/AppServiceProvider.php:6:use App\Contracts\PaymentProvider;
app/Providers/AppServiceProvider.php:24:use App\Services\Payments\MockPaymentProvider;
app/Providers/AppServiceProvider.php:50:        $this->app->bind(PaymentProvider::class, MockPaymentProvider::class);
app/Http/Controllers/Api/Storefront/V1/CheckoutController.php:8:use App\Exceptions\PaymentFailedException;
app/Http/Controllers/Api/Storefront/V1/CheckoutController.php:139:        } catch (PaymentFailedException $exception) {
app/Exceptions/PaymentFailedException.php:8:class PaymentFailedException extends RuntimeException
app/Livewire/Storefront/Checkout/Show.php:8:use App\Exceptions\PaymentFailedException;
app/Livewire/Storefront/Checkout/Show.php:244:        } catch (InvalidCheckoutTransitionException|PaymentFailedException $exception) {


cmd: sed -n '1,180p' app/Services/Payments/MockPaymentProvider.php && sed -n '1,120p' app/Exceptions/PaymentFailedException.php && sed -n '130,185p' app/Services/CheckoutService.php