更新记录

1.0.4(2026-10-09)

  1. 精简代码

1.0.3(2026-10-09)

  1. 优化

1.0.2(2026-10-08)

  1. 优化
查看更多

平台兼容性

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

本插件可以上线中国大陆

后台保活插件(支持熄屏)https://ext.dcloud.net.cn/plugin?id=20810

集成步骤

  1. 拷贝demo里的nativeResources、Info.plist文件到项目根目录
  2. ios需要商店证书的Identifiers对应app的Capabilities里勾选Push Notifications,然后在下载打包的.mobileprovision
  3. nativeResources/ios/UniApp.entitlements文件里的aps-environment对应的值是development、production,一般开发阶段使用development,生产发布使用production,根据情况设置
  4. 咨询或定制请点击上面"进入交流群"按钮私聊作者

iOS VOIP服务器配置

  1. 登录苹果开发者系统生成VOIP证书(百度有很多资料),这个证书是voip的证书(Certificates->Services->VoIP Services Certificate),不是消息推送的证书
  2. 将cer证书和p12文件生成各自后端语言支持的证书,如:
  3. 其他语言可以参考苹果官方https://developer.apple.com/documentation/usernotifications/sending-notification-requests-to-apns

技术方案

iOS

  • 仅支持iOS 17.4以后的系统 iOS采用通过后端接口调用官方的voip接口发送数据给app或者调用官方的远程消息推送接口,app收到数据后可以拉起系统来电界面或发送通知到消息栏,通过/voip接口参数判断哪种方式
    1. 接口/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"
  • 初始化voip

let params = {}
params.ringtoneName = "ring.wav" // 来电铃声,文件在nativeResources/ios/Resources/ring.wav,可以自行替换,替换后需要删除手机app,重新自定义基座运行才能生效
// params.iconTemplateImageData = ""
UTSLiveVoip.voipInit(params)
  • 请求通知权限,仅支持ios

// 请求通知权限
UTSLiveVoip.requestAuthorization({
    types: ["badge", "sound", "alert"]
}, (resp) => {
    let flag = resp.flag
    if (!flag) { // 请求权限失败
        console.log("requestAuthorization:" + JSON.stringify(resp))
    }

})
  • 设置VOIP回调

// 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;
    }
})
  • 来电,app主动拉起来电界面,仅支持iOS

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
    }
})
  • 判断是否支持voip

// 里面实际上是判断当前系统是否是iOS 17.4以后的系统
let support = UTSLiveVoip.isSupportLiveVOIP()

隐私、权限声明

1. 本插件需要申请的系统权限列表:

1. 通知

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

无