<?php
namespace app\services\message;
use app\services\charge\ChargeOrderLogServices;
use think\exception\ValidateException;
class VoiceCallServices
{
protected $callNotifyAppId;
protected $callNotifySecret;
protected $displayNbr;
protected $baseUrl;
protected $statusUrl;
protected $templateId;
protected $apiUrl;
public function __construct(){
$this->callNotifyAppId = sys_config('voice_call_key'); // Key
$this->callNotifySecret = sys_config('voice_call_secret'); // 秘钥
$this->displayNbr = sys_config('display_nbr'); // 固话号码
$this->baseUrl = sys_config('voice_request_url'); // app接入地址
$this->statusUrl = sys_config('voice_status_url'); // 呼叫状态接收地址
$this->templateId = sys_config('voice_template_id'); // 语音模板ID
$this->apiUrl = '/rest/httpsessions/callnotify/v2.0'; // 访问url
}
/**
* 语音通知api
* @param string $calleeNbr 被叫号码
* @param array $data 相关参数
*/
public function voiceNotifyAPI(string $calleeNbr,array $data){
if(empty($this->displayNbr)) throw new ValidateException('缺少固话号码');
if(empty($calleeNbr)) throw new ValidateException('缺少被叫号码');
$playInfoList = array();
// 通知模式:0模板通知,1放音文件通知
if(sys_config('play_content_type') == 1){
$playInfoList[] = [
'notifyVoice' => '',
];
}else{
// 示例:模板内容:"您有${NUM_2}件快递请到${TXT_32}领取";则templateParas为["3","人民公园正门"];API将播放文本“您有3件快递请到人民公园正门领取”
$playInfoList[] = [
'templateId' => $this->templateId, // 模板ID
'templateParas' => [$data['content']], // 自定义模板内容
];
}
if(empty($playInfoList)) throw new ValidateException('缺少播放信息');
$xaksk = $this->buildAKSKHeader($this->callNotifyAppId,$this->callNotifySecret);
$content = json_encode([
/* 必填参数 */
'displayNbr' => $this->displayNbr, // 固话号码,固话格式:+{国家码}{区号}{固话号码},其中区号需去掉首位的0。示例:国家码86,区号0755,固话号码28****01,填写为+8675528****01
'calleeNbr' => $calleeNbr, // 被叫号码
'playInfoList' => $playInfoList, // 播放信息列表,最多5个
/* 非必要参数 */
'statusUrl' => base64_encode($this->statusUrl), // 回调接口:语音通话平台将业务触发过程中通话的状态信息(包括呼出、振铃、摘机和挂机信息)推送至此服务器,,此参数请采用BASE64编码进行加密
'userData' => $data['order_id'], // 拨打时携带的参数,可用于回呼状态接收接口使用,不允许携带大括号{},不允许包含中文,如果包含中文则需要BASE64编码进行加密
]);
$requestUrl = $this->baseUrl . $this->apiUrl;
$context_options = $this->createOptions($xaksk,$content);
try {
$response = json_decode(file_get_contents($requestUrl,false,stream_context_create($context_options)),true); // 发送请求
if($response['resultcode'] != 0) throw new ValidateException($response['resultdesc']);
// 成功返回示例:["resultcode" => "0", "sessionId" => "1201_3772_4294967295_20231116094532@callenabler245.huaweicaas.com", "resultdesc" => "Success"]
return true;
} catch (\Exception $e){
throw new ValidateException($e->getMessage());
}
}
/**
* 构建AKSK请求头
* @param string $appKey 语音通知key
* @param string $appSecret 语音通知secret
* @return string
*/
public function buildAKSKHeader(string $appKey,string $appSecret){
date_default_timezone_set("UTC");
$created = date('Y-m-d\TH:i:s\Z');
$nonce = uniqid();
$base64 = base64_encode(hash_hmac('sha256',($nonce.$created),$appSecret,true));
return sprintf("UsernameToken Username=\"%s\",PasswordDigest=\"%s\",Nonce=\"%s\",Created=\"%s\"", $appKey, $base64, $nonce, $created);
}
/**
* createOptions
* @param $method
* @param $xaksk
* @param $content
* @return array
*/
public function createOptions($xaksk, $content){
$headers = [
'Content-Type: application/json;charset=UTF-8',
'Authorization: AKSK realm="SDP",profile="UsernameToken",type="Appkey"',
'X-AKSK: ' . $xaksk
];
return [
'http' => [
'method' => 'POST', // 请求方法
'header' => $headers,
'content' => $content,
'max_redirects' => '0', // 关闭重定向
'ignore_errors' => true // 获取错误码,方便调测
],
'ssl' => [
'verify_peer' => false,
'verify_peer_name' => false
]
];
}
/**
* 呼叫状态接收api
* 接口调用请求为POST,可以使用$request->param()获取回调的参数
* @param $jsonBody 回调参数
* @return bool
*/
public function voice_notify($jsonBody){
if(!is_array($jsonBody)){
$jsonArr = json_decode($jsonBody,true); // 将通知消息解析为关联数组
} else {
$jsonArr = $jsonBody;
}
$eventType = $jsonArr['eventType']; // 通知事件类型
if(strcasecmp($eventType,'fee') == 0) throw new ValidateException('EventType error:' . $eventType);
if(!array_key_exists('statusInfo',$jsonArr)) throw new ValidateException('param error:no statusInfo');
$statusInfo = $jsonArr['statusInfo']; // 呼叫状态事件信息
// callout:呼出事件
if (strcasecmp($eventType, 'callout') == 0) {
/**
* Example: 此处以解析sessionId为例,请按需解析所需参数并自行实现相关处理
* 'timestamp': 该呼叫事件发生时RTC业务平台的UNIX时间戳
* 'userData': 用户附属信息,语音通知api或语音回呼api携带的参数userData
* 'sessionId': 通话链路的标识ID
* 'caller': 主叫号码
* 'called': 被叫号码
*/
if (array_key_exists('sessionId', $statusInfo)) { // 你的业务逻辑
$text = '【呼出事件】---通话链路的标识ID:【'.$statusInfo['sessionId'].'】,呼叫事件发生时RTC业务平台的UNIX时间戳:【'.$statusInfo['timestamp'].'】,主叫号码:【'.$statusInfo['caller'].'】,被叫号码:【'.$statusInfo['called'].'】';
ChargeOrderLogServices::createLog('语音通知',$text,$statusInfo['userData']);
}
return true;
}
// alerting:振铃事件
if (strcasecmp($eventType, 'alerting') == 0) {
/**
* Example: 此处以解析sessionId为例,请按需解析所需参数并自行实现相关处理
* 'timestamp': 该呼叫事件发生时RTC业务平台的UNIX时间戳
* 'userData': 用户附属信息,语音通知api或语音回呼api携带的参数userData
* 'sessionId': 通话链路的标识ID
* 'caller': 主叫号码
* 'called': 被叫号码
*/
if (array_key_exists('sessionId', $statusInfo)) { // 你的业务逻辑
$text = '【振铃事件】---通话链路的标识ID:【'.$statusInfo['sessionId'].'】,呼叫事件发生时RTC业务平台的UNIX时间戳:【'.$statusInfo['timestamp'].'】,主叫号码:【'.$statusInfo['caller'].'】,被叫号码:【'.$statusInfo['called'].'】';
ChargeOrderLogServices::createLog('语音通知',$text,$statusInfo['userData']);
}
return true;
}
// answer:应答事件
if (strcasecmp($eventType, 'answer') == 0) {
/**
* Example: 此处以解析sessionId为例,请按需解析所需参数并自行实现相关处理
* 'timestamp': 该呼叫事件发生时RTC业务平台的UNIX时间戳
* 'userData': 用户附属信息,语音通知api或语音回呼api携带的参数userData
* 'sessionId': 通话链路的标识ID
* 'caller': 主叫号码
* 'called': 被叫号码
*/
if (array_key_exists('sessionId', $statusInfo)) { // 你的业务逻辑
$text = '【应答事件】---通话链路的标识ID:【'.$statusInfo['sessionId'].'】,呼叫事件发生时RTC业务平台的UNIX时间戳:【'.$statusInfo['timestamp'].'】,主叫号码:【'.$statusInfo['caller'].'】,被叫号码:【'.$statusInfo['called'].'】';
ChargeOrderLogServices::createLog('语音通知',$text,$statusInfo['userData']);
}
return true;
}
// collectInfo:放音收号结果事件,仅应用于语音通知场景
if (strcasecmp($eventType, 'collectInfo') == 0) {
/**
* Example: 此处以解析digitInfo为例,请按需解析所需参数并自行实现相关处理
* 'timestamp': 该呼叫事件发生时RTC业务平台的UNIX时间戳
* 'sessionId': 通话链路的标识ID
* 'digitInfo': 放音收号场景中,RTC业务平台对开发者进行放音收号操作的结果描述
*/
if (array_key_exists('digitInfo', $statusInfo)) { // 你的业务逻辑
$text = '【放音收号结构事件】---通话链路的标识ID:【'.$statusInfo['sessionId'].'】,呼叫事件发生时RTC业务平台的UNIX时间戳:【'.$statusInfo['timestamp'].'】,放音收号场景中,RTC业务平台对开发者进行放音收号操作的结果描述:【'.$statusInfo['digitInfo'].'】';
ChargeOrderLogServices::createLog('语音通知',$text,$statusInfo['userData']);
}
return true;
}
// disconnect:挂机事件
if (strcasecmp($eventType, 'disconnect') == 0) {
/**
* Example: 此处以解析sessionId为例,请按需解析所需参数并自行实现相关处理
* 'timestamp': 该呼叫事件发生时RTC业务平台的UNIX时间戳
* 'userData': 用户附属信息,语音通知api或语音回呼api携带的参数userData
* 'sessionId': 通话链路的标识ID
* 'caller': 主叫号码
* 'called': 被叫号码
* 'partyType': 挂机的用户类型,仅在语音回呼场景携带
* 'stateCode': 通话挂机的原因值
* 'stateDesc': 通话挂机的原因值的描述
*/
if (array_key_exists('sessionId', $statusInfo)) { // 你的业务逻辑
$text = '【挂机事件】---通话链路的标识ID:【'.$statusInfo['sessionId'].'】,呼叫事件发生时RTC业务平台的UNIX时间戳:【'.$statusInfo['timestamp'].'】,主叫号码:【'.$statusInfo['caller'].'】,被叫号码:【'.$statusInfo['called'].'】,通话挂机的原因值:【'.$statusInfo['stateCode'].'】,通话挂机的原因值的描述:【'.$statusInfo['stateDesc'].'】';
ChargeOrderLogServices::createLog('语音通知',$text,$statusInfo['userData']);
}
return true;
}
return true;
}
}
PHP对接华为云语音通知api
最新推荐文章于 2024-05-07 10:31:45 发布