Files
fotospiel-app/app/Services/Paddle/PaddleDiscountService.php

150 lines
4.4 KiB
PHP

<?php
namespace App\Services\Paddle;
use App\Enums\CouponType;
use App\Models\Coupon;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
class PaddleDiscountService
{
public function __construct(private readonly PaddleClient $client) {}
/**
* @return array<string, mixed>
*/
public function createDiscount(Coupon $coupon): array
{
$payload = $this->buildDiscountPayload($coupon);
$response = $this->client->post('/discounts', $payload);
return Arr::get($response, 'data', $response);
}
/**
* @return array<string, mixed>
*/
public function updateDiscount(Coupon $coupon): array
{
if (! $coupon->paddle_discount_id) {
return $this->createDiscount($coupon);
}
$payload = $this->buildDiscountPayload($coupon);
$response = $this->client->patch('/discounts/'.$coupon->paddle_discount_id, $payload);
return Arr::get($response, 'data', $response);
}
public function archiveDiscount(Coupon $coupon): void
{
if (! $coupon->paddle_discount_id) {
return;
}
$this->client->delete('/discounts/'.$coupon->paddle_discount_id);
}
/**
* @param array<int, array{price_id: string, quantity?: int}> $items
* @param array{currency?: string, address?: array{country_code: string, postal_code?: string}, customer_id?: string, address_id?: string} $context
* @return array<string, mixed>
*/
public function previewDiscount(Coupon $coupon, array $items, array $context = []): array
{
$payload = [
'items' => $items,
'discount_id' => $coupon->paddle_discount_id,
];
if (isset($context['currency'])) {
$payload['currency_code'] = Str::upper($context['currency']);
}
if (isset($context['address'])) {
$payload['address'] = $context['address'];
}
if (isset($context['customer_id'])) {
$payload['customer_id'] = $context['customer_id'];
}
if (isset($context['address_id'])) {
$payload['address_id'] = $context['address_id'];
}
$response = $this->client->post('/transactions/preview', $payload);
return Arr::get($response, 'data', $response);
}
/**
* @return array<string, mixed>
*/
protected function buildDiscountPayload(Coupon $coupon): array
{
$payload = [
'name' => $coupon->name,
'code' => $coupon->code,
'type' => $this->mapType($coupon->type),
'amount' => $this->formatAmount($coupon),
'currency_code' => $coupon->currency ?? config('app.currency', 'EUR'),
'enabled_for_checkout' => $coupon->enabled_for_checkout,
'description' => $coupon->description,
'mode' => $coupon->paddle_mode ?? 'standard',
'usage_limit' => $coupon->usage_limit,
'maximum_recurring_intervals' => null,
'recur' => false,
'restrict_to' => $this->resolveRestrictions($coupon),
'starts_at' => optional($coupon->starts_at)?->toIso8601String(),
'expires_at' => optional($coupon->ends_at)?->toIso8601String(),
];
if ($payload['type'] === 'percentage') {
unset($payload['currency_code']);
}
return Collection::make($payload)
->reject(static fn ($value) => $value === null || $value === '')
->all();
}
protected function formatAmount(Coupon $coupon): string
{
if ($coupon->type === CouponType::PERCENTAGE) {
return (string) $coupon->amount;
}
return (string) ((int) round($coupon->amount * 100));
}
protected function mapType(CouponType $type): string
{
return match ($type) {
CouponType::PERCENTAGE => 'percentage',
CouponType::FLAT => 'flat',
CouponType::FLAT_PER_SEAT => 'flat_per_seat',
};
}
protected function resolveRestrictions(Coupon $coupon): ?array
{
$packages = ($coupon->relationLoaded('packages')
? $coupon->packages
: $coupon->packages()->get())
->whereNotNull('paddle_price_id');
if ($packages->isEmpty()) {
return null;
}
$prices = $packages->pluck('paddle_price_id')->filter()->values();
return $prices->isEmpty() ? null : $prices->all();
}
}