Android 微信人脸识别+微信二维码

前言:公司需要做一个人脸识别项目。用户点击商品 》 选择支付方式 》 微信人脸支付或微信、支付宝二维码支付。但不是我们的后端来和我合作,我需要自己去微信官方拿rawdata和AuthInfo(需要注意的是微信的接口请求都是XML请求

 

微信人脸支付

1.选择设备。微信人脸需要选择微信官方提供的设备

微信人脸设备展示

2.商户APP Demo。仔细查看demo中的人脸支付流程。(demo中的流程同样是不需要有后端的,需要的参数都需要自己去微信要)

商户APP Demo(查看的是商户APP Demo)

微信刷脸支付文档 # 刷脸支付场景说明

3.把wxfacepay-release加到libs中。

4.Application中微信人脸SDK初始化

    private fun initWxpayface() {
        val m1: Map<String?, String?> = HashMap()
//      m1.put("ip", "192.168.1.1"); //若没有代理,则不需要此行
//      m1.put("port", "8888");//若没有代理,则不需要此行
//      m1.put("user", mEtnUser.getText().toString());//若没有代理,则不需要此行
//      m1.put("passwd", mEtnPassword.getText().toString());//若没有代理,则不需要此行
//      m1.put("proxy_type", 1 ); //若没有代理,则不需要此行
//      m1.put("perform_mode", "LOW_PERFORM");//低性能表现,默认关闭美颜等
        WxPayFace.getInstance().initWxpayface(this, m1, object : IWxPayfaceCallback() {
            @Throws(RemoteException::class)
            override fun response(info: Map<*, *>?) {
                if (info == null) {
                    e("调用返回为空, 请查看日志")
                    RuntimeException("调用返回为空").printStackTrace()
                    return
                }
                val code = info["return_code"] as String?
                val msg = info["return_msg"] as String?
                e("初始化完成")
            }
        })
    }

5.获取rawdata。getWxpayfaceRawdata

        /**
         *	获取rawdata
         */
        WxPayFace.getInstance().getWxpayfaceRawdata(object : IWxPayfaceCallback() {

            override fun response(info: Map<*, *>?) {
                if (info == null) {
                    e("调用返回为空, 请查看日志")
                    java.lang.RuntimeException("调用返回为空").printStackTrace()
                    return
                }
                val code = info["return_code"] as String?
                val msg = info["return_msg"] as String?
                val rawData = info["rawdata"] as String?
                e(rawData)
                getAuthInfo(rawData)
            }
        })

6.获取AuthInfo。https://payapp.weixin.qq.com/face/get_wxpayface_authinfo

   private fun getAuthInfo(rawData: String?) {
        var map = mutableMapOf<String, String>()
        map["rawdata"] = rawData!!
        //门店编号
        map["store_id"] = Constance.store_id
        //门店名称
        map["store_name"] = Constance.store_name
        //终端设备编号,由商户定义。
        map["device_id"] = Constance.device_id
        //商户号绑定的公众号/小程序 appid
        map["appid"] = orderInfoBean!!.data.wxappid
        //	商户号
        map["mch_id"] = orderInfoBean!!.data.wxmchid
        //版本号:固定是1
        map["version"] = "1"
        var now = (System.currentTimeMillis() / 1000).toString()
        map["now"] = now
        map["sign_type"] = "MD5"
        map["nonce_str"] = Constance.nonce_str

        //按字典顺序排序
        val infoIds: List<Map.Entry<String, String>> =
            ArrayList(map.entries)
        Collections.sort(infoIds, { o1, o2 -> o1.key.compareTo(o2.key) })
        //使用&符号进行拼接
        val sbR: String =
            Tools.getStringBuffer(infoIds).toString() + "&key=" + orderInfoBean!!.data.wxmchkey
        //进行MD5加密之后  转大写
        val sign: String = Tools.encode(sbR).toUpperCase()
        map["sign"] = sign


        /*将map集合转为xml*/
        val toXml = Tools.mapToXml(map)
//        e("认证参数XMl$toXml")
        val body = RequestBody.create(null, toXml)

        e(toXml.toString())

        OkGo.post<String>(UrlUtils.WECHAT)
            .tag(UrlUtils.WECHAT)
            .upRequestBody(body)
            .execute(object : StringCallback() {
                override fun onError(response: com.lzy.okgo.model.Response<String?>?) {
                    e("获取到的微信支付凭证${response!!.body()}")
                }

                override fun onSuccess(response: com.lzy.okgo.model.Response<String?>?) {
                    val payVoucher = response!!.body()!!
                    val headers = response.headers()!!

                    try {
                        var code = Tools.parseGetAuthInfoXML(payVoucher, "return_code")
                        if ("SUCCESS" == code) {
                            val authinfo: String = Tools.parseGetAuthInfoXML(payVoucher, "authinfo")
                            pay(authinfo)
                        } else {
                            val msg: String = Tools.parseGetAuthInfoXML(payVoucher, "return_msg")
                            e("获取微信凭证失败$msg")
                        }
                    } catch (e: Exception) {
                        e.printStackTrace()
                        e("获取微信凭证失败$e")
                    }
                }
            })
    }

7.调起微信人脸识别。进行人脸识别getWxpayfaceCode(获取用户信息facecode)

    private fun pay(mAuthInfo: String?) {
        e("pay", "onClick | pay ")

        var params: HashMap<String, String> = HashMap()

        //FACEPAY: 人脸凭证,常用于人脸支付
        params["face_authtype"] = "FACEPAY"

        //商户号绑定的公众号/小程序 appid
        params["appid"] = orderInfoBean!!.data.wxappid
        //	商户号
        params["mch_id"] = orderInfoBean!!.data.wxmchid
        //门店编号
        params["store_id"] = Constance.store_id
        //目标face_code类型,可选值:"1",刷卡付款码:18位数字,通过「付款码支付/被扫支付」接口完成支付。如果不填写则默认为"0"
        params["face_code_type"] = "1"
        //支付成功页是否需要展示人脸识别授权项。 展示:1 不展示:0
        params["ask_face_permit"] = "0"
        //商户端是否对SDK返回支付结果,可选值:"0",返回支付结果,商户需在确认?付结果后调?[updateWxpayfacePayResult]通知SDK;"1"
        params["ignore_update_pay_result"] = "1"
        //	指定刷脸界面的运行屏幕,可选值:“0”, 运行在默认屏幕; "1", 运行在第二屏幕(副屏); "2", 自动选择屏幕; "3", 强制用presentation方式在第二屏幕(副屏)启动。
        params["screen_index"] = "0"
        //商户订单号,须与调用支付接口时字段一致
        params["out_trade_no"] = orderInfoBean!!.data.order_number
        //订单金额(数字)
        params["total_fee"] = allPrice.times(100).toInt().toString()
        params["authinfo"] = mAuthInfo.toString()


        e("pay | single callback")
        WxPayFace.getInstance().getWxpayfaceCode(params, object : IWxPayfaceCallback() {
            @Throws(RemoteException::class)
            override fun response(info: Map<*, *>?) {
                if (info != null) {
                    e(info.toString())
                    getWxfaceCode(info)
                } else
                    e("调用返回为空, 请查看日志")
            }
        }, object : IWxPayfaceCallback() {
            override fun response(info: MutableMap<Any?, Any?>?) {
                e(info.toString())
                if (info == null) {
                    RuntimeException("调用返回为空").printStackTrace()
                    return
                }
                val code = info["return_code"] as String // 错误码
                val msg = info["return_msg"] as String // 错误码描述

                if (code == null || code != "SUCCESS") {
                    RuntimeException("调用返回非成功信息,return_msg:$msg   ")
                        .printStackTrace()
                    return
                }
            }
        })
    }

    private fun getWxfaceCode(info: Map<*, *>) {
        val code = info["return_code"] as String?
        val facecode = info["face_code"] as String?
        when {
            //用户确认支付
            TextUtils.equals(code, WxfacePayCommonCode.VAL_RSP_PARAMS_SUCCESS) ->
                doPay(facecode)

            TextUtils.equals(code, WxfacePayCommonCode.VAL_RSP_PARAMS_USER_CANCEL) ->
                e("用户取消")

            TextUtils.equals(code, WxfacePayCommonCode.VAL_RSP_PARAMS_SCAN_PAYMENT) ->
                e("扫码支付")

            TextUtils.equals(code, WxfacePayCommonCode.VAL_RSP_PARAMS_ERROR) ->
                e("发生错误")

            TextUtils.equals(code, WxfacePayCommonCode.VAL_RSP_PARAMS_USER_QUERY_CANCEL) ->
                e("请与商户确认支付结果")
        }
    }

8.进行发起订单支付micropay。接口地址:https://api.mch.weixin.qq.com/pay/micropay

    private fun doPay(facecode: String?) {
        var map = mutableMapOf<String, String>()

        map["auth_code"] = facecode.toString()
        map["spbill_create_ip"] = "192.168.0.143"
        map["body"] = "捷视商品"
        map["attach"] = "订单额外描述"
        map["device_info"] = Constance.device_id
        map["sign_type"] = "MD5"
        map["total_fee"] = allPrice.times(100).toInt().toString()

        map["out_trade_no"] = orderInfoBean!!.data.order_number

        //商户号绑定的公众号/小程序 appid
        map["appid"] = orderInfoBean!!.data.wxappid
        //	商户号
        map["mch_id"] = orderInfoBean!!.data.wxmchid

        var now = (System.currentTimeMillis() / 1000).toString()
        map["time_start"] = now

        map["nonce_str"] = Constance.nonce_str


        //按字典顺序排序
        val infoIds: List<Map.Entry<String, String>> = ArrayList(map.entries)
        Collections.sort(infoIds) { o1, o2 -> o1.key.compareTo(o2.key) }
        //使用&符号进行拼接商户Key
        val sbR: String =
            Tools.getStringBuffer(infoIds).toString() + "&key=" + orderInfoBean!!.data.wxmchkey
        //进行MD5加密之后  转大写
        val sign: String = Tools.encode(sbR).toUpperCase()
        map["sign"] = sign

        e(map.toString())


        /*将map集合转为xml*/
        val toXml = Tools.mapToXml(map)
//        e("认证参数XMl$toXml")
        val body = RequestBody.create(null, toXml)

        OkGo.post<String>("https://com.api.mch.weixin.qq.com/pay/micropay")
            .tag(this)
            .upRequestBody(body)
            .execute(object : StringCallback() {
                override fun onError(response: com.lzy.okgo.model.Response<String?>?) {
                    e("获取微信支付${response!!.body()}")
                }

                override fun onSuccess(response: com.lzy.okgo.model.Response<String?>?) {
                    val payVoucher = response!!.body()!!
                    val headers = response.headers()!!
                    e("微信支付${payVoucher}")

                    try {
                        var code = Tools.parseGetAuthInfoXML(payVoucher, "return_code")
                        var resultCode = Tools.parseGetAuthInfoXML(payVoucher, "result_code")
                        var msg = Tools.parseGetAuthInfoXML(payVoucher, "return_msg")
                        if ("SUCCESS" == code) {
                            if ("SUCCESS" == resultCode) {
                                //支付成功,返回,告诉后端我们支付成功了,把支付成功参数给后台
                                returServerCode(payVoucher)
                            } else {
                                queryPay()
                            }
                        } else {
                            e("微信支付失败$msg")
                        }
                    } catch (e: Exception) {
                        e.printStackTrace()
                        e("微信支付失败$e")
                    }
                }
            })
    }

9.因为微信的人脸支付没有“notify_url”参数,所以需要我们主动去给后台消息说我们支付成功了。有时候微信给我们的订单支付成功消息不是SUCCESS,而是SYSTEMERROR(系统超时)(我有时候遇到了这种情况,用户明明付款成功了,但是返回的不是SUCCESS),所以我们要主动去查询订单一次

查询订单状态orderquery。接口地址:https://api.mch.weixin.qq.com/pay/orderquery

    private fun returServerCode(payVoucher: String) {
        //微信支付订单号
        var transactionid = Tools.parseGetAuthInfoXML(payVoucher, "transaction_id")
        var openid = Tools.parseGetAuthInfoXML(payVoucher, "openid")
        var totalfee = Tools.parseGetAuthInfoXML(payVoucher, "total_fee")

        var map = mutableMapOf<String, String>()
        map["api_name"] = "wxFaceNotify"
        map["shop_id"] = orderInfoBean!!.data.shop_id
        map["order_id"] = orderInfoBean!!.data.order_id
        map["order_number"] = orderInfoBean!!.data.order_number
        map["transaction_id"] = transactionid
        map["openid"] = openid
        map["total_fee"] = (totalfee.toDouble() / 100).toString()

        e(map.toString())

        OkGo.post<String>(UrlUtils.SHOPLIST)
            .tag(this)
            .params(map)
            .execute(object : StringCallback() {
                override fun onError(response: com.lzy.okgo.model.Response<String?>?) {
                    ToastUtil.showTextToast("网络连接失败请重试!!")
                }

                override fun onSuccess(response: com.lzy.okgo.model.Response<String?>?) {
                    e(response!!.body()!!)
                }
            })
    }

微信二维码(NATIVE支付)

获取微信二维码要开启NATIVE支付,我们要去统一下单中获取二维码

微信统一下单API

URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder

   private fun showWeChatCodePay(payType: Int) {
        var map = mutableMapOf<String, String>()

        //商户号绑定的公众号/小程序 appid
        map["appid"] = orderInfoBean!!.data.wxappid
        //	商户号
        map["mch_id"] = orderInfoBean!!.data.wxmchid
        //	商户号
        map["device_info"] = MyApplication.IMEI.toString()
        //	商户系统内部订单号
        map["out_trade_no"] = orderInfoBean!!.data.order_number
        //	商户系统内部ID
        map["product_id"] = orderInfoBean!!.data.order_id
        //随机字符串
        map["nonce_str"] = Constance.nonce_str
        //商品描述
        map["body"] = "捷视商品"
        //订单金额(数字)
        map["total_fee"] = allPrice.times(100).toInt().toString()
        //终端IP
        map["spbill_create_ip"] = "192.168.0.143"
        //通知地址
        map["notify_url"] = "http://www.jieshishop.com/Wxsite/Wxpay/wxf2fNotify"
        //交易类型
        map["trade_type"] = "NATIVE"

        //按字典顺序排序
        val infoIds: List<Map.Entry<String, String>> =
            ArrayList(map.entries)
        Collections.sort(infoIds) { o1, o2 -> o1.key.compareTo(o2.key) }
        //使用&符号进行频率
        val sbR: String =
            Tools.getStringBuffer(infoIds).toString() + "&key=" + orderInfoBean!!.data.wxmchkey
        //进行MD5加密之后  转大写
        val sign: String = Tools.encode(sbR).toUpperCase()
        map["sign"] = sign

        e(map.toString())

        /*将map集合转为xml*/
        val toXml = Tools.mapToXml(map)
        //        e("认证参数XMl$toXml")
        val body = RequestBody.create(null, toXml)

        e(toXml.toString())

        OkGo.post<String>("https://api.mch.weixin.qq.com/pay/unifiedorder")
            .tag(this)
            .upRequestBody(body)
            .execute(object : StringCallback() {
                override fun onError(response: com.lzy.okgo.model.Response<String?>?) {
                    e("微信二维码支付失败${response!!.body()}")
                }

                override fun onSuccess(response: com.lzy.okgo.model.Response<String?>?) {
                    val payVoucher = response!!.body()
                    try {
                        var msg = Tools.parseGetAuthInfoXML(payVoucher, "return_msg")
                        var code = Tools.parseGetAuthInfoXML(payVoucher, "return_code")
                        var resultCode = Tools.parseGetAuthInfoXML(payVoucher, "result_code")
                        var codeUrl = Tools.parseGetAuthInfoXML(payVoucher, "code_url")

                        if ("SUCCESS" == code) {
                            if ("SUCCESS" == resultCode) {
                                //支付成功
                                dialog = PayCodeDialog(mContext, payType, codeUrl)
                                dialog!!.show()
                            }
                        } else {
                            ToastUtil.showTextToast("微信二维码支付失败:$msg")
                        }
                    } catch (e: Exception) {
                        e.printStackTrace()
                        ToastUtil.showTextToast("微信二维码支付失败:$e")
                    }
                }
            })
    }

Tools 工具类:

public class Tools {

    /**
     *      * 将Map转换为XML格式的字符串
     *      *
     *      * @param data Map类型数据
     *      * @return XML格式的字符串
     *      * @throws Exception
     *      
     */
    public static String mapToXml(Map<String, String> data) throws Exception {
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
        org.w3c.dom.Document document = documentBuilder.newDocument();
        org.w3c.dom.Element root = document.createElement("xml");
        document.appendChild(root);
        for (String key : data.keySet()) {
            String value = data.get(key);
            if (value == null) {
                value = "";
            }
            value = value.trim();
            org.w3c.dom.Element filed = document.createElement(key);
            filed.appendChild(document.createTextNode(value));
            root.appendChild(filed);
        }
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer();
        DOMSource source = new DOMSource(document);
        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        StringWriter writer = new StringWriter();
        StreamResult result = new StreamResult(writer);
        transformer.transform(source, result);
        String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");
        try {
            writer.close();
        } catch (Exception ex) {
        }
        return output;
    }

    /**
     * 拼接
     *
     * @param infoIds
     * @return
     */
    @NonNull
    public static String getStringBuffer(List<Map.Entry<String, Object>> infoIds) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < infoIds.size(); i++) {
            Map.Entry<String, Object> stringStringEntry = infoIds.get(i);
            if (stringStringEntry.getKey() == null) {
                stringStringEntry.getKey();
            }
            String key = stringStringEntry.getKey();
            Object val = stringStringEntry.getValue();
            if (i != infoIds.size() - 1) {
                if (val != null && !TextUtils.equals("", val.toString())) {
                    sb.append(key).append("=").append(val).append("&");
                }
            } else {
                if (val != null && !TextUtils.equals("", val.toString())) {
                    sb.append(key).append("=").append(val);
                }
            }
        }
        return sb.toString();
    }

    /**
     * 作 者: yzhg
     * 历 史: (版本) 1.0
     * 描 述: md5加密
     */
    public static String encode(String password) {
        try {
            MessageDigest digest = MessageDigest.getInstance("md5");
            byte[] result = digest.digest(password.getBytes());
            StringBuilder sb = new StringBuilder();
            for (byte b : result) {
                int number = b & 0xff;
                String str = Integer.toHexString(number);
                if (str.length() == 1) {
                    sb.append("0");
                }
                sb.append(str);
            }
            return sb.toString();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return "";
        }
    }

    public static String parseGetAuthInfoXML(String resultText, String indexText) throws Exception {
        InputStream is = new ByteArrayInputStream(resultText.getBytes());
        String result = null;
        XmlPullParser parser = Xml.newPullParser();
        parser.setInput(is, "UTF-8");

        int eventType = parser.getEventType();
        while (eventType != XmlPullParser.END_DOCUMENT) {
            switch (eventType) {
                case XmlPullParser.START_TAG:
                    if (parser.getName().equals(indexText)) {
                        eventType = parser.next();
                        result = parser.getText();
                    }
            }
            eventType = parser.next();
        }
        return result;
    }

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值