更新记录
1.0.4(2026-10-09)
- 精简代码
1.0.3(2026-10-09)
- 优化
1.0.2(2026-10-08)
- 优化
查看更多
平台兼容性
uni-app(3.8.5)
| Vue2 |
Vue3 |
Chrome |
Safari |
app-vue |
app-nvue |
Android |
iOS |
鸿蒙 |
| √ |
√ |
- |
- |
- |
- |
- |
17 |
- |
| 微信小程序 |
支付宝小程序 |
抖音小程序 |
百度小程序 |
快手小程序 |
京东小程序 |
鸿蒙元服务 |
QQ小程序 |
飞书小程序 |
小红书小程序 |
快应用-华为 |
快应用-联盟 |
| - |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
uni-app x(3.8.5)
| Chrome |
Safari |
Android |
iOS |
鸿蒙 |
微信小程序 |
| - |
- |
- |
17 |
- |
- |
其他
| 多语言 |
暗黑模式 |
宽屏模式 |
蒸汽模式 |
| √ |
√ |
√ |
√ |
VOIP,唤醒未启动app、后台app
本插件可以上线中国大陆
集成步骤
- 拷贝demo里的nativeResources、Info.plist文件到项目根目录
- ios需要商店证书的Identifiers对应app的Capabilities里勾选Push Notifications,然后在下载打包的.mobileprovision
- nativeResources/ios/UniApp.entitlements文件里的aps-environment对应的值是development、production,一般开发阶段使用development,生产发布使用production,根据情况设置
- 咨询或定制请点击上面"进入交流群"按钮私聊作者
iOS VOIP服务器配置
- 登录苹果开发者系统生成VOIP证书(百度有很多资料),这个证书是voip的证书(Certificates->Services->VoIP Services Certificate),不是消息推送的证书
- 将cer证书和p12文件生成各自后端语言支持的证书,如:
- 其他语言可以参考苹果官方https://developer.apple.com/documentation/usernotifications/sending-notification-requests-to-apns
技术方案
iOS
- 仅支持iOS 17.4以后的系统
iOS采用通过后端接口调用官方的voip接口发送数据给app或者调用官方的远程消息推送接口,app收到数据后可以拉起系统来电界面或发送通知到消息栏,通过/voip接口参数判断哪种方式
- 接口/livevoip,给手机发送voip来电
用户接听或拒绝来电都会拉起app
{
"token": "xxxxx",
"payload": {
"business": {
"type": 0,
"incomingCallParams": {
"members": [
{
"type": 1,
"value": "***",
"displayName": "张三来电"
}
],
"activeRemoteMembers": [],
"capabilities": [
"video"
]
}
}
}
}
nodeJS语言服务器
服务器代码在demo/static/project-voip-server下
将voip证书voip_services.cer导入到钥匙串,然后右键证书导出为文件voiptext.p12,然后通过下面命令将voip_services.cer和voiptext.p12都转换为pem格式
nodeJS语言需要cer和p12转成pem
cer转pem:
openssl x509 -inform DER -in voip_services.cer -outform PEM -out cer.pem
p12转pem:
openssl pkcs12 -in voiptext.p12 -nodes -legacy -out key.pem
// openssl pkcs12 -in voiptext.p12 -nodes -password pass:1234 -legacy -out key.pem
或者去掉-legacy,有些版本的openssl不需要-legacy参数:
openssl pkcs12 -in voiptext.p12 -nodes -legacy -out key.pem
// openssl pkcs12 -in voiptext.p12 -nodes -password pass:1234 -legacy -out key.pem
将cer.pem、key.pem文件分别替换project-voip-server/customer_cer/mine下的文件
修改project-voip-server/src/app.controller.ts代码下的passphrase证书密码:
// 证书密码
@ApiResponse({
status: 200,
description: '返回数据',
type: RespEntity,
})
@ApiOperation({ summary: '发起livevoip来电' })
@Post('livevoip')
livevoip(@Body() body: LiveVoipDto): any {
const token = body.token;
if (token) {
const dir = process.cwd();
const cert = join(dir, 'customer_cer/livevoip/cer.pem');
const key = join(dir, 'customer_cer/livevoip/key.pem');
const passphrase = '123456';
// key密码
// const cert = join(dir, 'customer_cer/customer02/cer.pem');
// const key = join(dir, 'customer_cer/customer02/key.pem');
// const passphrase = 'mjdq*2024';
const production = false;
const useCer = true;
let service = new apn.Provider({
cert: cert,
key: key,
passphrase: passphrase, //ios push 证书密码
production: production, //true使用ios正式环境push通道
});
if (!useCer) {
service = new apn.Provider({
token: {
key: 'path/to/your/voip_key.p8', // 证书路径
keyId: 'your_key_id', // 键ID
teamId: 'your_team_id', // 团队ID
},
production: false, // 设置为true发送到生产环境,设置为false发送到沙箱环境
});
}
try {
const note = new apn.Notification();
const jsonStr = JSON.stringify(body.payload);
const payload = JSON.parse(jsonStr);
note.payload = payload;
service.send(note, token).then((result) => {
console.log('苹果VOIP接口返回数据:' + JSON.stringify(result));
});
service.shutdown();
return getSucResp(null);
} catch (e) {
service.shutdown();
console.info('voip failed:', e);
return getFailResp(`error:${e}`);
}
} else {
return getFailResp('token is null');
}
}
java语言服务器
依赖下面第三方,具体实现接口百度有很多,有需要可以咨询作者
java语言需要cer和p12转成一个p12
<dependency>
<groupId>com.turo</groupId>
<artifactId>pushy</artifactId>
<version>0.13.10</version>
</dependency>
接口
import {
UTSLiveVoip
} from "@/uni_modules/wrs-uts-livevoip"
let params = {}
params.ringtoneName = "ring.wav" // 来电铃声,文件在nativeResources/ios/Resources/ring.wav,可以自行替换,替换后需要删除手机app,重新自定义基座运行才能生效
// params.iconTemplateImageData = ""
UTSLiveVoip.voipInit(params)
// 请求通知权限
UTSLiveVoip.requestAuthorization({
types: ["badge", "sound", "alert"]
}, (resp) => {
let flag = resp.flag
if (!flag) { // 请求权限失败
console.log("requestAuthorization:" + JSON.stringify(resp))
}
})
// ios流程:
// 1. app把token上传给后端
// 2. 后端调用苹果的apn接口发送数据给苹果服务器
// 3. 苹果服务器会把消息转发给手机
// 4. 手机app收到后,先回调didReceiveIncomingPush接口
// 5. 如果消息体里的数据符合插件里定义的voip数据(消息体参考文档),则会自动拉起接听电话界面
// 6. 用户接听或拒绝电话
UTSLiveVoip.onCallback((resp) => {
this.showMsg(JSON.stringify(resp))
let opt = resp.opt
switch (opt) {
// 仅支持iOS,获取到本机token,需要上传给后端,后端发送voip时需要token参数,token可以理解为手机的ID
case "didUpdatePushCredentials": {
// VOIP的token
let token = resp.token
this.token = token
}
break;
// 仅支持iOS,收到后端发送的voip数据
case "didReceiveIncomingPush": {
console.log("didReceiveIncomingPush:" + JSON.stringify(resp))
}
break;
// 仅支持iOS,拉起来电了界面结果
case "startCall": {
let flag = resp.flag
if(flag) {
let uuid = resp.uuid
} else {
console.log("来电显示一场:" + JSON.stringify(resp))
}
}
break;
// 仅支持iOS,用户接听了电话
case "performAnswerCall": {
// 获取最近voip来电的业务参数
let params = UTSLiveVoip.getRecentStartCallParams()
console.log("用户接听了来电:" + JSON.stringify(resp))
}
break;
// 仅支持iOS,电话挂断
case "performEndCall": {
// 获取最近voip来电的业务参数
let params = UTSLiveVoip.getRecentStartCallParams()
console.log("用户挂断了来电:" + JSON.stringify(resp))
}
break;
default:
break;
}
})
let person = {}
person.type = 2 // 1:generic 2:phoneNumber 3: emailAddress
person.value = "***" // 显示在来电界面上的
person.displayName = "张三" // 这个参数基本上没什么作用
let params = {}
params.members = [person] // 通话成员
params.activeRemoteMembers = [person] // 激活成员
params.capabilities = ["video"] // 视频:video 语音: playingTones
UTSLiveVoip.startCall(params, (resp) => {
console.log("startCall resp:" + JSON.stringify(resp))
if (resp.flag) {
this.uuid = resp.uuid
}
})
let params = {}
params.uuid = this.uuid
UTSLiveVoip.endCall(params, (resp) => {
console.log("endCall resp:" + JSON.stringify(resp))
if (resp.flag) {
this.uuid = resp.uuid
}
})
// 里面实际上是判断当前系统是否是iOS 17.4以后的系统
let support = UTSLiveVoip.isSupportLiveVOIP()