132 lines
4.6 KiB
PHP
132 lines
4.6 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\Log;
|
|
use Illuminate\Support\Facades\Notification;
|
|
|
|
class EventAddonWebhookService
|
|
{
|
|
public function __construct(private readonly EventAddonCatalog $catalog) {}
|
|
|
|
public function handle(array $payload): bool
|
|
{
|
|
$eventType = $payload['event_type'] ?? null;
|
|
$data = $payload['data'] ?? [];
|
|
|
|
if ($eventType !== 'transaction.completed') {
|
|
return false;
|
|
}
|
|
|
|
$metadata = $this->extractMetadata($data);
|
|
$intentId = $metadata['addon_intent'] ?? null;
|
|
$addonKey = $metadata['addon_key'] ?? null;
|
|
|
|
if (! $intentId || ! $addonKey) {
|
|
return false;
|
|
}
|
|
|
|
$transactionId = $data['id'] ?? $data['transaction_id'] ?? null;
|
|
$checkoutId = $data['checkout_id'] ?? null;
|
|
|
|
$addon = EventPackageAddon::query()
|
|
->where('addon_key', $addonKey)
|
|
->where(function ($query) use ($intentId, $checkoutId, $transactionId) {
|
|
$query->where('metadata->addon_intent', $intentId)
|
|
->orWhere('checkout_id', $checkoutId)
|
|
->orWhere('transaction_id', $transactionId);
|
|
})
|
|
->latest('id')
|
|
->first();
|
|
|
|
if (! $addon) {
|
|
Log::info('[AddonWebhook] Add-on intent not found', [
|
|
'intent' => $intentId,
|
|
'addon_key' => $addonKey,
|
|
'transaction_id' => $transactionId,
|
|
]);
|
|
|
|
return false;
|
|
}
|
|
|
|
if ($addon->status === 'completed') {
|
|
return true; // idempotent
|
|
}
|
|
|
|
$increments = $this->catalog->resolveIncrements($addonKey);
|
|
|
|
DB::transaction(function () use ($addon, $transactionId, $checkoutId, $data, $increments) {
|
|
$addon->forceFill([
|
|
'transaction_id' => $transactionId,
|
|
'checkout_id' => $addon->checkout_id ?: $checkoutId,
|
|
'status' => 'completed',
|
|
'amount' => Arr::get($data, 'totals.grand_total') ?? Arr::get($data, 'amount'),
|
|
'currency' => Arr::get($data, 'currency_code') ?? Arr::get($data, 'currency'),
|
|
'metadata' => array_merge($addon->metadata ?? [], ['webhook_payload' => $data]),
|
|
'receipt_payload' => Arr::get($data, 'receipt_url') ? ['receipt_url' => Arr::get($data, 'receipt_url')] : null,
|
|
'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));
|
|
}
|
|
}
|
|
});
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $data
|
|
* @return array<string, mixed>
|
|
*/
|
|
private function extractMetadata(array $data): array
|
|
{
|
|
$metadata = [];
|
|
|
|
if (isset($data['metadata']) && is_array($data['metadata'])) {
|
|
$metadata = $data['metadata'];
|
|
}
|
|
|
|
if (isset($data['custom_data']) && is_array($data['custom_data'])) {
|
|
$metadata = array_merge($metadata, $data['custom_data']);
|
|
}
|
|
|
|
return $metadata;
|
|
}
|
|
}
|