generated from boboko/starter
payment fix
This commit is contained in:
@@ -61,7 +61,9 @@ public function show(string $locale): View
|
||||
$shippingOptions = collect();
|
||||
|
||||
if ($cart?->shippingAddress) {
|
||||
$shippingOptions = $this->syncShipping($cart);
|
||||
// Nothing recreates the address row in this path — its own current
|
||||
// value is the correct "previous" to carry forward if still valid.
|
||||
$shippingOptions = $this->syncShipping($cart, $cart->shippingAddress->shipping_option);
|
||||
|
||||
// Cart's CachesProperties::refresh() explicitly nulls total/
|
||||
// subTotal/shippingTotal/etc. back to their defaults — every
|
||||
@@ -97,7 +99,13 @@ public function saveAddress(string $locale, Request $request): JsonResponse
|
||||
// Only the fields shipping rates resolve against — if none of these
|
||||
// changed (shopper edited their name, phone, email, …) there's no point
|
||||
// re-quoting shipping or re-rendering the summary.
|
||||
$rateKeyBefore = $this->cart->current()?->shippingAddress?->only(['postcode', 'state', 'country_id']);
|
||||
$addressBefore = $this->cart->current()?->shippingAddress;
|
||||
$rateKeyBefore = $addressBefore?->only(['postcode', 'state', 'country_id']);
|
||||
|
||||
// setShippingAddress() below always deletes + recreates this row (see
|
||||
// syncShipping()'s docblock) — capture what was selected NOW, before
|
||||
// it's gone, so it can be carried forward onto the fresh row.
|
||||
$previousOption = $addressBefore?->shipping_option;
|
||||
|
||||
$stateRule = $storeCountry
|
||||
? ['nullable', 'string', Rule::exists((new State)->getTable(), 'name')->where('country_id', $storeCountry->id)]
|
||||
@@ -195,7 +203,7 @@ public function saveAddress(string $locale, Request $request): JsonResponse
|
||||
// to restore/re-validate it, even on a save that only touched e.g. the
|
||||
// phone number. Only the fragment RE-RENDER is skippable when nothing
|
||||
// rate-relevant moved — the re-select itself is not optional.
|
||||
$options = $this->syncShipping($cart);
|
||||
$options = $this->syncShipping($cart, $previousOption);
|
||||
|
||||
if (! $rateChanged) {
|
||||
return $this->fragments($cart, null, $errors);
|
||||
@@ -265,7 +273,21 @@ public function placeOrder(string $locale, Request $request): JsonResponse
|
||||
return response()->json(['error' => __('checkout.page.choose_payment_method')], 422);
|
||||
}
|
||||
|
||||
$fingerprint = (string) ($this->cart->current()?->meta['checkout_fingerprint'] ?? '');
|
||||
$cart = $this->cart->current();
|
||||
|
||||
// The one incomplete-cart case worth a specific message + pointing the
|
||||
// shopper at the right section: a region resolving 2+ methods needs an
|
||||
// explicit pick (no auto-select), easy to miss since nothing else on
|
||||
// the page demands it. Everything else CartException catches below.
|
||||
if ($cart?->shippingAddress && ! $cart->shippingAddress->shipping_option) {
|
||||
return response()->json([
|
||||
'status' => 'invalid',
|
||||
'message' => __('checkout.page.shipping_method_required'),
|
||||
'field' => 'shipping_option',
|
||||
], 422);
|
||||
}
|
||||
|
||||
$fingerprint = (string) ($cart?->meta['checkout_fingerprint'] ?? '');
|
||||
|
||||
$data = $request->filled('payment_method')
|
||||
? ['payment_method' => (string) $request->input('payment_method')]
|
||||
@@ -360,35 +382,46 @@ private function placedOrder(): ?Order
|
||||
|
||||
/**
|
||||
* Re-resolve shipping options for the cart's current address and keep the
|
||||
* selection sane: auto-select when exactly one resolves, and drop a
|
||||
* previously-picked option that no longer applies (e.g. region changed).
|
||||
* selection sane: auto-select when exactly one resolves, or carry a
|
||||
* previous pick forward when it's still among the resolved options.
|
||||
*
|
||||
* $previousOption must be captured by the CALLER before setShippingAddress()
|
||||
* runs — Lunar's AddAddress action always deletes and recreates the
|
||||
* CartAddress row on every save (see saveAddress()), so by the time this
|
||||
* runs, $address->shipping_option is unconditionally null regardless of
|
||||
* what was selected a moment ago. There is nothing meaningful left to read
|
||||
* off $address itself; $previousOption is the only source of truth for
|
||||
* "what was chosen before this save wiped the row." show() passes the
|
||||
* address's own (not-just-wiped) current value, since nothing recreated
|
||||
* anything in that path.
|
||||
*
|
||||
* Always (re-)applies the resolved target via selectShippingOption() rather
|
||||
* than comparing against the (always-blank, post-recreation) current value
|
||||
* — the fresh row needs the write regardless of whether the decision
|
||||
* "which option" actually changed.
|
||||
*
|
||||
* @return Collection<int, \Lunar\DataTypes\ShippingOption>
|
||||
*/
|
||||
private function syncShipping(Cart $cart): Collection
|
||||
private function syncShipping(Cart $cart, ?string $previousOption): Collection
|
||||
{
|
||||
$address = $cart->shippingAddress;
|
||||
|
||||
if (! $address) {
|
||||
if (! $cart->shippingAddress) {
|
||||
return collect();
|
||||
}
|
||||
|
||||
$options = $this->checkout->getShippingOptions();
|
||||
$current = $address->shipping_option;
|
||||
|
||||
if ($options->count() === 1) {
|
||||
$only = $options->first();
|
||||
$target = match (true) {
|
||||
$options->count() === 1 => $options->first()->identifier,
|
||||
$previousOption !== null && $options->contains(fn ($option) => $option->identifier === $previousOption) => $previousOption,
|
||||
default => null,
|
||||
};
|
||||
|
||||
if ($current !== $only->identifier) {
|
||||
try {
|
||||
$this->checkout->selectShippingOption($only->identifier);
|
||||
} catch (InvalidShippingOptionException) {
|
||||
// nothing to select against — leave as is
|
||||
}
|
||||
if ($target !== null) {
|
||||
try {
|
||||
$this->checkout->selectShippingOption($target);
|
||||
} catch (InvalidShippingOptionException) {
|
||||
// $target came from $options itself — shouldn't happen, stay defensive
|
||||
}
|
||||
} elseif ($current !== null && ! $options->contains(fn ($option) => $option->identifier === $current)) {
|
||||
$address->update(['shipping_option' => null]);
|
||||
$cart->calculate();
|
||||
}
|
||||
|
||||
return $options;
|
||||
|
||||
Generated
+69
-69
@@ -515,11 +515,11 @@
|
||||
},
|
||||
{
|
||||
"name": "boboko/core",
|
||||
"version": "0.17.0",
|
||||
"version": "0.17.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://code.radical-elements.com/boboko/core.git",
|
||||
"reference": "57fc28ca0680841fddc222be4a5a936b43247883"
|
||||
"reference": "d9fb3bbde661bc8f49b3032462af029c5983b03a"
|
||||
},
|
||||
"require": {
|
||||
"laravel/framework": "^12.0",
|
||||
@@ -568,7 +568,7 @@
|
||||
}
|
||||
},
|
||||
"description": "Core module — authentication and shared panel behaviour",
|
||||
"time": "2026-09-14T17:18:26+00:00"
|
||||
"time": "2026-09-15T13:12:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "brick/math",
|
||||
@@ -4302,16 +4302,16 @@
|
||||
},
|
||||
{
|
||||
"name": "livewire/livewire",
|
||||
"version": "v3.8.8",
|
||||
"version": "v3.8.9",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/livewire/livewire.git",
|
||||
"reference": "ec19e5fb1e0b60df22ad15b901c2f7740267ba11"
|
||||
"reference": "993a3f5051774ca12768067ab8644ea97c417417"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/livewire/livewire/zipball/ec19e5fb1e0b60df22ad15b901c2f7740267ba11",
|
||||
"reference": "ec19e5fb1e0b60df22ad15b901c2f7740267ba11",
|
||||
"url": "https://api.github.com/repos/livewire/livewire/zipball/993a3f5051774ca12768067ab8644ea97c417417",
|
||||
"reference": "993a3f5051774ca12768067ab8644ea97c417417",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4366,7 +4366,7 @@
|
||||
"description": "A front-end framework for Laravel.",
|
||||
"support": {
|
||||
"issues": "https://github.com/livewire/livewire/issues",
|
||||
"source": "https://github.com/livewire/livewire/tree/v3.8.8"
|
||||
"source": "https://github.com/livewire/livewire/tree/v3.8.9"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -4374,7 +4374,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2026-09-07T16:02:29+00:00"
|
||||
"time": "2026-09-14T19:27:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "lukascivil/treewalker",
|
||||
@@ -9740,16 +9740,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v7.4.18",
|
||||
"version": "v7.4.19",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "23d6f88a29f6d0eac45bd77d70307adf83ba7ab0"
|
||||
"reference": "3a1973458b153f566a8d7499bc06ece323d60063"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/23d6f88a29f6d0eac45bd77d70307adf83ba7ab0",
|
||||
"reference": "23d6f88a29f6d0eac45bd77d70307adf83ba7ab0",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/3a1973458b153f566a8d7499bc06ece323d60063",
|
||||
"reference": "3a1973458b153f566a8d7499bc06ece323d60063",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -9814,7 +9814,7 @@
|
||||
"terminal"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/console/tree/v7.4.18"
|
||||
"source": "https://github.com/symfony/console/tree/v7.4.19"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -9834,7 +9834,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-08-25T14:18:37+00:00"
|
||||
"time": "2026-09-13T10:38:41+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/css-selector",
|
||||
@@ -10296,16 +10296,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/finder",
|
||||
"version": "v7.4.17",
|
||||
"version": "v7.4.19",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/finder.git",
|
||||
"reference": "5ce28827081f6d1f0c32eaf3882750f19cb5bbe6"
|
||||
"reference": "1b900bc6ae5ba60c5eee69c11ba44566190576fe"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/5ce28827081f6d1f0c32eaf3882750f19cb5bbe6",
|
||||
"reference": "5ce28827081f6d1f0c32eaf3882750f19cb5bbe6",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/1b900bc6ae5ba60c5eee69c11ba44566190576fe",
|
||||
"reference": "1b900bc6ae5ba60c5eee69c11ba44566190576fe",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -10340,7 +10340,7 @@
|
||||
"description": "Finds files and directories via an intuitive fluent interface",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/finder/tree/v7.4.17"
|
||||
"source": "https://github.com/symfony/finder/tree/v7.4.19"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -10360,20 +10360,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-08-21T12:09:28+00:00"
|
||||
"time": "2026-09-10T19:14:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/html-sanitizer",
|
||||
"version": "v8.1.6",
|
||||
"version": "v8.1.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/html-sanitizer.git",
|
||||
"reference": "f8bbdb0704e6b6e9a3412481c1a87886a0633199"
|
||||
"reference": "3d79bdecd01f71bccc45ddfa1d33c024df347a1f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/html-sanitizer/zipball/f8bbdb0704e6b6e9a3412481c1a87886a0633199",
|
||||
"reference": "f8bbdb0704e6b6e9a3412481c1a87886a0633199",
|
||||
"url": "https://api.github.com/repos/symfony/html-sanitizer/zipball/3d79bdecd01f71bccc45ddfa1d33c024df347a1f",
|
||||
"reference": "3d79bdecd01f71bccc45ddfa1d33c024df347a1f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -10412,7 +10412,7 @@
|
||||
"sanitizer"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/html-sanitizer/tree/v8.1.6"
|
||||
"source": "https://github.com/symfony/html-sanitizer/tree/v8.1.7"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -10432,20 +10432,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-08-30T01:03:44+00:00"
|
||||
"time": "2026-09-04T10:14:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-foundation",
|
||||
"version": "v7.4.18",
|
||||
"version": "v7.4.19",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-foundation.git",
|
||||
"reference": "d070b716a32fbe3bf04204db0f58ace73b86d133"
|
||||
"reference": "f7b953be7801974a5a0685e1228bbc3b12b35936"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/d070b716a32fbe3bf04204db0f58ace73b86d133",
|
||||
"reference": "d070b716a32fbe3bf04204db0f58ace73b86d133",
|
||||
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/f7b953be7801974a5a0685e1228bbc3b12b35936",
|
||||
"reference": "f7b953be7801974a5a0685e1228bbc3b12b35936",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -10494,7 +10494,7 @@
|
||||
"description": "Defines an object-oriented layer for the HTTP specification",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/http-foundation/tree/v7.4.18"
|
||||
"source": "https://github.com/symfony/http-foundation/tree/v7.4.19"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -10514,20 +10514,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-08-30T20:10:52+00:00"
|
||||
"time": "2026-09-14T17:41:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/http-kernel",
|
||||
"version": "v7.4.18",
|
||||
"version": "v7.4.19",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/http-kernel.git",
|
||||
"reference": "275d2d2d24530f2a0eaf17704a3a93860a036351"
|
||||
"reference": "683507424cc84ca85ac1551adb609b6b98216667"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/275d2d2d24530f2a0eaf17704a3a93860a036351",
|
||||
"reference": "275d2d2d24530f2a0eaf17704a3a93860a036351",
|
||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/683507424cc84ca85ac1551adb609b6b98216667",
|
||||
"reference": "683507424cc84ca85ac1551adb609b6b98216667",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -10613,7 +10613,7 @@
|
||||
"description": "Provides a structured process for converting a Request into a Response",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/http-kernel/tree/v7.4.18"
|
||||
"source": "https://github.com/symfony/http-kernel/tree/v7.4.19"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -10633,20 +10633,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-08-30T21:24:29+00:00"
|
||||
"time": "2026-09-15T07:09:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/mailer",
|
||||
"version": "v7.4.17",
|
||||
"version": "v7.4.19",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/mailer.git",
|
||||
"reference": "b17c9bf3a551d5f635638a3b6c05f06c4dc87584"
|
||||
"reference": "6f6f2441bef07a42d2617b9b3aadf04a321aa514"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/mailer/zipball/b17c9bf3a551d5f635638a3b6c05f06c4dc87584",
|
||||
"reference": "b17c9bf3a551d5f635638a3b6c05f06c4dc87584",
|
||||
"url": "https://api.github.com/repos/symfony/mailer/zipball/6f6f2441bef07a42d2617b9b3aadf04a321aa514",
|
||||
"reference": "6f6f2441bef07a42d2617b9b3aadf04a321aa514",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -10697,7 +10697,7 @@
|
||||
"description": "Helps sending emails",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/mailer/tree/v7.4.17"
|
||||
"source": "https://github.com/symfony/mailer/tree/v7.4.19"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -10717,20 +10717,20 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-08-21T17:40:08+00:00"
|
||||
"time": "2026-09-15T06:01:15+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/mime",
|
||||
"version": "v7.4.18",
|
||||
"version": "v7.4.19",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/mime.git",
|
||||
"reference": "bf328d82105831db3e409195db0540ff57f27c80"
|
||||
"reference": "f8ac7d0a800bcabbcd1f7de56594cf05e6cd5693"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/mime/zipball/bf328d82105831db3e409195db0540ff57f27c80",
|
||||
"reference": "bf328d82105831db3e409195db0540ff57f27c80",
|
||||
"url": "https://api.github.com/repos/symfony/mime/zipball/f8ac7d0a800bcabbcd1f7de56594cf05e6cd5693",
|
||||
"reference": "f8ac7d0a800bcabbcd1f7de56594cf05e6cd5693",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -10786,7 +10786,7 @@
|
||||
"mime-type"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/mime/tree/v7.4.18"
|
||||
"source": "https://github.com/symfony/mime/tree/v7.4.19"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -10806,7 +10806,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-08-22T09:04:42+00:00"
|
||||
"time": "2026-09-04T10:45:44+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/options-resolver",
|
||||
@@ -11870,16 +11870,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v7.4.18",
|
||||
"version": "v7.4.19",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/process.git",
|
||||
"reference": "058d17fc284cce14efb2385783b55014a461b176"
|
||||
"reference": "ed0ae095b86994d370d5791612e55984f15aa30e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/058d17fc284cce14efb2385783b55014a461b176",
|
||||
"reference": "058d17fc284cce14efb2385783b55014a461b176",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/ed0ae095b86994d370d5791612e55984f15aa30e",
|
||||
"reference": "ed0ae095b86994d370d5791612e55984f15aa30e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -11911,7 +11911,7 @@
|
||||
"description": "Executes commands in sub-processes",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/process/tree/v7.4.18"
|
||||
"source": "https://github.com/symfony/process/tree/v7.4.19"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -11931,7 +11931,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-08-21T17:40:08+00:00"
|
||||
"time": "2026-09-02T12:38:34+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/routing",
|
||||
@@ -12107,16 +12107,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/string",
|
||||
"version": "v8.1.2",
|
||||
"version": "v8.1.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/string.git",
|
||||
"reference": "286a76b7255e5cc4bf0101a0bc5388ecf1c38ccc"
|
||||
"reference": "d950140b5f56f31901e5b7a0c04ffc3a3deb943c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/string/zipball/286a76b7255e5cc4bf0101a0bc5388ecf1c38ccc",
|
||||
"reference": "286a76b7255e5cc4bf0101a0bc5388ecf1c38ccc",
|
||||
"url": "https://api.github.com/repos/symfony/string/zipball/d950140b5f56f31901e5b7a0c04ffc3a3deb943c",
|
||||
"reference": "d950140b5f56f31901e5b7a0c04ffc3a3deb943c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -12173,7 +12173,7 @@
|
||||
"utf8"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/string/tree/v8.1.2"
|
||||
"source": "https://github.com/symfony/string/tree/v8.1.7"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -12193,7 +12193,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-07-28T07:35:25+00:00"
|
||||
"time": "2026-09-11T14:51:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/translation",
|
||||
@@ -12665,16 +12665,16 @@
|
||||
},
|
||||
{
|
||||
"name": "technikermathe/blade-lucide-icons",
|
||||
"version": "v3.179.0",
|
||||
"version": "v3.180.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/PascaleBeier/blade-lucide-icons.git",
|
||||
"reference": "bfae6d39e35bd78f72b7372a26a30b500687c923"
|
||||
"reference": "adac9da3a65910fedc271129eea5340093910203"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/PascaleBeier/blade-lucide-icons/zipball/bfae6d39e35bd78f72b7372a26a30b500687c923",
|
||||
"reference": "bfae6d39e35bd78f72b7372a26a30b500687c923",
|
||||
"url": "https://api.github.com/repos/PascaleBeier/blade-lucide-icons/zipball/adac9da3a65910fedc271129eea5340093910203",
|
||||
"reference": "adac9da3a65910fedc271129eea5340093910203",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -12724,9 +12724,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/PascaleBeier/blade-lucide-icons/issues",
|
||||
"source": "https://github.com/PascaleBeier/blade-lucide-icons/tree/v3.179.0"
|
||||
"source": "https://github.com/PascaleBeier/blade-lucide-icons/tree/v3.180.0"
|
||||
},
|
||||
"time": "2026-09-12T02:06:59+00:00"
|
||||
"time": "2026-09-15T02:25:56+00:00"
|
||||
},
|
||||
{
|
||||
"name": "thecodingmachine/safe",
|
||||
|
||||
@@ -145,6 +145,10 @@ private function lines(): array
|
||||
],
|
||||
'page.place_order' => ['Place order — payment obligation', 'Παραγγελία με υποχρέωση πληρωμής'],
|
||||
'page.choose_payment_method' => ['Choose a payment method.', 'Επίλεξε τρόπο πληρωμής.'],
|
||||
'page.shipping_method_required' => [
|
||||
'Choose a shipping method below to continue.',
|
||||
'Επίλεξε τρόπο αποστολής παρακάτω για να συνεχίσεις.',
|
||||
],
|
||||
'page.payment_failed' => ['Payment failed. Please try again.', 'Η πληρωμή απέτυχε. Δοκίμασε ξανά.'],
|
||||
'page.payment_incomplete_details' => [
|
||||
'Complete your billing and shipping details above.',
|
||||
|
||||
@@ -30,6 +30,7 @@ export default class extends Controller {
|
||||
this.saveTimer = null
|
||||
this.saveController = null
|
||||
this.statusTimer = null
|
||||
this.shippingPromise = null
|
||||
|
||||
if (this.hasSameAsBillingTarget) this.applySameAsBilling()
|
||||
}
|
||||
@@ -108,6 +109,9 @@ export default class extends Controller {
|
||||
async flush() {
|
||||
clearTimeout(this.saveTimer)
|
||||
await this.save()
|
||||
// A shipping-method radio click fires its own (undebounced) request —
|
||||
// still async, still racy against an immediate "place order" click.
|
||||
if (this.shippingPromise) await this.shippingPromise
|
||||
}
|
||||
|
||||
async save() {
|
||||
@@ -137,11 +141,18 @@ export default class extends Controller {
|
||||
}
|
||||
|
||||
async selectShipping(event) {
|
||||
// Tracked so flush() can await it — nothing else stops "place order"
|
||||
// (a separate, unrelated click) from racing ahead of this request.
|
||||
this.shippingPromise = this.doSelectShipping(event.target.value)
|
||||
await this.shippingPromise
|
||||
}
|
||||
|
||||
async doSelectShipping(value) {
|
||||
this.saveController?.abort()
|
||||
this.setStatus('saving')
|
||||
|
||||
const body = new FormData()
|
||||
body.append('shipping_option', event.target.value)
|
||||
body.append('shipping_option', value)
|
||||
|
||||
try {
|
||||
const response = await fetch(this.selectShippingUrlValue, {
|
||||
@@ -160,6 +171,8 @@ export default class extends Controller {
|
||||
this.setStatus('saved')
|
||||
} catch {
|
||||
this.setStatus('error')
|
||||
} finally {
|
||||
this.shippingPromise = null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -215,6 +215,15 @@ export default class extends Controller {
|
||||
return
|
||||
}
|
||||
|
||||
// Points at the section that actually needs attention, rather than
|
||||
// leaving a generic error and making the shopper hunt for it — e.g. a
|
||||
// region with 2+ shipping methods needs an explicit pick, easy to miss.
|
||||
if (data.field === 'shipping_option') {
|
||||
document.getElementById('bbk-shipping-options')?.scrollIntoView({ block: 'center', behavior: 'smooth' })
|
||||
this.fail(data.message || data.error || this.genericErrorValue, { scroll: false })
|
||||
return
|
||||
}
|
||||
|
||||
this.fail(data.message || data.error || this.genericErrorValue)
|
||||
}
|
||||
|
||||
@@ -252,15 +261,15 @@ export default class extends Controller {
|
||||
|
||||
// ── helpers ──────────────────────────────────────────────────────
|
||||
|
||||
fail(message) {
|
||||
this.showError(message)
|
||||
fail(message, { scroll = true } = {}) {
|
||||
this.showError(message, { scroll })
|
||||
this.submitTarget.disabled = false
|
||||
}
|
||||
|
||||
showError(message) {
|
||||
showError(message, { scroll = true } = {}) {
|
||||
this.errorTarget.textContent = message
|
||||
this.errorTarget.hidden = false
|
||||
this.errorTarget.scrollIntoView({ block: 'center', behavior: 'smooth' })
|
||||
if (scroll) this.errorTarget.scrollIntoView({ block: 'center', behavior: 'smooth' })
|
||||
}
|
||||
|
||||
clearError() {
|
||||
|
||||
Reference in New Issue
Block a user