目录:
- 流程介绍
1.1 支付宝自助机刷脸支付交互图
1.2 支付宝自助机刷脸核身交互图 - 接入指引
2.1 准备工作
2.2 接口调用规范
2.2.1 请求说明 - 刷脸付
3.1 接入细节
3.2 获取刷脸所需的设备信息
3.3 刷脸初始化
3.4 唤起人脸识别
3.5 调用收单接口完成支付
3.6 交易结果查询接口 - 刷脸核身
4.1 接入细节
4.2 唤起用户授权
4.3 支付宝人脸核身获取用户详细信息 - 参考文档
- 修订记录
1. 流程介绍
1.1 支付宝自助机刷脸支付交互图
- 获取刷脸所需的设备信息【自助机】商户pid+服务商pid+服务商appid->metainfo
- 刷脸初始化【聚合支付平台】metainfo->zim_id
- 唤起人脸识别【自助机】zim_id->ftoken
- 调用收单接口完成支付【聚合支付平台】auth_code=ftoken
1.2 支付宝自助机刷脸核身交互图
(1)授权获取用户信息方案
- 获取刷脸所需的设备信息【自助机】商户pid+服务商pid+服务商appid->metainfo
- 刷脸初始化【聚合支付平台】metainfo->zim_id
- 唤起人脸识别【自助机】zim_id->ftoken
- 通过ftoken唤起用户授权【自助机】ftoken->auth_code
- 获取用户信息【聚合支付平台】auth_code->userinfo
(2)用户填写信息验证方案
- 用户填写身份证和姓名
- 获取刷脸所需的设备信息【自助机】商户pid+服务商pid+服务商appid->metainfo
- 刷脸初始化【聚合支付平台】把第一步的信息放入metainfo的extInfo中->zim_id
- 唤起人脸识别【自助机】zim_id->ftoken
- 刷脸认证后返回识别结果
2. 接入指引
2.1 准备工作
第一步、获取开发者数据
调用聚合支付平台接口,需要与平台商务对接后,获取开发者ID(developId),密钥(privateKey),公钥(publicKey)和对称密钥(desKey),这些参数让商户拥有调用平台接口的权限,对请求的数据进行加密以保证数据的安全性。
- 开发者ID(developerId):
developerId在所有的请求中都需要传入,与密钥公钥信息相对应,鉴别请求是否是由平台商户发起。
- 密钥(privateKey):
密钥在接口请求中用于对数据进行RSA加密,生成签名sign。
- 公钥(publicKey):
公钥用于对签名进行解密
- 对称密钥(desKey):
在线上支付中,<body>标签的中的参数,需要通过des进行对称加密,得到密文进行请求。
第二步、获取平台商户号
每个商户对接聚合支付平台,在第三方平台想要拥有的支付能力,需要在支付宝或者微信签约对应的支付方式,平台的商务会为每个商户分配一个平台商户号(merchantNo),该商户号账户下所有的交易流水都会进入到对应的支付宝和微信的账户下,并且平台账单流水都是以商户号进行隔离,这是一个非常重要的参数。
由于自助机通过获取设备信息,需要传入平台信息,
因此,除了上述参数,还需要拿到以下信息:
平台应用id(appId)
平台pid(partnerId)
第三步、下载加解密sdk和demo
- java版本:加解密工具java版本
2.2 接口调用规范
2.2.1 请求说明
接口通信地址:
测试环境:http://offlinetest.bsoftpay.com/gatewayOffline/pay/offline/execute
正式环境:由平台商务对接人员提供http请求格式:
Content-type:x-www-form-urlencoded
公共请求参数说明:
参数 | 类型 | 是否必填 | 最大长度 | 描述 | 示例 |
---|---|---|---|---|---|
data | String | 是 | 300 | 请求参数转为xml格式 | <xml><paytype>2</paytype><merchant_no>10000</merchant_no></xml> |
developerId | String | 是 | 10 | 开发者id | 12345 |
funcId | String | 是 | 20 | 功能码 | P7001 |
sign | String | 是 | 100 | 将公共参数以“参数名”ASCII码顺序排序拼接成新字符串(参数名ASCII码从小到大排序(字典序))得到:“data=value1&developerId=value2&funcId=value3”,再将新的字符串经过RSA签名得到签名后的字符串 | Sa/hb9jaTZZZq89mN62DDooLv/YTvfE9ayrbzgOMsucfIPZfJGmhUia6YWK8vQ5/NQana3Pub7BDswEenm0sBg== |
- 公共返回参数说明:
参数 | 类型 | 是否必填 | 最大长度 | 描述 | 示例 |
---|---|---|---|---|---|
retCode | String | 是 | 30 | 0000:表示接口业务调用正常,9999:表示处理失败,如参数错误,验签失败等(注:retCode并不表示交易行为成功或失败,某笔交易是否成功请根据对应接口返回报文的状态字段来判定) | 0000 |
retMsg | String | 是 | 100 | 接口调用异常返回信息 | 验签失败 |
3. 刷脸付
3.1 接入细节
- 接口说明:可以在支付宝平台先下载demo,了解接入细节
https://docs.open.alipay.com/20180402104715814204/cgxcze/
3.2 获取刷脸所需的设备信息
说明:调用接口zolozGetMetaInfo采集刷脸所需的设备信息并完成刷脸的准备工作。如果zolozGetMetaInfo返回成功结果,则请求商户服务端调用支付宝开放平台的人脸初始化接口。
示例:
private void smilePay() { //调用zolozGetMetaInfo完成设备信息采集 zoloz.zolozGetMetaInfo(mockInfo(), new ZolozCallback() { // 解析zolozGetMetaInfo返回的结果,如果成功,则请求商户服务端调用人脸初始化接口 @Override public void response(Map smileToPayResponse) { if (smileToPayResponse != null) { String code = (String)smileToPayResponse.get("code"); String metaInfo = (String)smileToPayResponse.get("metainfo"); if (CODE_SUCCESS.equalsIgnoreCase(code) && metaInfo != null) { // 将metaInfo发送给商户服务端,由商户服务端发起刷脸初始化OpenAPI的调用 ... } } /** * mock数据,真实商户请填写真实信息. */ private Map mockInfo() { Map merchantInfo = new HashMap(); //以下信息请根据真实情况填写 //商户 Pid merchantInfo.put("merchantId", partnerId); //ISV PID merchantInfo.put("partnerId", partnerId); //添加刷脸付功能的appid merchantInfo.put("appId", appId); //机具编号,便于关联商家管理的机具 merchantInfo.put("deviceNum", "TEST_ZOLOZ_TEST"); //商户的门店编号 merchantInfo.put("storeCode", "TEST"); //支付宝门店编号 merchantInfo.put("alipayStoreCode", "TEST"); return merchantInfo; }
参数 | 说明 |
---|---|
merchantId | 必填项,签约商户的pid。以2088开头 |
partnerId | 可选项,ISV的pid。对于自用型商户,填写签约商户的pid,和merchantId保持一致 |
appId | 可选项,支付宝分配给开发者的应用ID,和当面付请求的appid保持一致。 |
deviceNum | 必填项,商户机具终端编号,和当面付请求的terminal_id 保持一致 |
storeCode | 可选,商户门店编号,和当面付请求的store_id保持一致。 |
alipayStoreCode | 可选,支付宝内部门店编号,和当面付请求中的alipay_store_id保持一致。 |
- 响应参数:
关键出參:回调函数返回的metainfo对象结构,该对象中的参数请不需要
做任何值的修改,否则会导致后续刷脸出错。请直接传给商户服务端作为下一步调用的入參。
参数 | 说明 |
---|---|
apdidToken | 设备指纹 |
appName | 应用名称 |
appVersion | 应用版本 |
bioMetaInfo | 生物信息 |
osVersion | 系统版本 |
machineInfo | 设备硬件相关信息 |
merchantInfo | 商户相关信息 |
remoteLogID | 刷脸调用的事务ID |
extInfo | 扩展参数,用于商户服务端传入信息 |
extInfo信息介绍
参数 | 说明 |
---|---|
certNo | 身份证号 当bizType=1时使用 |
certName | 身份证姓名 当bizType=1时使用 |
certType | IDCARD 当bizType=1时使用 |
bizType | 1身份核验场景、4刷脸核身(手机号码) |
例子:
JSONObject extInfo = new JSONObject();
/* start: 如果是1:1刷脸认证(姓名+身份证号),请加入以下代码 */
extInfo.put("certNo", "身份证号"); //必填,当前被认证用户的身份证号
extInfo.put("certName", "身份证姓名");//必填,当前被认证用户的姓名(保持和身份证一致)
extInfo.put("certType", "IDCARD"); //写为IDCARD,表明身份证
extInfo.put("bizType", "1"); //固定写为1,表明1:1身份核验场景
/* end: -------------------------------------------- */
/* start: 如果是1:1刷脸认证(手机号),请加入以下代码 */
extInfo.put("bizType", "4"); //固定写为4,表明1:1刷脸核身(手机号)场景
/* end: -------------------------------------- */
zimmetainfo.put("extInfo", extInfo);
3.3 刷脸初始化
- 功能码:P5001
- 接口说明:通过该接口初始化人脸初始化参数
- 请求参数:
<xml>
参数 | 名称 | 类型 | 长度 | 是否必填 | 说明 | 示例 |
---|---|---|---|---|---|---|
paytype | 支付渠道 | String | 是 | 2-支付宝 3-微信 | ||
deviceId | 微信设备Id | String | 微信支付必填 |
微信人脸支付设备序列号 | ||
meta_info | 透传体 | String | 是 | 获取刷脸所需的设备信息返回的metainfo 微信SDK返回的rawData |
||
amt | 订单金额 | String | 32 | 否 | 订单金额,值为double类型 | 0.01 |
merchant_no | 收款商户号 | String | 32 | 是 | 商户号 | |
transType | 交易类型 | String | 32 | 是 | 7-刷脸支付 |
- 响应参数:
参数 | 名称 | 类型 | 长度 | 是否必输 | 说明 | 示例 |
---|---|---|---|---|---|---|
return_code | 返回状态码 | String | 16 | 否 | SUCCESS/OK代表成功 FAIL=代表失败 | SUCCESS |
return_msg | 返回信息 | String | 128 | 否 | 返回信息 | OK |
zim_id | 刷脸调用的标识,将作为下一步zolozVerify接口的入參 | String | 否 | 刷脸调用的标识 | ||
zim_init_client_data | 支付宝 :刷脸的下发协议数据,将作为下一步zolozVerify接口的入參 微信: authInfo 信息 SDK调用凭证。用于调用SDK的人脸识别接口。为便于xml文件解析,authInfo进行了URLEncode,取出时可能需要进行相应的URLDecode操作 |
String | 否 | 刷脸的下发协议数据 |
java请求示例:
String xml = "<xml>" + "<meta_info></meta_info>" + "<merchant_no>xxxxxx</merchant_no>" + "</xml>"; List<NameValuePair> list = new ArrayList<NameValuePair>(); NameValuePair valu1 = new BasicNameValuePair("data",data); NameValuePair valu2 = new BasicNameValuePair("developerId",[your developerId]); NameValuePair valu3 = new BasicNameValuePair("funcId","P5001"); String privateKey = [your privateKey]; String signData = "data="+data+"&developerId=[your developerId]&funcId=P5001"; String sign = SecurityUtil.signRSA(signData,privateKey,"utf-8"); NameValuePair valu4 = new BasicNameValuePair("sign",sign); list.add(valu1); list.add(valu2); list.add(valu3); list.add(valu4); String xml = null; try { CloseableHttpResponse response = HttpClients.custom().build().execute(RequestBuilder.post().setUri("http://offlinetest.bsoftpay.com/gatewayOffline/pay/offline/execute").setEntity(new UrlEncodedFormEntity(list, Consts.UTF_8)).build()); xml = EntityUtils.toString(response.getEntity()); } catch (Exception e) { e.printStackTrace(); } System.out.println(xml);
返回示例:
developerId=31331&funcId=P5001&retCode=0000&retMsg= &data=<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <xml> <return_code>SUCCESS</return_code> <return_msg>OK</return_msg> <zim_id></zim_id> <zim_init_client_data></zim_init_client_data> </xml> &sign=Seko5j5kYUs1Vpmpon03IgX7GqiNYWvOkhcUq7alPneOGWSjNPuY3QeXMzUSTBiMC1WQGVwlK+TBPzQeDvvUnQ==
3.4 唤起人脸识别
说明:调用zolozVerify接口唤起人脸采集页面并完成比对过程。如果认证成功,将返回一个ftoken。该ftoken用于后续查询被认证用户的信息。
示例:
//将上一步中initialize接口返回的zim_init_client_data对象和zim_id作为入參 Map params = new HashMap(); params.put("zim.init.resp", protocal); 可选:smile_mode用于指定选择刷脸模式,值为int类型,0:主屏幕显示(默认值);1:双面屏机具,副屏幕显示 params.put("smile_mode", 0); zoloz.zolozVerify(zimId, params, new ZolozCallback() { @Override public void response(final Map smileToPayResponse) { if (smileToPayResponse != null) { String code = (String)smileToPayResponse.get("code"); String fToken = (String)smileToPayResponse.get("ftoken"); String subCode = (String)smileToPayResponse.get("subCode"); String msg = (String)smileToPayResponse.get("msg"); //刷脸成功 if (CODE_SUCCESS.equalsIgnoreCase(code) && fToken != null) { } } } }
返回结果示例:
注意:在result对象中会返回当前认证用户的支付宝UID,这个UID是客户端接口返回的结果,并不安全。如果需要依赖UID进行业务决策,请不要使用这个UID数据。请通过服务端ftoken查询接口(query)得到可信的UID{ "code":"返回码", "subCode":"二级返回码", "msg":"返回信息", "ftoken":"ftoken",//用来支付或完成其他操作 "result":{ "alipayUid":"alipayUid", //支付宝账户的uid } }
关键入参:
参数名称 | 参数类型 | 是否必填 | 参数说明 |
---|---|---|---|
zimId | String | 是 | 人脸初始化接口返回的zimId, 其中包含了唤起人脸验证的关键参数 |
params | Map | 是 | zim.init.resp 必填,调用服务初始化返回的结果中的协议内容 phone_number 可选,用于会员刷脸付方案。填入用户登录会员系统的手机号 smile_mode 可选,用于指定选择刷脸模式,值为int类型,0: 主屏幕显示(默认值);1:双面屏机具,副屏幕显示 |
- 关键出参:
参数名称 | 描述 |
---|---|
zimId | 错误码,如果刷脸认证成功返回,则为CODE_SUCCESS。常见错误码见下面表格 |
msg | 返回信息 |
subCode | 二级返回码 |
ftoken | 标识刷脸认证成功,作为查询接口的入參 有效期2分钟 |
result | alipayUid被认证用户的支付宝账户UID |
3.5 调用收单接口完成支付
- 功能码:P7001
- 接口说明:当自助机调用唤起人脸识别接口完成后,会得到ftoken,用该ftoken作为授权码auth_code,传入聚合支付平台的支付接口(P7001)进行支付。调用支付接口,需要注意以下事项:
- merchant_no传入的是平台的商户号,不是支付宝商户的商户号;
- paytype支付渠道请选择2=支付宝
- trade_type交易类型,选择7=刷脸付
- auth_code授权码,传入ftoken
- scene_type场景信息,传入1=自助机
- terminal_id商户机具终端编号,请与获取设备信息接口入参deviceNum保持一致
- store_id商户门店编号,请与获取设备信息接口入参storeCode保持一致
- alipay_store_id支付宝内部门店编号,请与获取设备信息接口入参alipayStoreCode保持一致
- 请求参数:
<xml>
参数 | 名称 | 类型 | 长度 | 是否必填 | 说明 | 示例 |
---|---|---|---|---|---|---|
treatment_id | 诊疗卡卡号 | String | 30 | 是 | 患者的诊疗卡卡号 | 123 |
merchant_no | 收款商户号 | String | 30 | 是 | 创业聚合支付系统为医院分配的平台商户号 | 809900000001 |
username | 支付者姓名 | String | 30 | 是 | 付款者姓名 | 张三 |
order_no | 业务单号 | String | 30 | 是 | 商户业务系统的唯一订单号 | BX20190722902141 |
paytype | 支付渠道 | String | 2 | 是 | 2=支付宝 | 2 |
trade_type | 交易类型 | String | 2 | 是 | 7=刷脸付 | 1 |
total_amt | 支付金额 | Price | 20.2 | 是 | 以元为单位 | 0.01 |
spbill_create_ip | 用户IP | String | 10 | 是 | 客户端实际ip(微信需要) | 183.156.115.113 |
order_subject | 商品标题 | String | 150 | 是 | 如:门诊、住院预交、住院结算、自助缴费等 | 门诊 |
order_detail | 商品描述 | String | 300 | 是 | 如:门诊挂号、门诊结算、住院预交、住院结算、自助挂号、自助结算、自助住院预交等 | 门诊挂号 |
sub_merchant | 分店号 | String | 30 | 否 | 商户内部区分订单字段 | 001 |
paytimeout | 订单超时时间 | String | 3 | 否 | 单位是: m (分钟) 如果不填,默认是 6分钟,如果填,必须大于5分钟 | 6 |
mobile | 手机号码 | String | 15 | 否 | 用户手机号 | 13211112222 |
auth_code | 授权码 | String | 128 | 是 | 传入ftoken | 134692901956119825 |
scene_type | 场景类型 | String | 1 | 否 | 1=自助机 | 1 |
terminal_id | 商户机具终端编号 | String | 1 | 否 | 与获取设备信息接口传入deviceNum字段保持一致 | NJ_T_001 |
store_id | 商户门店编号 | String | 1 | 否 | 与获取设备信息接口入参storeCode保持一致 | NJ_001 |
alipay_store_id | 支付宝内部门店编号 | String | 1 | 否 | 与获取设备信息接口入参alipayStoreCode保持一致 | |
remark | 备注 | String | 200 | 否 | 备注 | |
big_merchant_no | 统一商户号 | String | 30 | 否 | 流水转入统收商户中(不建议使用,对账无法区分) |
- 响应参数:
参数 | 名称 | 类型 | 长度 | 是否必输 | 说明 | 示例 |
---|---|---|---|---|---|---|
return_code | 返回状态码 | String | 16 | 否 | SUCCESS/OK代表成功 FAIL=代表失败 WAIT=待支付 不能以此作为支付成功标志,支付结果以status为准 |
SUCCESS |
return_msg | 返回信息 | String | 128 | 否 | 返回信息 | OK |
code_url | 二维码串 | String | 500 | 否 | 扫码付时返回 | |
status | 支付状态 | String | 2 | 否 | 1=成功;0=支付中 需要调用查询接口轮询查询订单状态 刷卡支付时使用 |
1 |
pay_orderno | 支付渠道流水号 | String | 32 | 否 | 刷卡支付时使用 | 4200000376201908224052992001 |
order_no | 平台流水号 | String | 32 | 否 | 刷卡支付时使用 | 34411000 |
out_tradeno | 业务单号 | String | 32 | 否 | 刷卡支付时使用 | BX20190722902141 |
time_end | 支付完成时间 | String | 32 | 否 | 刷卡支付时使用(yyyyMMddHHmmss) | 20190722205306 |
amt | 实际支付金额 | Price | 16.2 | 否 | 刷卡支付时使用 | 0.01 |
receipt_amount | 商户收到的金额 | Price | 16.2 | 否 | 刷卡支付时使用(相当于订单支付金额) | 0.01 |
- Java请求示例:
String data = "<xml>" + "<treatment_id>123123123123</treatment_id>" + "<merchant_no>1001003</merchant_no>" + "<username>测试者</username>" + "<order_no>"+ "xw20190722001"+"</order_no>" + "<paytype>3</paytype>" + "<trade_type>2</trade_type>" + "<total_amt>0.01</total_amt>" + "<spbill_create_ip>192.168.11.1</spbill_create_ip>" + "<order_subject>测试</order_subject>" + "<order_detail>测试</order_detail>" + "<auth_code>130195331105619585</auth_code>" + "</xml>"; List<NameValuePair> list = new ArrayList<NameValuePair>(); NameValuePair valu1 = new BasicNameValuePair("data",data); NameValuePair valu2 = new BasicNameValuePair("developerId",[your developerId]); NameValuePair valu3 = new BasicNameValuePair("funcId","P7001"); String privateKey = [your privateKey]; String signData = "data="+data+"&developerId=[your developerId]&funcId=P7001"; String sign = SecurityUtil.signRSA(signData,privateKey,"utf-8"); NameValuePair valu4 = new BasicNameValuePair("sign",sign); list.add(valu1); list.add(valu2); list.add(valu3); list.add(valu4); String xml = null; try { CloseableHttpResponse response = HttpClients.custom().build().execute(RequestBuilder.post().setUri("http://offlinetest.bsoftpay.com/gatewayOffline/pay/offline/execute").setEntity(new UrlEncodedFormEntity(list, Consts.UTF_8)).build()); xml = EntityUtils.toString(response.getEntity()); } catch (Exception e) { e.printStackTrace(); } System.out.println(xml);
- 返回示例:
developerId=12345&funcId=P7001&retCode=0000&retMsg= &data=<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <xml> <return_code>SUCCESS</return_code> <return_msg>OK</return_msg> <status>1</status> <pay_orderno>4200000376201907220000000000</pay_orderno> <order_no>34411000</order_no> <out_tradeno>WX15637998177239200001</out_tradeno> <time_end>20190722205306</time_end> <amt>0.01</amt> <receipt_amount>0.01</receipt_amount> </xml> &sign=Apu4i49RBQkh94n5BVEA/Ao35tjfT8BsTPA9fOqKUmbKJ37Pszfi0sS9R15OLEpXJYo6p2v+uw1fr/BE+PSxHg==
- 异常返回示例:
developerId=12345&funcId=P7001&retCode=9999&retMsg=请扫描微信支付授权码(以10/11/12/13/14/15为前缀的18位数字)&sign=XD1unE4I2pM/jWMXy1o7NwVB2H3yfkmxluH2qz3xRt+mB8VQNSLhKnz79+5PuRfz9Q4y44CKCyL7S21iQzOHbg==
- 注意:签名sign生成之后,在请求发送之前,需要对签名字符串进行urlEncode编码,否则字符串在传递过程中可能会不完整,从而导致“验证签名失败”
3.6 交易结果查询接口
- 功能码:P2005
- 接口说明:通过该接口获取某个订单的支付结果
- 请求参数:
<Data>
参数 | 名称 | 类型 | 长度 | 是否必填 | 说明 | 示例 |
---|---|---|---|---|---|---|
order_no | 业务单号 | String | 32 | 是 | 商户内部业务单号 | WX2019072200001 |
merchant_no | 收款商户号 | String | 32 | 是 | 商户号 | 809900000001 |
- 响应参数:
参数 | 名称 | 类型 | 长度 | 是否必输 | 说明 | 示例 |
---|---|---|---|---|---|---|
TradeNo | 平台流水号 | String | 32 | 是 | 聚合支付平台流水号 | 34411000 |
PayTradeNo | 渠道流水号 | String | 32 | 是 | 各个支付渠道返回的流水号 | 4200000376201907220000000000 |
OrderNo | 业务单号 | String | 32 | 是 | 商户内部业务单号 | WX2019072200001 |
Amt | 实际支付金额 | Price | 16.2 | 是 | 用户实际付款金额 | 0.01 |
PayTime | 实际支付时间 | String | 16 | 是 | yyyy-MM-dd hh:mm:ss | 2019-07-22 21:37:31 |
PayStatus | 支付状态 | String | 6 | 是 | 0=未支付;1=支付成功;2=支付失败;-1=关闭或撤销 | 1 |
receipt_amount | 商户收到的金额 | Price | 16.2 | 是 | 相当于订单支付金额 | 0.01 |
- JAVA请求示例:
String data = "<Data>" + "<order_no>WX2019072200001</order_no>\n" + "<merchant_no>809900000001</merchant_no>\n"+ "</Data>"; List<NameValuePair> list = new ArrayList<NameValuePair>(); NameValuePair valu1 = new BasicNameValuePair("data",data); NameValuePair valu2 = new BasicNameValuePair("developerId","[your developerId]"); NameValuePair valu3 = new BasicNameValuePair("funcId","P2005"); String privateKey = [your privateKey]; String signData = "data="+data+"&developerId=[your developerId]&funcId=P2005"; String sign = SecurityUtil.signRSA(signData,privateKey,"utf-8"); NameValuePair valu4 = new BasicNameValuePair("sign",sign); list.add(valu1); list.add(valu2); list.add(valu3); list.add(valu4); String xml = null; try { CloseableHttpResponse response = HttpClients.custom().build().execute(RequestBuilder.post().setUri("http://offlinetest.bsoftpay.com/gatewayOffline/pay/offline/execute").setEntity(new UrlEncodedFormEntity(list, Consts.UTF_8)).build()); xml = EntityUtils.toString(response.getEntity()); } catch (Exception e) { e.printStackTrace(); } System.out.println(xml);
- 返回示例:
developerId=66337&funcId=P2005&retCode=0000&retMsg= &data=<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <Data> <TradeNo>34411000</TradeNo> <PayTradeNo>4200000376201907220000000000</PayTradeNo> <OrderNo>WX2019072200001</OrderNo> <Amt>0.01</Amt> <receipt_amount>0.01</receipt_amount> <PayTime>2019-07-22 21:37:31</PayTime> <PayStatus>1</PayStatus> </Data> &sign=JxUbXFPS4Kp3bS9ipmznDoAwBd3TzLX4Df8xd5VFR97Z1Kyb5XiJNxZ4LXA1RIlOafm+BmmnPXZXDfykss3geg==
- 注意: P2005功能中请求参数是用
<Data></Data>
包围,不是<xml></xml>
4. 人脸核身
4.1 接入细节
- 接口说明:按照刷脸流程1-2-3,同刷脸支付。细节见文档
方案一(传入身份证进行认证):
https://docs.alipay.com/pre-open/20171214171953173616/gwqlk0
方案二(获取用户授权信息):
https://docs.open.alipay.com/289/1056564.2 唤起用户授权
- 接口说明:扫脸核身完成后进行用户授权接入获取用户基础信息
身份证信息需要找支付宝客户经理申请
。解决方案技术实现如下
- 扫码授权请求拼接规则
需要用户扫码登录
参数说明 | 是否必须 | 说明 |
---|---|---|
app_id | 是 | 开发者应用的app_id; 相同支付宝账号下,不同的app_id获取的token切忌混用。 |
redirect_uri | 是 | 授权回调地址,是经过URLENCODE转义 的url链接(url必须以http或者https开头); 在请求之前,开发者需要先到开发者中心对应应用内,配置授权回调地址。 redirect_uri与应用配置的授权回调地址域名部分必须一致。http%3A%2F%2Fpay.bsoftpay.com%3A53503%2FtoAuthPage |
scopes | 是 | 接口权限值,目前只支持auth_user(获取用户信息、网站支付宝登录)、auth_base(用户信息授权)、auth_ecard(商户会员卡)、auth_invoice_info(支付宝闪电开票)、auth_puc_charge(生活缴费)五个值;多个scope时用”,”分隔,如scope为”auth_user,auth_ecard”时,此时获取到的access_token,既可以用来获取用户信息,又可以给用户发送会员卡。 |
state | 否 | 商户自定义参数,用户授权后,重定向到redirect_uri时会原样回传给商户。 为防止CSRF攻击,建议开发者请求授权时传入state参数,该参数要做到既不可预测,又可以证明客户端和当前第三方网站的登录认证状态存在关联。只允许base64字符(长度小于等于100) |
- 拼接样例:
注意:各个参数需要做urlEncode,redirect_uri参数尽量放置在最后一位https://openauth.alipay.com/oauth2/publicAppAuthorize.htm?app_id=APPID&scope=SCOPE&redirect_uri=ENCODED_URL
授权时如遇”人气大爆发”提示,可通过客户端开发者模式查看报错原因
- 刷脸授权请求拼接规则
参数说明 | 是否必须 | 说明 |
---|---|---|
app_id | 是 | 开发者应用的app_id; 相同支付宝账号下,不同的app_id获取的token切忌混用。 |
redirect_uri | 是 | 授权回调地址(url必须以http或者https开头); 在请求之前,开发者需要先到开发者中心对应应用内,配置授权回调地址。 redirect_uri与应用配置的授权回调地址域名部分必须一致。http://pay.bsoftpay.com:53503/toAuthPage |
scopes | 是 | 接口权限值,目前只支持auth_user、auth_base、auth_zhima、auth_ecard、auth_certdoc五个值;多个scope时用”,”分隔,如scope为“auth_user,auth_certdoc” |
state | 否 | 商户自定义参数,用户授权后,重定向到redirect_uri时会原样回传给商户。 为防止CSRF攻击,建议开发者请求授权时传入state参数,该参数要做到既不可预测,又可以证明客户端和当前第三方网站的登录认证状态存在关联。只允许base64字符(长度小于等于100) |
auth_type | 是 | 刷脸:FACE_REC_OAUTH |
ftoken | 是 | 人脸token |
- 拼接样例:
注意:授权时如遇”人气大爆发”提示,可通过客户端开发者模式查看报错原因,常见原因为Ftoken过期,回调地址异常等https://authweb.alipay.com/auth?auth_type=FACE_REC_OAUTH&ftoken=XXXXX&app_id=&scope=xxxx&state=xxx&redirect_uri=http://pay.bsoftpay.com:53503/toAuthPage
- 获取auth_code
当用户授权成功后,会跳转至开发者定义的回调页面,支付宝会在回调页面请求中加入参数,包括auth_code、app_id、scope等,需要注意的是支付宝仅保证auth_code、app_id以及scope参数的有效性。支付宝请求开发者回调页面示例如下:http://pay.bsoftpay.com:53503/toAuthPage?app_id=2014101500013658&scope=auth_user&state=XXXX&auth_code=ca34ea491e7146cc87d25fca24c4cD11
4.3 支付宝人脸核身获取用户详细信息
- 功能码:P5002
- 接口说明:通过该接口获取用户详细信息
- 请求参数:
<xml>
参数 | 名称 | 类型 | 长度 | 是否必填 | 说明 | 示例 |
---|---|---|---|---|---|---|
auth_code | 授权码 | String | 32 | 是 | 授权码 | ca34ea491e7146cc87d25fca24c4cD11 |
merchant_no | 收款商户号 | String | 32 | 是 | 商户号 |
- 响应参数:
参数 | 名称 | 类型 | 长度 | 是否必输 | 说明 | 示例 |
---|---|---|---|---|---|---|
return_code | 返回状态码 | String | 16 | 否 | SUCCESS/OK代表成功 FAIL=代表失败 | SUCCESS |
return_msg | 返回信息 | String | 128 | 否 | 返回信息 | OK |
user_id | 支付宝用户的userId | String | 否 | 2088102104794936 | ||
avatar | 用户头像地址 | String | 否 | http://tfsimg.alipay.com/images/partner/T1uIxXXbpXXXXXXXX | ||
province | 省份名称 | String | 否 | 安徽省 | ||
city | 市名称。 | String | 否 | 安庆 | ||
nick_name | 用户昵称 | String | 否 | 用户昵称 | 支付宝小二 | |
is_student_certified | 是否是学生 | String | 否 | 是否是学生 | T | |
user_type | 用户类型 | String | 否 | 用户类型(1/2) 1代表公司账户2代表个人账户 | 1 | |
user_status | 用户状态 | String | 否 | 用户状态(Q/T/B/W)。Q代表快速注册用户 T代表已认证用户 B代表被冻结账户 W代表已注册,未激活的账户 | T | |
is_certified | 是否通过实名认证。 | String | 否 | T是通过 F是没有实名认证 | F | |
gender | 性别 | String | 否 | 【注意】只有is_certified为T的时候才有意义,否则不保证准确性. 性别(f:女性;m:男性)。 | f | |
user_name | 用户真实姓名 | String | 否 | |||
mobile | 手机号码 | String | 否 | |||
cert_no | 身份证 | String | 否 | |||
cert_type | 证件类型 | String | 否 |
- java请求示例:
String xml = "<xml>" + "<auth_code>ftoken</auth_code>" + "<merchant_no>xxxxxxx</merchant_no>" + "</xml>"; List<NameValuePair> list = new ArrayList<NameValuePair>(); NameValuePair valu1 = new BasicNameValuePair("data",data); NameValuePair valu2 = new BasicNameValuePair("developerId",[your developerId]); NameValuePair valu3 = new BasicNameValuePair("funcId","P5002"); String privateKey = [your privateKey]; String signData = "data="+data+"&developerId=[your developerId]&funcId=P5002"; String sign = SecurityUtil.signRSA(signData,privateKey,"utf-8"); NameValuePair valu4 = new BasicNameValuePair("sign",sign); list.add(valu1); list.add(valu2); list.add(valu3); list.add(valu4); String xml = null; try { CloseableHttpResponse response = HttpClients.custom().build().execute(RequestBuilder.post().setUri("http://offlinetest.bsoftpay.com/gatewayOffline/pay/offline/execute").setEntity(new UrlEncodedFormEntity(list, Consts.UTF_8)).build()); xml = EntityUtils.toString(response.getEntity()); } catch (Exception e) { e.printStackTrace(); } System.out.println(xml);
- 返回示例:
developerId=31331&funcId=P5002&retCode=0000&retMsg= &data=<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <xml> <return_code>SUCCESS</return_code> <return_msg>OK</return_msg> <user_id></user_id> <avatar></avatar> <province></province> <city></city> <nick_name></nick_name> <is_student_certified></is_student_certified> <user_type></user_type> <user_status></user_status> <is_certified></is_certified> <gender></gender> <user_name></user_name> <mobile></mobile> <cert_no></cert_no> <cert_type></cert_type> </xml> &sign=Seko5j5kYUs1Vpmpon03IgX7GqiNYWvOkhcUq7alPneOGWSjNPuY3QeXMzUSTBiMC1WQGVwlK+TBPzQeDvvUnQ==
5. 参考文档
刷脸支付-支付宝官方文档:https://docs.open.alipay.com/20180402104715814204/cgxcze/
扫码核身-支付宝官方文档:https://docs.open.alipay.com/289/105656
刷脸核身-支付宝官方文档:https://www.yuque.com/fmsprh/ippl9x/hq0kds
6. 修订记录
日期 | 修订内容 | 修订人 |
---|---|---|
2019-11-25 | 新增刷脸付文档 | 邓辉 |