Skip to content

v2.3.7

Latest

Choose a tag to compare

@github-actions github-actions released this 08 Oct 15:51
· 8 commits to main since this release
57fa9cf

Mercur v2.3.7

A smaller release with one new building block and a batch of fixes. Offers
learn about conditions, so operators can define "new", "refurbished" or "used"
and sellers pick one per listing. The admin order list gets a payment status
filter, server-side search in its filters and smarter search by order number.
Two security fixes close cross-seller gaps in the vendor API and in cart
promotions, and payouts no longer call the provider before a row is written.

⚠️ Before you upgrade

  • Run migrations — the offer module adds an offer_condition table and
    offer.condition_id (seeded with a default new condition), and the payout
    module widens the payout.status check constraint to accept processing
    (#1645, #1641).
  • Payouts are briefly pending. createPayouts now inserts the row
    before calling the provider, so payout.created fires for a pending row and
    readers that assumed every row is already sent should check status (#1641).
  • Seller promotions are scoped on every cart refresh. A vendor promotion
    without seller-specific target rules, or an automatic one, no longer
    discounts other sellers' items. If your storefront relied on that, it was a
    bug (#1648).

✨ Highlights

🏷️ Offer conditions

Operators define the physical conditions an offer may be listed under through
/admin/offer-conditions (code, label, is_active, rank), and sellers pick
an active one via condition_id when creating or updating an offer. Admin,
vendor and store offer responses expose condition.{id,code,label} and filter
on condition_id. Deleting a condition in use is refused; deactivating keeps
existing offers but blocks new assignments. API only for now, with
createOfferConditionsWorkflow / update / delete hooks and
offer_condition.* events. (#1645)

📋 Admin order list

  • Filter order groups by payment status. The status is computed from
    payment collections, so GET /admin/order-groups?payment_status=captured
    matches exactly what the list shows (#1639).
  • Search by the numbers operators actually see: 264, #264 or #G258 now
    match the group's display id or any child order's display id (#1638).
  • Customer, Store and Sales channel filters search on the server, so stores
    with more than 1000 customers or sellers can select any of them (#1640).

🔒 Security

Both issues below were reported privately by @gregbarajas, with clean
end-to-end reproductions on a v2.3.6 starter that made the fixes
straightforward. Thank you, Greg!

  • Close cross-seller authorization gaps in the vendor API: fields traversal
    into other sellers' members, payment details and invite tokens, product
    detail routes on another seller's drafts, capture of shared cart payments,
    and offer / inventory body ids pointing at another seller's items or
    locations (GHSA-mhch-gq7f-v5p7, #1649).
  • Keep seller promotions scoped to the seller's own items on every cart
    refresh, not only when applied through POST /store/carts/:id/promotions
    (GHSA-w4vq-6jvh-cfpg, #1648).

🐛 Fixes

  • Insert the payout row before calling the provider, use the payout id as the
    default idempotency_key, and accept the processing status in the
    database (#1641).
  • Intersect id and seller_id filters on GET /admin/order-groups instead
    of returning every group (#1637).
  • Rebuild the Variants table on every attribute change in Products → Create in
    both panels, and surface create errors as a toast instead of a silent 400
    (#1650).

Full Changelog: v2.3.6...v2.3.7