【微信支付】微信端的手机网页支付 开发流程

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: -----------------------------------------------------------------------------------------------1.微信 手机网页支付 流程图--------------------------------------...

 

-----------------------------------------------------------------------------------------------1.微信 手机网页支付 流程图------------------------------------------------------------------------------------------------------

 

------------------------------------------------------------------------------------------------2.前台页面--------------------------------------------------------------------------------------------------

根据上面的流程,下面一步一步的实现这个微信支付的逻辑。

前台页面   userPayView.jsp

<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
    
    <title>模拟支付页面--微信支付/支付宝支付</title>
    
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">    
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="This is my page">

  </head>
  
  <body>
    <input type="text" value="" name="openID"/> <!-- 微信    所需OpenID -->
    <input type="text" value="" name="orderID"/><!-- 微信 支付宝[out_trade_no] -->
    <input type="number" value="" name="money"/><!-- 微信[分为单位,不允许小数] 支付宝[total_amount 元为单位,精确到小数点后2位] 商品价格  -->
    
    <input type="text" value="" name="subject"/><!-- 支付宝 商品的标题/交易标题/订单标题/订单关键字等。 -->
    <input type="text" value="" name="product_code"/><!-- 支付宝 销售产品码,商家和支付宝签约的产品码。该产品请填写固定值:QUICK_WAP_WAY。 -->
     <input type="text" value="" name="body"/><!-- 支付宝  商品描述 -->
    
    <button class="payButton">微信支付</button>
    <button class="alipayButton">支付宝支付</button>
  </body>
  <script type="text/javascript" src="/resources/bootstrap-3.3.5-dist/js/jquery-1.10.2.min.js"></script>
  <script type="text/javascript" src="/wx/pay/pay.js"></script>
</html>
View Code

 

微信支付按钮的点击事件   pay.js

/**
     * 微信支付按钮的点击事件 
     */
    $(".payButton").click(function(){
        
        var openID = $("input[name='openID']").val();
        var orderID = $("inpuut[name='orderID']").val();
        var money = $("input[name='money']").val();
        /**
         * ①ajax  微信支付按钮点击事件
         */
        $.ajax({ 
            url: "/wx/PayOrder/unifiedOrder.jhtml", 
            type:"GET",
            data: {"openId":openID,"orderId":orderID,"money":money },
            traditional:true,
            success: function(response){
                if(response.length > 0){
                    /**
                     * ⑤获取到prepayId  继续ajax请求商户服务器
                     */
                    $.ajax({ 
                        url: "/wx/PayOrder/createPayConfig.jhtml", 
                        type:"GET",
                        data: {"prepayId":response },
                        traditional:true,
                        success: function(response){
                            var data = eval('(' + response + ')'); 
                            var obj = data.msg;
                            
                            /**
                             * ⑦ 根据支付配置,使用微信浏览器内置对象WeixinJSBridge 调用支付页面,完成支付操作,将支付结果返回给①中配置的回调函数
                             */
                            WeixinJSBridge.invoke('getBrandWCPayRequest',{  
                                "appId" : obj.appId,              //公众号名称,由商户传入  
                                "timeStamp":obj.timestamp,        //时间戳,自 1970 年以来的秒数  
                                "nonceStr" : obj.nonce,           //随机串  
                                "package" : obj.packageName,      //商品包信息 prepay_Id拼接值
                                "signType" : obj.signType,        //微信签名方式
                                "paySign" : obj.signature         //微信签名  
                                },function(res){  
                                    
                                    if(res.err_msg == "get_brand_wcpay_request:ok" ) {  
                                        alert('支付成功');  
                                    }else if(res.err_msg == "get_brand_wcpay_request:cancel"){
                                        alert("支付过程中用户取消");
                                    }else if(res.err_msg == "get_brand_wcpay_request:fail"){
                                        alert("支付失败");
                                    }
                            });  
                        }
                    });
                }
        }});
    });
View Code

 

----------------------------------------------------------------------------------------------3.获取prepay_id时的配置实体类 也就是统一下单实体类-------------------------------------------------------------------------------------------------

统一下单 入参  参考文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1

package net.shopxx.wx.pay;

/**
 * 微信支付  统一下单 接口提供必填参数的实体类
 * @author SXD
 *
 */
public class Unifiedorder {
    /**
     * 公众账号ID
     */
    private String appid;
    /**
     * 商户号
     */
    private String mch_id;
    /**
     * 随机字符串,长度要求在32位以内
     */
    private String nonce_str;
    /**
     * 签名    通过签名算法计算得出的签名值
     */
    private String sign;
    /**
     * 商品描述
     */
    private String body;
    /**
     * 商户系统内部订单号  要求32个字符内
     */
    private String out_trade_no;
    /**
     * 本次支付总金额  单位为分 不能带小数点
     */
    private Integer total_fee;
    /**
     * 用户IP
     */
    private String spbill_create_ip;
    /**
     * 通知地址
     * 异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数
     */
    private String notify_url;
    /**
     * 交易类型
     * 取值如下:JSAPI,NATIVE,APP等
     * JSAPI  公众号支付
     * NATIVE 扫描二维码支付
     * APP  app支付
     */
    private String trade_type; 
    /**
     * 用户标识  
     * trade_type=JSAPI时(即公众号支付),此参数必传,此参数为微信用户在商户对应appid下的唯一标识
     */
    private String openid;
    
    
    
    public Unifiedorder() {
        this.trade_type = "JSAPI";//公众号支付的方式
    }
    
    
    public String getAppid() {
        return appid;
    }
    public void setAppid(String appid) {
        this.appid = appid;
    }
    public String getMch_id() {
        return mch_id;
    }
    public void setMch_id(String mch_id) {
        this.mch_id = mch_id;
    }
    public String getNonce_str() {
        return nonce_str;
    }
    public void setNonce_str(String nonce_str) {
        this.nonce_str = nonce_str;
    }
    public String getSign() {
        return sign;
    }
    public void setSign(String sign) {
        this.sign = sign;
    }
    public String getBody() {
        return body;
    }
    public void setBody(String body) {
        this.body = body;
    }
    public String getOut_trade_no() {
        return out_trade_no;
    }
    public void setOut_trade_no(String out_trade_no) {
        this.out_trade_no = out_trade_no;
    }
    public Integer getTotal_fee() {
        return total_fee;
    }
    public void setTotal_fee(Integer total_fee) {
        this.total_fee = total_fee;
    }
    public String getSpbill_create_ip() {
        return spbill_create_ip;
    }
    public void setSpbill_create_ip(String spbill_create_ip) {
        this.spbill_create_ip = spbill_create_ip;
    }
    public String getNotify_url() {
        return notify_url;
    }
    public void setNotify_url(String notify_url) {
        this.notify_url = notify_url;
    }
    public String getTrade_type() {
        return trade_type;
    }
    public void setTrade_type(String trade_type) {
        this.trade_type = trade_type;
    }
    public String getOpenid() {
        return openid;
    }
    public void setOpenid(String openid) {
        this.openid = openid;
    }    
    
    
    
    
    
    
}
View Code

----------------------------------------------------------------------------------------------4.微信支付的实体类 也就是JS-SDK使用的配置信息实体类----------------------------------------------------------------------------------------------

微信内H5调起支付  配置信息 入参   参考文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6

package net.shopxx.wx.pay;

/**
 * 微信支付  
 * JS-SDK使用的配置信息 实体
 * @author SXD
 *
 */
public class JsAPIConfig {
    
    /**
     * 公众号id
     */
    private String appId;    
    /**
     * 时间戳
     */
    private String timeStamp;
    /**
     * 随机字符串
     */
    private String nonceStr;
    /**
     * 订单详情扩展字符串 prepay_id=***
     */
    private String packageStr;
    /**
     * 签名方式  暂支持MD5
     */
    private String signType;
    /**
     * 签名
     * 
     * 签名算法
     * 第一步,设所有发送或者接收到的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA。
        特别注意以下重要规则:
        ◆ 参数名ASCII码从小到大排序(字典序);
        ◆ 如果参数的值为空不参与签名;
        ◆ 参数名区分大小写;
        ◆ 验证调用返回或微信主动通知签名时,传送的sign参数不参与签名,将生成的签名与该sign值作校验。
        ◆ 微信接口可能增加字段,验证签名时必须支持增加的扩展字段
       第二步,在stringA最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。
     * 
     */
    private String paySign;

    public JsAPIConfig(){
        signType = "MD5";
    }

    public String getAppId() {
        return appId;
    }

    public void setAppId(String appId) {
        this.appId = appId;
    }

    public String getTimeStamp() {
        return timeStamp;
    }

    public void setTimeStamp(String timeStamp) {
        this.timeStamp = timeStamp;
    }

    public String getNonceStr() {
        return nonceStr;
    }

    public void setNonceStr(String nonceStr) {
        this.nonceStr = nonceStr;
    }

    public String getPackageStr() {
        return packageStr;
    }

    public void setPackageStr(String packageStr) {
        this.packageStr = packageStr;
    }

    public String getSignType() {
        return signType;
    }

    public void setSignType(String signType) {
        this.signType = signType;
    }

    public String getPaySign() {
        return paySign;
    }

    public void setPaySign(String paySign) {
        this.paySign = paySign;
    }
    
    
}
View Code

----------------------------------------------------------------------------------------------5.商户处理后同步返回给微信   订单成功的  实体类--------------------------------------------------------------------------------------------------------

返回信息实体    参考文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1    返回结果

package net.shopxx.wx.pay;

/**
 * 微信支付    商户处理后同步返回给微信 订单成功的  实体
 * @author SXD
 *
 */
public class PayCallback {
    
        private String return_code;
        private String return_msg;

        public PayCallback() {
            this.return_code = "SUCCESS";
            this.return_msg = "OK";
        }

        public String getReturn_code() {
            return return_code;
        }

        public void setReturn_code(String return_code) {
            this.return_code = return_code;
        }

        public String getReturn_msg() {
            return return_msg;
        }

        public void setReturn_msg(String return_msg) {
            this.return_msg = return_msg;
        }
}
View Code

----------------------------------------------------------------------------------------------6.HTTP通信类  在后台访问微信服务器的  通信工具类----------------------------------------------------------------------------------------------------

package net.shopxx.wx.pay;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.Iterator;
import java.util.Map;

import javax.activation.MimetypesFileTypeMap;

import org.springframework.stereotype.Component;


/**
 * 微信支付 
 * HTTP通信类
 *
 */
@Component
public class HttpConnection {
    /** 
     * 向指定URL发送GET方法的请求 
     * @param url 发送请求的URL 
     * @param param 请求参数,请求参数应该是name1=value1&name2=value2的形式。 
     * @return URL所代表远程资源的响应 
     */ 
    public  String get(String url, String param) throws Exception {  
        String urlName = url + "?" + param;  
        return get(urlName);
    }  
    
    /**
     * 向指定URL发送GET方法的请求
     * 
     * @param url
     *            发送请求的URL
     * @return URL所代表远程资源的响应
     */
    public  String get(String url) throws Exception {
        String result = "";
        BufferedReader in = null;
        URL realUrl = new URL(url);
        URLConnection conn = realUrl.openConnection();
        conn.setRequestProperty("accept", "*/*");
        conn.setRequestProperty("Accept-Language", "zh-CN,zh;q=0.8");
        conn.setRequestProperty("Content-Type", "text/xml;charset=utf-8");
        conn.setRequestProperty("connection", "keep-alive");
        conn.setRequestProperty("user-agent",
                "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");
        // 建立实际的连接
        conn.connect();

        // 定义BufferedReader输入流来读取URL的响应
        in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
        String line;
        while ((line = in.readLine()) != null) {
            result += line;
        }
        
        in.close();
        return result;
    }
    
    /**  
     * 向指定URL发送POST方法的请求  
     * @param url 发送请求的URL  
     * @param content 内容
     * @return URL所代表远程资源的响应  
     * @throws Exception 
     */  
    public String post(String url,String content) throws Exception{
        String result = "";
        URL postUrl = new URL(url); 
        HttpURLConnection connection = (HttpURLConnection) postUrl 
                .openConnection(); 
        connection.setDoOutput(true); 
        connection.setDoInput(true); 
        connection.setRequestMethod("POST"); 
        connection.setUseCaches(false); 
        connection.setInstanceFollowRedirects(true); 
        connection.setRequestProperty("Content-Type", 
                "application/x-www-form-urlencoded"); 
        connection.connect(); 
        DataOutputStream out = new DataOutputStream(connection 
                .getOutputStream()); 
//        out.writeBytes(content); 
        out.write(content.getBytes("UTF-8"));
        out.flush(); 
        out.close(); // flush and close 
        BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(),"utf-8"));//设置编码,否则中文乱码 
        String line=""; 
        while ((line = reader.readLine()) != null){ 
            result += line;
        } 
        reader.close(); 
        connection.disconnect();
        return result; 
    }
    /**
     * 向指定URL发送POST方法的请求
     * @Title: post
     * @Description: TODO
     * @param @param url
     * @param @param textMap
     * @param @return    
     * @return String    
     * @throws
     */
    public String post(String url, Map<String, String> textMap){
        String res = "";
        HttpURLConnection conn = null;
        String BOUNDARY = "---------------------------123821742118716"; //boundary就是request头和上传文件内容的分隔符
        try {
            URL postUrl = new URL(url);
            conn = (HttpURLConnection) postUrl.openConnection();
            conn.setConnectTimeout(5000);
            conn.setReadTimeout(30000);
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Connection", "Keep-Alive");
            conn.setRequestProperty("User-Agent",
                            "Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.6)");
            conn.setRequestProperty("Content-Type",
                    "multipart/form-data; boundary=" + BOUNDARY);

            OutputStream out = new DataOutputStream(conn.getOutputStream());
            // text
            if (textMap != null) {
                StringBuffer strBuf = new StringBuffer();
                Iterator iter = textMap.entrySet().iterator();
                while (iter.hasNext()) {
                    Map.Entry entry = (Map.Entry) iter.next();
                    String inputName = (String) entry.getKey();
                    String inputValue = (String) entry.getValue();
                    if (inputValue == null) {
                        continue;
                    }
                    strBuf.append("\r\n").append("--").append(BOUNDARY).append(
                            "\r\n");
                    strBuf.append("Content-Disposition: form-data; name=\""
                            + inputName + "\"\r\n\r\n");
                    strBuf.append(inputValue);
                }
                out.write(strBuf.toString().getBytes());
            }

            byte[] endData = ("\r\n--" + BOUNDARY + "--\r\n").getBytes();
            out.write(endData);
            out.flush();
            out.close();

            // 读取返回数据
            StringBuffer strBuf = new StringBuffer();
            BufferedReader reader = new BufferedReader(new InputStreamReader(
                    conn.getInputStream()));
            String line = null;
            while ((line = reader.readLine()) != null) {
                strBuf.append(line).append("\n");
            }
            res = strBuf.toString();
            reader.close();
            reader = null;
        } catch (Exception e) {
            System.out.println("发送POST请求出错。" + url);
            e.printStackTrace();
        } finally {
            if (conn != null) {
                conn.disconnect();
                conn = null;
            }
        }
        return res;
    }
    
    /**  
     * 向指定URL发送POST方法的请求 (带文件) 
     * @param url 发送请求的URL  
     * @param textMap 文本参数键值
     * @param fileMap 文件键值
     * @return URL所代表远程资源的响应  
     * @throws Exception 
     */  
    public String filePost(String url, Map<String, String> textMap,
            Map<String, String> fileMap) {
        String res = "";
        HttpURLConnection conn = null;
        String BOUNDARY = "---------------------------123821742118716"; //boundary就是request头和上传文件内容的分隔符
        try {
            URL postUrl = new URL(url);
            conn = (HttpURLConnection) postUrl.openConnection();
            conn.setConnectTimeout(5000);
            conn.setReadTimeout(30000);
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Connection", "Keep-Alive");
            conn.setRequestProperty("User-Agent",
                            "Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.6)");
            conn.setRequestProperty("Content-Type",
                    "multipart/form-data; boundary=" + BOUNDARY);

            OutputStream out = new DataOutputStream(conn.getOutputStream());
            // text
            if (textMap != null) {
                StringBuffer strBuf = new StringBuffer();
                Iterator iter = textMap.entrySet().iterator();
                while (iter.hasNext()) {
                    Map.Entry entry = (Map.Entry) iter.next();
                    String inputName = (String) entry.getKey();
                    String inputValue = (String) entry.getValue();
                    if (inputValue == null) {
                        continue;
                    }
                    strBuf.append("\r\n").append("--").append(BOUNDARY).append(
                            "\r\n");
                    strBuf.append("Content-Disposition: form-data; name=\""
                            + inputName + "\"\r\n\r\n");
                    strBuf.append(inputValue);
                }
                out.write(strBuf.toString().getBytes());
            }

            // file
            if (fileMap != null) {
                Iterator iter = fileMap.entrySet().iterator();
                while (iter.hasNext()) {
                    Map.Entry entry = (Map.Entry) iter.next();
                    String inputName = (String) entry.getKey();
                    String inputValue = (String) entry.getValue();
                    if (inputValue == null) {
                        continue;
                    }
                    File file = new File(inputValue);
                    String filename = file.getName();
                    String contentType = new MimetypesFileTypeMap()
                            .getContentType(file);
                    if (filename.endsWith(".png")) {
                        contentType = "image/png";
                    }
                    if (contentType == null || contentType.equals("")) {
                        contentType = "application/octet-stream";
                    }

                    StringBuffer strBuf = new StringBuffer();
                    strBuf.append("\r\n").append("--").append(BOUNDARY).append(
                            "\r\n");
                    strBuf.append("Content-Disposition: form-data; name=\""
                            + inputName + "\"; filename=\"" + filename
                            + "\"\r\n");
                    strBuf.append("Content-Type:" + contentType + "\r\n\r\n");

                    out.write(strBuf.toString().getBytes());

                    DataInputStream in = new DataInputStream(
                            new FileInputStream(file));
                    int bytes = 0;
                    byte[] bufferOut = new byte[1024];
                    while ((bytes = in.read(bufferOut)) != -1) {
                        out.write(bufferOut, 0, bytes);
                    }
                    in.close();
                }
            }

            byte[] endData = ("\r\n--" + BOUNDARY + "--\r\n").getBytes();
            out.write(endData);
            out.flush();
            out.close();

            // 读取返回数据
            StringBuffer strBuf = new StringBuffer();
            BufferedReader reader = new BufferedReader(new InputStreamReader(
                    conn.getInputStream()));
            String line = null;
            while ((line = reader.readLine()) != null) {
                strBuf.append(line).append("\n");
            }
            res = strBuf.toString();
            reader.close();
            reader = null;
        } catch (Exception e) {
            System.out.println("发送POST请求出错。" + url);
            e.printStackTrace();
        } finally {
            if (conn != null) {
                conn.disconnect();
                conn = null;
            }
        }
        return res;
    }
}
View Code

----------------------------------------------------------------------------------------------7.微信 签名 规则 工具类-------------------------------------------------------------------------------------------------------------------------------------------

签名算法 规则:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3

package net.shopxx.wx.pay;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

import javax.net.ssl.SSLContext;
import javax.security.cert.CertificateException;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.http.ssl.SSLContexts;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;


/**
 * 微信支付  微信公众号发红包 
 * 工具类
 * @author SXD
 *
 */
public class WeXinUtil {
    
    
    /**
     * 获取用户IP
     * @param request
     * @return
     */
    public static String getIp(HttpServletRequest request){
        String ipAddress = null;
         if (request.getHeader("x-forwarded-for") == null) {  
             ipAddress = request.getRemoteAddr();
         }else{
            if(request.getHeader("x-forwarded-for").length()  > 15){
                String [] aStr = request.getHeader("x-forwarded-for").split(",");
                ipAddress = aStr[0];
            } else{
                ipAddress = request.getHeader("x-forwarded-for");
            }
         } 
         return ipAddress;
    }
    
    /**
     * 签名算法,生成统一下单中 必填项签名
     * @param unifiedOrder  1.将统一下单实体中各个字段拼接  2.MD5加密  3.全部转化为大写
     * @return    返回经过签名算法生成的签名 sign
     * 第一步的规则
     *     ◆ 参数名ASCII码从小到大排序(字典序);
     *    ◆ 如果参数的值为空不参与签名;
     *    ◆ 参数名区分大小写;
     *    ◆ 验证调用返回或微信主动通知签名时,传送的sign参数不参与签名,将生成的签名与该sign值作校验。
     *    ◆ 微信接口可能增加字段,验证签名时必须支持增加的扩展字段
     */
    
    /* 手动拼接方式
    public String createUnifiedOrderSign(Unifiedorder unifiedOrder){
        StringBuffer sign = new StringBuffer();
        sign.append("appid=").append(unifiedOrder.getAppid());
        sign.append("&body=").append(unifiedOrder.getBody());
        sign.append("&mch_id=").append(unifiedOrder.getMch_id());
        sign.append("&nonce_str=").append(unifiedOrder.getNonce_str());
        sign.append("&notify_url=").append(unifiedOrder.getNotify_url());
        sign.append("&openid=").append(unifiedOrder.getOpenid());
        sign.append("&out_trade_no=").append(unifiedOrder.getOut_trade_no());
        sign.append("&spbill_create_ip=").append(unifiedOrder.getSpbill_create_ip());
        sign.append("&total_fee=").append(unifiedOrder.getTotal_fee());
        sign.append("&trade_type=").append(unifiedOrder.getTrade_type());
        sign.append("&key=").append(KEY);

        return DigestUtils.md5Hex(sign.toString()).toUpperCase();
    }
    */
    
    /**
     * 拼接生成sign 签名
     * @param unifiedOrder
     * @param KEY
     * @return
     * @throws Exception
     */
     public static String createUnifiedOrderSign(Object object,String KEY) throws Exception{
            StringBuffer sign = new StringBuffer();
            Map<String, String> map = getSortMap(object);

            boolean isNotFirst = false;

            for (Map.Entry<String, String> entry : map.entrySet()) {
                if(isNotFirst == true){
                    sign.append("&");
                }else{
                    isNotFirst = true;
                }

                sign.append(entry.getKey()).append("=").append(entry.getValue());
            }
            sign.append("&key=").append(KEY);

            return DigestUtils.md5Hex(sign.toString()).toUpperCase();

        }
     
     /**
      * 使用java反射机制,动态获取对象的属性和参数值,排除值为null的情况,并按字典序排序
      * @param object
      * @return
      * @throws Exception
      */
     private static Map<String, String> getSortMap(Object object) throws Exception{
            Field[] fields = object.getClass().getDeclaredFields();
            Map<String, String> map = new HashMap<String, String>();

            for(Field field : fields){
                 String name = field.getName();
                 String methodName = "get" + name.replaceFirst(name.substring(0, 1), name.substring(0, 1)
                         .toUpperCase());
                 // 调用getter方法获取属性值
//                 Method getter = object.getClass().getMethod(methodName);
//                 String value =  getter.invoke(object)+"";
                 field.setAccessible(true);
                 Object value = field.get(object);
                 if (value != null){
                     map.put(name, value.toString());
                 }
            }

            Map<String, String> sortMap = new TreeMap<String, String>(
                    new Comparator<String>() {
                        @Override
                        public int compare(String arg0, String arg1) {
                           
                            return arg0.compareTo(arg1);
                        }
                    });
            sortMap.putAll(map);
            return sortMap;
        }
     
     
     
     public static SSLContext getSSL() throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyManagementException, java.security.cert.CertificateException {
            KeyStore keyStore = KeyStore.getInstance("PKCS12");
            //证书位置  放在自己的项目下面
            Resource resource = new ClassPathResource("apiclient_cert.p12");
            InputStream instream = resource.getInputStream();
            try {
                keyStore.load(instream, "填写证书密码,默认为商户号".toCharArray());
            } finally {
                instream.close();
            }
            SSLContext sslcontext = SSLContexts.custom()
                    .loadKeyMaterial(keyStore, "填写证书密码,默认为商户号".toCharArray())
                    .build();
            return sslcontext;
        }
     
}
View Code

----------------------------------------------------------------------------------------------8.封装/解析xml消息的工具类-------------------------------------------------------------------------------------------------------------------------------------

package net.shopxx.wx.pay;

import java.io.Writer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.naming.NoNameCoder;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XppDriver;


/**
 * 微信支付   微信公众号发红包
 * 封装/解析xml消息的工具类
 * @author SXD
 *
 */
public class XmlUtil {

    
    public XStream getXstreamInclueUnderline(){
         XStream stream = new XStream(new XppDriver(new NoNameCoder()) {

             @Override
             public PrettyPrintWriter createWriter(Writer out) {
                 return new PrettyPrintWriter(out) {
                     // 对所有xml节点的转换都增加CDATA标记
                     boolean cdata = true;

                     @Override
                     @SuppressWarnings("rawtypes")
                     public void startNode(String name, Class clazz) {
                         super.startNode(name, clazz);
                     }

                     @Override
                     public String encodeNode(String name) {
                         return name;
                     }


                     @Override
                     protected void writeText(QuickWriter writer, String text) {
                         if (cdata) {
                             writer.write("<![CDATA[");
                             writer.write(text);
                             writer.write("]]>");
                         } else {
                             writer.write(text);
                         }
                     }
                 };
             }
         });
         
         return stream;
    }
    
    /**
     * 根据字符串 解析XML map集合
     * @param xml
     * @return
     * @throws DocumentException
     */
    public Map<String, String> parseXML(String xml) throws DocumentException{
        Document document = DocumentHelper.parseText(xml);
        Element element =document.getRootElement();
        List<Element> childElements = element.elements();
        Map<String,String> map = new HashMap<String, String>();
        
        map = getAllElements(childElements,map);
        
        map.forEach((k,v)->{
            System.out.println(k+">>>>"+v);
        });
        
        return map;
    }
    /**
     * 获取 子节点的被迭代方法
     * @param childElements
     * @param mapEle
     * @return
     */
    private Map<String, String> getAllElements(List<Element> childElements,Map<String,String> mapEle) {
        for (Element ele : childElements) {
            if(ele.elements().size()>0){
                mapEle = getAllElements(ele.elements(), mapEle);
            }else{
                mapEle.put(ele.getName(), ele.getText());
            }
        }
        return mapEle;
    }

    
}
View Code

----------------------------------------------------------------------------------------------9.微信支付  商户服务器  逻辑处理中心------------------------------------------------------------------------------------------------------------------------

package net.shopxx.wx.pay;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.Map;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.thoughtworks.xstream.XStream;


/**
 * 微信支付    逻辑处理
 * @author SXD
 *
 */
@Controller
@RequestMapping("/wx/PayOrder")
public class PayOrderController {
    
        /**
         * 公众账号ID
         */
        @Value("${member.appid}")
        private String APPID;
        /**
         * 商户号  暂未配置
         */
        private String MCHID;
        /**
         * key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
         * 暂未配置
         */
        private String KEY;
        /**
         * 统一下单 URL
         */
        @Value("${unifiedOrderUrl}")
        private String unifiedOrderUrl;
        private XmlUtil xmlUtil = new XmlUtil();
        private HttpConnection httpConnection  = new HttpConnection();
    
        
        
        /**
         * ② 用户支付按钮后,先进入统一下单这个方法获取prepay_id
         *进行统一下单 ,获取预支付订单的prepay_id
         * @param request
         * 
         * @param openId
         * @param orderId
         * @param money
         * @return
         * @throws Exception
         */
        @ResponseBody
        @RequestMapping("/unifiedOrder")
        public String unifiedOrder(HttpServletRequest request,String openId,String orderId,int money) throws Exception{
            
             Unifiedorder unifiedOrder = new Unifiedorder();
             unifiedOrder.setAppid(APPID);
            unifiedOrder.setMch_id(MCHID);
            String nonce = UUID.randomUUID().toString().replaceAll("-", "");
            unifiedOrder.setNonce_str(nonce);
            unifiedOrder.setBody("商品的描述");
            unifiedOrder.setOut_trade_no(orderId);
            unifiedOrder.setTotal_fee(money);
            unifiedOrder.setSpbill_create_ip(WeXinUtil.getIp(request));
            unifiedOrder.setNotify_url("http://weixin.myagen.com.cn/wx/PayOrder/wechatPayNotify");
            unifiedOrder.setOpenid(openId);
            String sign = WeXinUtil.createUnifiedOrderSign(unifiedOrder,KEY);
            unifiedOrder.setSign(sign);
            

            /**
             * 转成XML格式 微信可接受的格式
             */
            xmlUtil.getXstreamInclueUnderline().alias("xml", unifiedOrder.getClass());
            String xml = xmlUtil.getXstreamInclueUnderline().toXML(unifiedOrder);

            /**
             * ③请求微信服务器 返回结果 获取prepay_id
             */
            String response = httpConnection.post(unifiedOrderUrl, xml);
            System.out.println(response);
            Map<String, String> responseMap = xmlUtil.parseXML(response);
            /**
             * ④商户服务器将prepay_id返回给前台ajax
             */
            return responseMap.get("prepay_id");
        }
        
        
        /**
         * ⑥根据获取到的prepay_Id ,组装支付需要的相关配置参数,返回给前台网页
         * 获取支付  配置
         * @param prepayId
         * @return
         * @throws Exception
         */
        @ResponseBody
        @RequestMapping("/createPayConfig")
         public JsAPIConfig createPayConfig(String prepayId) throws Exception {
                JsAPIConfig config = new JsAPIConfig();

                String nonce = UUID.randomUUID().toString().replaceAll("-", "");
                String timestamp = Long.toString(System.currentTimeMillis() / 1000);
                String packageName = "prepay_id="+prepayId;
                /**
                 * 生成签名
                 */
                StringBuffer sign = new StringBuffer();
                sign.append("appId=").append(APPID);
                sign.append("&nonceStr=").append(nonce);
                sign.append("&package=").append(packageName);
                sign.append("&signType=").append(config.getSignType());
                sign.append("&timeStamp=").append(timestamp);
                sign.append("&key=").append(KEY);
                String signature = DigestUtils.md5Hex(sign.toString()).toUpperCase();

                config.setAppId(APPID);
                config.setNonceStr(nonce);
                config.setPackageStr(packageName);
                config.setTimeStamp(timestamp);
                config.setPaySign(signature);
                return config;
            }
        
        
        
        
        /**
         * ⑧ ⑨回调方法 接收到支付完成后的相关信息,根据是否支付成功 进行商户服务器端的业务逻辑操作,并在最后返回给微信服务器 已经确认成功的信息
         * 回调方法 
         * 异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数
         * @param request
         * @return
         */
        @ResponseBody
        @RequestMapping("/wechatPayNotify")
        public String wechatPayNotify (HttpServletRequest request){
            try {
                 Map<String, String> map = getCallbackParams(request);
                 if (map.get("result_code").toString().equalsIgnoreCase("SUCCESS")) {
                     //这里写成功后的业务逻辑
//                     String orderId = map.get("out_trade_no");
//                     orderService.updateConfirm(orderId);
                 }
            }catch(Exception e){
                System.out.println(e);
            }
            /**
             * ⑩返回给微信服务器 确认交易完成 
             */
            return getPayCallback(); 
        }
        
        
        
        /**
         * 接收支付完成后 微信服务器返回的request,解析返回字符串为键值对
         * @param request
         * @return
         * @throws Exception
         */
         public Map<String, String> getCallbackParams(HttpServletRequest request)
                    throws Exception {
                InputStream inStream = request.getInputStream();
                ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
                byte[] buffer = new byte[1024];
                int len = 0;
                while ((len = inStream.read(buffer)) != -1) {
                    outSteam.write(buffer, 0, len);
                }
                System.out.println("~~~~~~~付款成功~~~~~~~~~");
                outSteam.close();
                inStream.close();
                String result = new String(outSteam.toByteArray(), "utf-8");
                return xmlUtil.parseXML(result);
         }
        
        
         
        
        /**
         * https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7
         * 商户处理后同步返回给微信参数
         * @return
         */
          public String getPayCallback(){
                PayCallback callback = new PayCallback();
                XStream stream = new XStream();
                stream.alias("xml",callback.getClass() );
                String xml = stream.toXML(callback);
                return xml;
          }
        
        
}
View Code

 

 

--------------------------------------------------------------------------------------------------------------如上,整个的微信 手机网页内支付-------------------------------------------------------------------------------------------------

 

相关文章
|
1月前
|
移动开发 安全 API
微信H5支付--微信JS-SDK支付--点金计划
本文详细介绍了微信H5支付和JS-SDK支付的原理、配置和开发流程,涵盖了H5支付在移动端浏览器外唤起微信支付的细节,以及JS-SDK支付在微信内置浏览器中完成支付的相关注意事项。文章还针对微信支付常见问题,提供了解决方案和代码示例。最后,文章深入解析了微信支付点金计划,包括商家小票的自定义开发、API接口以及支付成功后的页面展示逻辑,为开发者提供了完整的开发参考。
40 0
微信H5支付--微信JS-SDK支付--点金计划
|
1月前
|
小程序 JavaScript API
微信小程序开发之:保存图片到手机,使用uni-app 开发小程序;还有微信原生保存图片到手机
这篇文章介绍了如何在uni-app和微信小程序中实现将图片保存到用户手机相册的功能。
568 0
微信小程序开发之:保存图片到手机,使用uni-app 开发小程序;还有微信原生保存图片到手机
|
1月前
|
Web App开发 移动开发 前端开发
H5微信外支付(移动端浏览器)
H5微信外支付(移动端浏览器)
36 0
 H5微信外支付(移动端浏览器)
|
3月前
|
小程序 安全 Java
|
3月前
|
小程序 前端开发 JavaScript
微信小程序实现微信支付(代码和注释很详细)
微信小程序实现微信支付(代码和注释很详细)
|
4月前
|
开发框架 移动开发 JSON
利用微信公众号实现商品的展示和支付(1)
利用微信公众号实现商品的展示和支付(1)
|
3月前
|
移动开发 前端开发 JavaScript
|
3月前
|
存储 前端开发 算法
|
3月前
|
存储 小程序 JavaScript
|
3月前
|
存储 小程序 JavaScript