Commit Graph

19 Commits

Author SHA1 Message Date
Fabian @ Blax Software f6f8916bed feat(metrics): subscription MRR API on Shop facade
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>
2026-08-28 13:38:42 +02:00
Fabian @ Blax Software 07a794c028 feat(pricing): conditional prices via meta.requires + entitlement-checker resolver
Add a generic conditional-pricing mechanic: a ProductPrice may carry
meta.requires (e.g. {"role":"seat"}) so it only applies to buyers who
already hold a prerequisite (loyalty / bundle pricing). Product::resolvePriceFor
returns the cheapest price the buyer is eligible for, delegating the
'does the buyer satisfy this requirement' test to a host-bound
EntitlementChecker contract. Defaults to DenyAllEntitlementChecker so an
unconfigured host never leaks a discount. Adds requires()/isConditional()/
requiresLabel()/requiresSatisfiedBy() and conditional()/standalone() scopes.
2026-08-26 12:35:14 +02:00
Fabian @ Blax Software f10d5c2f04 feat(tax): configurable TaxService applied across every checkout path
Blax\Shop\Services\TaxService::rates($exempt, $rates=null) is one source of
truth that turns "which rate(s)" (config shop.tax.rates or an explicit list)
and "is this customer exempt" into the array Stripe expects. It fails loud
(TaxRateNotConfiguredException) when shop.tax.require is set and no rate is
configured for a non-exempt charge, instead of silently billing 0% VAT.

New config('shop.tax') block (SHOP_TAX_RATES / SHOP_TAX_REQUIRE). Unit tested.

Consumers (learn-atc #1509) delegate getApplicableTaxRates() here so no path
can apply the rate twice (Stripe rejects duplicate tax rates) or drop VAT to 0%.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-21 08:23:56 +02:00
Fabian @ Blax Software b154d8fea2 feat(seats): assignable license seats — buy N, assign/reassign/reclaim per user
A seat-based product (meta.seat_based) sold at quantity N provisions N
assignable LicenseSeat rows instead of granting the buyer. Assigning a seat
runs the product's existing ProductAction grants against the seat holder via
an explicit `grantee` (resolution: grantee ?? purchaser ?? subscription->user),
so a seat confers exactly the product's access with zero duplicated grant logic.

- LicenseSeat model: assign / reassign / reclaim / retire / refreshGrant
- SeatService: idempotent provisioning for one-time purchases and subscriptions
  (sizes the pool to quantity; renewal extends expiry + re-grants held seats)
- SeatAssigned / SeatReassigned / SeatRevoked events
- Product::isSeatBased(); ProductPurchase + Subscription provision on
  completion / lifecycle hooks
- license_seats migration + factory; config `shop.seats` block
- 15 new tests (purchase, subscription, renewal, retire, delegation, gating)

Backward compatible: with no `grantee` the buyer grant is unchanged, and seat
behavior is gated behind the per-product meta flag + config `shop.seats.enabled`.
Full package suite green: 1425 tests / 3812 assertions / 0 failures.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-29 10:07:58 +02:00
Fabian @ Blax Software afdcd8bc75 feat: Enhance traits with strict types and improve method signatures
- 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.
2026-05-15 20:26:24 +02:00
Fabian @ Blax Software 7aeffd27a9 RI optimizations 2025-12-29 11:11:27 +01:00
Fabian @ Blax Software 6e9c9043ae I order, A handy methods 2025-12-29 10:26:51 +01:00
Fabian @ Blax Software 145c629786 BFI cart stock, A exceptions 2025-12-19 14:26:57 +01:00
Fabian @ Blax Software c5b78071e7 IA booking checking & simplifications, A test/trait 2025-12-18 16:54:33 +01:00
Fabian @ Blax Software 2303b9f703 BF decimals, I test consitency 2025-12-18 10:54:42 +01:00
Fabian @ Blax Software abbfbd3649 CAI per minute booking pricing 2025-12-17 17:57:17 +01:00
Fabian @ Blax Software 7478f69bcf I cart facade 2025-12-17 09:24:42 +01:00
Fabian @ Blax Software 3045f72304 BFI cart, A stripe logic, checkout & tests, A tests, RA pool product 2025-12-15 14:10:59 +01:00
Fabian @ Blax Software 67917e6a31 A booking/pool procedures & tests, IA tests, exceptions 2025-12-15 11:32:31 +01:00
Fabian @ Blax Software cabae43950 A facades, U readme 2025-12-09 10:59:46 +01:00
a6a2f5842 929e87bc28 AI payment process & payment provider files 2025-11-26 11:09:52 +01:00
a6a2f5842 856686e292 A traits, concerns, services 2025-11-26 00:05:46 +01:00
a6a2f5842 2c31b6ea98 IM product purchase process 2025-11-22 15:13:30 +01:00
a6a2f5842 d610cc5717 init 2025-11-21 11:49:41 +01:00