256 lines
8.0 KiB
PHP
256 lines
8.0 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Paddle;
|
|
|
|
use App\Models\Package;
|
|
use Illuminate\Support\Arr;
|
|
use Illuminate\Support\Str;
|
|
|
|
class PaddleCatalogService
|
|
{
|
|
public function __construct(private readonly PaddleClient $client) {}
|
|
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
public function fetchProduct(string $productId): array
|
|
{
|
|
return $this->extractEntity($this->client->get("/products/{$productId}"));
|
|
}
|
|
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
public function fetchPrice(string $priceId): array
|
|
{
|
|
return $this->extractEntity($this->client->get("/prices/{$priceId}"));
|
|
}
|
|
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
public function createProduct(Package $package, array $overrides = []): array
|
|
{
|
|
$payload = $this->buildProductPayload($package, $overrides);
|
|
|
|
return $this->extractEntity($this->client->post('/products', $payload));
|
|
}
|
|
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
public function updateProduct(string $productId, Package $package, array $overrides = []): array
|
|
{
|
|
$payload = $this->buildProductPayload($package, $overrides);
|
|
|
|
return $this->extractEntity($this->client->patch("/products/{$productId}", $payload));
|
|
}
|
|
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
public function createPrice(Package $package, string $productId, array $overrides = []): array
|
|
{
|
|
$payload = $this->buildPricePayload($package, $productId, $overrides, includeProduct: true);
|
|
|
|
return $this->extractEntity($this->client->post('/prices', $payload));
|
|
}
|
|
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
public function updatePrice(string $priceId, Package $package, array $overrides = []): array
|
|
{
|
|
$payload = $this->buildPricePayload(
|
|
$package,
|
|
$overrides['product_id'] ?? $package->paddle_product_id,
|
|
$overrides,
|
|
includeProduct: false
|
|
);
|
|
|
|
return $this->extractEntity($this->client->patch("/prices/{$priceId}", $payload));
|
|
}
|
|
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
public function buildProductPayload(Package $package, array $overrides = []): array
|
|
{
|
|
$payload = array_merge([
|
|
'name' => $this->resolveName($package, $overrides),
|
|
'description' => $this->resolveDescription($package, $overrides),
|
|
'tax_category' => $overrides['tax_category'] ?? 'standard',
|
|
'type' => $overrides['type'] ?? 'standard',
|
|
'custom_data' => $this->buildCustomData($package, $overrides['custom_data'] ?? []),
|
|
], Arr::except($overrides, ['tax_category', 'type', 'custom_data']));
|
|
|
|
return $this->cleanPayload($payload);
|
|
}
|
|
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
public function buildPricePayload(Package $package, string $productId, array $overrides = [], bool $includeProduct = true): array
|
|
{
|
|
$unitPrice = $overrides['unit_price'] ?? [
|
|
'amount' => (string) $this->priceToMinorUnits($package->price),
|
|
'currency_code' => Str::upper((string) ($package->currency ?? 'EUR')),
|
|
];
|
|
|
|
$base = [
|
|
'description' => $this->resolvePriceDescription($package, $overrides),
|
|
'unit_price' => $unitPrice,
|
|
'custom_data' => $this->buildCustomData($package, $overrides['custom_data'] ?? []),
|
|
];
|
|
|
|
if ($includeProduct) {
|
|
$base['product_id'] = $productId;
|
|
}
|
|
|
|
$payload = array_merge($base, Arr::except($overrides, ['unit_price', 'description', 'custom_data', 'product_id']));
|
|
|
|
return $this->cleanPayload($payload);
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $response
|
|
* @return array<string, mixed>
|
|
*/
|
|
protected function extractEntity(array $response): array
|
|
{
|
|
return Arr::get($response, 'data', $response);
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $payload
|
|
* @return array<string, mixed>
|
|
*/
|
|
protected function cleanPayload(array $payload): array
|
|
{
|
|
$filtered = collect($payload)
|
|
->reject(static fn ($value) => $value === null || $value === '' || $value === [])
|
|
->all();
|
|
|
|
if (array_key_exists('custom_data', $filtered)) {
|
|
$filtered['custom_data'] = collect($filtered['custom_data'])
|
|
->reject(static fn ($value) => $value === null || $value === '' || $value === [])
|
|
->all();
|
|
}
|
|
|
|
return $filtered;
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $extra
|
|
* @return array<string, mixed>
|
|
*/
|
|
protected function buildCustomData(Package $package, array $extra = []): array
|
|
{
|
|
$base = [
|
|
'fotospiel_package_id' => (string) $package->id,
|
|
'slug' => $package->slug,
|
|
'type' => $package->type,
|
|
'features' => $package->features,
|
|
'limits' => array_filter([
|
|
'max_photos' => $package->max_photos,
|
|
'max_guests' => $package->max_guests,
|
|
'gallery_days' => $package->gallery_days,
|
|
'max_tasks' => $package->max_tasks,
|
|
'max_events_per_year' => $package->max_events_per_year,
|
|
], static fn ($value) => $value !== null),
|
|
'translations' => array_filter([
|
|
'name' => $package->name_translations,
|
|
'description' => $package->description_translations,
|
|
], static fn ($value) => ! empty($value)),
|
|
];
|
|
|
|
return array_merge($base, $extra);
|
|
}
|
|
|
|
protected function resolveName(Package $package, array $overrides): string
|
|
{
|
|
if (isset($overrides['name']) && is_string($overrides['name'])) {
|
|
return $overrides['name'];
|
|
}
|
|
|
|
if (! empty($package->name)) {
|
|
return $package->name;
|
|
}
|
|
|
|
$translations = $package->name_translations ?? [];
|
|
|
|
return $translations['en'] ?? $translations['de'] ?? $package->slug;
|
|
}
|
|
|
|
protected function resolveDescription(Package $package, array $overrides): string
|
|
{
|
|
if (array_key_exists('description', $overrides)) {
|
|
$value = is_string($overrides['description']) ? trim($overrides['description']) : null;
|
|
|
|
if ($value !== null && $value !== '') {
|
|
return $value;
|
|
}
|
|
}
|
|
|
|
if (! empty($package->description)) {
|
|
return strip_tags((string) $package->description);
|
|
}
|
|
|
|
$translations = $package->description_translations ?? [];
|
|
|
|
$fallback = $translations['en'] ?? $translations['de'] ?? null;
|
|
|
|
if ($fallback !== null) {
|
|
$fallback = trim(strip_tags((string) $fallback));
|
|
if ($fallback !== '') {
|
|
return $fallback;
|
|
}
|
|
}
|
|
|
|
return sprintf('Fotospiel package %s', $package->slug ?? $package->id);
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $overrides
|
|
*/
|
|
protected function resolvePriceDescription(Package $package, array $overrides): string
|
|
{
|
|
if (array_key_exists('description', $overrides)) {
|
|
$value = is_string($overrides['description']) ? trim($overrides['description']) : null;
|
|
|
|
if ($value !== null && $value !== '') {
|
|
return $value;
|
|
}
|
|
}
|
|
|
|
if (! empty($package->description)) {
|
|
return strip_tags((string) $package->description);
|
|
}
|
|
|
|
$translations = $package->description_translations ?? [];
|
|
$fallback = $translations['en'] ?? $translations['de'] ?? null;
|
|
|
|
if ($fallback !== null) {
|
|
$fallback = trim(strip_tags((string) $fallback));
|
|
if ($fallback !== '') {
|
|
return $fallback;
|
|
}
|
|
}
|
|
|
|
$name = $package->name ?? $package->getNameForLocale('en');
|
|
|
|
if ($name) {
|
|
return sprintf('%s package', trim($name));
|
|
}
|
|
|
|
return sprintf('Package %s', $package->slug ?? $package->id);
|
|
}
|
|
|
|
protected function priceToMinorUnits(mixed $price): int
|
|
{
|
|
$value = is_string($price) ? (float) $price : (float) ($price ?? 0);
|
|
|
|
return (int) round($value * 100);
|
|
}
|
|
}
|