支付场景本地人脸app
主要指用户扫脸检测、活体和识别在端上进行,刷脸过程可支持短暂离线,主要是应用于K12团餐、地铁、景区等封闭式场景。
单次刷脸模式是指isv每次唤起刷脸时都需调用刷脸接口(verify),刷脸完成后人脸采集页面自动退出,常用于支付和单次核身等场景。
流程如下:
1. isv机具端app启动时,调用刷脸SDK接口(install)传入isv和商户、gourpId信息,进行人脸和iot sdk初始化。调用register,等待初始化回调完成。register回调返回后才能进行后续刷脸。
2. 刷脸时,isv机具端app调用刷脸SDK接口(verify):
1)设备未进行iot授权,刷脸直接失败。
2)设备已授权,可正常唤起人脸采集页面并进行人脸识别过程。识别完成后返回人脸识别的结果(VerifyCallback)。如果识别成功,会返回当前用户的支付宝账号信息(ftoken/uid)、比对信息(流程图第2步)
3. 根据刷脸识别结果,isv接入方可进行相应业务逻辑(核身或者代扣)。
对于代扣,获取ftoken后调用加签接口signWithFaceToken进行签名,提交签名到isv服务端后完成支付宝代扣流程(流程图第3步)。
1、业务方客户端需集成SmileService.jar文件,调用见《5、调用示例》。
2、机具上安装zoloz刷脸app(根据摄像头类型(2d或者3d)选择安装对应摄像头版本的刷脸apk)。
上述集成的jar和刷脸app从项目临客蚁上获取,由版本负责人@从彦统一管理。
/** * 初始化人脸应用 * * @param params 扩展参数,参数key为: * {@link #MERCHANT_INFO_DEVICE_NUM}, 值类型为String,必填项 * {@link #MERCHANT_INFO_STORE_CODE}, 值类型为String,可选项 * {@link #MERCHANT_INFO_ALIPAY_STORE_CODE}, 值类型为String,可选项 * {@link ZolozConstants#KEY_MERCHANT_INFO_ISV_NAME},值类型为String,必填项 * {@link ZolozConstants#KEY_MERCHANT_INFO_ISV_PID},值类型为String,必填项 * {@link ZolozConstants#KEY_MERCHANT_INFO_MERCHANT_ID},值类型为String,必填项 * {@link ZolozConstants#KEY_MERCHANT_INFO_MERCHANT_NAME},值类型为String,必填项 * {@link ZolozConstants#KEY_GROUP_ID},值类型为String,本地模式为必填项 * @param callback 回调接口 */ public void install(Map<String, Object> extInfo)
参数说明:
字段 | 参数 | 是否必填 | 具体说明 |
---|---|---|---|
info | Map | 是 | 必传参数(key定义参考ZolozConstants): _deviceNum:使用设备序列号SN,保证准确、唯一_ _isvPid:支付宝开发平台分配的isv pid_ _isvName:isv名称merchantId:商户id(比如:学校:外标,景区:机构代码)_ _merchantName:商户name(比如:学校/景区名称)_ groupID:库id |
/** * 监听初始化状态 * * @param params 扩展用,可传空 * @param callback 回调接口 */ public void register(Map<String, Object> extInfo, InstallCallback callback)
参数说明:
字段 | 参数 | 是否必填 | 具体说明 |
---|---|---|---|
extInfo | Map | 是 | 扩展用,目前未用到,可传空 |
callback | InstallCallback | 是 | 返回install成功或者失败 |
InstallCallback参数:
/** * Install回调函数 */ public abstract class InstallCallback { /** * install结果回调函数 * * @param smile2PayResponse 回调response,{@link Smile2PayResponse} */ public abstract void onResponse(Smile2PayResponse smile2PayResponse); }
onResponse回调返回smile2PayResponse,涉及的重要参数如下:
字段 | 类型 | 具体说明 |
---|---|---|
code | int | 取值如下: Smile2PayResponse.CODE_SUCCESS: install成功 其他值:人脸库操作失败 |
mExtInfo | Map<String, Object> | 本地模式时,map中包括详细失败原因(FeatureResult),获取方式参考《5、调用示例》 |
/** * 调起人脸验证 * * @param params * @param verifyCallback 人脸验证回调接口{@link VerifyCallback} * @return */ public void verify(final Map<String, Object> params, final VerifyCallback verifyCallback)
参数说明:
字段 | 参数 | 是否必填 | 具体说明 |
---|---|---|---|
info | Map | 是 | 配置参数,参考4.5.3 |
verifyCallback | VerifyCallback | 是 | onResponse返回的结果 |
调用verify/detect启动刷脸时,isv可根据需要传入map配置参数。所有的配置参数定义在类**ZolozConfig**中,主要分为以下几类:
key定义 | 说明 | 具体说明 |
---|---|---|
KEY_MODE***_ | 业务配置 | 可配置刷脸模式、降级模式 |
KEY_ALGORITHM***_ | 算法配置 | 可配置人脸检测/活体/比对等相关参数 |
KEY_FACE_SEARCH***_ | 比对配置 | 可配置人脸比对相关参数 |
KEY_TASK_FLOW***_ | 流程配置 | 可配置串并行等相关参数,目前仅支持串行,所以连续模式时,需要设置:configInfo.put(ZolozConfig.KEY_TASK_FLOW_FEATURE_POWER_MODE, PowerMode.POWER_MOD_LOW) 参考《5、示例代码》 |
KEY_DEVICE_SETTING***_ | 相机配置 | 可配置相机相关参数,2D/IR摄像头时较常用 |
KEY_UI***_ | 刷脸UI配置 | 可配置刷脸UI相关参数 |
常用参数说明:
key定义 | 参数说明 | 取值说明 |
---|---|---|
KEY_MODE_FACE_MODE | 业务模式,根据实际应用场景设置 | 默认值:_FACEPAY,_其他取值参考_FaceMode_定义 |
KEY_SMILE_MODE | 刷脸页面显示在主屏或辅屏 | 默认值:主屏,参考SmileMode定义 主屏:_SMILE_MODE_DEFAULT_DISPLAY_ 铺屏:_SMILE_MODE_EXT_DISPLAY_ |
KEY_ALGORITHM_MAX_DETECT_DISTANCE | 3d摄像头检测最大距离 | 类型:int 默认值:1000(mm) |
KEY_ALGORITHM_MIN_IOD | 2d摄像头检测最大距离 | 默认值:0.18,取值范围:0~1.0f 值越小检测距离越大 |
KEY_ALGORITHM_MAX_IOD | 2d摄像头检测最小距离 | 默认值:0.45,取值范围:0~1.0f 值越小检测距离越大 |
KEY_FACE_SEARCH_SECURITY_LEVEL | 人脸识别的阈值 | 类型:int 默认值:14,取值范围:0~20, |
KEY_TASK_FLOW_TOYGER_POWER_MODE | 串并行控制,目前仅支持串行 | 需设置为_POWER_MODE_LOW_ |
注:ZolozConfig还包括其他更多的参数,对于接入方理解起来成本比较高。为了降低理解成本,设置verify/detect接口的map参数(params)时,可根据《4、业务场景配置(本地模式)》部分直接选取适合于自己的业务场景。
单次刷脸onResponse(VerifyCallback)和连续刷脸onFaceVerify(DetectCallback)中返回的参数是Smile2PayResponse,定义如下:
public class Smile2PayResponse { /** * 人脸验证成功 */ public static final int CODE_SUCCESS = 1000; /** * 人脸验证过程被中断 */ public static final int CODE_INTERNAL_ERROR = 1001; /** * 人脸验证过程被用户取消操作 */ public static final int CODE_CANCEL_BY_USER = 1003; /** * 人脸验证过程,用户操作超时 */ public static final int CODE_TIMEOUT_BY_USER = 1004; /** * 人脸验证过程,用户重试次数过多,选择其他支付方式 */ public static final int CODE_CHOOSE_OTHER_PAYMENT = 1005; /** * 人脸验证失败 */ public static final int CODE_VERIFY_FAIL = 2006; /** * result code */ private int mCode; /** * face token */ private String mFaceToken; /** * sub code */ private String mSubCode; /** * sub message */ private String mSubMsg; /** * alipay user id */ private String mAlipayUid; /** * ext info */ private final Map<String, Object> mExtInfo = new HashMap<String, Object>();
参数说明:
字段 | 参数 | 具体说明 |
---|---|---|
mCode | int | 刷脸是否成功 CODE_SUCCESS:成功 CODE_CHOOSE_OTHER_PAYMENT:其他支付方式 |
mAlipayUid | String | 支付宝uid,刷脸成功时有效 |
mFaceToken | String | 刷脸ftoken,可用于交易,刷脸成功时有效 |
mSubCode mSubMsg |
String | 失败详细原因,刷脸失败时有效 |
mExtInfo | Map<String, Object> | 本地模式时,刷脸返回的其他信息,包括刷脸比对结果(FaceVerifyResult)。获取方式参考《5、调用示例》 |
FaceVerifyResult说明:
字段 | 参数 | 具体说明 |
---|---|---|
faceID | String | 人脸特征唯一标示 |
uid | String | 用户_uid_ |
securityLevel | int | 比对等级 |
compScore | float | 比对分数 |
compScore3D | float | 3D比对分数 |
compScoreIR | float | IR比对分数 |
extInfo | Map<String, Object> | 扩展用,目前为空,可忽略 |
正常调用verify唤起刷脸过程,完成刷脸整体流程后人脸apk会自己完成关闭,无需调用该接口。如存在特殊逻辑刷脸过程中需要强制退出刷脸(如刷脸过程中判断到nfc刷卡扣款成功,需要退出人脸),则可以调用下列接口强制退出当此刷脸。
/** * 主动退出刷脸 * * @param params 参考{@link CommandCode} */ public void command(final Map<String, Object> params)
CommandCode定义如下:
public static final class CommandCode { /** * 退出刷脸 */ public static final int EXIT = 0; /** * 暂停刷脸(仅预览、不检测) */ public static final int DETECT_PAUSE = 2; /** * 继续刷脸 */ public static final int DETECT_COTINUE = 3; }
2.7 扫码
2.7.1 注册扫码
/** * 注册扫码 * * @param params 暂时未用到,可传空 * @param scanCallback 回调接口 */ public void register(final Map<String, Object> params, final ScanCallback scanCallback)
2.7.2 停止扫码
/** * 注销,停止扫码 * @param scanCallback register设置的回调接口 */ public void unregister(ScanCallback scanCallback)
2.7.3 返回扫码结果
public abstract class ScanCallback { /** * 扫码服务链接成功 */ public static final String CODE_SUCCESS = "0"; /** * 扫码成功返回的码值 * * @param code 码值 */ public abstract void onResult(String code); /** * 扫码服务返回的状态信息 * * @param eventCode event码,0:扫码服务服务链接成功,其他值为失败 * @param eventMsg 错误描述 */ public abstract void onEvent(String eventCode, String eventMsg); }
使用方式参考《3、调用示例》。
极端情况下,人脸app运行过程中,由于摄像头问题、算法异常等一些无法自恢复的原因,会进行kill掉刷脸进程。为了保证正常的刷脸,isv调用方需要调用此接口进行连接状态的监听,一旦断开,需要调用方再次调用register接口。代码示例参考《3、调用示例》。
/** * 监听人脸service * @param connectCallback 人脸apk连接bind/unbind回调 */ public void setConnectCallback(ZolozConnectCallback connectCallback)
import com.alipay.zoloz.smile2pay.Zoloz; mZoloz = Zoloz.getInstance(getApplicationContext());
import com.alipay.zoloz.smile2pay.ZolozConstants; mZoloz.install(mockMerchantInfo()); mZoloz.register(null, new InstallCallback() { @Overide public void onResponse(Smile2PayResponse smileToPayResponse) { Log.d(TAG, "loadFeature()...response"); if (smileToPayResponse != null) { int code = smileToPayResponse.getCode(); String msg = Smile2PayResponse.CODE_SUCCESS == code ? "install成功" : "install失败"; showToast(String.format(Locale.getDefault(), "code: %s, result: %s", code, msg)); if (smileToPayResponse.getExtInfo() != null) { FeatureResult featureResult = (FeatureResult)smileToPayResponse.getExtInfo().get(ZolozConstants.KEY_FEATURE_RESULT); Log.d(TAG, "featureResult:" + featureResult); } } } }); /** * mock数据,真实商户请填写真实信息. */ private Map mockMerchantInfo() { Map merchantInfo = new HashMap(); // 必填项,商户机具终端编号设备SN merchantInfo.put(ZolozConstants.KEY_MERCHANT_INFO_DEVICE_NUM, "TEST"); // 必填项,ISV PID merchantInfo.put(ZolozConstants.KEY_MERCHANT_INFO_ISV_PID, "TEST"); // 必填项,ISV 名称 merchantInfo.put(ZolozConstants.KEY_MERCHANT_INFO_ISV_NAME, "TEST"); // 必填项,商户id(如团餐场景:学校社会信用代码或者学校国标码) merchantInfo.put(ZolozConstants.KEY_MERCHANT_INFO_MERCHANT_ID, "TEST"); // 必填项,商户名称(如团餐场景:学校名称) merchantInfo.put(ZolozConstants.KEY_MERCHANT_INFO_MERCHANT_NAME, "TEST"); // 必填项,库id merchantInfo.put(ZolozConstants.KEY_GROUP_ID, "TEST"); return merchantInfo; }
import com.alipay.zoloz.smile2pay.ZolozConfig.FaceMode; import com.alipay.zoloz.smile2pay.verify.FaceVerifyResult; import com.alipay.zoloz.smile2pay.verify.BehaviourLog; /** * 调起刷脸 */ mZoloz.verify(mockConfigInfo(), new VerifyCallback() { @Override public void onResponse(Smile2PayResponse smileToPayResponse) { Log.d(TAG, "verify()...response"); if (smileToPayResponse != null) { int code = smileToPayResponse.getCode(); String uid = smileToPayResponse.getAlipayUid(); String msg = Smile2PayResponse.CODE_SUCCESS == code ? "识别成功" : "识别失败"; BehaviourLog behaviourLog = (BehaviourLog)smileToPayResponse.getExtInfo().get(ZolozConstants.KEY_BEHAVIOR_LOG); FaceVerifyResult faceVerifyResult = (FaceVerifyResult)smileToPayResponse.getExtInfo().get(ZolozConstants.KEY_FACE_VERIFY_RESULT); // toast显示刷脸结果,isv应按照真实场景处理和显示刷脸结果 showToast(String.format(Locale.getDefault(), "code:%s, result: %s, %s", code, msg, JSON.toJSONString(faceVerifyResult))); Log.d(TAG, "map:" + smileToPayResponse.getExtInfo().size()); } } }); /** * mock数据,真实场景请填写实际配置. */ private Map mockConfigInfo() { JSONObject configInfo = new JSONObject(); // 必填项,模式 configInfo.put(ZolozConfig.KEY_MODE_FACE_MODE, FaceMode.FACEPAY); // 可选 //configInfo.put(ZolozConfig.KEY_FACE_SEARCH_SECURITY_LEVEL, 14); //configInfo.put(ZolozConfig.KEY_FACE_SEARCH_TOP_N, 2); // 其他可选参数参考ZolozConfig定义 ...... Map zolozConfig = new HashMap(2); zolozConfig.put(ZolozConfig.KEY_ZOLOZ_CONFIG, configInfo.toJSONString()); // 显示在客显屏,默认主屏 // zolozConfig.put(ZolozConfig.KEY_SMILE_MODE, SmileMode.SMILE_MODE_EXT_DISPLAY); // 设置UI config // JSONObject uiInfo = new JSONObject(); // uiInfo.put(ZolozConfig.KEY_UI_SHOW_PAYMENT_CODE, true); // uiInfo.put(ZolozConfig.KEY_UI_PAY_AMOUNT, 20.05f); // uiInfo.put(ZolozConfig.KEY_UI_ENABLE_TIME_OUT, false); // uiInfo.put(ZolozConfig.KEY_UI_SHOW_CLOCK_TIME, true); // uiInfo.put(ZolozConfig.KEY_UI_CONFIRM_TEXT, "确认"); // zolozConfig.put(ZolozConfig.KEY_UI_CONFIG, uiInfo.toJSONString()); return zolozConfig; }
Map<String, Object> command = new HashMap<>(2); command.put(ZolozConfig.KEY_COMMAND_CODE, CommandCode.EXIT); // exit退出 mZoloz.command(command);
mZoloz.setConnectCallback(new ZolozConnectCallback() { @Override public void onConnect(boolean connected, ComponentName componentName) { Log.d(TAG, "registerConnectCall, connected:" + connected); if (!connected) { mZoloz.register(null, new InstallCallback() { @Overide public void onResponse(Smile2PayResponse smileToPayResponse) { ...... } } } });
调用verify接口启动刷脸时需要传入params参数。为了方便接入,接入方可根据自身业务场景从下面章节、选择合适的配置参数,直接copy使用即可(切记:参数值要根据实际场景进行设置)。
本地人脸apk的默认的支付模式。代码示例如下:
/** * mock数据,真实场景请填写实际配置. */ private Map mockConfigInfo() { JSONObject configInfo = new JSONObject(); // 必填项,模式 configInfo.put(ZolozConfig.KEY_MODE_FACE_MODE, FaceMode.FACEPAY); // 可选 //configInfo.put(ZolozConfig.KEY_FACE_SEARCH_SECURITY_LEVEL, 14); ...... Map zolozConfig = new HashMap(2); zolozConfig.put(ZolozConfig.KEY_ZOLOZ_CONFIG, configInfo.toJSONString()); // 显示在客显屏,默认主屏 // zolozConfig.put(ZolozConfig.KEY_SMILE_MODE, SmileMode.SMILE_MODE_EXT_DISPLAY); // 设置UI config //JSONObject uiInfo = new JSONObject(); //uiInfo.put(ZolozConfig.KEY_UI_PAY_AMOUNT, 20.05f); <--如果需要显示金额,非必须 //uiInfo.put(ZolozConfig.KEY_UI_SHOW_CLOCK_TIME, true); <--如果需要显示时钟时间,非必须 //zolozConfig.put(ZolozConfig.KEY_UI_CONFIG, uiInfo.toJSONString()); }
4.2 定额支付模式
定额模式用于消费金额固定的场景。刷脸界面调起后一直显示,直到刷脸成功。代码示例如下:
/** * mock数据,真实场景请填写实际配置. */ private Map mockConfigInfo() { JSONObject configInfo = new JSONObject(); // 必填项,模式 configInfo.put(ZolozConfig.KEY_MODE_FACE_MODE, FaceMode.FACEPAY); // 可选 //configInfo.put(ZolozConfig.KEY_FACE_SEARCH_SECURITY_LEVEL, 14); ...... Map zolozConfig = new HashMap(2); zolozConfig.put(ZolozConfig.KEY_ZOLOZ_CONFIG, configInfo.toJSONString()); // 显示在客显屏,默认主屏 // zolozConfig.put(ZolozConfig.KEY_SMILE_MODE, SmileMode.SMILE_MODE_EXT_DISPLAY); ...... // 设置UI config JSONObject uiInfo = new JSONObject(); uiInfo.put(ZolozConfig.KEY_UI_ENABLE_TIME_OUT, false); <--必须设置 uiInfo.put(ZolozConfig.KEY_UI_PAY_AMOUNT, 20.05f); <--金额,必须设置 //uiInfo.put(ZolozConfig.KEY_UI_SHOW_CLOCK_TIME, true); <--如果需要显示时钟时间,非必须 zolozConfig.put(ZolozConfig.KEY_UI_CONFIG, uiInfo.toJSONString()); }
查询模式用于非支付场景,仅用于单次身份核验。代码示例如下:
/** * mock数据,真实场景请填写实际配置. */ private Map mockConfigInfo() { JSONObject configInfo = new JSONObject(); // 必填项,模式 configInfo.put(ZolozConfig.KEY_MODE_FACE_MODE, FaceMode.ENTRANCE); configInfo.put(ZolozConfig.KEY_TASK_FLOW_TOYGER_POWER_MODE, PowerMode.POWER_MODE_LOW); // 可选 //configInfo.put(ZolozConfig.KEY_FACE_SEARCH_SECURITY_LEVEL, 14); // 可选,2d摄像头时有用 //configInfo.put(ZolozConfig.KEY_DEVICE_SETTING_SOLUTION_WIDTH, 640); //configInfo.put(ZolozConfig.KEY_DEVICE_SETTING_CAMERA_ID, 0); //configInfo.put(ZolozConfig.KEY_ALGORITHM_MIN_IOD, 0.05); //configInfo.put(ZolozConfig.KEY_ALGORITHM_MAX_IOD, 1.0); //configInfo.put(ZolozConfig.KEY_DEVICE_SETTING_IS_MIRROR, true); //configInfo.put(ZolozConfig.KEY_DEVICE_SETTING_ALG_ANGLE, 0); //configInfo.put(ZolozConfig.KEY_DEVICE_SETTING_DISPLAY_ANGLE, 0); //configInfo.put(ZolozConfig.KEY_DEVICE_SETTING_IS_DISPLAY_MIRROR, true); // 其他可选参数参考ZolozConfig定义 ...... Map zolozConfig = new HashMap(2); zolozConfig.put(ZolozConfig.KEY_ZOLOZ_CONFIG, configInfo.toJSONString()); // 显示在客显屏,默认主屏 // zolozConfig.put(ZolozConfig.KEY_SMILE_MODE, SmileMode.SMILE_MODE_EXT_DISPLAY); ...... // 设置UI config JSONObject uiInfo = new JSONObject(); uiInfo.put(ZolozConfig.KEY_UI_CONFIRM_TEXT, "确认"); <--必传,文本可以自定义 //uiInfo.put(ZolozConfig.KEY_UI_COUNT_DOWN_TIME, -1); <--确认页面是否显示,可选 //uiInfo.put(ZolozConfig.KEY_UI_SHOW_CLOCK_TIME, true); <--如果需要显示时钟时间,非必须 zolozConfig.put(ZolozConfig.KEY_UI_CONFIG, uiInfo.toJSONString()); }
附录:
1.支付app与收银页面的交互可参考下图
如需原图可@从彦。