基于PHP/Laravel与Nexmo Voice API实现双向私密语音通话技术咨询
I get it—you’ve got the TTS one-way call working, but now you need that anonymous two-way bridge for your ride-hailing/food delivery platform where neither the user nor the driver sees each other’s real numbers. Let’s walk through how to make this happen, including clearing up those web-side questions.
Core Concept: Anonymous Call Bridging with NCCOs
Unlike your TTS setup, two-way anonymous calls rely on Vonage’s Call Control Objects (NCCO). Here’s the flow:
- When your user clicks the call button, your Laravel backend tells Vonage to initiate a call to the user’s phone (or their browser via the Vonage Client SDK).
- Once the user answers, Vonage automatically dials the driver’s number using your purchased Nexmo/Vonage number as the "from" ID for both parties.
- The two calls are bridged—both sides only see your Vonage number, not each other’s real digits.
Web端 Requirements
Yes, your web users will need a headset (or speakers + microphone) to participate. Browsers require media permissions (microphone and audio output) to handle voice calls, so your frontend will need to prompt the user for access. We’ll cover a basic frontend snippet later.
Step-by-Step Implementation in Laravel
1. Set Up the Vonage Laravel Package
First, ditch the raw CURL and use the official Laravel integration—it’s way cleaner:
composer require vonage/vonage-laravel
Then add your credentials to .env:
VONAGE_KEY=your_api_key VONAGE_SECRET=your_api_secret VONAGE_NUMBER=your_purchased_nexmo_number # e.g., 16169281703
2. Backend: Initiate the Anonymous Call
Create a controller method to handle the call request (e.g., CallController@initiateAnonymousCall). This will generate the NCCO and trigger the call via Vonage’s Voice API.
<?php namespace App\Http\Controllers; use Vonage\Client; use Vonage\Voice\NCCO\Action\Connect; use Vonage\Voice\NCCO\NCCO; use Vonage\Voice\OutboundCall; use Illuminate\Http\Request; class CallController extends Controller { public function initiateAnonymousCall(Request $request) { // Validate incoming request (user phone, driver phone) $request->validate([ 'user_phone' => 'required|string', // e.g., +14083934444 (use E.164 format) 'driver_phone' => 'required|string' // e.g., +15551234567 ]); $vonage = app(Client::class); // Build the NCCO to bridge the two calls $ncco = new NCCO(); // First, connect to the user (show your Vonage number as caller ID) $userConnect = new Connect(); $userConnect->setTo($request->user_phone); $userConnect->setFrom(env('VONAGE_NUMBER')); // Once user answers, connect to the driver (again, use your Vonage number as caller ID) $driverConnect = new Connect(); $driverConnect->setTo($request->driver_phone); $driverConnect->setFrom(env('VONAGE_NUMBER')); $ncco->addAction($userConnect); $ncco->addAction($driverConnect); // Initiate the outbound call to the user first $call = new OutboundCall( $request->user_phone, env('VONAGE_NUMBER') ); $call->setNCCO($ncco); $response = $vonage->voice()->createOutboundCall($call); // Optionally, save call ID to your database for tracking // DB::table('calls')->insert([ // 'call_id' => $response['uuid'], // 'user_phone' => $request->user_phone, // 'driver_phone' => $request->driver_phone, // 'created_at' => now() // ]); return response()->json([ 'success' => true, 'call_id' => $response['uuid'] ]); } }
3. Handle Webhooks (Call Status Updates)
Vonage will send webhook events (like call started, answered, ended) to a URL you specify. Create a webhook controller to log these events:
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; class VonageWebhookController extends Controller { public function callStatus(Request $request) { // Log call status to your database for auditing DB::table('call_logs')->insert([ 'call_id' => $request->input('uuid'), 'status' => $request->input('status'), 'from' => $request->input('from'), 'to' => $request->input('to'), 'timestamp' => now() ]); return response('', 200); // Vonage expects a 200 OK response to confirm receipt } }
Add the route in routes/api.php:
Route::post('/vonage/webhook/call-status', [VonageWebhookController::class, 'callStatus']);
Then configure this webhook URL in your Vonage dashboard under Voice > Applications > Your App > Webhooks.
4. Frontend: Trigger the Call (Web User)
If your user is initiating the call from the browser (instead of a phone), use the Vonage Client SDK to handle in-browser audio. Here’s a basic example:
<button id="callDriverBtn">Call Driver</button> <script src="https://cdn.jsdelivr.net/npm/@vonage/client@latest/dist/vonageClient.min.js"></script> <script> const callDriverBtn = document.getElementById('callDriverBtn'); callDriverBtn.addEventListener('click', async () => { try { // Request microphone/audio permissions first await navigator.mediaDevices.getUserMedia({ audio: true }); // Fetch session ID and token from your Laravel backend (generate these server-side) const authResponse = await fetch('/api/vonage/generate-client-token'); const { sessionId, token } = await authResponse.json(); // Initialize Vonage Client const client = new VonageClient({ apiKey: 'your_vonage_key', sessionId, token }); await client.connect(); // Trigger the call (your backend will handle the bridging logic) const call = await client.call({ number: 'driver_phone_number', from: 'your_vonage_number' }); // Handle call events call.on('connected', () => alert('Call connected!')); call.on('ended', () => alert('Call ended.')); } catch (error) { console.error('Call failed:', error); alert('Failed to start call. Please check your audio permissions.'); } }); </script>
Note: You’ll need to add a server-side endpoint to generate the sessionId and token using Vonage’s Client SDK—check the official docs for generating user tokens for browser calls.
Key Notes to Avoid Headaches
- Number Formatting: Always use E.164 format for phone numbers (e.g.,
+14083934444instead of14083934444). - Anonymous Guarantee: By setting the
fromparameter to your Vonage number for both connections, neither party will see the other’s real number. - Error Handling: Add try/catch blocks in your Laravel controller to handle cases like invalid numbers, insufficient balance, or API timeouts.
内容的提问来源于stack exchange,提问作者Ganesh Selvarajan




