微信支付-JSAPI:代码实现(支付异步回调中传中文参数解决)

  专栏简介

个人主页

本栏目录

                          心灵鸡汤

不是成功离我们太远,而是我们坚持的太少。

✍相关博文✍

微信支付配置


微信支付-JASPI:准备工作[微信公众平台配置,微信商户平台配置]

微信支付所需的条件配置已经完成,接下来就开始实现(以下仅供参考)

一,实现前准备

①,阅读官方支付文档,主要是业务流程这一点https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_4

②,官方demo下载 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1

微信支付-JSAPI:代码实现(支付异步回调中传中文参数解决)_第1张图片③,js支付请求方式选择(我选择的是chooseWXPay,这个根据自身情况选择)

1.商户平台:网页通过JavaScript调用getBrandWCPayRequest接口,发起微信支付请求

微信支付-JSAPI:代码实现(支付异步回调中传中文参数解决)_第2张图片2.公众平台:调用chooseWXPay实现

微信支付-JSAPI:代码实现(支付异步回调中传中文参数解决)_第3张图片

3.两者区别:选择getBrandWCPayRequest的话,支付页面不需要引入任何的js,选用chooseWXPay的话,需要引入JS文件

二,代码实现

微信支付-JSAPI:代码实现(支付异步回调中传中文参数解决)_第4张图片

1.前端页面核心代码

①,引入js

    

②,config接口注入权限验证(requestUrl为当前页面的路由地址)

 var reUrl =window.location.href;
            $.ajax({
                url: "/wxPay/getWXConfig?requestUrl="+encodeURIComponent(reUrl),
                dataType: "json",
                async: false,
                type: "GET",
                success: function (data) {
                    wx.config({
                        debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
                        appId: data.appId, // 必填,公众号的唯一标识
                        timestamp:data.timestamp, // 必填,生成签名的时间戳
                        nonceStr: data.nonceStr, // 必填,生成签名的随机串
                        signature: data.signature,// 必填,签名
                        jsApiList: ['chooseWXPay'] // 必填,需要使用的JS接口列表
                    });
                }
            });

geWxConfig的实现方法可参考:微信分享开发:代码实现[前端+后端](二)

③,支付调用统一下单接口(请根据自身业务定义后端接口

        function  SaveOrWxPay() {
            $.ajax({
                url: "/wxPay/pay2?openId="+ GetPar("token")+"&totalFee="+$("#labTotalCost").text()+"&operationNO="+$("#labOperationNO").text()+"&userName="+$("#UserName").val()+"&userPhone="+$("#UserPhone").val(),
                dataType: "json",
                async: false,
                type: "GET",
                success: function (data) {
                    wx.chooseWXPay({
                        timestamp: data.timeStamp, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
                        nonceStr: data.nonceStr, // 支付签名随机串,不长于 32 位
                        package: data.package, // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=\*\*\*)
                        signType: data.signType, // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
                        paySign: data.paySign, // 支付签名
                        success: function (res) {
                            //此处为支付成功后点击[完成]后的回调,可根据自身业务需求做相应处理
                        },
                    });
                }
            });
        }

2.后端核心代码

微信支付-JSAPI:代码实现(支付异步回调中传中文参数解决)_第5张图片

根据统一订单API,必备的参数有:

appid:公众账号ID          mch_id:商户号                   nonce_str:随机字符串            sign:签名     

body:商品描述              out_trade_no:商户订单号   total_fee:标价金额                 spbill_create_ip:终端IP   

trade_type:交易类型        openid:用户标识,授权登录公众号获得                   

notify_url:通知地址(我们可以在这个里面处理支付成功后的业务逻辑,比如修改支付状态)

①openid获取可参考微信授权登录:移动端[unionid](一),自行修改

②接下来就是关于支付的核心代码了

    /**
     * @description: 微信支付-统一下单
     * @author: lvyq
     * @date: 2020/9/16 16:30
     * @version 1.0
     */
    @ApiOperation(value = "微信支付-统一下单")
    @RequestMapping(value = "/pay2", method = {RequestMethod.POST, RequestMethod.GET})
    public Object Orders(HttpServletRequest request, String openId, String tradeNo, String totalFee,String operationNO,String userName,String userPhone) {
        CommonUtils commonUtils = new CommonUtils();
        totalFee=String.format("%.0f", Float.valueOf(totalFee)*100);
        //TODO 订单号暂自生成
        tradeNo = new CommonUtils().getRandomStringByLength(32);
        String nonce_str = WXPayUtil.generateNonceStr();
        try {
            Map paraMap = new HashMap<>();
            //获取请求ip地址
            String ip = commonUtils.getIPAddress(request);
            paraMap.put("appid", appid);
            paraMap.put("mch_id", mchid);
            paraMap.put("nonce_str", nonce_str);
            //签名
            String sign = WXPayUtil.generateSignature(paraMap, paternerKey);
            paraMap.put("sign", sign);
            paraMap.put("body", "维护管理服务费结算");
            paraMap.put("out_trade_no", tradeNo);
            paraMap.put("total_fee", totalFee);
            paraMap.put("spbill_create_ip", ip);
            // TODO 支付异步回调地址
            //此处对中文进行转码
            String RuserName=URLEncoder.encode(userName, "UTF-8");
            paraMap.put("notify_url", commonUtils.getDomainName(request).toString() + "/wxPay/callback2/"+operationNO+"/"+RuserName+"/"+userPhone);
            System.out.print("notify_url==="+commonUtils.getDomainName(request).toString() + "/wxPay/callback2/"+operationNO+"/"+RuserName+"/"+userPhone);
            paraMap.put("trade_type", "JSAPI");
            paraMap.put("openid", openId);
            paraMap.put("sign_type", "MD5");
            //将所有参数(map)转xml格式
            // String xml = WXPayUtil.mapToXml(paraMap);
            String xml = WXPayUtil.generateSignedXml(paraMap, paternerKey);
            //发送post请求"统一下单接口"返回预支付id:prepay_id
            String xmlStr = HttpRequest.sendPost(unifiedorderUrl, xml);
            String prepay_id = "";//预支付id
            if (xmlStr.indexOf("SUCCESS") != -1) {
                Map map = WXPayUtil.xmlToMap(xmlStr);
                prepay_id = (String) map.get("prepay_id");
            }
            Map payMap = new HashMap();
            payMap.put("appId", appid);
            payMap.put("timeStamp", WXPayUtil.getCurrentTimestamp() + "");
            payMap.put("nonceStr", WXPayUtil.generateNonceStr());
            payMap.put("signType", "MD5");
            payMap.put("package", "prepay_id=" + prepay_id);
            String paySign = WXPayUtil.generateSignature(payMap, paternerKey);
            payMap.put("paySign", paySign);
            return payMap;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * @description: 支付回调地址
     * @author: lvyq
     * @date: 2020/9/17 16:06
     * @version 1.0
     */
    @ApiOperation(value = "支付回调地址")
    @RequestMapping(value = "/callback2/{operationNO}/{userName}/{userPhone}", method = {RequestMethod.POST, RequestMethod.GET})
    public String CallBack(HttpServletRequest request, HttpServletResponse response,@PathVariable("operationNO") String operationNO,@PathVariable("userName") String userName,@PathVariable("userPhone") String userPhone) throws Exception {
        InputStream is = null;
        //解码
        String RuserName=URLDecoder.decode(userName,"UTF-8");
        System.out.print("RuserName==="+RuserName+"userName=="+userName);
        try {
            //获取请求的流信息(这里是微信发的xml格式所有只能使用流来读)
            is = request.getInputStream();
            String xml = commonUtils.nputStream2String(is);
            //将微信发的xml转map
            Map notifyMap = WXPayUtil.xmlToMap(xml);
            if (WXPayUtil.isSignatureValid(notifyMap,paternerKey)) {
                if (notifyMap.get("return_code").equals("SUCCESS")) {
                    if (notifyMap.get("result_code").equals("SUCCESS")) {
                        //TODO 业务代码,对数据增删改查等。。。
                    }
                }
            }else {
                // 签名错误,如果数据里没有sign字段,也认为是签名错误
            }
            //告诉微信服务器收到信息了,防止微信重复回调
            response.getWriter().write("");
            is.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

③,以下附上commonUtil部分代码

package com.jmdz.util;

import com.alibaba.fastjson.JSONObject;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.ConnectException;
import java.net.URL;
import java.util.Random;

/**
 * @description: 公共工具
 * @author: lvyq
 * @date: 2020/9/15 11:35
 * @version 1.0
 */
public class CommonUtils {

    /**
     * @description: 获取域名(协议+域名)
     * @author: lvyq
     * @date: 2020/9/15 11:36
     * @version 1.0
     */
    public String getDomainName(HttpServletRequest request){
       // return request.getServerName()+":"+request.getServerPort();
        return request.getScheme()+"://"+request.getServerName();
    }

    public String getIPAddress(HttpServletRequest request){
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        if (ip.indexOf(",") != -1) {
            String[] ips = ip.split(",");
            ip = ips[0].trim();
        }
        return ip;
    }


    public static String getRandomStringByLength(int length) {
        String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }


    public static String nputStream2String(InputStream in) {
        InputStreamReader reader = null;
        try {
            reader = new InputStreamReader(in, "UTF-8");
        } catch (UnsupportedEncodingException e1) {
            e1.printStackTrace();
        }
        BufferedReader br = new BufferedReader(reader);
        StringBuilder sb = new StringBuilder();
        String line = "";
        try {
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return sb.toString();
    }
}

③.HttpRequest

package com.jmdz.util;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.Map;

public class HttpRequest {

    /**
     * 向指定URL发送GET方法的请求
     *
     * @param url
     *            发送请求的URL
     * @param param
     *            请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
     * @return URL 所代表远程资源的响应结果
     */
    public static String sendGet(String url, String param) {
        String result = "";
        BufferedReader in = null;
        try {
            String urlNameString = url + "?" + param;
            System.out.println(urlNameString);
            URL realUrl = new URL(urlNameString);
            // 打开和URL之间的连接
            URLConnection connection = realUrl.openConnection();
            // 设置通用的请求属性
            connection.setRequestProperty("accept", "*/*");
            connection.setRequestProperty("connection", "Keep-Alive");
            connection.setRequestProperty("user-agent",
                    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            // 建立实际的连接
            connection.connect();
            // 获取所有响应头字段
            Map> map = connection.getHeaderFields();
            // 遍历所有的响应头字段
            for (String key : map.keySet()) {
                System.out.println(key + "--->" + map.get(key));
            }
            // 定义 BufferedReader输入流来读取URL的响应
            in = new BufferedReader(new InputStreamReader(
                    connection.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            System.out.println("发送GET请求出现异常!" + e);
            e.printStackTrace();
        }
        // 使用finally块来关闭输入流
        finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
        return result;
    }

    /**
     * 向指定 URL 发送POST方法的请求
     *
     * @param url
     *            发送请求的 URL
     * @param param
     *            请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
     * @return 所代表远程资源的响应结果
     */
    public static String sendPost(String url, String param) {
        PrintWriter out = null;
        BufferedReader in = null;
        String result = "";
        try {
            URL realUrl = new URL(url);
            // 打开和URL之间的连接
            URLConnection conn = realUrl.openConnection();
            // 设置通用的请求属性
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent",
                    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            // 发送POST请求必须设置如下两行
            conn.setDoOutput(true);
            conn.setDoInput(true);
            // 获取URLConnection对象对应的输出流
            out = new PrintWriter(conn.getOutputStream());
            // 发送请求参数
            out.print(param);
            // flush输出流的缓冲
            out.flush();
            // 定义BufferedReader输入流来读取URL的响应
            in = new BufferedReader(
                    new InputStreamReader(conn.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            System.out.println("发送 POST 请求出现异常!"+e);
            e.printStackTrace();
        }
        //使用finally块来关闭输出流、输入流
        finally{
            try{
                if(out!=null){
                    out.close();
                }
                if(in!=null){
                    in.close();
                }
            }
            catch(IOException ex){
                ex.printStackTrace();
            }
        }
        return result;
    }
}

④,WXPayUtil (这个官网提供的demo里面就有,直接拿来用就行。我就不贴代码了)

三,查看效果

微信支付-JSAPI:代码实现(支付异步回调中传中文参数解决)_第6张图片微信支付-JSAPI:代码实现(支付异步回调中传中文参数解决)_第7张图片微信支付-JSAPI:代码实现(支付异步回调中传中文参数解决)_第8张图片微信支付-JSAPI:代码实现(支付异步回调中传中文参数解决)_第9张图片

==============================分割线==============================

1.为了notify_url接口里面接受参数,我们的接口采用restful。伪造一个不带参数的url

2.回调地址含中文的话无法回调地址,故在统一下单时,对中文参数进行编码,接收时再解码即可

你可能感兴趣的:(微信开发-java,前端,java)