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

 

-----------------------------------------------------------------------------------------------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

 

 

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

 

时间: 2024-11-03 22:22:23

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

php支付宝手机网页支付类实例_php实例

本文实例讲述了php支付宝手机网页支付类.分享给大家供大家参考.具体分析如下: 此处注意: ① 该类是用在Yii框架里面的,没有去掉一些框架的东西. ② 本类不能不做任何修改而使用. 1. PHP代码部分如下:     复制代码 代码如下: <?php namespace weixin\components; use Yii; /**  * 支付宝手机网页支付  *  * @example  *     创建支付请求  *     $params = []; //支付宝文档中所需的全部参数  *

Ecshop 支付宝手机网页支付免费版

Ecshop 支付宝手机网页支付,针对ecshop wap手机版 <?php   /** * ECSHOP 支付宝手机网页插件 */   if (!defined('IN_ECS')) { die('Hacking attempt'); }   $payment_lang = ROOT_PATH . 'languages/' .$GLOBALS['_CFG']['lang']. '/payment/alipay_wap.php';   if (file_exists($payment_lang)

手机APP系统开发流程

手机APP系统开发流程,随着移动互联网的发展,越来越多人拥有手机,手机APP也成了风口,手机app软件开发已经成为时代的潮流,大部分创业者看准了商机找专业的服务平台服务商去开发一款属于自己的商业APP,来实现自己的创业梦.但是很多创业者都只是看到了成功者的案例,跟软件服务商介绍自己的需求的时候就说我想做一个像滴滴打车一样的APP. 在设计过程中的手机app应用程序,手机应用系统开发方面,很多创业者都不明白自己的一个需求.还有可能是不知道怎么写.因为他们大部分都是没有接触过这个行业的人,根据这种情

PHP 在ecshop上集成 手机网页支付

参考alipay网页支付接口的代码  其实原理跟ecshop上集成的alipay支付差不多  就是因为利用curl请求的时候相应时间过长 所以不能直接去先post数据再生成button   /** * 生成支付代码 * @param   array   $order      订单信息 * @param   array   $payment    支付方式信息 */ function get_code($order, $payment) { if (!defined('EC_CHARSET'))

手机网站的开发流程

目前手机上网的速度越来越快,手机的功能也越来越强大,手机网站开发也变得越来越繁荣,该如何做好手机网站的开发呢? A.首先我们要了解手机上网的特点 手机屏幕一般在240*320以上的称之为大屏幕手机.因为收的CPU频率低,不能像电脑一样快的浏览.做手机网站的时候像JS等要少用.手机上网速度慢,联通的3G网络还可以,但我都是喜欢支持中国移动我们国家自行研发的3G上网技术,浏览器众多,兼容性差.一不小心就显示不出来. 了解了手机上功能点,我们可以确定手机网站制作原则. 1.网站要简洁,功能能不要的就删

支付宝钱包支付密码怎么改?手机支付宝支付密码修改教程

第一步.如图所示在手机中点击"支付宝钱包"进入之后我们再找到"更多"然后我们点击打开; 第二步,之后再找到"更多"打开进入然后找到"账户安全"--"修改支付密码"之后再点击打开; 然后我们根据软件提示输入密码就可以了. 好了这里我们的支付密码就修改成功了,大家快去试一下吧.

微信公众平台开发(39)支付宝手机网站支付

微信公众平台开发 微信公众平台开发模式 企业微信公众平台 微信手机支付 支付宝 在线支付 手机支付 网页支付作者:方倍工作室 原文: http://www.cnblogs.com/txw1958/p/weixin39-wireless-alipay.html 支付宝无线产品 手机网站支付 产品简介 手机网站支付主要应用于手机.掌上电脑等无线设备的网页上,通过网页跳转或浏览器自带的支付宝快捷支付实现买家付款的功能,资金即时到账. 申请条件 1.您必须有已建设完成的无线网站(不包含淘宝网店.团购类网

微信jsapi网页支付成功显示body问题

问题描述 微信jsapi网页支付成功显示body问题 第一次做微信jsapi支付,生成预支付订单的时候需要把body进行urf-8转码,但是支付 成功之后页面显示的商品描述body确实转码过后的内容,哪位大神帮帮怎么解决 解决方案 抱歉没有回答你的问题,我在做裂变红包,希望能加群一起探讨 89890449

微信公开课(北京站)速记 微信、微信支付、O2O的定义与关联

本文为4月29日微信公开课(北京站)微信产品部演讲全文速记,讲述了微信官方对微信.微信支付.O2O的定义与关联等问题的看法与观点. 作者:微信产品部 刘涵涛 吴毅           去年夏天有一个全民打飞机的盛况,这实际上是微信的第一款社交类手游,它通过微信大平台的海量用户,一上线之后就有过亿的用户,甚至在淘宝上面都有代客打游戏的服务,通过这个游戏大家突然想到,微信以前是一个沟通工具,微信竟然也可以玩儿,甚至出现了这样一个段子,如果要自己的排行榜排在前面,最简单的方法是把玩这个游戏的好朋友全部