forked from admin/services_core
91 lines
3.5 KiB
PHP
91 lines
3.5 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Notification;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\NotificationChannel;
|
|
use App\Models\NotificationDelivery;
|
|
use App\Models\NotificationWebhookLog;
|
|
use Illuminate\Http\Request;
|
|
|
|
class NotificationWebhookController extends Controller
|
|
{
|
|
public function verify(Request $request, NotificationChannel $notificationChannel)
|
|
{
|
|
abort_unless(
|
|
$request->query('hub.mode') === 'subscribe'
|
|
&& hash_equals((string) data_get($notificationChannel->credentials, 'webhook_verify_token'), (string) $request->query('hub.verify_token')),
|
|
403,
|
|
);
|
|
|
|
return response($request->query('hub.challenge'), 200);
|
|
}
|
|
|
|
public function handle(Request $request, NotificationChannel $notificationChannel)
|
|
{
|
|
$valid = $this->validSignature($request, $notificationChannel);
|
|
$payload = $request->all();
|
|
$updates = $this->updates($payload);
|
|
$log = NotificationWebhookLog::create([
|
|
'notification_channel_id' => $notificationChannel->id,
|
|
'provider' => $notificationChannel->provider,
|
|
'event_type' => data_get($payload, 'entry.0.changes.0.field') ?? data_get($payload, 'event'),
|
|
'provider_message_id' => data_get($updates, '0.id'),
|
|
'signature_valid' => $valid,
|
|
'payload' => $payload,
|
|
]);
|
|
abort_unless($valid, 401);
|
|
|
|
foreach ($updates as $update) {
|
|
$delivery = NotificationDelivery::where('provider_message_id', $update['id'] ?? null)->first();
|
|
if (! $delivery) {
|
|
continue;
|
|
}
|
|
$status = match ($update['status'] ?? null) {
|
|
'delivered' => 'delivered',
|
|
'read' => 'read',
|
|
'failed' => 'failed',
|
|
default => 'sent',
|
|
};
|
|
$delivery->update([
|
|
'status' => $status,
|
|
'delivered_at' => $status === 'delivered' ? now() : $delivery->delivered_at,
|
|
'read_at' => $status === 'read' ? now() : $delivery->read_at,
|
|
'failed_at' => $status === 'failed' ? now() : $delivery->failed_at,
|
|
'error_code' => data_get($update, 'errors.0.code'),
|
|
'error_message' => data_get($update, 'errors.0.title'),
|
|
]);
|
|
}
|
|
$log->update(['processed_at' => now()]);
|
|
|
|
return response()->json(['success' => true]);
|
|
}
|
|
|
|
private function validSignature(Request $request, NotificationChannel $channel): bool
|
|
{
|
|
if ($channel->channel === 'whatsapp_official') {
|
|
$secret = data_get($channel->credentials, 'app_secret');
|
|
$signature = $request->header('X-Hub-Signature-256');
|
|
|
|
return $secret && $signature && hash_equals('sha256='.hash_hmac('sha256', $request->getContent(), $secret), $signature);
|
|
}
|
|
$secret = data_get($channel->credentials, 'webhook_secret');
|
|
|
|
return $secret && hash_equals((string) $secret, (string) $request->header('X-Webhook-Secret'));
|
|
}
|
|
|
|
private function updates(array $payload): array
|
|
{
|
|
$statuses = data_get($payload, 'entry.0.changes.0.value.statuses');
|
|
if (is_array($statuses)) {
|
|
return $statuses;
|
|
}
|
|
|
|
return [[
|
|
'id' => data_get($payload, 'message_id') ?? data_get($payload, 'id'),
|
|
'status' => data_get($payload, 'status') ?? data_get($payload, 'event'),
|
|
'errors' => data_get($payload, 'errors', []),
|
|
]];
|
|
}
|
|
}
|