- ProductType::SERVICE for intangible/served products (subscriptions, licences,
consulting) — no stock, behaves like SIMPLE for cart purposes. Lets hosts
whose catalogue includes services adopt the package without a value clash.
- it_creates_separate_line_items_for_multiple_products pinned its two prices to
one_time; without a type the factory randomised it and could mix recurring +
one-time, tripping MixedCheckoutModeException intermittently.
Cart::checkoutSession() now inspects each line's price type and selects the
session mode automatically: `subscription` when the cart carries any recurring
price, `payment` otherwise. Recurring lines reuse a synced Stripe Price
(stripe_price_id) when present and fall back to dynamic price_data with a
`recurring` block otherwise; quarterly cadence maps to a 3-month interval since
Stripe has no native quarter. The cart id is propagated via subscription_data
metadata for webhook mapping.
Mixing recurring and one-time prices in one cart throws the new
MixedCheckoutModeException, since a Stripe Checkout session is single-mode.
The recurring resolver tolerates both the package's enum-cast price model and a
host model storing type/interval as plain strings, so it keeps working when
shop.models.product_price is overridden.
- Added strict types declaration to multiple traits for better type safety.
- Updated method signatures in traits to use nullable types where applicable.
- Improved documentation for traits, including host-model contracts and method descriptions.
- Added new tests to ensure correct behavior of loan checkout and stock management.
- Fixed regression in order number generation to ensure proper string formatting.
- Ensured that currency codes sent to Stripe are consistently lowercased.