nexafon/nexafon-php needs PHP 8.1 or later with ext-curl, and no framework.
composer require nexafon/nexafon-phpToken endpoint
Section titled “Token endpoint”if (!nexafon_may_use($user)) { http_response_code(403); exit;}// Signed with NEXAFON_SECRET_KEY (sk_test_… or sk_live_…), which never leaves this server.$token = Tokens::createUserToken([ 'userId' => $user['id'], 'name' => $user['name'], 'role' => 'agent', 'email' => $user['email'], // Nexafon links a first sign-in to the member with this email only when it is verified: never send true for // an address your app has not confirmed, or whoever sets a member's email on your app acts as that member. 'emailVerified' => $user['emailVerified'],]);header('Content-Type: text/plain');header('Cache-Control: no-store');echo $token;Options (second argument): secretKey (defaults to NEXAFON_SECRET_KEY) and expiresIn (seconds, at most 600).
Invalid input throws Nexafon\TokenException.
Webhook receiver
Section titled “Webhook receiver”// The raw body, exactly as received: parsing it first changes the bytes the signature covers.$body = file_get_contents('php://input');try { $event = Webhook::verify(getallheaders(), $body, getenv('NEXAFON_WEBHOOK_SECRET'));} catch (WebhookVerificationException) { http_response_code(400); exit;}// Deliveries can repeat: skip an $event['id'] you have already handled.error_log($event['type'] . ' ' . $event['id']);http_response_code(204);Webhook::verify takes getallheaders(), $_SERVER or a PSR-7 request’s getHeaders(), and a list of secrets
while you rotate. In Laravel or Symfony pass $request->getContent() as the body.
The full files are docs/snippets/php/nexafon-token.php and docs/snippets/php/webhook.php in the repository.