|
|
@@ -0,0 +1,177 @@
|
|
|
+<?php
|
|
|
+/**
|
|
|
+ * 身份认证服务
|
|
|
+ * @author wesmiler
|
|
|
+ */
|
|
|
+
|
|
|
+namespace app\weixin\service;
|
|
|
+
|
|
|
+class FaceAuth
|
|
|
+{
|
|
|
+ // 文档 https://market.aliyun.com/products/57124001/cmapi030146.html?spm=5176.2020520132.101.3.588172184iFKo0
|
|
|
+ protected static $apiUrl = 'https://fidlite.market.alicloudapi.com';
|
|
|
+ protected static $appKey = '203953968';
|
|
|
+ protected static $appSecret = 'nbP0eTttVbGFjXSI8GbY04tE89Mnw9ow';
|
|
|
+ protected static $appCode = '689f5d471a2140ea8091c8f4e979f8dd';
|
|
|
+ protected static $apiUrls = [
|
|
|
+ 'getToken'=>'/lite/v1/get_biz_token',
|
|
|
+ 'getResult'=>'/lite/v1/get_result?biz_token=%s',
|
|
|
+ 'checkUrl'=>'/lite/v1/do/%s',
|
|
|
+ ];
|
|
|
+ protected static $errors = [
|
|
|
+ 'MISSING_ARGUMENTS'=> '缺少某个必选参数',
|
|
|
+ 'BAD_ARGUMENTS'=> '参数解析错误',
|
|
|
+ 'AUTHORIZATION_ERROR'=> '鉴权失败或签名错误',
|
|
|
+ 'CONCURRENCY_LIMIT_EXCEEDED'=> '请求超过并发',
|
|
|
+ 'INTERNAL_ERROR'=> '内部错误',
|
|
|
+ 'Request Entity Too Large'=> '请求内容大小超出限制',
|
|
|
+ ];
|
|
|
+ protected static $resultErrors = [
|
|
|
+ 'SUCCESS'=> '待比对照片与权威数据照片或参考照片对比是同一个人',
|
|
|
+ 'PASS_LIVING_NOT_THE_SAME'=> '待比对照片与权威数据照片或参考照片对比不是同一个人',
|
|
|
+ 'NO_ID_CARD_NUMBER'=> '无此身份证号',
|
|
|
+ 'ID_NUMBER_NAME_NOT_MATCH'=> '身份证号与姓名不相符',
|
|
|
+ 'IDCARD_PHOTO_FRONTSIDE'=> '身份证人像面识别错误或信息不匹配',
|
|
|
+ 'IDCARD_BACKSIDE_BLURRED'=> '身份证国徽面识别错误',
|
|
|
+ 'NO_FACE_FOUND_IDCARD'=> '身份证人像面找不到人脸',
|
|
|
+ 'IDCARD_PHOTO_NOTFRONTSIDE'=> '非身份证人像面',
|
|
|
+ 'IDCARD_PHOTO_NOTBACKSIDE'=> '非身份证国徽面',
|
|
|
+ 'FAIL_OCR_FAKE_IDCARD'=> '假证',
|
|
|
+ 'FAIL_LIVING_FACE_ATTACK'=> '云端活体验证失败',
|
|
|
+ 'CHANGE_FACE_ATTACK'=> '活体验证视频中发生了换脸攻击',
|
|
|
+ 'NO_FACE_FOUND'=> '活体验证视频中没有检测到人脸',
|
|
|
+ 'FACE_QUALITY_TOO_LOW'=> '活体验证视频中质量太差',
|
|
|
+ 'INVALID_VIDEO_DURATION'=> '活体验证视频中长度不符合要求(2s~20s)',
|
|
|
+ 'VIDEO_TOO_LARGE'=> '活体验证视频过大',
|
|
|
+ 'SR_ERROR'=> '活体验证视频中,用户读数语音不符合要求',
|
|
|
+ 'NOT_SYNCHRONIZED'=> '活体验证视频中,用户读数唇语不符合要求',
|
|
|
+ 'NO_AUDIO'=> '活体验证视频无声音',
|
|
|
+ 'VIDEO_FORMAT_UNSUPPORTED'=> '活体验证视频格式无法识别',
|
|
|
+ 'LIP_VOICE_NOT_SYNC'=> '活体验证视频中语音唇语不同步',
|
|
|
+ 'VIDEO_OK'=> '活体验证视频可用',
|
|
|
+ 'VIDEO_MANY_TIMES'=> '活体验证视频上传超过阈值',
|
|
|
+ 'VIDEO_INTERNAL_ERROR'=> '活体验证内部错误',
|
|
|
+ 'NON_ENTERPRISE_CERTIFICATION'=> '客户未进行企业认证',
|
|
|
+ 'BALANCE_NOT_ENOUGH'=> '余额不足',
|
|
|
+ 'ACCOUNT_DISABLED'=> '账户已停用',
|
|
|
+ 'USER_CANCEL'=> '用户主动退出流程',
|
|
|
+ 'LIVING_NOT_START'=> '验证流程尚未开始',
|
|
|
+ ];
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取验证参数Token
|
|
|
+ * @param $params
|
|
|
+ * @return array|int
|
|
|
+ */
|
|
|
+ public static function getToken($params){
|
|
|
+ $realname = isset($params['realname'])? trim($params['realname']) : '';
|
|
|
+ $idcard = isset($params['idcard'])? trim($params['idcard']) : '';
|
|
|
+ if(empty($realname) || empty($idcard)){
|
|
|
+ return 2012;
|
|
|
+ }
|
|
|
+
|
|
|
+ $data = [
|
|
|
+ 'biz_no'=> makeTradeNo('F'),
|
|
|
+ 'idcard_name'=> $realname,
|
|
|
+ 'idcard_number'=> $idcard,
|
|
|
+ 'idcard_threshold'=> 0.8, // 身份证识别阈值,0-1,group=1有效
|
|
|
+ 'idcard_side'=> 0, // 拍摄身份证但双面,0-双面,1-单面
|
|
|
+ 'idcard_retry_time'=> 5, // 人脸重拍次数,1-5
|
|
|
+ 'liveness_type'=> 'video_number', // 活体检测类型,可选:video_number
|
|
|
+ 'notify_url'=> request()->domain().'/api/notify/face', // 异步回调地址
|
|
|
+ 'return_url'=> request()->domain().'/weixin/auth/idcard?type=back', // 回调跳转页面
|
|
|
+ 'security_level'=> 2, // 检测严格程度,1-宽松,2-常规,3-严格
|
|
|
+ ];
|
|
|
+
|
|
|
+ // 参数配置
|
|
|
+ $smsConfig = cmf_get_option('idenauth_config');
|
|
|
+ $appKey = isset($params['appKey']) && $params['appKey']?trim($params['appKey']) : self::$appKey;
|
|
|
+ $appCode = isset($params['appCode'])&&$params['appCode']?trim($params['appCode']) : self::$appCode;
|
|
|
+ $appSecret = isset($params['appSecret'])&&$params['appSecret']?trim($params['appSecret']) : self::$appSecret;
|
|
|
+
|
|
|
+
|
|
|
+ // 构建请求参数
|
|
|
+ $headers = [
|
|
|
+ "Authorization:APPCODE {$appCode}",
|
|
|
+ "Content-Type:application/x-www-form-urlencoded; charset=UTF-8",
|
|
|
+ ];
|
|
|
+
|
|
|
+ $url = self::$apiUrl.self::$apiUrls['getToken'];
|
|
|
+ $query = FaceAuth::getParams($data);
|
|
|
+ PRedis::set("caches:faceAuth:{$idcard}:request", ['url'=> $url,'query'=> $query,'headers'=> $headers], 600);
|
|
|
+ $result = httpHeaderRequest($url,$query,'post',$headers);
|
|
|
+ $respError = isset($result['error'])? $result['error'] : '';
|
|
|
+ $codeData = $respError? explode(':',$respError) : [];
|
|
|
+ $code = isset($codeData[0])? $codeData[0] : '';
|
|
|
+ $bizToken = isset($result['biz_token'])? $result['biz_token'] : '';
|
|
|
+ PRedis::set("caches:faceAuth:{$idcard}:result", $result, 600);
|
|
|
+ if($respError || empty($bizToken)){
|
|
|
+ $msg = isset(self::$errors[$code])? self::$errors[$code] : '获取在线验证参数失败,请刷新重试';
|
|
|
+ return ['code'=> 'error', 'msg'=> $msg, 'result'=> $result];
|
|
|
+ }
|
|
|
+
|
|
|
+ $url = sprintf(self::$apiUrl.self::$apiUrls['checkUrl'],$bizToken);
|
|
|
+ return ['code'=> 'success', 'msg'=> '获取验证参数成功', 'result'=> ['token'=> $bizToken, 'url'=> $url]];
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public static function getResult($token){
|
|
|
+ if(empty($token)){
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 参数配置
|
|
|
+ $smsConfig = cmf_get_option('idenauth_config');
|
|
|
+ $appKey = isset($params['appKey']) && $params['appKey']?trim($params['appKey']) : self::$appKey;
|
|
|
+ $appCode = isset($params['appCode'])&&$params['appCode']?trim($params['appCode']) : self::$appCode;
|
|
|
+ $appSecret = isset($params['appSecret'])&&$params['appSecret']?trim($params['appSecret']) : self::$appSecret;
|
|
|
+
|
|
|
+ // 构建请求参数
|
|
|
+ $headers = [
|
|
|
+ "Authorization:APPCODE {$appCode}",
|
|
|
+ "Content-Type:application/x-www-form-urlencoded; charset=UTF-8",
|
|
|
+ ];
|
|
|
+
|
|
|
+ $url = sprintf(self::$apiUrl.self::$apiUrls['getResult'], $token);
|
|
|
+ PRedis::set("caches:faceAuth:result:request_{$token}", ['url'=> $url,'headers'=> $headers], 600);
|
|
|
+ $result = httpHeaderRequest($url, '','get',$headers);
|
|
|
+ $resultCode = isset($result['result_code'])? $result['result_code'] : '';
|
|
|
+ $resultMsg = isset($result['result_message'])? $result['result_message'] : '';
|
|
|
+ $bizToken = isset($result['biz_token'])? $result['biz_token'] : '';
|
|
|
+ $images = isset($result['images'])? $result['images'] : [];
|
|
|
+ PRedis::set("caches:faceAuth:result:result_{$token}", $result, 600);
|
|
|
+ if($resultCode != 'SUCCESS'){
|
|
|
+ $msg = isset(self::$resultErrors[$resultMsg])? self::$resultErrors[$resultMsg] : '获取验证结果失败';
|
|
|
+ return ['code'=> 'error', 'msg'=> $msg, 'result'=> $result];
|
|
|
+ }
|
|
|
+
|
|
|
+ return ['code'=> 'success', 'msg'=> '获取验证结果', 'result'=> $result];
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 格式化请求参数
|
|
|
+ * @param array $params
|
|
|
+ * @return false|string
|
|
|
+ */
|
|
|
+ public static function getParams(array $params){
|
|
|
+ $data = [];
|
|
|
+ if(empty($params)){
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ foreach ($params as $key => $val){
|
|
|
+ $data[] = $key.'='.$val;
|
|
|
+ }
|
|
|
+
|
|
|
+ return $data? implode('&', $data) : '';
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理回调
|
|
|
+ * @param $params
|
|
|
+ */
|
|
|
+ public static function catchNotify($params){
|
|
|
+
|
|
|
+ }
|
|
|
+}
|