From 1eb7434336e4f007b3abf2a1d129f38a97def642 Mon Sep 17 00:00:00 2001 From: Ivan Moroz Date: Tue, 2 Jun 2026 20:44:34 +0300 Subject: [PATCH] CONV-260: Create CreditPackRepository Co-Authored-By: Claude Sonnet 4.6 --- app/Data/Billing/CreditPackDto.php | 2 +- app/Services/Billing/CreditPackRepository.php | 51 +++++++++++++++++++ .../Unit/Billing/CreditPackRepositoryTest.php | 37 ++++++++++++++ 3 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 app/Services/Billing/CreditPackRepository.php create mode 100644 tests/Unit/Billing/CreditPackRepositoryTest.php diff --git a/app/Data/Billing/CreditPackDto.php b/app/Data/Billing/CreditPackDto.php index b7f82c2..db7230b 100644 --- a/app/Data/Billing/CreditPackDto.php +++ b/app/Data/Billing/CreditPackDto.php @@ -10,7 +10,7 @@ public function __construct( public string $key, public string $label, public int $credits, - public string $stripePriceId, + public ?string $stripePriceId, public string $description, ) {} diff --git a/app/Services/Billing/CreditPackRepository.php b/app/Services/Billing/CreditPackRepository.php new file mode 100644 index 0000000..005e122 --- /dev/null +++ b/app/Services/Billing/CreditPackRepository.php @@ -0,0 +1,51 @@ + */ + public function all(): Collection + { + return collect($this->packsConfig()) + ->map(fn (array $config, string $key) => CreditPackDto::fromConfig($key, $config)) + ->values(); + } + + public function find(string $key): ?CreditPackDto + { + $config = $this->packsConfig(); + + if (! array_key_exists($key, $config)) { + return null; + } + + return CreditPackDto::fromConfig($key, $config[$key]); + } + + public function findOrFail(string $key): CreditPackDto + { + $pack = $this->find($key); + + if ($pack === null) { + throw new \InvalidArgumentException("Unknown credit pack key: {$key}"); + } + + return $pack; + } + + public function findByStripePriceId(string $stripePriceId): ?CreditPackDto + { + return $this->all()->first(fn (CreditPackDto $pack) => $pack->stripePriceId === $stripePriceId); + } + + private function packsConfig(): array + { + return config('billing.credit_packs', []); + } +} diff --git a/tests/Unit/Billing/CreditPackRepositoryTest.php b/tests/Unit/Billing/CreditPackRepositoryTest.php new file mode 100644 index 0000000..d97497a --- /dev/null +++ b/tests/Unit/Billing/CreditPackRepositoryTest.php @@ -0,0 +1,37 @@ +all(); + + expect($packs)->not->toBeEmpty(); + expect($packs->first())->toBeInstanceOf(CreditPackDto::class); +}); + +it('finds credit pack by key', function () { + $pack = app(CreditPackRepository::class)->findOrFail('small'); + + expect($pack->key)->toBe('small'); +}); + +it('throws for unknown credit pack key', function () { + app(CreditPackRepository::class)->findOrFail('unknown'); +})->throws(InvalidArgumentException::class); + +it('finds credit pack by stripe price id', function () { + config()->set('billing.credit_packs.small.stripe_price_id', 'price_small'); + + $pack = app(CreditPackRepository::class)->findByStripePriceId('price_small'); + + expect($pack?->key)->toBe('small'); +}); + +it('returns null for unknown stripe price id', function () { + $pack = app(CreditPackRepository::class)->findByStripePriceId('price_nonexistent'); + + expect($pack)->toBeNull(); +});