基于WebRTC实现WhatsApp云API用户发起呼叫的技术咨询
基于WebRTC实现WhatsApp云API用户发起呼叫的技术咨询
我正在基于WhatsApp云API的用户发起呼叫功能做开发,按照官方流程,当用户发起呼叫时,我的Node.js后端会收到对应的Webhook事件,目前已经完成了后端的初步处理逻辑,同时React前端也在对接中,以下是详细的实现细节和收到的事件数据:
1. 收到的Webhook事件数据
当用户发起呼叫并建立连接时,后端收到的Webhook payload如下:
{ "object": "whatsapp_business_account", "entry": [ { "id": "114255454940862", "changes": [ { "value": { "messaging_product": "whatsapp", "metadata": { "display_phone_number": "91912995****", "phone_number_id": "115017831530658" }, "contacts": [ { "profile": { "name": "Rahul" }, "wa_id": "91832753****" } ], "calls": [ { "id": "wacid.HBgMOTE4MDk3NTM0Njg0FQIAEhggMURENUU5MDg2asdfNjk3OEUzMDM5MzEyRTccGAw5MTkxNjk5NTk5NTkVAgAVAgA=", "from": "91809753****", "to": "91916995****", "event": "connect", "timestamp": "1753115233", "direction": "USER_INITIATED", "session": { "sdp": "v=0\r\no=- 1753115233717 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=group:BUNDLE audio\r\na=msid-semantic: WMS 7dc1f8d4-d804-48bf-af97-8c6fe9973828\r\na=ice-lite\r\nm=audio 3484 UDP/TLS/RTP/SAVPF 111 126\r\nc=IN IP4 57.144.125.35\r\na=rtcp:9 IN IP4 0.0.0.0\r\na=candidate:2776204705 1 udp 2122260223 57.144.125.35 3484 typ host generation 0 network-cost 50\r\na=candidate:282356334 1 udp 2122262783 2a03:2880:f33e:120:face:b00c:0:699c 3484 typ host generation 0 network-cost 50\r\na=ice-ufrag:MkBWnyZXw+wMTKqA\r\na=ice-pwd:X29zIx+RGAlhVLF1/lo0oQ==\r\na=fingerprint:sha-256 D8:C5:19:9F:8C:AD:AA:21:24:7A:71:6B:4d:6D:54:BC:45:75:FA:AA:61:F8:13:E6:F5:7E:D9:14:1F:59:6D:5E\r\na=setup:actpass\r\na=mid:audio\r\na=sendrecv\r\na=msid:7dc1f8d4-d804-48bf-af97-8c6fe9973828 WhatsAppTrack1\r\na=rtcp-mux\r\na=rtpmap:111 opus/48000/2\r\na=rtcp-fb:111 transport-cc\r\na=fmtp:111 maxaveragebitrate=20000;maxplaybackrate=16000;minptime=20;sprop-maxcapturerate=16000;useinbandfec=1\r\na=rtpmap:126 telephone-event/8000\r\na=maxptime:20\r\na=ptime:20\r\na=ssrc:3435760938 cname:WhatsAppAudioStream1\r\n", "sdp_type": "offer" } } ] }, "field": "calls" } ] } ] }
这里的核心信息包括:
- 呼叫方向为USER_INITIATED,事件类型是connect
- 携带了WebRTC的SDP Offer,需要后端处理后返回Answer
2. Node.js 后端处理逻辑
我在Node.js后端写了对应的Webhook处理函数,核心逻辑包括:
- 立即返回200响应确认收到Webhook(避免WhatsApp重复推送)
- 提取呼叫ID、SDP Offer、商家账号ID等关键参数
- 通过内部接口关联对应的租户ID
- 生成简化的Pre-Accept SDP(修改DTLS模式和媒体方向)
- 调用WhatsApp云API的
pre_accept接口确认呼叫 - 通过WebSocket把处理后的SDP推送给前端
代码实现如下:
const handleUserInitiatedCallRequest = async ({ req, res }) => { try { // Send immediate 200 response for the webhook res.sendStatus(200); // Extract and validate webhook data const change = req.body.entry?.[0]?.changes?.[0] || {}; const call = change?.value?.calls?.[0] || {}; const callId = call?.id; const receivedSdp = call.session?.sdp; const WHATSAPP_BUSINESS_ACCOUNT_ID = change.value?.metadata?.phone_number_id; if (!callId || !receivedSdp || !WHATSAPP_BUSINESS_ACCOUNT_ID) { console.error("Missing required call data:", { callId, receivedSdp, WHATSAPP_BUSINESS_ACCOUNT_ID }); return; } // Retrieve tenant ID let tenantRes = await getTenantIdFromUserMetaData( "whatsapp_id", WHATSAPP_BUSINESS_ACCOUNT_ID ); console.log("Tenant response:", tenantRes); const tenant_id = tenantRes?.tenant_id; if (!tenant_id) { console.error("Failed to retrieve tenant ID"); return; } // Generate a minimal answer SDP for pre_accept (placeholder) const preAcceptSdp = receivedSdp .replace("setup:actpass", "setup:passive") .replace("a=sendrecv", "a=recvonly"); // Indicate receiving only for pre_accept // Pre-accept the call with WhatsApp API using a placeholder answer SDP const whatsappApiEndpoint = `/${WHATSAPP_BUSINESS_ACCOUNT_ID}/calls`; const preAcceptPayload = { messaging_product: "whatsapp", call_id: callId, action: "pre_accept", session: { sdp_type: "answer", // Required by API for pre_accept sdp: preAcceptSdp, // Modified SDP as a placeholder answer }, }; const requestObj = { url: `https://graph.facebook.com/${ process.env.META_GRAPH_API_VERSION || "v20.0" }${whatsappApiEndpoint}`, method: "POST", data: preAcceptPayload, headers: { Authorization: `Bearer ${process.env.JWERO_META_TOKEN}`, }, }; console.log("WhatsApp API Pre-accept Request Payload:", requestObj); // Call the WhatsApp API to pre-accept the call const { data: preAcceptResponse } = await axios(requestObj); console.log("Pre-accept API Response:", preAcceptResponse); // Emit the original offer SDP to the frontend via WebSocket const io = getIO(); io.emit(`user_call_request_${tenant_id}`, { success: true, callId: callId, sdp: preAcceptSdp, // Send the processed SDP to the frontend }); } catch (error) { console.error("Error handling call request:", getAxiosError(error)); } };
3. React 前端代码片段
前端部分我用React+Redux+Socket.io做对接,目前的代码片段如下:
import { useEffect, useRef, useState } from "react"; import { useSelector } from "react-redux"; import { io } from "socket.io-client"; // 后续将实现:接收WebSocket推送的SDP,创建RTCPeerConnection,处理媒体流渲染和呼叫控制逻辑
内容来源于stack exchange
相关产品推荐
相关产品推荐

