*/ public function all(): array { $dbAddons = PackageAddon::query() ->where('active', true) ->orderBy('sort') ->get() ->mapWithKeys(function (PackageAddon $addon) { $metadata = $this->normalizeMetadata($addon->metadata); return [$addon->key => [ 'label' => $addon->label, 'variant_id' => $addon->variant_id, 'increments' => $addon->increments, 'price' => $addon->resolvePaypalPrice(), 'currency' => 'EUR', 'metadata' => $metadata, ]]; }) ->all(); // Fallback to config and merge (DB wins) $configAddons = collect((array) config('package-addons', [])) ->filter(static fn (mixed $addon): bool => is_array($addon)) ->map(function (array $addon): array { $metadata = $this->normalizeMetadata($addon['metadata'] ?? []); if (! isset($metadata['scope']) && is_string($addon['scope'] ?? null) && trim((string) $addon['scope']) !== '') { $metadata['scope'] = trim((string) $addon['scope']); } if (! isset($metadata['entitlements']) && is_array($addon['entitlements'] ?? null)) { $metadata['entitlements'] = $addon['entitlements']; } return [ 'label' => $addon['label'] ?? null, 'variant_id' => $addon['variant_id'] ?? null, 'increments' => Arr::get($addon, 'increments', []), 'price' => $this->resolveConfiguredPrice($addon), 'currency' => $addon['currency'] ?? 'EUR', 'metadata' => $metadata, ]; }) ->all(); return array_merge($configAddons, $dbAddons); } /** * @return array|null */ public function find(string $key): ?array { return $this->all()[$key] ?? null; } public function resolveVariantId(string $key): ?string { $addon = $this->find($key); return $addon['variant_id'] ?? null; } public function resolvePrice(string $key): ?float { $addon = $this->find($key); if (! $addon) { return null; } $price = $addon['price'] ?? $addon['price_eur'] ?? null; return is_numeric($price) ? (float) $price : null; } protected function resolveConfiguredPrice(array $addon): ?float { $price = $addon['price'] ?? $addon['price_eur'] ?? null; return is_numeric($price) ? (float) $price : null; } /** * @return array */ protected function normalizeMetadata(mixed $metadata): array { if (is_string($metadata)) { $decoded = json_decode($metadata, true); if (json_last_error() === JSON_ERROR_NONE) { $metadata = $decoded; } } return is_array($metadata) ? $metadata : []; } /** * @return array */ public function resolveIncrements(string $key): array { $addon = $this->find($key) ?? []; $increments = Arr::get($addon, 'increments', []); return collect($increments) ->map(fn ($value) => is_numeric($value) ? (int) $value : 0) ->filter(fn ($value) => $value > 0) ->all(); } }