微信小程序> Andorid微信刷脸支付使用过程解析

Andorid微信刷脸支付使用过程解析

浏览量:998 时间: 来源:Darkside#

微信刷脸支付全部流程(普通商户号)

    • 1.创建移动应用
    • 2.申请商户号, 开通APP支付和刷脸支付,关联APPID
    • 3.开发指引 | 微信刷脸支付
    • 4.刷脸支付流程
        • 名词解释
        • 时序图
    • 5.接口调用踩坑
        • (1)初始化(initWxpayface)
        • (2)获取数据 (getWxpayfaceRawdata)
        • (3)获取调用凭证(get_wxpayface_authinfo)
          • Sign参数获取
        • (4)获取人脸支付凭证(getWxpayfaceCode)
        • (5)支付 (pay),并更新支付结果(updateWxpayfacePayResult)
          • 1. pay
          • 2. updateWxpayfacePayResult(final Map info, final IWxPayfaceCallback wxpayfaceCallBack)
            • 接口参数
            • 接口返回
            • 实践指引

本文着重是 开发前的准备工作和开发过程中遇到的一些坑,不涉及 优化 ,订单查询 ,退款和 具体商户后台server的开发。
开发环境: rk开发板 +华捷艾米A200 camera
最终实现  用自己的应用和商户信息成功进行了一笔刷脸支付过程的demo 并且不需要搭建自己的商户后台server。

1.创建移动应用

微信开放平台
在微信开放平台中创建 移动应用,填写 包名 和 签名信息 等。创建需要等审核完成,一般审核过程很快。

小程序
然后开通微信支付,未认证用户需要进行认证。认证过程也需要审核,这个比创建应用的时候要慢。
小程序
小程序
小程序
认证过程需要填写一些企业资料 和收取一定费用 ,好像是 300/年 。

2.申请商户号, 开通APP支付和刷脸支付,关联APPID

微信商户号申请

小程序
点击APP支付  申请开通
小程序
小程序
小程序
开通后 在APPID授权管理 标签页中 关联 步骤1 中所申请的 APPID (必须已经完成认证并且开通APP支付权限)。
小程序
如果APPID未认证 会提示
小程序
如果未开通APP支付权限
小程序
所以必须先要认证并且开通支付权限。

正常情况:
小程序
然后去微信开放平台 对应的APP设置中确认 关联。
小程序
小程序
至此绑定完成


3.开发指引 | 微信刷脸支付

微信刷脸支付 开发指引
微信刷脸支付SDK 目前应该也在快速迭代中,前几天还是1.30版本,现在就已经更新2.10版本了。
使用方式:

  1. 安装人脸App。WxPayFace 微信刷脸支付SDK
  2. 商户接入人脸SDK。项目中引入1中的aar包。商户APP demo
  3. 商户server .商户server demo 此server是在商户自己开发整套流程的参考demo ,如果只是跑通商户App demo 则不需要此server。


4.刷脸支付流程

名词解释

人脸授权 :通过人脸识别,返回微信用户信息(openid, face_code)。

face_code:人脸凭证。常用于人脸支付,作为订单的支付凭证。

时序图

注:

  1. 初始化 initWxpayface, 只需要在程序启动时调用;
  2. 释放资源 releaseWxpayface,只需要在程序退出时调用;
    每个方法的具体参数可在文档内查看。接口文档

5.接口调用踩坑

(1)初始化(initWxpayface)

这个一般放在自己定义的Application#onCreate()中调用就可以了。官方示例copy即可

//对人脸SDK进行初始化WxPayFace.getInstance().initWxpayface(this, new IWxPayfaceCallback() {@Overridepublic void response(Map info) throws RemoteException {if (info == null) {new RuntimeException("调用返回为空").printStackTrace();return ;}String code = (String) info.get("return_code");String msg = (String) info.get("return_msg");Log.d(TAG, "response info :: " + code + " | " + msg);if (code == null || !code.equals("SUCCESS")) {new RuntimeException("调用返回非成功信息: " + msg).printStackTrace();return ;}Log.d(TAG, "调用返回成功");}});

(2)获取数据 (getWxpayfaceRawdata)

此过程 一定要保证 两点:
1.设备能联网,应用要添加uses-permission android:name="android.permission.INTERNET"/权限,
官方demo中还添加以下权限:

   uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /   uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /   uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /   uses-permission android:name="android.permission.READ_PHONE_STATE" /   uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /   uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /

2.设备有SN号 。在android设备上,开机后进入setting-about devices-status-Serial number查看
当时使用的是 rk开发板,IMG没有写入SN号。当时在论坛上找的写入SN号工具只能写入 "ro.boot.serialno"的值,"ro.serialno"还是空的,所以最后只能修改源代码,重新编译烧录版本。

    /** 2. 人脸识别第二步 获取raw data*/    private void getWxpayfaceRawdata() {        WxPayFace.getInstance().getWxpayfaceRawdata(new IWxPayfaceCallback() {            @Override            public void response(Map info) throws RemoteException {                if (info == null) {                    new RuntimeException("调用返回为空").printStackTrace();                    return;                }                String code = (String) info.get("return_code");                String msg = (String) info.get("return_msg");                rawdata = info.get("rawdata").toString();                Log.d(TAG, "rawdata ==" + rawdata);                if (code == null || rawdata == null || !code.equals("SUCCESS")) {                    new RuntimeException("调用返回非成功信息,return_msg:" + msg + "   ").printStackTrace();                    return ;                }               /**                在这里处理您自己的业务逻辑                 可以紧接着执行第三步 获取调用凭证getAuthInfo,                 这应该是向 商户server 发起请求。                */               getAuthInfo(rawdata);            }        });    }

(3)获取调用凭证(get_wxpayface_authinfo)

这是一个后端调用接口  采用xml格式
因为demo为了省事,省去商户后台server的开发,所以这一步也是在Android端直接调用。
获取凭证需要很多的 参数

参数必填类型说明
store_idstring(32)门店编号, 由商户定义, 各门店唯一。
store_namestring(128)门店名称,由商户定义。(可用于展示)
device_idstring(32)终端设备编号,由商户定义。
attachstring附加字段。字段格式使用Json
rawdatastring(2048)初始化数据。由微信人脸SDK的接口返回。
获取方式参见:
[获取数据 getWxpayfaceRawdata](#获取数据 getWxpayfaceRawdata)
[获取数据 getWxpayfaceRawdata](#获取数据 getWxpayfaceRawdata)
appidstring(32)商户号绑定的公众号/小程序 appid
mch_idstring(32)商户号
sub_appidstring(32)子商户绑定的公众号/小程序 appid(服务商模式)
sub_mch_idstring(32)子商户号(服务商模式)
nowint取当前时间,10位unix时间戳。  例如:1239878956
versionstring版本号。固定为1
sign_typestring签名类型,目前支持HMAC-SHA256和MD5,默认为MD5
nonce_strstring(32)随机字符串,不长于32位
signstring参数签名。详见微信支付签名算法
Sign参数获取

签名算法

签名生成的通用步骤如下:

第一步,设所有发送或者接收到的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA。

特别注意以下重要规则:

参数名ASCII码从小到大排序(字典序)
◆ 如果参数的值为空不参与签名;
◆ 参数名区分大小写;
◆ 验证调用返回或微信主动通知签名时,传送的sign参数不参与签名,将生成的签名与该sign值作校验。
◆ 微信接口可能增加字段,验证签名时必须支持增加的扩展字段
第二步,在stringA最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。

◆ key设置路径:微信商户平台(pay.weixin.qq.com)–账户设置–API安全–密钥设置
,获取方法:
小程序
安装插件 ,浏览器最好别用QQ浏览器。小程序
自家的插件不知道为什么唤不起来。重启N遍无用,推荐Chrome.
密钥是32位字符串 。
在上面第二步代码回调成功后紧接着 可以调用第三步。

    private void getAuthInfo(String rawdata){        //now 参数 是unix 10位时间戳        long  time = System.currentTimeMillis()/1000L;        //为了对比起来方便 下面所有请求 都是用 字符串拼接的方式。        //注意参数顺序 参数名ASCII码从小到大排序(字典序).        String a ="appid=appid应用ID&device_id=DEV001&mch_id=mch_id商户号&nonce_str=V37ZHZVf2OrwsUV7kXTjTguP74c0byvE&now="+time+"&rawdata="+rawdata+"&sign_type=MD5&store_id=IMG001&store_name=门店名称&version=1";        String stringSignTemp=a+"&key=32位的字符串";//注:key为商户平台设置的密钥key        String sign= md5(stringSignTemp).toUpperCase(); //注:MD5签名方式        Log.d(TAG, "sign : " +sign);        String  finalStr = "xml" +                "     appidappid应用ID/appid" +                "     device_idDEV001/device_id" +                "     nonce_strV37ZHZVf2OrwsUV7kXTjTguP74c0byvE/nonce_str" +                "     now"+time+"/now" +                "     mch_idmch_id商户号/mch_id" +                "     rawdata"+rawdata+"/rawdata" +                "     store_idIMG001/store_id" +                "     store_name门店名称/store_name" +                "     sign_typeMD5/sign_type" +                "     version1/version" +                "     sign"+sign+"/sign" +                "/xml";               //SSL可以不用管        try {            // Create a trust manager that does not validate certificate chains            final TrustManager[] trustAllCerts = new TrustManager[]{                    new X509TrustManager() {                        @Override                        public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {                        }                        @Override                        public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {                        }                        @Override                        public java.security.cert.X509Certificate[] getAcceptedIssuers() {                            return new java.security.cert.X509Certificate[]{};                        }                    }            };            // Install the all-trusting trust manager            final SSLContext sslContext = SSLContext.getInstance("SSL");            sslContext.init(null, trustAllCerts, new java.security.SecureRandom());            // Create an ssl socket factory with our all-trusting manager            final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();            OkHttpClient client = new OkHttpClient.Builder()                    .sslSocketFactory(sslSocketFactory)                    .hostnameVerifier(new HostnameVerifier() {                        @Override                        public boolean verify(String hostname, SSLSession session) {                            return true;                        }                    })                    .build();                                //请求格式xml ,请求方式post            RequestBody body=RequestBody.create(MediaType.parse("application/xml"),finalStr);            Request request = new Request.Builder()                    .url("https://payapp.weixin.qq.com/face/get_wxpayface_authinfo")//后台接口地址,具体见后台开发文档                    .post(body)                    .build();            client.newCall(request)                    .enqueue(new Callback() {                        @Override                        public void onFailure(Call call, IOException e) {                            Log.d(TAG, "onFailure | getAuthInfo " + e.toString());                        }                        @Override                        public void onResponse(Call call, Response response) throws IOException {                            try {                                String bodyStr = response.body().string();                                Log.d(TAG, "onResponse | getAuthInfo " + bodyStr);                                //这里返回的不是标准的xml格式,缺少 开始标签 ,拼接后用XmlPullParser解析                                String  xmlStr = "?xml version="1.0" encoding="utf-8"?"+bodyStr;                                //ReturnXMLParser 类在官方的demo中                                //最终获取到 AuthInfo信息。                                mAuthInfo = ReturnXMLParser.parseGetAuthInfoXML( new ByteArrayInputStream(xmlStr.getBytes()));                            } catch (Exception e) {                                e.printStackTrace();                            }                        }                    });        } catch (Exception e) {            e.printStackTrace();            throw new RuntimeException(e);        }    

这里需要注意的很多 :

  1. now 参数 注意是 10位的 时间戳 。并且拼接字符串a 时用的now 和 最后请求Body中xml内容中的Now的值一定要相同,两个地方切勿直接用 System.currentTimeMillis()/1000L
  2. 获取sign值 拼接的字符串a  一定要按照参数名ASCII码从小到大排序。sign值可以用签名校验工具验证
  3. 请求接口 https://payapp.weixin.qq.com/face/get_wxpayface_authinfo
  4. 返回值不是标准的xml格式,不包括"?xml version="1.0" encoding="utf-8"?"部分,所以可以先拼接在解析。

就这样最终拿到了AuthInfo字符串 。


(4)获取人脸支付凭证(getWxpayfaceCode)

启动人脸APP主界面入口,开启人脸识别,获取用户信息(openid)和支付凭证()。

参数必填类型说明
appidstring商户号绑定的公众号/小程序 appid
mch_idstring商户号
sub_appidstring(32)子商户绑定的公众号/小程序 appid(可不填)
sub_mch_idstring(32)子商户号(非服务商模式不填)
store_idstring门店编号
telephonestring用户手机号。用于传递会员手机,此手机将作为默认值, 填写到手机输入栏。
out_trade_nostring商户订单号,须与调用支付接口时字段一致,该字段在在face_code_type为"1"时可不填,为"0"时必填
total_feestring订单金额(数字), 单位分. 该字段在在face_code_type为"1"时可不填,为"0"时必填
face_authtypestring可选值:
FACEPAY: 人脸凭证,常用于人脸支付
FACEPAY_DELAY: 延迟支付(提供商户号信息联系微信支付开通权限)
authinfostring调用凭证。获取方式参见: get_wxpayface_authinfo
ask_face_permitstring支付成功页是否需要展示人脸识别授权项。
    展示:1
    不展示:0
人脸识别授权项:
用户授权后用于1:N识别,可返回用户信息openid,建议商户有自己会员系统时,填1。
ask_ret_pagestring是否展示微信支付成功页,可选值:“0”,不展示;“1”,展示
face_code_typestring目标face_code类型,可选值:“0”,人脸付款码:数字字母混合,通过「刷脸支付」接口完成支付;“1”,刷卡付款码:18位数字,通过「付款码支付/被扫支付」接口完成支付。如果不填写则默认为"0"
ignore_update_pay_resultstring商户端是否对SDK返回支付结果,可选值:“0”,返回支付结果,商户需在确认⽀付结果后调⽤[updateWxpayfacePayResult]通知SDK;“1”,不返回支付结果。如果不填写则默认为"0"。
    private void getWXPayFaceCode(String mAuthInfo){        
              
              
            

版权声明

即速应用倡导尊重与保护知识产权。如发现本站文章存在版权问题,烦请提供版权疑问、身份证明、版权证明、联系方式等发邮件至197452366@qq.com ,我们将及时处理。本站文章仅作分享交流用途,作者观点不等同于即速应用观点。用户与作者的任何交易与本站无关,请知悉。

产品经理

手机 : 13312967497

擅长 : 小程序流量变现

扫码领取礼包

热门模板

  • 头条
  • 搜狐
  • 微博
  • 百家
  • 一点资讯
  • 知乎