Implements GachaPointService.TryExchangeAsync: validates pack
exchangeability, balance >= threshold, card in catalog, not already
received; debits balance, marks received, grants the card through
RewardGrantService (cascade handles cosmetics). Re-adds the
RewardGrantService injection that was removed in the Task 3 fix-up
(matches the "inject when you call" convention).
Card grant produces the wire-shape reward_list directly via the
cosmetic cascade — the catalog's reward_list remains the display-only
shape for /pack/get_gacha_point_rewards.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>