GET /api/v1/payg/wallet returns the snapshot the FE useWallet hook
consumes — derived from EntitlementService (spend / cap / period),
PaygTeamExtensions (subscription state), and the wallet_category_summary
view (per-category breakdown). Leader callers get the team roster +
per-member sub-caps; member callers see an empty roster.
PATCH /api/v1/payg/cap updates wallet_policy.cap_units (no Stripe call —
cap is application-layer, enforced via the entitlement guard) and
invalidates the team's snapshot cache so the next read reflects the
change immediately. Leader-only — authorised inside the method since the
team is derived from the caller, not a path variable.
Adds findPrimaryMembership(userId) to TeamMembershipRepository so the
wallet endpoint can resolve the caller's team + role in a single query.
Tests: 11-case pure-Mockito slice covering free vs subscribed, leader vs
member, no-cap, anonymous → 401, teamless → empty free snapshot, plus
the cap update endpoint's leader-only enforcement and cache invalidation.
Known dependency on PR #6532: stripeSubscriptionId stays null until the
payg_subscription_id column lands; "subscribed" derived from
stripe_customer_id presence as a stand-in. billableLimit fallback of 500
units mirrors EntitlementService.DEFAULT_FREE_TIER_UNITS until
pricing_policy.free_tier_units_per_cycle ships.