FaceAuth.php 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. <?php
  2. /**
  3. * 身份认证服务
  4. * @author wesmiler
  5. */
  6. namespace app\weixin\service;
  7. class FaceAuth
  8. {
  9. // 文档 https://market.aliyun.com/products/57000002/cmapi00039118.html?spm=5176.730005.productlist.d_cmapi00039118.599b3524mEL1X2&innerSource=search_%E5%85%AC%E4%BC%97%E5%8F%B7%20%E4%BA%BA%E8%84%B8%E8%AF%86%E5%88%AB%E8%AE%A4%E8%AF%81
  10. protected static $apiUrl = 'https://faceidh5.market.alicloudapi.com';
  11. protected static $checkUrl = 'https://api.megvii.com/faceid/lite/videoverify';
  12. protected static $appKey = '203953968';
  13. protected static $appSecret = 'nbP0eTttVbGFjXSI8GbY04tE89Mnw9ow';
  14. protected static $appCode = '689f5d471a2140ea8091c8f4e979f8dd';
  15. protected static $apiUrls = [
  16. 'getToken'=>'/edis_ctid_id_name_video_ocr_h5',
  17. ];
  18. protected static $errors = [
  19. '0000'=> '成功',
  20. '9999'=> '系统异常',
  21. '1999'=> '服务异常',
  22. ];
  23. protected static $resultErrors = [
  24. 'SUCCESS'=> '待比对照片与权威数据照片或参考照片对比是同一个人',
  25. 'PASS_LIVING_NOT_THE_SAME'=> '待比对照片与权威数据照片或参考照片对比不是同一个人',
  26. 'NO_ID_CARD_NUMBER'=> '无此身份证号',
  27. 'ID_NUMBER_NAME_NOT_MATCH'=> '身份证号与姓名不相符',
  28. 'IDCARD_PHOTO_FRONTSIDE'=> '身份证人像面识别错误或信息不匹配',
  29. 'IDCARD_BACKSIDE_BLURRED'=> '身份证国徽面识别错误',
  30. 'NO_FACE_FOUND_IDCARD'=> '身份证人像面找不到人脸',
  31. 'IDCARD_PHOTO_NOTFRONTSIDE'=> '非身份证人像面',
  32. 'IDCARD_PHOTO_NOTBACKSIDE'=> '非身份证国徽面',
  33. 'FAIL_OCR_FAKE_IDCARD'=> '假证',
  34. 'FAIL_LIVING_FACE_ATTACK'=> '云端活体验证失败',
  35. 'CHANGE_FACE_ATTACK'=> '活体验证视频中发生了换脸攻击',
  36. 'NO_FACE_FOUND'=> '活体验证视频中没有检测到人脸',
  37. 'FACE_QUALITY_TOO_LOW'=> '活体验证视频中质量太差',
  38. 'INVALID_VIDEO_DURATION'=> '活体验证视频中长度不符合要求(2s~20s)',
  39. 'VIDEO_TOO_LARGE'=> '活体验证视频过大',
  40. 'SR_ERROR'=> '活体验证视频中,用户读数语音不符合要求',
  41. 'NOT_SYNCHRONIZED'=> '活体验证视频中,用户读数唇语不符合要求',
  42. 'NO_AUDIO'=> '活体验证视频无声音',
  43. 'VIDEO_FORMAT_UNSUPPORTED'=> '活体验证视频格式无法识别',
  44. 'LIP_VOICE_NOT_SYNC'=> '活体验证视频中语音唇语不同步',
  45. 'VIDEO_OK'=> '活体验证视频可用',
  46. 'VIDEO_MANY_TIMES'=> '活体验证视频上传超过阈值',
  47. 'VIDEO_INTERNAL_ERROR'=> '活体验证内部错误',
  48. 'NON_ENTERPRISE_CERTIFICATION'=> '客户未进行企业认证',
  49. 'BALANCE_NOT_ENOUGH'=> '余额不足',
  50. 'ACCOUNT_DISABLED'=> '账户已停用',
  51. 'USER_CANCEL'=> '用户主动退出流程',
  52. 'LIVING_NOT_START'=> '验证流程尚未开始',
  53. ];
  54. /**
  55. * 获取验证参数Token
  56. * @param $params
  57. * @return array|int
  58. */
  59. public static function getToken($userId, $params){
  60. $realname = isset($params['realname'])? trim($params['realname']) : '';
  61. $idcard = isset($params['idcard'])? trim($params['idcard']) : '';
  62. if(empty($realname) || empty($idcard) || empty($userId)){
  63. return 2012;
  64. }
  65. $data = [
  66. 'bizNo'=> 'F'.date('Ymd').rand(10,99).$userId,
  67. 'idName'=> $realname,
  68. 'idNumber'=> $idcard,
  69. 'retIdImg'=> 'true', // 是否返回上传身份证照片,可选,默认否
  70. // 'ocrOnly'=> 'true', // 是否上传身份证拍照,不用传身份证号和姓名
  71. 'procedureType'=> 'video', // 活体检测模式,video:读数活体;still静默活体(安全等级不高,不推荐使用)
  72. 'notifyUrl'=> request()->domain().'/api/notify/face', // 异步回调地址
  73. 'returnUrl'=> request()->domain().'/weixin/auth/authResult', // 回调跳转页面
  74. ];
  75. // 参数配置
  76. $smsConfig = cmf_get_option('idenauth_config');
  77. $appKey = isset($params['appKey']) && $params['appKey']?trim($params['appKey']) : self::$appKey;
  78. $appCode = isset($params['appCode'])&&$params['appCode']?trim($params['appCode']) : self::$appCode;
  79. $appSecret = isset($params['appSecret'])&&$params['appSecret']?trim($params['appSecret']) : self::$appSecret;
  80. // 构建请求参数
  81. $headers = [
  82. "Authorization:APPCODE {$appCode}",
  83. "Content-Type:application/x-www-form-urlencoded; charset=UTF-8",
  84. ];
  85. $url = self::$apiUrl.self::$apiUrls['getToken'];
  86. $query = FaceAuth::getParams($data);
  87. PRedis::set("caches:faceAuth:{$idcard}:request", ['url'=> $url,'query'=> $query,'headers'=> $headers], 600);
  88. $result = httpHeaderRequest($url,$query,'post',$headers);
  89. $code = isset($result['code'])? $result['code'] : '';
  90. $msg = isset($result['msg'])? $result['msg'] : '';
  91. PRedis::set("caches:faceAuth:{$idcard}:result", $result, 600);
  92. $url = isset($result['verifyUrl'])? $result['verifyUrl'] : '';
  93. if($code!='0000' || empty($url)){
  94. $msg = $msg? $msg : (isset(self::$errors[$code])? self::$errors[$code] : '获取在线验证参数失败,请刷新重试');
  95. return ['code'=> 'error', 'msg'=> $msg, 'result'=> $result];
  96. }
  97. //$url = str_replace('https://edis.esandcloud.com/faceId/lite/ocridcard/fronthd', self::$checkUrl, $url);
  98. return ['code'=> 'success', 'msg'=> '获取验证参数成功', 'result'=> ['result'=> $result, 'url'=> $url]];
  99. }
  100. public static function getResult($token){
  101. if(empty($token)){
  102. return false;
  103. }
  104. // 参数配置
  105. $smsConfig = cmf_get_option('idenauth_config');
  106. $appKey = isset($params['appKey']) && $params['appKey']?trim($params['appKey']) : self::$appKey;
  107. $appCode = isset($params['appCode'])&&$params['appCode']?trim($params['appCode']) : self::$appCode;
  108. $appSecret = isset($params['appSecret'])&&$params['appSecret']?trim($params['appSecret']) : self::$appSecret;
  109. // 构建请求参数
  110. $headers = [
  111. "Authorization:APPCODE {$appCode}",
  112. "Content-Type:application/x-www-form-urlencoded; charset=UTF-8",
  113. ];
  114. $url = sprintf(self::$apiUrl.self::$apiUrls['getResult'], $token);
  115. PRedis::set("caches:faceAuth:result:request_{$token}", ['url'=> $url,'headers'=> $headers], 600);
  116. $result = httpHeaderRequest($url, '','get',$headers);
  117. $resultCode = isset($result['result_code'])? $result['result_code'] : '';
  118. $resultMsg = isset($result['result_message'])? $result['result_message'] : '';
  119. $bizToken = isset($result['biz_token'])? $result['biz_token'] : '';
  120. $images = isset($result['images'])? $result['images'] : [];
  121. PRedis::set("caches:faceAuth:result:result_{$token}", $result, 600);
  122. if($resultCode != 'SUCCESS'){
  123. $msg = isset(self::$resultErrors[$resultMsg])? self::$resultErrors[$resultMsg] : '获取验证结果失败';
  124. return ['code'=> 'error', 'msg'=> $msg, 'result'=> $result];
  125. }
  126. return ['code'=> 'success', 'msg'=> '获取验证结果', 'result'=> $result];
  127. }
  128. /**
  129. * 格式化请求参数
  130. * @param array $params
  131. * @return false|string
  132. */
  133. public static function getParams(array $params){
  134. $data = [];
  135. if(empty($params)){
  136. return false;
  137. }
  138. foreach ($params as $key => $val){
  139. $data[] = $key.'='.$val;
  140. }
  141. return $data? implode('&', $data) : '';
  142. }
  143. /**
  144. * 处理回调
  145. * @param $params
  146. */
  147. public static function catchNotify($params){
  148. }
  149. }