Add ProductPrice.cost_amount (integer cents) — the cost to deliver one unit at
that price: manufacturing, shipping, a content license or royalty; per billing
period for recurring prices. Shop now reports margin natively: subscriptionMetrics()
returns `cogs` + `net_mrr`, and Shop::netMrr() gives MRR after cost of goods.
Additive: cost_amount defaults to 0, so existing consumers are unchanged.
Covered by MrrMetricsTest; full suite green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add Shop::mrr(), activeSubscribers(), mrrByProduct(), subscriptionMetrics()
and subscriberStatuses() to ShopService. Counts active/past_due/trialing
subscribers (not `ends_at IS NULL`, which silently drops still-billing
cancel-at-period-end subs and under-reports MRR), normalizes every interval
to a monthly run-rate (year/12, quarter/3, week & day by the average
days-per-month constant so they match Stripe), and returns `unpriced_items`
so a stale or incomplete price mirror is surfaced, never a silent undercount.
Additive only: no existing method changed. Covered by MrrMetricsTest.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
callProductActions() now resolves and runs actions for ALL subscription line items (new resolveProducts()), not just items()->first(), so combined/configurator bundle subscriptions fulfill every product instead of silently granting only the first. resolveProduct() is kept for single-product callers; product_id is still cached to the first resolved product. Adds a multi-item bundle regression test to SubscriptionLifecycleTest.
Adds the package's missing subscription lifecycle so any host app gets
duration-aware, product-linked subscriptions without re-implementing billing:
- Models: Subscription (extends Laravel\Cashier\Subscription) + SubscriptionItem,
in the package's UUID convention, resolved through shop.models/shop.tables.
Subscription gains product()/resolveProduct(), callProductActions() (runs the
product's ProductActions with the subscription + an access-expiry override),
and recordStarted()/recordRenewed()/recordCanceled() lifecycle hooks.
- Events: SubscriptionStarted / SubscriptionRenewed / SubscriptionCanceled,
carrying the Cashier subscription so host subclasses work too.
- Migration: UUID subscriptions / subscription_items tables (hasTable-guarded,
config table names, nullable product_id + current_period_* columns).
- ShopServiceProvider points Cashier at these models by default; opt out via
shop.subscriptions.register_cashier_models for apps that subclass Cashier.
Additive and backward-compatible (registration is config-gated, tables are
guarded). Adds SubscriptionLifecycleTest; full suite 1409 green. Docs + README.