You don’t seem to be handling the challenge POST at all. See example flow.
Here’s how I handle the POSTs in my small php script (notice the hub.challenge part):
<?php
ob_start();
header('Content-Type: text/plain');
if ($_REQUEST['hub.challenge']) {
die($_REQUEST['hub.challenge']);
}
if ($_SERVER['REQUEST_METHOD'] !== 'POST') die('not post');
$reqbody = file_get_contents('php://input');
header('Connection: close');
header('Content-Length: '.ob_get_length());
ob_end_flush();
ob_flush();
fastcgi_finish_request();
$json = [
'content' => "```\npostdata: $reqbody\n```"
];
$c = curl_init();
curl_setopt($c, CURLOPT_URL, "https://canary.discordapp.com/api/webhooks/-snip-");
curl_setopt($c, CURLOPT_POST, 1);
curl_setopt($c, CURLOPT_POSTFIELDS, json_encode($json));
curl_setopt($c, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_exec($c);
curl_close($c);