https://a.storyblok.com/f/270183/1368x665/68e78d6262/25mar_dev_blog_php-stream-audio-2.jpg

Reproducir un archivo de audio en una llamada de voz con PHP

Publicado el June 18, 2025

Tiempo de lectura: 10 minutos

Cada vez que llamo mi centro de ocio para reservar una sesión de natación, me reciben con un anuncio de las últimas novedades: el centro cierra dos horas el próximo lunes, el horario del baño familiar ha cambiado, etc. En este artículo, te mostraré un ejemplo de cómo hacer esto con Voice API de Vonage y un backend PHP escrito en el Slim Framework para reproducir audio en una llamada telefónica en curso.

Requisitos previos

Vonage API Account

To complete this tutorial, you will need a Vonage API account. If you don’t have one already, you can sign up today and start building with free credit. Once you have an account, you can find your API Key and API Secret at the top of the Vonage API Dashboard.

En primer lugar, necesitamos una nueva aplicación Slim. Voy a hacer esto tan absolutamente vainilla como sea posible - sin grandes boilerplates, sin plantillas automáticas. Vamos a tener un punto final que desencadenará una llamada saliente, y un punto final entrante para leer el webhook generado cuando el usuario final coge una llamada.

Crea un directorio que contendrá nuestro código, y luego utiliza Composer para generar un nuevo proyecto:

composer init

Puedes saltarte todas las preguntas interactivas pulsando return, que utilizará los valores por defecto. Cuando se le pregunte si desea agregar paquetes de forma interactiva, asegúrese de negarse; esto es para que pueda mostrar los comandos en este artículo para tirar en el marco.

Composer generará su archivo de configuración composer.json, y una carpeta de dependencias, vendor. La configuración por defecto también creará el directorio que albergará tu código , src.

En la línea de comandos, tendremos que meter el framework Slim y una interfaz PSR-7 para las clases request/response. El comando final incluirá el Vonage PHP SDKque se encarga de todo el trabajo pesado para realizar solicitudes de API por ti. Por último, nuestro cliente de Vonage está configurado con variables de entorno, por lo que necesitaremos la biblioteca de facto para manejar eso.

composer require slim/slim
composer require slim/psr7
composer require vonage/client
composer require vlucas/phpdotenv

Ahora que tenemos Slim y las demás dependencias necesarias, podemos comenzar a escribir la aplicación. El SDK PHP de Vonage requiere un ID de aplicación y una clave privada, los cuales obtienes al crear una nueva instancia de aplicación en el panel de Vonage. También puedes hacerlo usando la CLI de Vonageque es el camino que tomaremos.

Instalar y configurar la CLI de Vonage

Instala la CLI de Vonage globalmente usando el siguiente comando:

npm install @vonage/cli -g

A continuación configura la CLI con tu clave y secreto de API de Vonage, que encontrarás en el panel para desarrolladores:

Vonage auth set –api-key=’VONAGE_API_KEY –api-secret=’VONAGE_API_SECRET’

Sustituya el VONAGE_API_KEY y VONAGE_API_SECRET con sus datos para autenticar la CLI.

Abra su Página de configuración de API para acceder a tu clave y secreto de API de Vonage, que aparecen como se muestra en la captura de pantalla a continuación. La clave de API se encuentra en la parte superior de la página, y para acceder a tu secreto de API, consulta la subsección "Secreto de Account".

Nota: En caso de que no recuerde su secreto de API creado anteriormente, haga clic en "+ Crear nuevo secreto" y guárdelo de forma segura.

Puedes validar tu autenticación para asegurarte de que todo funciona utilizando el siguiente comando:

vonage auth check

Si has instalado el CLI en tu proyecto local en lugar de globalmente, puedes añadir una bandera extra para comprobarlo:

Vonage auth check --local

Para comprar un número de teléfono virtual, vaya a su panel API y siga los pasos que se indican a continuación.

Steps on how to purchase a phone number from the dashboard, from selecting the number and confirming the selection.Purchase a phone number

  1. Vaya a su Panel API

  2. Vaya a CONSTRUIR Y GESTIONAR > Numbers > Comprar Numbers.

  3. Elija los atributos necesarios y haga clic en Buscar

  4. Pulse el botón Comprar junto al número que desee y valide su compra

  5. Para confirmar que ha adquirido el número virtual, vaya al menú de navegación de la izquierda, en CONSTRUIR Y GESTIONAR, haga clic en Numbers y, a continuación, en Sus Numbers.

Crear la aplicación Voice

Para utilizar la Voice API, debe crear una aplicación Voice API de voz. Esto no es lo mismo que la aplicación web que estás creando. Es simplemente un contenedor para la información de configuración y seguridad que necesitas para conectarte a las API de Vonage.

vonage apps create "Play audio app" --private-key-file=~/private.key

Recibirá los siguientes datos (de ejemplo, ficticios) para que los anote:

Nombre: play audio app

ID de solicitud: 7a319bf9-49e7-413d-914b-402fe8e68XXX

Mejorar la IA: Off

Clave privada/pública: Establecer

Observará que hemos dado al comando el argumento private-key-file. Esto descargará tu clave privada por ti una vez que el comando se haya ejecutado: localiza este archivo de clave (en el comando, he usado el argumento de tipo UNIX ~/ locationque es la carpeta de inicio; querrás ponerlo en algo como C:\Users\YOUR-USER-NAME si estás usando Powershell en Windows) y coloca el archivo en el directorio raíz de tu proyecto.

Todavía no hemos configurado la aplicación para que utilice la Voice API, así que ejecute el siguiente comando para añadir la capacidad de Voice:

vonage apps capabilities update [VONAGE_APPLICATION_ID] --voice-answer-url=https://example.com/answer --voice-event-url=https://example.com/event-status

Observará que estamos configurando el voice-answer-url y el voice-event-url a nivel de aplicación. Estos son marcadores de posición por ahora, y no es necesario cambiar estos valores. La razón es que el código utilizado para realizar la llamada creará dinámicamente estos valores durante las llamadas HTTP que se realicen.

Porque nunca nunca poner secretos en tu código basecree un nuevo archivo en la raíz de su proyecto llamado .env. Rellena las variables con la información correspondiente, siendo el TO_NUMBER el número al que llama el marcador. Nos ocuparemos del BASE_URL más adelante. La dirección VONAGE_PRIVATE_KEY_PATH puede dejarse como está en el ejemplo a continuación, y el VONAGE_NUMBER debe ser un número virtual de Vonage comprado en el panel de control.

En el directorio src cree un nuevo archivo, index.php, y añade el siguiente código:

<?php

use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;
use Vonage\Client\Credentials\Keypair;
use Vonage\Voice\Endpoint\Phone;
use Vonage\Voice\OutboundCall;
use Vonage\Voice\Webhook;

require DIR . '/../vendor/autoload.php';

$dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . '/../');
$dotenv->load();

$app = AppFactory::create();

$app->get('/dial', function (Request $request, Response $response, array $args) {
   $privateKeyPath = realpath(__DIR__ . '/../' . $_ENV['VONAGE_APPLICATION_PRIVATE_KEY_PATH']);
   $credentials = new Keypair(
       file_get_contents($privateKeyPath),
       $_ENV['VONAGE_APPLICATION_ID']
   );

   $client = new \Vonage\Client($credentials);

   $call = new OutboundCall(
       new Phone($_ENV['TO_NUMBER']),
       new Phone($_ENV['VONAGE_NUMBER'])
   );

   $call
       ->setAnswerWebhook(
           new Webhook($_ENV['BASE_URL'] . '/webhooks/answer', 'POST')
       )
       ->setEventWebhook(
           new Webhook($_ENV['BASE_URL'] . '/webhooks/event', 'POST')
       )
   ;

   $client->voice()->createOutboundCall($call);
   $response->getBody()->write('Outbound Call Started');
   return $response;
});

$app->post('/webhooks/answer', function (Request $request, Response $response, array $args) {
   $builder = new Vonage\Voice\NCCO\NCCO();
   $builder->addAction(new \Vonage\Voice\NCCO\Action\Talk('Here is some soothing music for you'));
   $builder->addAction(new \Vonage\Voice\NCCO\Action\Stream($_ENV['BASE_URL'] . '/music.mp3'));

   $response->getBody()->write(json_encode($builder->jsonSerialize()));

   return $response->withHeader('Content-Type', 'application/json');

});

$app->post('/webhooks/event', function (Request $request, Response $response, array $args) {
   var_dump($request->getParsedBody());
});

$app->run();

Aquí pasan muchas cosas, así que vamos a desglosarlas.

$dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . '/../');
$dotenv->load();

Este código carga el contenido del archivo .env en el superglobal de PHP $_ENV para configurar nuestro cliente de Vonage. $app = AppFactory::crear() crea una nueva instancia de aplicación Slim para que podamos configurar nuestros puntos finales.

Nuestro marcador saliente se define como una ruta HTTP GET, /dial. Por lo tanto, el código a continuación se ejecutará al presionar esto. Es aquí donde configuramos el cliente de Vonage utilizando nuestras credenciales, a las que se puede acceder desde el objeto $_ENV objeto:

$privateKeyPath = realpath(__DIR__ . '/../' . $_ENV['VONAGE_APPLICATION_PRIVATE_KEY_PATH']);
$credentials = new Keypair(
   file_get_contents($privateKeyPath),
   $_ENV['VONAGE_APPLICATION_ID']
);

$client = new \Vonage\Client($credentials);

Existen múltiples maneras de usar las credenciales en Vonage. Aún así, lo importante es tener en cuenta que el par de claves toma la cadena del archivo de clave privada de tu aplicación de Vonage y el ID de la aplicación. Lo que hace el SDK de PHP que es bastante práctico es que, en tiempo de ejecución, tu JWT se crea y se agrega al encabezado automáticamente por ti. Esto es importante para la aplicación porque mientras tú puede utilizar la autenticación básica con algunas API de Vonage, la configuración de Webhook entrante sólo funcionará con una JWT por razones de seguridad.

Es posible configurar una aplicación de Vonage dentro del panel para que siempre se conecte con la misma URL de respuesta y la misma URL de evento para las actualizaciones continuas de la aplicación. Sin embargo, como puedes ver en el código utilizado, también puedes configurar dinámicamente estos puntos finales de webhook. Esto sería útil cuando tiene instancias de varios flujos de trabajo diferentes y está rastreando el progreso de llamadas individuales. Por ejemplo, puede añadir una tabla de base de datos que puede hacer operaciones de estilo CRUD con un UUID en la ruta URL. Esto le da la flexibilidad de manejar múltiples consultas a la vez, si utiliza algo como Enlace de modelo de ruta con Laravel Octane. Nuestro código Slim es un poco más simple para facilitar la lectura:

$call = new OutboundCall(
   new Phone($_ENV['TO_NUMBER']),
   new Phone($_ENV['VONAGE_NUMBER'])
);

$call
   ->setAnswerWebhook(
       new Webhook($_ENV['BASE_URL'] . '/webhooks/answer', 'POST')
   )
   ->setEventWebhook(
       new Webhook($_ENV['BASE_URL'] . '/webhooks/event', 'POST')
   );

$client->voice()->createOutboundCall($call);

$response->getBody()->write('Outbound Call Started');

return $response;

La ruta del Webhook se configura mediante la variable de entorno BASE_URL. Ahora que tenemos la ruta de la llamada saliente, tendremos que definir lo que ese BASE_URL es.

Primero tendrás que servir la aplicación localmente. Para hacer las cosas lo más simple posible, usaremos el servidor incorporado en PHP. Navega hasta el directorio src y arráncalo:

php -S localhost:8080

Nuestros Webhooks son POST que provienen directamente de Vonage. Por lo tanto, sin exponer nuestra aplicación a Internet, no hay forma de que Vonage pueda llegar a nuestra aplicación localhost. La herramienta más utilizada para exponerla sería Ngrokque puedes encontrar instrucciones de instalación aquí. Si quiere mantenerlo estrictamente PHP, puede usar Expose de Beyond Codede Beyond Code, que aprovecha el poder de un bucle de eventos proporcionado por ReactPHP. Lo importante es ejecutar una de estas herramientas y anotar la URL que te devuelve. A partir de ahí, se pega en el BASE_URL en el archivo .env y ¡listo! Una forma de consumir los datos entrantes.

Nuestra última parte del código de ejecución toma esos datos, anuncia que el sonido está llegando, y luego reproduce el sonido.

$app->post('/webhooks/answer', function (Request $request, Response $response, array $args) {
   $builder = new Vonage\Voice\NCCO\NCCO();
   $builder->addAction(new \Vonage\Voice\NCCO\Action\Talk('Here is some soothing music for you'));
   $builder->addAction(new \Vonage\Voice\NCCO\Action\Stream($_ENV['BASE_URL'] . '/audio.mp3'));

   $response->getBody()->write(json_encode($builder->jsonSerialize()));

   return $response->withHeader('Content-Type', 'application/json');
});

Este código consiste básicamente en crear un JSON para responder a Vonage, que luego procesará las instrucciones. Esto se hace usando objetos NCCO únicos de Vonage, ¡y la buena noticia es que el SDK de PHP tiene un constructor para facilitar esto! Tenemos dos acciones: primero, una Hablar que se activará cuando se responda la llamada, y luego una acción Flujo para el audio. Te darás cuenta de que hay una URL aquí que utiliza la directiva BASE_URL y un archivo llamado audio.mp3. Este es un marcador de posición - por lo que para que funcione, coloque cualquier archivo llamado audio.mp3 en el directorio src directamente, y eso será lo que se reproduzca en la llamada. Vonage puede hacer esto porque el archivo de audio vive en el mismo directorio que será expuesto por el servidor web incorporado.

Ya está todo listo: navegue hasta su URL para /dial y disfruta de lo que elijas para tu archivo de audio, ya sea relajante jazz de ascensor o, si realmente no te gustan tus usuarios finales, algo de dubstep moderno.

Conclusión

En este post, aprendiste cómo reproducir audio en una llamada existente y sobre las NCCO acciones que rigen el flujo de llamadas. Siéntase libre de experimentar sustituyendo diferentes archivos de audio y acciones NCCO acciones. Los siguientes recursos pueden serle de ayuda:

¿Tienes alguna pregunta o algo que compartir? Únete a la conversación en Slack de la comunidad de Vonagey mantente actualizado con el Boletín para desarrolladoressíguenos en X (antes Twitter)suscríbete a nuestro canal de YouTube para ver tutoriales en video, y sigue la página de página para desarrolladores de Vonage en LinkedInun espacio para que los desarrolladores aprendan y se conecten con la comunidad. Mantente conectado, comparte tu progreso y entérate de las últimas noticias, consejos y eventos para desarrolladores.

Compartir:

https://a.storyblok.com/f/270183/400x385/12b3020c69/james-seconde.png
James SecondePromotor senior de desarrollo PHP

Actor de formación con una disertación sobre la comedia, llegué al desarrollo de PHP a través de la escena de las reuniones. Puedes encontrarme hablando y escribiendo sobre tecnología, o tocando/comprando discos raros de mi colección de vinilos.