Skip to content

v0.1.6

Release date: May 3, 2026

New Features

EU-Compliant Price Reductions

  • Products have a new "Show previous price" toggle on the product form to opt in to displaying a price reduction
  • The strikethrough on the storefront now uses the lowest price from the past 30 days, in line with the EU Omnibus Directive — not the manually entered prior price
  • The product form warns when no sale badge will display (because the current price is not lower than the 30-day low), so admins know their reduction will not show
  • Price changes are recorded automatically and a weekly cleanup job purges history older than 90 days

Unit Pricing

  • Products can now declare a unit quantity and unit (kg, g, L, ml, m, cm, m²), and the storefront shows a "per kg" / "per L" / etc. line under the price on cards, the product page, collection, and search
  • Count-based products (sold "per item") are exempt from unit pricing as required by the directive
  • Unit pricing is exported to the Google Merchant feed as unit_pricing_measure and unit_pricing_base_measure

Tax-Inclusive Storefront Prices

  • All storefront prices (cards, product page, cart, checkout, order emails) now always include tax, as required for B2C sales in the EU
  • The product page shows a notice when a tax rule has per-country overrides, signaling the final amount may be recalculated at checkout once the customer's billing country is known
  • The admin product form has a new tax preview that shows what each country will be charged when a tax rule has per-country overrides

Billing Address in Checkout

  • The checkout flow has a new billing address step: keep the shipping address (default), pick a saved address, or enter a new one
  • Customers can optionally save a custom billing address to their address book for future orders
  • Changing the billing country clears the selected payment method and any in-flight Stripe PaymentIntent so taxes are recomputed for the new country
  • Order confirmation emails and the customer order history page show shipping and billing addresses separately when they differ
  • The admin order view shows shipping and billing side by side

Improvements

Analytics Dashboard

  • Tables on the Traffic, Products, and Abandoned Carts pages now use server-side pagination, so large stores stay responsive
  • Funnel chart UI was refined for clearer drop-off visualization
  • Sessions from signed-in admins are excluded from analytics so the numbers reflect real customer activity
  • Page paths are normalized so the same logical page no longer fragments across multiple rows

Orders

  • New order source filter on the order list separates checkout orders from manually created orders
  • Manual order creation blocks placeholder emails when a real customer email is available, so notifications go to the right inbox

Onboarding

  • The Languages step now pre-selects English, Spanish, Dutch, and Portuguese (previously only English); admins can still adjust the list

Checkout Stability

  • Advisory locks and serializable snapshot updates protect concurrent checkout actions from race conditions
  • Dates in the checkout date selector and order emails now respect the customer's locale

Shipping & Locale

  • Shipping minimum-order thresholds are now compared against subtotal + tax minus any merchandise discount, matching what the customer sees in the order summary (shipping-only coupons are excluded from the discount)
  • Server-rendered storefront pages and order emails resolve language via the customer's stored preference, then their locale cookie, then the first store-enabled language, falling back to English — so a stale cookie no longer renders a language the store has since disabled

Operations

  • A new optional ENABLE_AUTH_RATE_LIMIT env var lets operators disable Better Auth rate limiting in CI and test environments; production behavior is unchanged unless the variable is explicitly set

Bug Fixes

  • Cart merge now validates stock for both branches and notifies customers when quantities are adjusted
  • A warning toast now fires two minutes before the cart reservation expires
  • Stripe webhook handlers (succeeded and failed) are now idempotent and use atomic guards to prevent double-processing
  • Coupon apply and remove are serialized via the checkout lock; single-use-per-customer coupons no longer count cancelled or payment-failed orders
  • Shipping-coupon revalidation is skipped when the customer changes country before picking a shipping method
  • The visitor cohort is persisted on the order snapshot so server-side purchase events stay in the right cohort
  • The user edit form's Cancel button now resets fields without leaving them marked dirty

Security Fixes

  • The page-builder iframe channel is now locked to same-origin postMessage
  • Address PATCH and DELETE operations are ownership-bound at the SQL level
  • Per-IP rate limiting is now enforced on the Stripe webhook endpoint
  • Analytics aggregate queries now use parameterized make_interval() calls instead of string interpolation
  • Stripe constructEvent timestamp tolerance is pinned to 300 seconds
  • Order.stripePaymentIntentId now has a unique index and the webhook uses findUnique for safety