getContent(); $verifier=match($provider){'paddle'=>app(PaddleWebhookVerifier::class),'lemon-squeezy'=>app(LemonSqueezyWebhookVerifier::class),default=>null}; if (!$verifier) return response()->json(['message'=>'Unknown webhook provider.'],404); try { $verifier->verify($request,$raw); } catch (\Throwable $e) { report($e); return response()->json(['message'=>'Invalid webhook signature.'],400); } try { $payload=json_decode($raw,true,flags:JSON_THROW_ON_ERROR); } catch (\Throwable) { return response()->json(['message'=>'Invalid JSON.'],400); } $externalID=(string)($provider==='paddle'?($payload['event_id']??''):($payload['meta']['event_name']??'').':'.($payload['data']['id']??'')); $eventType=(string)($provider==='paddle'?($payload['event_type']??'unknown'):($payload['meta']['event_name']??'unknown')); if ($externalID==='') return response()->json(['message'=>'Webhook event has no stable identifier.'],400); try { DB::transaction(function() use($provider,$externalID,$eventType,$payload,$raw,$provisioner){ $event=LicenseWebhookEvent::firstOrCreate(['provider'=>$provider,'external_event_id'=>$externalID],['event_type'=>$eventType,'payload_sha256'=>hash('sha256',$raw),'payload'=>$payload]); if ($event->processed_at) return; if (!hash_equals($event->payload_sha256,hash('sha256',$raw))) throw new \RuntimeException('Webhook ID was reused with a different payload.'); try { $provisioner->provision($provider,$payload); $event->update(['processed_at'=>now(),'processing_error'=>null]); } catch (\Throwable $e) { $event->update(['processing_error'=>mb_substr($e->getMessage(),0,4000)]); throw $e; } }); } catch (\Throwable $e) { report($e); return response()->json(['message'=>'Webhook could not be processed.'],500); } // Delivery is intentionally outside the webhook transaction. A temporary mail // outage must not roll back an otherwise valid Paddle event. Pending outbox // rows remain available for portal resend or a later webhook delivery attempt. try { $delivery->deliverPending(10); } catch (\Throwable $e) { report($e); } return response()->json(['ok'=>true]); } }