Hello everyone, I’ve been struggling with using the Web SDK for the component view in React JS. I have checked out the samples and have read the documentation but keep encountering an error like this:
error log: {type: ‘JOIN_MEETING_FAILED’, reason: ‘Fail to join the meeting.’, errorCode: 200}
I want to be able to start and join the meeting that I created as a host through my React application. My issue with this is that the error is very generic. I can’t understand what’s going on with this? To break down whats happening:
-
The meeting is successfully being created after getting an access token (I can join the meeting in zoom natively on the windows application by using the url from the response object)
-
I have checked my client secret key against jwt.io and am receiving ‘signature verified’
-
I am including the users ZAK key that I receive through the endpoint: ‘https://api.zoom.us/v2/users/me/token?type=zak’
I have verified that all values that can be seen in the code below are being passed correctly. I am using Chrome as my browser Version 119.0.6045.124 (Official Build) (64-bit)
but have also tried Edge and am encountering the same issue:
Version 119.0.2151.58 (Official build) (64-bit)
Which Web Meeting SDK version?
2.18
Meeting SDK Code Snippets
import React, { useEffect, useState } from 'react';
import axios from 'axios';
import { Template } from './template';
import { useSearchParams } from 'react-router-dom';
import ZoomMtgEmbedded from '@zoomus/websdk/embedded';
import { Button } from 'react-bootstrap';
export const ZoomMeeting = () => {
const [searchParams] = useSearchParams();
const code = searchParams.get('code');
const [meetingNumber, setMeetingNumber] = useState(null);
const [meetingPassword, setMeetingPassword] = useState('');
const [zak, setZak] = useState('');
const client_id = '87esZo1CRee1aSSfMssyFA';
const requestAccessToken = async () => {
try {
const zakRequest = await axios.post(
`https://localhost:8081/api/v2/zoom/access`,
{ code: code, url: 'https://localhost:3000/zoom/meeting' },
{ headers: { 'Content-Type': 'application/json' } }
);
setZak(zakRequest.data.zak);
setMeetingNumber(zakRequest.data.mn);
setMeetingPassword(zakRequest.data.password);
console.log(zakRequest.data);
} catch (err) {
console.log(err);
}
};
const startMeeting = async () => {
try {
const client = ZoomMtgEmbedded.createClient();
await requestAccessToken();
console.log('After state update:', zak, meetingNumber, meetingPassword);
const username = localStorage.getItem('user');
let meetingSDKElement = document.getElementById('meetingSDKElement');
console.log('Meeting Number: ' + meetingNumber);
const sig = await axios.post('https://localhost:8081/api/v2/zoom/CreateMeetingToken', {
mn: meetingNumber,
});
console.log({
sdkKey: client_id,
signature: sig.data,
meetingNumber: meetingNumber,
password: meetingPassword,
userName: username,
zak: zak})
await client.init({ zoomAppRoot: meetingSDKElement, language: 'en-US' });
await client.join({
sdkKey: client_id,
signature: sig.data,
meetingNumber: meetingNumber,
password: meetingPassword,
userName: username,
zak: zak,
});
console.log(client.getCurrentMeetingInfo())
} catch (error) {
ZoomMtgEmbedded.destroyClient();
console.error('Error starting the meeting:', error);
}
};
useEffect(() => {
// Fetch meeting details when the component mounts
if (code) {
requestAccessToken();
}
}, [code]);
useEffect(() => {
// This will log the updated state values after they are set
console.log('State values:', zak, meetingNumber, meetingPassword);
}, [zak, meetingNumber, meetingPassword]);
return (
<Template>
<div id='meetingSDKElement'>ZoomRoom</div>
<Button onClick={startMeeting}>Start the meeting</Button>
</Template>
);
};
Server Code Snippets
- Generate Meeting Signature
ZoomRouter.post('/CreateMeetingToken', async (request, response) => {
try {
const iat = Math.round(new Date().getTime() / 1000);
const exp = iat + 60 * 60 * 2;
const oHeader = { alg: 'HS256', typ: 'JWT' };
const sHeader = JSON.stringify(oHeader);
const oPayload = {
appKey: process.env.ZOOM_SDK_CLIENT_ID,
sdkKey: process.env.ZOOM_SDK_CLIENT_ID,
meetingNumber: request.body.mn,
role: 1,
iat: iat,
exp: exp,
tokenExp: exp,
};
const sPayload = JSON.stringify(oPayload);
// Sign the JWT
const sdkJWT = KJUR.jws.JWS.sign('HS256', sHeader, sPayload, process.env.ZOOM_SDK_CLIENT_SECRET);
return response.status(200).json(sdkJWT);
} catch (err) {
console.log(err);
return response.status(500).json({ error: 'Internal server error' });
}
});
- Get ZAK and Meeting Number + Password
ZoomRouter.post('/access', async (request, response) => {
if (!request.body.code || !request.body.url) {
return response.status(400).json({ error: 'Invalid request body' });
}
try {
const client_id = process.env.ZOOM_SDK_CLIENT_ID;
const client_secret = process.env.ZOOM_SDK_CLIENT_SECRET;
const data = {
code: request.body.code,
grant_type: 'authorization_code',
redirect_uri: request.body.url,
};
const formData = querystring.stringify(data);
const authHeader = Buffer.from(`${client_id}:${client_secret}`).toString('base64');
const config = {
headers: {
Authorization: `Basic ${authHeader}`,
'Content-Type': 'application/x-www-form-urlencoded',
},
};
const accessToken = (await axios.post('https://zoom.us/oauth/token', formData, config)).data.access_token;
console.log(`Bearer ${accessToken}`)
const zakToken = await axios.get('https://api.zoom.us/v2/users/me/token?type=zak',{headers:{Authorization:'Bearer ' + accessToken}})
let working = false
let tryCounter = 0
let meeting = null
const pause = (milliseconds:number) => {
return new Promise(resolve => setTimeout(resolve, milliseconds));
};
await pause(5000);
while(!working && tryCounter <= 2){
try{
meeting = await axios.post('https://api.zoom.us/v2/users/me/meetings',{},{
headers: {
Authorization: `Bearer ${accessToken}`
},
})
working = true
console.log('success')
}catch{
tryCounter += 1
console.log('catch')
await pause(10000);
}
}
console.log(meeting?.data)
return response.status(200).json({zak:zakToken.data.token,mn:meeting?.data.id,password:meeting?.data.encrypted_password,email:meeting?.data.host_email});
} catch (err) {
console.log(err)
return response.status(500).json({ error: 'Internal server error' });
}
});