118 lines
4.1 KiB
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);
|
|
}
|
|
}
|