Skip to content

nexafon/nexafon-php needs PHP 8.1 or later with ext-curl, and no framework.

Terminal window
composer require nexafon/nexafon-php
nexafon-token.php
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.php
// 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.