七鱼开发文档中心
总览服务端 APIWebiOSAndroid鸿蒙Uniapp微信小程序插件三方接入
呼叫中心呼叫系统接入

双向回呼微信小程序插件

微信小程序呼叫插件的开通、登录、插件配置、页面调用和授权说明。

微信小程序呼叫插件使用说明

插件接入步骤拆解

1. 联系七鱼技术支持同学,开通微信小程序呼叫插件白名单

2. 业务方服务端实现登录接口,获取小程序插件登录所需的 corp_code、token、appKey和csrfToken​

  • 业务方小程序通过异步接口向业务方服务端发起请求;
  • 业务方服务端需要根据当前登录的客服,获取对应的账号信息,向七鱼服务端发起 http 请求,获取七鱼企业域名和七鱼动态生成的登录口令,并将返回值透传给业务方小程序。业务方服务端 curl 请求样例如下:

接口请求地址

curl -X POST
'http://qyhjcs.qiyukf.com/openapi/ipcc/applets/login?appKey=1c088a89de51d0af457616605f28390f&checksum=f570a5eb049eb803b086e45829b07e48&time=1511832531'
-H 'content-type: application/json;charset=utf-8'
-d '{"staffName": "admin", "fromPage": "APPLETS"}'

请求参数

Query
参数数据类型是否必须参数说明
appKeyString是企业appkey
timeInteger是当前 UTC 时间戳,从 1970 年 1 月 1 日 0 点 0 分 0 秒开始到现在的秒数
checksumString是SHA1(appSecret + md5 + time), 三个参数拼接的字符串,进行SHA1哈希计算,转化成16进制字符(String,小写)
Body
参数是否必须数据类型参数说明
staffName是String使用呼叫插件的客服在七鱼系统对应的客服名称(此处对应的参数值为客服账号)
fromPage是String区分登录来源,值为固定值’APPLETS’

示例

返回示例

接口返回数据为 JSON 格式,返回样例如下:

{
  "code": 200, // 错误码,200表示获取成功
  "message": "", // 错误提示信息
  "result": {
    "corp_code": "xxx", // 登录客服对应的七鱼企业域名
    "token": "xxx", // 动态登录口令
    "appKey": "xxx",
    "csrfToken": "xxx",
  }
}

<u>3.</u><u>接入微信小程序呼叫插件</u>,插件名称为 call-plugin

3.1 在 app.json 文件中引入插件 call-plugin

{
  ...
  "plugins": {
    "call-plugin": {
      "version": "3.0.0",
      "provider": "wx7ff4c8a25efce68b"
    }
  },
  ···
}

3.2 在页面的 js 文件中引入呼叫插件,使用插件中的动态数据、将插件绑定到页面

const plugin = requirePlugin('call-plugin');

Page({
  data: {
    // 插件data数据是动态变化的,支持渲染到视图中
    ...plugin.data,
  },
  onLoad() {
    // 绑定页面
    plugin.attach(this);
  },
  onShow() {
    // 长时间离开页面,可能导致状态不同步、长连接断开等问题
    // 需要同步状态,确保长连接是连接的状态
    plugin.updateData();
  },
  onUnload() {
    // 取消绑定页面
    plugin.detach(this);
  },
});

3.3 如果您想使用网络电话模式进行外呼,需要额外做一些操作:

  • 3.3.1 联系网易七鱼技术支持同学,开启多端登录白名单
  • 3.3.2 登录微信小程序管理后台,在开发管理-业务域名中,添加域名https://{corpCode}.qiyukf.com,corpCode请咨询网易七鱼技术支持同学获取
  • 3.3.3 在页面的wxml文件中引入page-container,内部加载web-view
<!-- toolbarAuthUrl会在插件动态数据中提供,在适当的时机动态变化,开发者无需修改其值 -->
<page-container overlay="{{false}}" position="center" duration="{{0}}" show="{{toolbarAuthUrl}}" bind:beforeleave="handleToolbarAuthError">
  <web-view wx:if="{{toolbarAuthUrl}}" src="{{toolbarAuthUrl}}" binderror="handleToolbarAuthError"></web-view>
</page-container>
  • 3.3.4 在页面的js文件中定义wxml中用到的handleToolbarAuthError方法
Page({
  ...
  // 处理网络电话模式下的外呼异常
  handleToolbarAuthError() {
    plugin.handleSessionFailed();
  },
  ...
});

3.4 插件提供了一系列 API,可以根据自己的业务需要调用

以下代码演示了如何一键登录、通过网络电话服务模式外呼,并在通话结束后自动登出

const plugin = requirePlugin('call-plugin');

const { constants } = plugin;
const { CallMode, CallStatus } = constants; // 服务模式、坐席状态枚举

// 展示信息提示
const showToast = (title) => {
  return wx.showToast({
    title,
    icon: 'none',
    duration: 3000,
  });
};

Page({
  data: {
    // 插件data数据是动态的,支持渲染到视图中
    ...plugin.data,
  },

  onLoad() {
    // 绑定页面
    plugin.attach(this);

    // 7. 监听会话结束事件,执行登出逻辑
    plugin.addEventListener('sessionEnd', this.logoutAfterSession);
  },

  onShow() {
    // 长时间离开页面,可能导致状态不同步、长连接断开等问题
    // 需要同步状态,确保长连接是连接的状态
    plugin.updateData();
  },

  onUnload() {
    // 8. 页面卸载取消监听事件,避免内存泄漏
    plugin.addEventListener('sessionEnd', this.logoutAfterSession);

    // 取消绑定页面
    plugin.detach(this);
  },
  
  handleToolbarAuthError() {
    plugin.handleSessionFailed();
  },

  // 一键登录、外呼
  async loginAndCall() {
    // 1. 事先调用第2步所述的接口,获取corp_code、token、appKey和csrfToken
    // 代码略...

    // 2. 调用插件登录接口
    const { status: loginStatus, message: loginMessage } =
      await plugin.login({ corpCode: corp_code, token, appKey, csrfToken });
    showToast(loginStatus ? '登录成功' : loginMessage);
    if (!loginStatus) return;

    // 3. 设置服务模式为网络电话
    const { status: modeStatus, code, message: modeMessage } = await plugin.setMode({
      mode: CallMode.WEB,
    });
    
    if (!modeStatus) return showToast(modeMessage);
    
    // 4. 设置坐席状态为挂起
    const { status, message: statusMessage } = await plugin.setStatus({
      status: CallStatus.HANGUP
    });
    
    if (!status) return showToast(statusMessage);
    
    // 5. 开启随机外呼模式(您也可以关闭随机外呼模式,指定中继号进行外呼,相关api请参考setDidNumber)
    const { status: didNumberRandomStatus, message: didNumberRandomMessage } = await plugin.setDidNumberRandom({ value: true });
    
    if (!didNumberRandomStatus) return showToast(didNumberRandomMessage);

    // 6. 外呼
    const { status: callOutStatus, message: callOutMessage } =
      await plugin.callOut({ phoneNumber: '13000000000' });
    showToast(callOutStatus ? '外呼成功' : callOutMessage);
    
    // 调用成功后,会展示web-view页面,用户需要在web-view页面授权使用麦克风
  },

  async logoutAfterSession() {
    // 9. 登出前设置为软电话离线
    await plugin.setMode({ mode: CallMode.WEB });

    // 10. 登出
    plugin.logout();
  },
});

以下代码演示了如何一键登录、通过双向回呼服务模式外呼,并在通话结束后自动登出

const plugin = requirePlugin('call-plugin');

const { constants } = plugin;
const { CallMode } = constants; // 服务模式枚举

// 展示信息提示
const showToast = (title) => {
  return wx.showToast({
    title,
    icon: 'none',
    duration: 3000,
  });
};

Page({
  data: {
    // 插件data数据是动态的,支持渲染到视图中
    ...plugin.data,
  },

  onLoad() {
    // 绑定页面
    plugin.attach(this);

    // 5. 监听会话结束事件,执行登出逻辑
    plugin.addEventListener('sessionEnd', this.logoutAfterSession);
  },

  onShow() {
    // 长时间离开页面,可能导致状态不同步、长连接断开等问题
    // 需要同步状态,确保长连接是连接的状态
    plugin.updateData();
  },

  onUnload() {
    // 6. 页面卸载取消监听事件,避免内存泄漏
    plugin.addEventListener('sessionEnd', this.logoutAfterSession);

    // 取消绑定页面
    plugin.detach(this);
  },

  // 一键登录、外呼
  async loginAndCall() {
    // 1. 事先调用第1步所述的接口获取corp_code、token、appKey和csrfToken
    // 代码略...

    // 2. 调用插件登录接口
    const { status: loginStatus, message: loginMessage } =
      await plugin.login({ corpCode: corp_code, token, appKey, csrfToken });
    showToast(loginStatus ? '登录成功' : loginMessage);
    if (!loginStatus) return;

    // 3. 设置服务模式为双向回呼
    const { status, code, message } = await plugin.setMode({
      mode: CallMode.CLICK_CALL,
    });
    if (code === 18051) {
      // code为18051,表示未绑定手机号,需要调用plugin.getCaptcha获取短信验证码,再调用plugin.bindPhone绑定手机号。具体逻辑参考demo
      // 代码略...
    }
    if (!status) return showToast(message);

    // 4. 外呼
    const { status: callOutStatus, message: callOutMessage } =
      await plugin.callOut({ phoneNumber: '13000000000' });
    showToast(callOutStatus ? '外呼成功' : callOutMessage);
  },

  async logoutAfterSession() {
    // 7. 登出前设置为软电话离线
    await plugin.setMode({ mode: CallMode.WEB });

    // 8. 登出
    plugin.logout();
  },
});

4. 参考demo

插件 API

插件动态数据(plugin.data)

字段类型说明
hasLoginboolean是否已登录
mode0 | 1 | 2 | 3 | 4服务模式:0-网络电话,1-手机在线,2-SIP 话机,3-双向回呼,4-AXB
status0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8坐席状态:0-离线,1-网络电话在线,2-小休,3-通话中,4-手机在线,5-SIP 话机在线,6-挂起,7-处理中,8-自动外呼
phoneStatus‘Idle’ | ‘CallingOut’ | ‘WaitingForOthers’ | ‘Speaking’ | ‘Processing’通话状态:Idle-空闲,CallingOut-外呼中,WaitingForOthers-双向回呼本端已接听等待对端接听,Speaking-通话中,Processing-处理中
didNumberRandomboolean是否开启随机外呼模式(开启后,当客服拥有多个外呼号码进行外呼时,将随机使用一个号码进行外呼)
didNumberListstring[]外呼中继号码列表
didNumberstring当前选用的外呼中继号码
toolbarAuthUrlstring网络电话模式下执行外呼的web页面的url,需要通过web-view加载

插件方法

登录和初始化

login(options: {
  corpCode: string; // 七鱼企业code
  token: string; // 动态登录口令
  appKey: string; // 七鱼appKey
  csrfToken: string;
}:Promise<{
  status: boolean; // 是否调用成功
  message: string; // 错误信息
}>

内部逻辑:

  • 根据传入参数获取 cookie
  • 获取坐席数据(服务模式、坐席状态、im 账号信息等)
  • 建立 im 长连接

注意:以上任何一个步骤失败,都会导致登录失败

登出和销毁

logout():Promise<{
  status: boolean; // 是否调用成功
  message: string; // 错误信息
}>

内部逻辑:

  • 调用登出接口
  • 断开 im 长连接
  • 还原插件数据

注意:登出后呼叫插件所有功能将不可用,若想再次启用呼叫功能需重新走一遍功能使用的几个步骤(从登录开始)

设置是否开启随机外呼模式

// 调用成功后,可以看到动态数据didNumberRandom被修改
setDidNumberRandom(options: { value: boolean }):Promise<{
  status: boolean; // 是否调用成功
  message: string; // 错误信息
}>

使用场景:

  • 当需要指定中继号进行外呼时,应该先调用setDidNumberRandom将didNumberRandom设置为false,再调用setDidNumber设置中继号,然后使用callOut外呼
  • 当无需指定中继号进行外呼时,可以先调用setDidNumberRandom将didNumberRandom设置为true,直接使用callOut外呼

​

设置当前选用的外呼中继号码

// 调用成功后,可以看到动态数据didNumber被修改
setDidNumber(didNumber: string): void

使用场景:

  • 当需要指定中继号进行外呼时,应该先调用setDidNumberRandom将didNumberRandom设置为false,再调用setDidNumber设置中继号,然后使用callOut外呼
  • 外呼中继号码didNumber可以从动态数据didNumberList中选取

设置服务模式

setMode(options: {
  mode: 0 | 3; // 服务模式;0-网络电话,3-双向回呼
}):Promise<{
  status: boolean; // 是否设置成功
  code: number; // 错误码
  message: string;  // 错误信息
}>

内部逻辑:

  • 设置服务模式
  • 设置坐席状态为该服务模式下的默认状态(网络电话对应离线,双向回呼对应手机在线)

注意:使用手机在线或双向回呼模式之前,要绑定客服手机号,为了确保手机号为客服所有,要先获取手机验证码,校验通过后再绑定手机号。错误码为 18051,即表示客服未绑定手机号,需要调用 plugin.getCaptcha 获取短信验证码,再调用 plugin.bindPhone 绑定手机号。具体逻辑参考 demo

设置坐席状态

setStatus(options: {
  status: 0 | 6; // 坐席状态;0-离线,6-挂起
}):Promise<{
  status: boolean; // 是否设置成功
  message: string;  // 错误信息
}>

使用场景:

  • 在服务模式为网络电话时支持调用。设置为离线,坐席无法外呼;设置为挂起,坐席可以外呼

外呼

callOut(options:{
  phoneNumber: string; // 外呼号码
}):Promise<{
  status: boolean;  // 是否外呼成功
  message: string;  // 错误信息
}>

内部逻辑:

  • 校验 im 长连接状态
  • 校验服务模式是否为网络电话/双向回呼
  • 校验坐席状态是否为挂起/手机在线
  • 校验是否不在通话中
  • 校验外呼号码是否为空
  • 校验外呼号码是否在黑名单中
  • 如果以上条件均校验通过,使用当前服务模式、当前中继号策略进行外呼
  • 通话结束后不会进入处理中状态

注意:微信小程序中,仅网络电话/双向回呼服务模式才支持外呼

强制更新数据​

updateData():Promise<{
  status: boolean;  // 是否调用成功
  message: string;  // 错误信息
}>

内部逻辑:

  • 未登录状态下不会执行任何逻辑
  • 获取坐席数据(服务模式、坐席状态、im 账号信息等)
  • 建立 im 长连接

​使用场景:

  • 长时间离开页面,可能导致状态不同步、长连接断开等问题。需要在页面展示时(onShow 生命周期)调用
  • 插件登录时,需要获取初始数据,建立 im 长连接(插件内部已处理)
  • 网络状态从断开变为已连接时,需要更新数据,确保 im 已连接(插件内部已处理)

获取短信验证码

getCaptcha(options:{
  mobile: string; // 本机号码
}):Promise<{
  status: boolean;  // 是否获取成功
  message: string;  // 错误信息
}>

使用场景:

  • 调用设置服务模式(setMode)API,处理错误码为 18051 的场景。获取短信验证码时间间隔要大于 60 秒

绑定手机号

bindPhone(options:{
  mobile: string; // 本机号码
  captcha: string; // 调用plugin.getCaptcha获取到的手机号
}):Promise<{
  status: boolean;  // 是否绑定成功
  message: string;  // 错误信息
}>

使用场景:

  • 调用设置服务模式(setMode)API,处理错误码为 18051 的场景,获取到手机验证码后,绑定客服手机号。切换到手机在线/双向回呼模式都会将通话接通至该手机号

处理网络电话模式下的外呼失败

handleSessionFailed():void

使用场景:

  • 在【插件接入步骤拆解】中的3-3.3-3.3.2中使用

插件事件监听

addEventListener(type: 'sessionStart'|'acceptedByKefu'|'accepted'|'sessionEnd'|'sessionFailed', callback: (data: any) => void): void

参数说明:

  • type:监听的事件
  • callback:监听到事件后需要触发的回调函数

事件含义、事件对象

事件 type事件对象 data说明
sessionStartISession通话开始
acceptedByKefu{sessionId: number}客服接起
accepted{sessionId: number}访客接起
sessionEndISession通话结束
sessionFailed{tip: string}外呼失败

其中 ISession 为会话信息,具体字段包括

字段类型说明
sessionIdnumber会话 id
direction1 | 2会话方向;1-呼入,2-呼出
addressstring号码归属地
userNumberstring访客号码

移除事件监听,可以执行:

removeEventListener(type: 'sessionStart'|'acceptedByKefu'|'accepted'|'sessionEnd'|'sessionFailed', callback: (data: any) => void): void

注意:由于手机端无法确保网络一直保持连接,通话事件又依赖网络通信,通话事件可能丢失或延迟,请谅解

注意事项

  • 如果您在网络电话模式下进行外呼,在通话界面中,无法返回上一级页面,且尽量不要做出离开通话页面的行为(关闭小程序、杀应用、手势返回操作、安卓系统的常驻返回操作等),通话结束后系统将自动返回上级页面

FAQ

  • 同一号码外呼两次后无法再次接通

如果对方两次拒接电话,七鱼会限制当天再次拨打号码,防止被投诉。可在第二天重试

  • 外呼后通话立刻结束

跟对方确认是否立刻挂断或者设置了号码拦截

这篇文档有帮助吗?

目录