diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b5a3a5..4ddd8a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,53 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). +## [0.16.3] - 2026-09-10 + +### Fixed +- Stripe `createAndConfirm()` built its `PaymentIntent` params with + `'automatic_payment_methods' => isset($data['payment_method']) ? null : ['enabled' => true]`. The + Stripe PHP SDK does not omit `null`-valued params from `create()` — it serializes them to an empty + string (`ApiRequestor::_encodeObjects()` → `Util::utf8(null)`), and Stripe's API rejects an empty + `automatic_payment_methods`. Every Stripe charge failed before it started whenever a + `payment_method` was supplied (i.e. every real charge in this flow). Fixed by building `$params` + conditionally so the key is either omitted entirely or set to `['enabled' => true]`, never `null`. +- `Modules\Core\Payment\Filament\Resources\PaymentMethodResource`'s "Driver status" column only + flagged a payment method whose driver *class* no longer resolves (`driver_missing_at`) — it gave + no indication when a driver resolves fine but fails `Configurable::isConfigured()` (e.g. Stripe + enabled in the DB with no `services.stripe.key` set), which `CheckoutService::getPaymentMethods()` + filters out identically. An admin had no way to tell "this method is silently absent at checkout + because of missing config" from "everything's fine" at a glance. The same icon column now also + reflects `isConfigured()`, with a tooltip distinguishing "driver not found" from "missing required + configuration" from "fully configured." +- `Modules\Core\Payment\Pipelines\Cart\ApplyCashOnDeliveryFee` (now `ApplyPaymentMethodFee`) had two + stacked bugs that together meant a configured payment-method fee (e.g. €5 on Cash on Delivery) + never actually reached the cart total: + - `PaymentMethod::where(...)->value('data->fee')` silently returned `null` on Postgres — Laravel's + query builder does not translate the `->` JSON-path column-selector syntax in `value()`/`pluck()` + the way it does inside `where()` clauses, so this resolved to a discarded + `stdClass::$data->fee` property access instead of the actual fee. Fixed by loading the model and + reading the cast `->data['fee']` attribute instead. + - Even with the fee correctly read, adding it directly to `$cart->shippingTotal` didn't survive: + `Lunar\Pipelines\Cart\CalculateTax`, which runs later in the same cart-calculation pipeline, + unconditionally recomputes `shippingTotal` (and shipping tax) from `$cart->shippingBreakdown`'s + item sum — silently discarding anything set only on the plain property. Fixed by adding the fee + as its own `Lunar\Base\ValueObjects\Cart\ShippingBreakdownItem` on `shippingBreakdown` instead, + so it survives `CalculateTax`'s recompute and is correctly included in shipping tax too. + - Also generalized while fixing: the pipeline was hardcoded to the literal type string + `cash-on-delivery`. Renamed to `ApplyPaymentMethodFee` and changed it to look up whichever + `PaymentMethod` row matches `Cart::meta['payment_method']` and apply its own `data.fee` if + present — works for any payment method configured with a fee, not just one specific slug. +- `Modules\Core\Checkout\Services\CheckoutService::selectPaymentMethod()` called `$cart->calculate()` + after saving the new payment method, but `Lunar\Models\Cart::calculate()` no-ops if the cart + instance was already calculated earlier in the same request (`Cart::isCalculated()`) — + `Lunar\Managers\CartSessionManager` memoizes one `Cart` instance per request, so this was true on + every request where the checkout page's initial render had already calculated the cart. The + result: after switching payment methods, the just-saved `meta['payment_method']` change was + persisted, but the cart's totals silently kept reflecting whichever method was calculated *first* + in the request — a shopper switching from Cash in Hand to Cash on Delivery would keep seeing Cash + in Hand's total, with no COD fee applied, until something else forced a fresh calculation. Fixed + by calling `$cart->recalculate()` instead, which forces the pipeline to re-run. + ## [0.16.2] - 2026-09-09 ### Fixed diff --git a/composer.json b/composer.json index 1872d99..34b7a67 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "boboko/core", "description": "Core module — authentication and shared panel behaviour", "type": "library", - "version": "0.16.2", + "version": "0.16.3", "autoload": { "psr-4": { "Modules\\Core\\": "src/"