Files
fotospiel-app/app/Services/Addons/EventAddonPurchaseService.php

118 lines
4.1 KiB
PHP

<?php
namespace App\Services\Addons;
use App\Models\EventPackage;
use App\Models\EventPackageAddon;
use App\Notifications\Addons\AddonPurchaseReceipt;
use App\Notifications\Ops\AddonPurchased;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Notification;
class EventAddonPurchaseService
{
public function __construct(private readonly EventAddonCatalog $catalog) {}
/**
* @param array<string, mixed> $payload
*/
public function complete(
EventPackageAddon $addon,
array $payload,
?string $transactionId,
?string $checkoutId,
?float $amount,
?string $currency,
array $metadata = [],
): void {
if ($addon->status === 'completed') {
return;
}
$increments = $this->resolveAddonIncrements($addon, $addon->addon_key);
DB::transaction(function () use ($addon, $payload, $transactionId, $checkoutId, $amount, $currency, $metadata, $increments) {
$addon->forceFill([
'transaction_id' => $transactionId ?? $addon->transaction_id,
'checkout_id' => $addon->checkout_id ?: $checkoutId,
'status' => 'completed',
'amount' => $amount ?? $addon->amount,
'currency' => $currency ?? $addon->currency,
'metadata' => array_merge($addon->metadata ?? [], array_filter([
'payment_payload' => $payload,
]), $metadata),
'purchased_at' => now(),
])->save();
/** @var EventPackage $eventPackage */
$eventPackage = EventPackage::query()
->lockForUpdate()
->find($addon->event_package_id);
if (! $eventPackage) {
return;
}
$eventPackage->forceFill([
'extra_photos' => ($eventPackage->extra_photos ?? 0) + (int) ($increments['extra_photos'] ?? 0) * $addon->quantity,
'extra_guests' => ($eventPackage->extra_guests ?? 0) + (int) ($increments['extra_guests'] ?? 0) * $addon->quantity,
'extra_gallery_days' => ($eventPackage->extra_gallery_days ?? 0) + (int) ($increments['extra_gallery_days'] ?? 0) * $addon->quantity,
]);
if (($increments['extra_gallery_days'] ?? 0) > 0) {
$base = $eventPackage->gallery_expires_at ?? now();
$eventPackage->gallery_expires_at = $base->copy()->addDays((int) ($increments['extra_gallery_days'] ?? 0) * $addon->quantity);
}
$eventPackage->save();
$tenant = $addon->tenant;
if ($tenant) {
Notification::route('mail', [$tenant->contact_email ?? $tenant->user?->email])
->notify(new AddonPurchaseReceipt($addon));
$opsEmail = config('mail.ops_address');
if ($opsEmail) {
Notification::route('mail', $opsEmail)->notify(new AddonPurchased($addon));
}
}
});
}
/**
* @param array<string, mixed> $payload
*/
public function fail(EventPackageAddon $addon, string $reason, array $payload = []): void
{
$addon->forceFill([
'status' => 'failed',
'error' => $reason,
'metadata' => array_merge($addon->metadata ?? [], array_filter([
'payment_payload' => $payload,
])),
])->save();
}
/**
* @return array<string, int>
*/
private function resolveAddonIncrements(EventPackageAddon $addon, string $addonKey): array
{
$stored = Arr::get($addon->metadata ?? [], 'increments', []);
if (is_array($stored) && $stored !== []) {
$filtered = collect($stored)
->map(fn ($value) => is_numeric($value) ? (int) $value : 0)
->filter(fn ($value) => $value > 0)
->all();
if ($filtered !== []) {
return $filtered;
}
}
return $this->catalog->resolveIncrements($addonKey);
}
}