feat(addons): finalize event addon catalog and ai styling upgrade flow
This commit is contained in:
@@ -27,21 +27,22 @@ class PackageController extends Controller
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
$type = $request->query('type', 'endcustomer');
|
||||
$provider = strtolower((string) config('checkout.default_provider', CheckoutSession::PROVIDER_PAYPAL));
|
||||
$packages = Package::where('type', $type)
|
||||
->orderBy('price')
|
||||
->get();
|
||||
|
||||
$packages->each(function ($package) {
|
||||
$packages->each(function ($package) use ($provider) {
|
||||
if (is_string($package->features)) {
|
||||
$decoded = json_decode($package->features, true);
|
||||
$package->features = is_array($decoded) ? $decoded : [];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (! is_array($package->features)) {
|
||||
} elseif (! is_array($package->features)) {
|
||||
$package->features = [];
|
||||
}
|
||||
|
||||
$package->setAttribute('checkout_provider', $provider);
|
||||
$package->setAttribute('can_checkout', $this->canCheckoutPackage($package, $provider));
|
||||
});
|
||||
|
||||
return response()->json([
|
||||
@@ -365,4 +366,17 @@ class PackageController extends Controller
|
||||
'cancel_url' => $cancelUrl,
|
||||
]);
|
||||
}
|
||||
|
||||
private function canCheckoutPackage(Package $package, string $provider): bool
|
||||
{
|
||||
if ((float) $package->price <= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($provider === CheckoutSession::PROVIDER_LEMONSQUEEZY) {
|
||||
return filled($package->lemonsqueezy_variant_id);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,13 +4,10 @@ namespace App\Http\Controllers\Api\Tenant;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Tenant\EventAddonCheckoutRequest;
|
||||
use App\Http\Requests\Tenant\EventAddonRequest;
|
||||
use App\Http\Resources\Tenant\EventResource;
|
||||
use App\Models\Event;
|
||||
use App\Services\Addons\EventAddonCheckoutService;
|
||||
use App\Support\ApiError;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
class EventAddonController extends Controller
|
||||
{
|
||||
@@ -51,64 +48,4 @@ class EventAddonController extends Controller
|
||||
'expires_at' => $checkout['expires_at'] ?? null,
|
||||
]);
|
||||
}
|
||||
|
||||
public function apply(EventAddonRequest $request, Event $event): JsonResponse
|
||||
{
|
||||
$tenantId = $request->attributes->get('tenant_id');
|
||||
|
||||
if ($event->tenant_id !== $tenantId) {
|
||||
return ApiError::response(
|
||||
'event_not_found',
|
||||
'Event not accessible',
|
||||
__('Das Event konnte nicht gefunden werden.'),
|
||||
404,
|
||||
['event_slug' => $event->slug ?? null]
|
||||
);
|
||||
}
|
||||
|
||||
$eventPackage = $event->eventPackage;
|
||||
|
||||
if (! $eventPackage && method_exists($event, 'eventPackages')) {
|
||||
$eventPackage = $event->eventPackages()
|
||||
->with('package')
|
||||
->orderByDesc('purchased_at')
|
||||
->orderByDesc('created_at')
|
||||
->first();
|
||||
}
|
||||
|
||||
if (! $eventPackage) {
|
||||
return ApiError::response(
|
||||
'event_package_missing',
|
||||
'Event package missing',
|
||||
__('Kein Paket ist diesem Event zugeordnet.'),
|
||||
409,
|
||||
['event_slug' => $event->slug ?? null]
|
||||
);
|
||||
}
|
||||
|
||||
$data = $request->validated();
|
||||
|
||||
$eventPackage->fill([
|
||||
'extra_photos' => ($eventPackage->extra_photos ?? 0) + (int) ($data['extra_photos'] ?? 0),
|
||||
'extra_guests' => ($eventPackage->extra_guests ?? 0) + (int) ($data['extra_guests'] ?? 0),
|
||||
'extra_gallery_days' => ($eventPackage->extra_gallery_days ?? 0) + (int) ($data['extend_gallery_days'] ?? 0),
|
||||
]);
|
||||
|
||||
if (isset($data['extend_gallery_days'])) {
|
||||
$base = $eventPackage->gallery_expires_at ?? Carbon::now();
|
||||
$eventPackage->gallery_expires_at = $base->copy()->addDays((int) $data['extend_gallery_days']);
|
||||
}
|
||||
|
||||
$eventPackage->save();
|
||||
|
||||
$event->load([
|
||||
'eventPackage.package',
|
||||
'eventPackages.package',
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Add-ons applied successfully.'),
|
||||
'data' => new EventResource($event),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ use App\Http\Requests\Tenant\BillingAddonHistoryRequest;
|
||||
use App\Models\Event;
|
||||
use App\Models\EventPackageAddon;
|
||||
use App\Models\PackagePurchase;
|
||||
use App\Services\Addons\EventAddonCatalog;
|
||||
use App\Services\LemonSqueezy\Exceptions\LemonSqueezyException;
|
||||
use App\Services\LemonSqueezy\LemonSqueezySubscriptionService;
|
||||
use Dompdf\Dompdf;
|
||||
@@ -22,6 +23,7 @@ class TenantBillingController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private readonly LemonSqueezySubscriptionService $subscriptions,
|
||||
private readonly EventAddonCatalog $addonCatalog,
|
||||
) {}
|
||||
|
||||
public function transactions(Request $request): JsonResponse
|
||||
@@ -132,11 +134,19 @@ class TenantBillingController extends Controller
|
||||
->orderByDesc('created_at')
|
||||
->paginate($perPage, ['*'], 'page', $page);
|
||||
|
||||
$data = $paginator->getCollection()->map(function (EventPackageAddon $addon) {
|
||||
$addonLabels = collect($this->addonCatalog->all())
|
||||
->mapWithKeys(fn (array $addon, string $key): array => [$key => $addon['label'] ?? null])
|
||||
->all();
|
||||
|
||||
$data = $paginator->getCollection()->map(function (EventPackageAddon $addon) use ($addonLabels) {
|
||||
$label = $addon->metadata['label']
|
||||
?? ($addonLabels[$addon->addon_key] ?? null)
|
||||
?? $addon->addon_key;
|
||||
|
||||
return [
|
||||
'id' => $addon->id,
|
||||
'addon_key' => $addon->addon_key,
|
||||
'label' => $addon->metadata['label'] ?? null,
|
||||
'label' => $label,
|
||||
'quantity' => (int) ($addon->quantity ?? 1),
|
||||
'status' => $addon->status,
|
||||
'amount' => $addon->amount !== null ? (float) $addon->amount : null,
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Tenant;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class EventAddonRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'extra_photos' => ['nullable', 'integer', 'min:1'],
|
||||
'extra_guests' => ['nullable', 'integer', 'min:1'],
|
||||
'extend_gallery_days' => ['nullable', 'integer', 'min:1'],
|
||||
'reason' => ['nullable', 'string', 'max:255'],
|
||||
];
|
||||
}
|
||||
|
||||
protected function passedValidation(): void
|
||||
{
|
||||
if (
|
||||
$this->input('extra_photos') === null
|
||||
&& $this->input('extra_guests') === null
|
||||
&& $this->input('extend_gallery_days') === null
|
||||
) {
|
||||
throw ValidationException::withMessages([
|
||||
'addons' => __('Please provide at least one add-on to apply.'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Http\Resources\Tenant;
|
||||
|
||||
use App\Models\WatermarkSetting;
|
||||
use App\Services\Addons\EventAddonCatalog;
|
||||
use App\Services\AiEditing\AiStylingEntitlementService;
|
||||
use App\Services\AiEditing\EventAiEditingPolicyService;
|
||||
use App\Services\Packages\PackageLimitEvaluator;
|
||||
@@ -222,11 +223,17 @@ class EventResource extends JsonResource
|
||||
? $eventPackage->addons
|
||||
: $eventPackage->addons()->latest()->take(10)->get();
|
||||
|
||||
return $addons->map(function ($addon) {
|
||||
$addonLabels = collect(app(EventAddonCatalog::class)->all())
|
||||
->mapWithKeys(fn (array $addon, string $key): array => [$key => $addon['label'] ?? null])
|
||||
->all();
|
||||
|
||||
return $addons->map(function ($addon) use ($addonLabels) {
|
||||
return [
|
||||
'id' => $addon->id,
|
||||
'key' => $addon->addon_key,
|
||||
'label' => $addon->metadata['label'] ?? null,
|
||||
'label' => $addon->metadata['label']
|
||||
?? ($addonLabels[$addon->addon_key] ?? null)
|
||||
?? $addon->addon_key,
|
||||
'status' => $addon->status,
|
||||
'variant_id' => $addon->variant_id,
|
||||
'transaction_id' => $addon->transaction_id,
|
||||
|
||||
Reference in New Issue
Block a user