博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
微信公众号H5支付(SSM框架)
阅读量:4992 次
发布时间:2019-06-12

本文共 43463 字,大约阅读时间需要 144 分钟。

 首先感谢一下【架构之路】盆友!我就是在他的基础之上改的。。再三感谢!

在最下,我会贴上所有的代码。我代码log日志打的非常的多,可能会显得很罗嗦。见谅。

前面的一些纯文字介绍看的会很累。但是建议好好看一下。尤其是我贴上的微信的官方文档。里面其实已经写的非常清楚了。

前言

  • 微信公众号内支付在页面端较为简单。只需要调用微信内置的js方法,将微信需要的参数传递过去。
  • 与其他支付方式不同的是,公众号支付需要的参数有一项名为open_id。这个参数需要客户端请求固定连接、用户手动同意后才可以获取到。

开发

  开发前的准备

    • 申请微信商户

        略

    • 微信相关设置

        IP白名单:首页->基本配置->IP白名单。写入服务器的地址。

        进入微信商户中心,下载证书,安装控件、证书,设置API密钥(KEY)。

        设置支付授权目录。http://www.*.com/cms/wepay/。

    • 获取微信商户信息

        MCH_ID:首页->微信支付。可以查看到微信支付的商户号。

        APPID、APP_SECRET:首页->基本配置;可以找到开发者ID。同时重置APP_SECRET。

    • 注意1:我们已获得了4个微信支付的常量。包括APPID、APP_SECRET、MCH_ID、KEY;我们再加一个支付类型的常量:TRADE_TYPE=JSAPI。
    • 注意2:授权目录要求:1必须为通过ICP备案的地址。2访问项目端口必须为80或334。像我的项目最早部署在8080端口,就一直在报错。其中cms为项目名;wepay为controller的mapping值。下面是微信的要求。

        所有使用公众号支付方式发起支付请求的链接地址,都必须在支付授权目录之下;

        最多设置5个支付授权目录,且域名必须通过ICP备案;

        头部要包含http或https,须细化到二级或三级目录,以左斜杠“/”结尾。

  正式开发

    步骤1:【微信网页授权】获取用户open_id 。

         (在线调试工具)

     简单说。设置好授权方式,来获取code。由code获取accessToken,由accessToken再获取用户的open_id和其他信息。

      

      

      。

      

      

    由微信的tab直接请求toPay方法。这个过程,首先是发起授权请求。用户允许后,开始获取openid。之后,再跳转至购买页面。

首先强调第一点:token是有区别的。通过code换取的access_token(第二步)和之后拉取用户时的access_token(第四步)不一样。这点尤其需要注意。第二步的token是oauth2加密的token,而第四步的token只是普通的token。具体请看官方文档。其次:token的使用。下面代码中的使用方式为:每次使用时,都去微信api申请一个token。但是微信api每日调用token接口的次数是有限的(一天1万次)。所以需要更精确的对其进行控制。可以将token存入数据库。使用定时任务刷新token。我采用了比较简单的方式,将信息存到了session里面。取出时,判断下时间即可。

    步骤2 :【统一下单】准备工作完成。开始正式支付

     、

    本项目操作过程为:用户手动点击支付按钮->获取支付需要的参数->将预付款订单参数传递给微信。

1 function onBridgeReady(){ 2         WeixinJSBridge.invoke( 3             'getBrandWCPayRequest', { 4                  "appId":appId,     //公众号名称,由商户传入 5                  "paySign":sign,         //微信签名 6                  "timeStamp":timeStamp, //时间戳,自1970年以来的秒数 7                  "nonceStr":nonceStr , //随机串 8                  "package":packageStr,  //预支付交易会话标识 9                  "signType":signType     //微信签名方式10              },11              function(res){12           alert(JSON.stringify(res));//出错可以在这里看看.....13                  if(res.err_msg == "get_brand_wcpay_request:ok" ) {14                 //window.location.replace("index.html");15                   alert('支付成功');16               }else if(res.err_msg == "get_brand_wcpay_request:cancel"){17                alert('支付取消');18                }else if(res.err_msg == "get_brand_wcpay_request:fail" ){19                 alert('支付失败');20               }21                  //使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回    ok,但并不保证它绝对可靠。22              }23         );24     }

    

    步骤3:【异步结果通知】微信支付异步回调通知

    

    • 该链接是通过【统一下单API】中提交的参数notify_url设置,如果链接无法访问,商户将无法接收到微信通知。
    • 通知url必须为直接可访问的url,不能携带参数。示例:notify_url:“https://pay.weixin.qq.com/wxpay/pay.action”

    如果出现签名错误相关错误,我是用的此页面的校验工具进行测试的。。

dto实体类。

用户信息类。如果只做支付不保存用户信息,此类可无。

public class UserInfo {        private String openid;    private String unionid;    private String headimgurl;    private String nickname;    private String refreshtoken;    private int siteid;}

微信常量信息类。

public class WeChatConst {    // 公众号支付APPID    public static final String APPID = WePropertieUtil.getValue("APPID");        // 公众号支付AppSecret    public static final String APP_SECRET = WePropertieUtil.getValue("APP_SECRET");        // 公众号支付商户号    public static final String MCH_ID = WePropertieUtil.getValue("MCH_ID");        // 商户后台配置的一个32位的key,位置:微信商户平台-账户中心-API安全    public static final String KEY = WePropertieUtil.getValue("KEY");        // 交易类型    public static final String TRADE_TYPE = WePropertieUtil.getValue("TRADE_TYPE");        // 根路径    public static final String BASE_URL = WePropertieUtil.getValue("BASE_URL");}

用户信息类。如果只做支付不保存用户信息,此类可无。

public class WeixinLoginUser implements Serializable {    private static final long serialVersionUID = -8449856597137213512L;    private String openID;    private String unionID;    private String headImageUrl;    private String nickName;    private String refreshToken;    private int siteID;}

微信支付参数。事实上我只用了里面极少一部分。

public class WxPaySendData {    /**     *  公众账号ID     */    private String appid;        /**     *  附加数据     */    private String attach;        /**     *  商品描述     */    private String body;    /**     *  商户号     */    private String mch_id;        /**     * 随机字符串     */    private String nonce_str;        /**     * 通知地址     */    private String notiry_url;        /**     * 商户订单号     */    private String out_trade_no;        /**     * 标价金额     */    private String total_fee;        /**     * 交易类型     */    private String trade_type;        /**     * 终端IP     */    private String spbill_create_ip;        /**     * 用户标识     */    private String openid;    /**     *  签名     */    private String sign;    /**     *  预支付id     */    private String prepay_id;        /**     * 签名类型:MD5     */    private String signType;        /**     * 时间戳     */    private String timeStamp;        /**     * 微信支付时用到的prepay_id     */    private String packageStr;    private String return_code;    private String return_msg;    private String result_code;    private String bank_type;    private Integer cash_fee;    private String fee_type;    private String is_subscribe;    private String time_end;    /**     *  微信支付订单号     */    private String transaction_id;}

所用的工具类

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.Iterator;import java.util.Map;import java.util.Map.Entry;import javax.servlet.http.HttpServletRequest;/** * @Description http请求工具类 */public class HttpUtil {    /**     * 向指定URL发送GET方法的请求     *      * @param url     *            发送请求的URL     * @param param     *            请求Map参数,请求参数应该是 {"name1":"value1","name2":"value2"}的形式。     * @param charset     *            发送和接收的格式     * @return URL 所代表远程资源的响应结果     */    public static String sendGet(String url, Map
map) { StringBuffer sb = new StringBuffer(); // 构建请求参数 if (map != null && map.size() > 0) { Iterator
it = map.entrySet().iterator(); // 定义迭代器 while (it.hasNext()) { Map.Entry
er = (Entry
) it.next(); sb.append(er.getKey()); sb.append("="); sb.append(er.getValue()); sb.append("&"); } } return sendGet(url, sb.toString()); } /** * 向指定URL发送POST方法的请求 * * @param url * 发送请求的URL * @param param * 请求Map参数,请求参数应该是 {"name1":"value1","name2":"value2"}的形式。 * @param charset * 发送和接收的格式 * @return URL 所代表远程资源的响应结果 */ public static String sendPost(String url, Map
map) { StringBuffer sb = new StringBuffer(); // 构建请求参数 if (map != null && map.size() > 0) { for (Entry
e : map.entrySet()) { sb.append(e.getKey()); sb.append("="); sb.append(e.getValue()); sb.append("&"); } } return sendPost(url, sb.toString()); } /** * 向指定URL发送GET方法的请求 * * @param url * 发送请求的URL * @param param * 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 * @param charset * 发送和接收的格式 * @return URL 所代表远程资源的响应结果 */ public static String sendGet(String url, String param) { String result = ""; String line; StringBuffer sb = new StringBuffer(); BufferedReader in = null; try { String urlNameString = url + "?" + param; URL realUrl = new URL(urlNameString); // 打开和URL之间的连接 URLConnection conn = realUrl.openConnection(); // 设置通用的请求属性 设置请求格式 conn.setRequestProperty("contentType", "utf-8"); conn.setRequestProperty("content-type", "application/x-www-form-urlencoded"); // 设置超时时间 conn.setConnectTimeout(600); conn.setReadTimeout(600); // 建立实际的连接 conn.connect(); // 定义 BufferedReader输入流来读取URL的响应,设置接收格式 in = new BufferedReader(new InputStreamReader( conn.getInputStream(), "utf-8")); while ((line = in.readLine()) != null) { sb.append(line); } result = sb.toString(); } 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 的形式。 * @param charset * 发送和接收的格式 * @return 所代表远程资源的响应结果 */ public static String sendPost(String url, String param) { PrintWriter out = null; BufferedReader in = null; String result = ""; String line; StringBuffer sb = new StringBuffer(); try { URL realUrl = new URL(url); // 打开和URL之间的连接 URLConnection conn = realUrl.openConnection(); // 设置通用的请求属性 设置请求格式 conn.setRequestProperty("contentType", "utf-8"); conn.setRequestProperty("content-type", "application/x-www-form-urlencoded"); // 设置超时时间 conn.setConnectTimeout(600); conn.setReadTimeout(600); // 发送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(), "utf-8")); while ((line = in.readLine()) != null) { sb.append(line); } result = sb.toString(); } 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; } public static String getRemoteIpAddr(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(); }     // 如果手机是双卡双待,那么会获取到两个IP。我们仅需要有一个。     return ip.split(",")[0];   } }
import java.security.MessageDigest; public class MD5Util {    private final static String[] hexDigits = { "0", "1", "2", "3", "4", "5",            "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };    /**     * MD5加密解密工具类     *      * @param b     *            字节数组     * @return 16进制字串     */    public static String byteArrayToHexString(byte[] b) {        StringBuffer resultSb = new StringBuffer();        for (int i = 0; i < b.length; i++) {            resultSb.append(byteToHexString(b[i]));        }        return resultSb.toString();    }    private static String byteToHexString(byte b) {        int n = b;        if (n < 0)            n = 256 + n;        int d1 = n / 16;        int d2 = n % 16;        return hexDigits[d1] + hexDigits[d2];    }    public static String MD5Encode(String origin) {        String resultString = null;        try {            resultString = new String(origin);            MessageDigest md = MessageDigest.getInstance("MD5");            resultString = byteArrayToHexString(md.digest(resultString                    .getBytes("utf-8")));        } catch (Exception ex) {        }        return resultString;    }        public static boolean isValidate(String input,String output){                boolean status = false;                if(MD5Util.MD5Encode(input).equals(output)){            status = true;        }else{            status = false;        }                return status;    }    }
import java.io.ByteArrayOutputStream;import java.io.InputStream;import java.nio.charset.Charset;import java.text.DateFormat;import java.text.SimpleDateFormat;import java.util.Date;import java.util.Random;import java.util.SortedMap;import java.util.TreeMap;import org.apache.http.HttpResponse;import org.apache.http.client.methods.HttpPost;import org.apache.http.entity.StringEntity;import org.apache.http.impl.client.DefaultHttpClient;import org.apache.http.util.EntityUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import com.meihe.dto.wePay.WeChatConst;@SuppressWarnings("deprecation")public class WeChatUtil {    private static final Logger log = LoggerFactory.getLogger(WeChatUtil.class);    /**     * 统一下单 获得PrePayId     *      * @param body     *            商品或支付单简要描述     * @param out_trade_no     *            商户系统内部的订单号,32个字符内、可包含字母     * @param total_fee     *            订单总金额,单位为分     * @param IP     *            APP和网页支付提交用户端ip     * @param notify_url     *            接收微信支付异步通知回调地址     * @param openid     *            用户openId     * @throws Exception      */    public static String unifiedorder(String body, String out_trade_no,            Integer total_fee, String ip, String notify_url, String openId)            throws Exception {        log.debug(">>>>>>>>>>>>>>>>>开始准备统一下单流程....");                /**         * 组装请求参数 按照ASCII排序         */        SortedMap
parameters = new TreeMap
(); String nonce_str = getNonceStr();// 随机数据 parameters.put("appid", WeChatConst.APPID); parameters.put("body", body); parameters.put("mch_id", WeChatConst.MCH_ID); parameters.put("nonce_str", nonce_str); parameters.put("notify_url", notify_url); parameters.put("openid", openId); parameters.put("out_trade_no", out_trade_no); parameters.put("spbill_create_ip", ip); parameters.put("total_fee", total_fee.toString()); parameters.put("trade_type", WeChatConst.TRADE_TYPE); String sign = WxSign.createSign(parameters, WeChatConst.KEY); /** * 组装XML */ log.debug(">>>>>>>>>>>>>>>>>统一下单数据准备完成,准备组装....{}",parameters.toString()); StringBuilder sb = new StringBuilder(""); sb.append("
"); setXmlKV(sb, "appid", WeChatConst.APPID); setXmlKV(sb, "body", body); setXmlKV(sb, "mch_id", WeChatConst.MCH_ID); setXmlKV(sb, "nonce_str", nonce_str); setXmlKV(sb, "notify_url", notify_url); setXmlKV(sb, "openid", openId); setXmlKV(sb, "out_trade_no", out_trade_no); setXmlKV(sb, "spbill_create_ip", ip); setXmlKV(sb, "total_fee", total_fee.toString()); setXmlKV(sb, "trade_type", WeChatConst.TRADE_TYPE); setXmlKV(sb, "sign", sign); sb.append("
"); // 这个处理是为了防止传中文的时候出现签名错误 StringEntity reqEntity = new StringEntity(new String(sb.toString().getBytes("UTF-8"), "ISO8859-1")); log.debug(">>>>>>>>>>>>>>>>>统一下单数据组装完成,准备下单。"); HttpPost httppost = new HttpPost("https://api.mch.weixin.qq.com/pay/unifiedorder"); httppost.setEntity(reqEntity); log.debug(">>>>>>>>>>>>>>>>>请求地址、请求数据封装完成。开始下单。"); @SuppressWarnings("resource") DefaultHttpClient httpclient = new DefaultHttpClient(); HttpResponse response = httpclient.execute(httppost); log.debug(">>>>>>>>>>>>>>>>>下单请求结束。获取返回值。"); String strResult = EntityUtils.toString(response.getEntity(),Charset.forName("utf-8")); log.debug(">>>>>>>>>>>>>>>>>下单请求结束。转换字符串返回值结束。"); return strResult; } // 获得随机字符串 public static String getNonceStr() { String str = ""; Random random = new Random(); try { str = MD5Util.MD5Encode(String.valueOf(random.nextInt(10000))); } catch (Exception e) { e.printStackTrace(); } return str; } // 插入XML标签 public static StringBuilder setXmlKV(StringBuilder sb, String Key, String value) { sb.append("<"); sb.append(Key); sb.append(">"); sb.append(value); sb.append("
"); return sb; } // 解析XML 获得 PrePayId public static String getPrePayId(String xml) { int start = xml.indexOf("
"); int end = xml.indexOf("
"); if (start < 0 && end < 0) { return null; } return xml.substring(start + "
".length(), end) .replace("", ""); } // 商户订单号 public static String getOut_trade_no() { DateFormat df = new SimpleDateFormat("yyyyMMddHHmmssSSS"); return df.format(new Date()) + buildRandom(7); } // 时间戳 public static String getTimeStamp() { return String.valueOf(System.currentTimeMillis() / 1000); } // 随机4位数字 public static int buildRandom(int length) { int num = 1; double random = Math.random(); if (random < 0.1) { random = random + 0.1; } for (int i = 0; i < length; i++) { num = num * 10; } return (int) ((random * num)); } public static String inputStream2String(InputStream inStream, String encoding) { String result = null; try { if (inStream != null) { ByteArrayOutputStream outStream = new ByteArrayOutputStream(); byte[] tempBytes = new byte[1024]; int count = -1; while ((count = inStream.read(tempBytes, 0, 1024)) != -1) { outStream.write(tempBytes, 0, count); } tempBytes = null; outStream.flush(); result = new String(outStream.toByteArray(), encoding); } } catch (Exception e) { result = null; } return result; }}
import java.io.IOException;import java.text.MessageFormat;import java.util.Properties;/** * 读取微信properties配置文件 *  * */public class WePropertieUtil {    private static Properties env;    private static Properties getEnv() {        try {            if (env == null) {                env = new Properties();                env.load(WePropertieUtil.class.getClassLoader().getResourceAsStream("config/wechat-const.properties"));            }        } catch (IOException e) {            e.printStackTrace();        }        return env;    }    /**     * 获取对应的key     *      * @param key     * @return     */    public static String getValue(String key) {        if (env == null) {            return getEnv().getProperty(key);        } else {            return env.getProperty(key);        }    }    /**     * 获取key对应的值,并替换其中的参数     *      * @param key     * @param arguments     * @return     */    public static String getValue(String key, Object... arguments) {        return MessageFormat.format(getValue(key), arguments);    }    }
import java.util.Iterator;import java.util.Map;import java.util.Set;import java.util.SortedMap;import org.slf4j.Logger;import org.slf4j.LoggerFactory;public class WxSign {        private static final Logger log = LoggerFactory.getLogger(WeChatUtil.class);    /**     * 签名封装类。创建签名。     * @param parameters     * @param key     * @return     * @throws Exception     */    @SuppressWarnings("rawtypes")    public static String createSign(SortedMap
parameters,String key) throws Exception { log.info(">>>>>>>>>>>>>>>>>开始封装数据,创建签名...."); StringBuffer sb = new StringBuffer(); // 所有参与传参的参数按照accsii排序(升序) Set es = parameters.entrySet(); Iterator it = es.iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); String k = (String) entry.getKey(); Object v = entry.getValue(); if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) { sb.append(k + "=" + v + "&"); } } sb.append("key=" + key); log.info(">>>>>>>>>>>>>>>>>封装数据{}",sb.toString()); String str = sb.toString(); log.info(str); String sign = MD5Util.MD5Encode(str).toUpperCase(); log.info(">>>>>>>>>>>>>>>>>封装数据完成,签名为:{}",sign); return sign; } }

 

import java.text.ParseException;import java.text.SimpleDateFormat;import java.time.Instant;import java.time.LocalDate;import java.time.LocalDateTime;import java.time.LocalTime;import java.time.ZoneId;import java.time.format.DateTimeFormatter;import java.util.Date;import java.util.TimeZone;import org.apache.commons.lang3.StringUtils;public abstract class DateTimeUtils {    /**     * 获取今天日期     *      * @return     */    public static String getToday(String format) {        if (StringUtils.isEmpty(format)) {            format = "yyyyMMdd";        }        return LocalDate.now().format(DateTimeFormatter.ofPattern(format));    }    /**     * 获取当前时间     *      * @param format     * @return     */    public static String getCurrentTime(String format) {        if (StringUtils.isEmpty(format)) {            format = "HHmmss";        }        return LocalTime.now().format(DateTimeFormatter.ofPattern(format));    }        /**     * 将字符串转换成LocalDate格式     *      * @param format     * @return     * @throws ParseException      */    public static LocalDate toLocalDate(String LocalDate) throws ParseException{        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");        sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));        Date date = sdf.parse(LocalDate);        Instant instant = date.toInstant();        ZoneId zone = ZoneId.systemDefault();        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);        LocalDate localDate = localDateTime.toLocalDate();        return localDate;    }        /**     * 将字符串转换成LocalTime格式     *      * @param format     * @return     * @throws ParseException      */    public static LocalTime toLocalTime(String LocalTime) throws ParseException{        SimpleDateFormat sdf = new SimpleDateFormat("HHmmss");        sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));        Date time = sdf.parse(LocalTime);        Instant instant = time.toInstant();        ZoneId zone = ZoneId.systemDefault();        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);        LocalTime localTime = localDateTime.toLocalTime();        return localTime;    }}

准备完成!开始进行业务操作。

代码流程:页面->controller->service;

 支付页面:

// 页面在执行成功之前,pay方法只允许调用一次。    clickstatus=true;    var sign ;    var appId ;    var timeStamp ;    var nonceStr ;    var packageStr ;    var signType,    var url = 'http://**/cms/wepay/';    function pay(){        if(clickstatus){            clickstatus=false;            $.ajax({                type:"post",                url:url+'pay',                dataType:"json",                data:{openId:'${openId}',totalFee:totalFee,pointAmount:pointAmount,memberId:$.cookie('memberId'),cellphone: $.cookie('cellphone'),accessToken: $.cookie('accessToken')},                success:function(data) {                    if(data.result_code == 'SUCCESS'){                        appId = data.appid;                        sign = data.sign;                        timeStamp = data.timeStamp;                        nonceStr = data.nonce_str;                        packageStr = data.packageStr;                        signType = data.signType;                        // 调用微信支付控件                        if (typeof WeixinJSBridge == "undefined"){                            if( document.addEventListener ){                                document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);                            }else if (document.attachEvent){                                document.attachEvent('WeixinJSBridgeReady', onBridgeReady);                                document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);                            }                        }else{                            onBridgeReady();                        }                    }else{                        alert("统一下单失败");                    }                }            })        }    }    function onBridgeReady(){        WeixinJSBridge.invoke('getBrandWCPayRequest', {            "appId":appId,            "timeStamp":timeStamp,            "nonceStr":nonceStr,            "package": packageStr,            "signType":signType,            "paySign": sign        },function (res) {            if (res.err_msg == "get_brand_wcpay_request:ok") {                window.location.href = url+'paySuccess';            } else if (res.err_msg == "get_brand_wcpay_request:fail") {                window.location.href = url+'payFailure';            } else if (res.err_msg == "get_brand_wcpay_request:cancel") {                alert("支付取消");            }        })    }

Controller

import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.servlet.ModelAndView;import com.meihe.dto.wePay.WxPaySendData;import com.meihe.service.wePay.IPreparedToPayService;@RestController@RequestMapping("wepay")public class wePayController {    @Autowired    private IPreparedToPayService preparedToPayService;    /**     *   跳转支付界面,将code带过去     **/    @ResponseBody    @RequestMapping("toPay")    public ModelAndView toPay(HttpServletRequest request,HttpServletResponse response){        ModelAndView res = new  ModelAndView();        try {            res = preparedToPayService.getUserOpenId(request, response);        } catch (Exception e) {            e.printStackTrace();        }        return res;      }    /**     *  点击确认充值 统一下单,获得预付id(prepay_id)        * @param request     * @param response     * @return        */    @ResponseBody    @RequestMapping("pay")    public WxPaySendData prePay(HttpServletRequest request,HttpServletResponse response,String openId, String totalFee, String pointAmount,            String memberId){        WxPaySendData data = new WxPaySendData();        try {            data = preparedToPayService.prePay(request, response, openId,totalFee,pointAmount,memberId);        } catch (Exception e) {            e.printStackTrace();        }        return data;    }        /**     * 微信支付回调接口     * @param request     * @param response     * @throws Exception      */    @ResponseBody    @RequestMapping("callback")    public void callBack(HttpServletRequest request, HttpServletResponse response) {        try {            preparedToPayService.callBack(request, response);        } catch (Exception e) {            e.printStackTrace();        }    }        /**     * 支付成功     **/    @ResponseBody    @RequestMapping("paySuccess")    public ModelAndView paySuccess(HttpServletRequest request,HttpServletResponse response){        return new ModelAndView("wepay/paySuccess");    }        /**     * 支付失败     **/    @ResponseBody    @RequestMapping("payFailure")    public ModelAndView payFailure(HttpServletRequest request,HttpServletResponse response){        return new ModelAndView("wepay/payFailure");      }

Service

import java.io.IOException;import java.io.InputStream;import java.net.URLEncoder;import java.util.Map;import java.util.SortedMap;import java.util.TreeMap;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.apache.commons.io.IOUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import org.springframework.web.servlet.ModelAndView;import com.alibaba.fastjson.JSON;import com.meihe.dto.AjaxResult;import com.meihe.dto.wePay.UserInfo;import com.meihe.dto.wePay.WeChatConst;import com.meihe.dto.wePay.WeixinLoginUser;import com.meihe.dto.wePay.WxPaySendData;import com.meihe.model.marketing.MarketingOrder;import com.meihe.service.marketing.IMarketingOrderService;import com.meihe.service.wePay.IOauthService;import com.meihe.service.wePay.IPreparedToPayService;import com.meihe.utils.wePayUtils.HttpUtil;import com.meihe.utils.wePayUtils.WeChatUtil;import com.meihe.utils.wePayUtils.WxSign;import com.thoughtworks.xstream.XStream;import com.thoughtworks.xstream.io.xml.DomDriver;@Servicepublic class PreparedToPayServiceImpl implements IPreparedToPayService {        @Autowired    private IOauthService oauth = null;        @Autowired    private IMarketingOrderService mOrder = null;    private static final Logger log = LoggerFactory.getLogger(PreparedToPayServiceImpl.class);        @Override    public ModelAndView getUserOpenId(HttpServletRequest request,HttpServletResponse response) throws Exception {        log.info(">>>>>>>>>>>>>>>>>准备获取用户code....");        ModelAndView modelAndView = new ModelAndView();        @SuppressWarnings("deprecation")        String redirecUri = URLEncoder.encode(WeChatConst.BASE_URL + "toPay");        String code = null;        if (request.getParameter("code") != null) {            code = request.getParameter("code");        }        if (code == null) {            log.info(">>>>>>>>>>>>>>>>>用户首次登陆,授权中...准备获取code....");            String siteURL = "redirect:https://open.weixin.qq.com/connect/oauth2/authorize?appid="                    +WeChatConst.APPID+"&redirect_uri="+redirecUri+                    "&response_type=code&scope=snsapi_userinfo&state=1234#wechat_redirect";            return new ModelAndView(siteURL);        }        code = request.getParameter("code");        log.debug(">>>>>>>>>>>>>>>>>获得用户code[{}]成功!准备获取用户信息...",code);        WeixinLoginUser weixinLoginUser = getWeixinLoginUser(code,request,response);        if(null == weixinLoginUser){            log.debug(">>>>>>>>>>>>>>>>>code[{}]已被使用,重新获取code...",code);            // 进入页面后,如果用户刷新,那么会提示code被使用的错误。此判断主要应对获取用户信息后刷新的操作。            String siteURL = "redirect:https://open.weixin.qq.com/connect/oauth2/authorize?appid="                    +WeChatConst.APPID+"&redirect_uri="+redirecUri+                    "&response_type=code&scope=snsapi_userinfo&state=1234#wechat_redirect";            return new ModelAndView(siteURL);        }        modelAndView.addObject("openId", weixinLoginUser.getOpenID());        request.setAttribute("openId", weixinLoginUser.getOpenID());        log.debug(">>>>>>>>>>>>>>>>>准备工作完成!跳转至购买页面...");        String viewName = "wepay/payIndex";        modelAndView.setViewName(viewName);        return modelAndView;    }        /**     * 获取微信授权登陆用户     *      * @param code     * @return     * @throws Exception     */    private WeixinLoginUser getWeixinLoginUser(String code,HttpServletRequest request,HttpServletResponse response) throws Exception {        String str = oauth.getToken(code, WeChatConst.APPID,WeChatConst.APP_SECRET);        String openID = (String) JSON.parseObject(str, Map.class).get("openid");        String accessToken = (String) JSON.parseObject(str, Map.class).get("access_token");        String refreshToken = (String) JSON.parseObject(str, Map.class).get("refresh_token");        if(null == openID || null == accessToken || null == refreshToken){            return null;        }        log.debug(">>>>>>>>>>>>>>>>>openid[{}],access_token[{}],refresh_token[{}]..转JSON为String中...",openID,accessToken,refreshToken);        // 获取到的用户信息。暂时没用。后台暂时只保存一个openId。        // 支付只需要用户的openId即可完成。这个方法可以在此结束。        UserInfo userInfo = oauth.getSnsUserInfo(openID, accessToken);        WeixinLoginUser weixinLoginUser = new WeixinLoginUser();        weixinLoginUser.setOpenID(openID);        weixinLoginUser.setUnionID(userInfo.getUnionid());        weixinLoginUser.setHeadImageUrl(userInfo.getHeadimgurl());        weixinLoginUser.setNickName(userInfo.getNickname());        weixinLoginUser.setRefreshToken(refreshToken);        log.debug(">>>>>>>>>>>>>>>>>获取用户信息中完成!");        return weixinLoginUser;    }    @Override    public WxPaySendData prePay(HttpServletRequest request,HttpServletResponse response, String openId, String totalFee, String pointAmount, String memberId) throws Exception {        log.debug(">>>>>>>>>>>>>>>>>预备工作完成。开始准备预付订单信息...");        WxPaySendData result = new WxPaySendData();        MarketingOrder marketingOrder = new MarketingOrder();        // 商户订单号             String out_trade_no = WeChatUtil.getOut_trade_no();            // 产品价格,单位:分            Double total_fee = Double.parseDouble(totalFee)*100;            // 客户端IP            String ip = HttpUtil.getRemoteIpAddr(request);            // 支付成功后回调的url地址            String notify_url = WeChatConst.BASE_URL+"callback";            // 商品或支付产品的简要描述            String body = pointAmount+"积分";            log.debug(">>>>>>>>>>>>>>>>>预付订单信息准备完成。");            log.debug(">>>>>>>>>>>>>>>>>订单号:[{}],价格:[{}],客户端IP[{}],回调URL:[{}],商品支付信息:[{}]",out_trade_no,total_fee,ip,notify_url,body);            //统一下单            String strResult = WeChatUtil.unifiedorder(body, out_trade_no, total_fee.intValue(), ip, notify_url,openId);                    log.debug(">>>>>>>>>>>>>>>>>解析统一下单XML....");            XStream stream = new XStream(new DomDriver());            stream.alias("xml", WxPaySendData.class);            WxPaySendData wxReturnData = (WxPaySendData)stream.fromXML(strResult);            log.debug(">>>>>>>>>>>>>>>>>统一下单XML解析完成....[{}]",wxReturnData.toString());                        //两者都为SUCCESS才能获取prepay_id            if(wxReturnData.getResult_code().equals("SUCCESS") &&wxReturnData.getReturn_code().equals("SUCCESS") ){                log.debug(">>>>>>>>>>>>>>>>>统一下单请求成功!开始准备正式支付....");                //时间戳、随机字符串                String timeStamp = WeChatUtil.getTimeStamp();            String nonce_str = WeChatUtil.getNonceStr();            // 注:上面这两个参数,一定要拿出来作为后续的value,不能每步都创建新的时间戳跟随机字符串,不然H5调支付API,会报签名参数错误            result.setResult_code(wxReturnData.getResult_code());            result.setAppid(WeChatConst.APPID);            result.setTimeStamp(timeStamp);            result.setNonce_str(nonce_str);            result.setPackageStr("prepay_id="+wxReturnData.getPrepay_id());            result.setSignType("MD5");                        // 第二次签名,将微信返回的数据再进行签名            SortedMap
signMap = new TreeMap
(); signMap.put("appId", WeChatConst.APPID); signMap.put("nonceStr", nonce_str); // 值为:prepay_id=xxx signMap.put("package", "prepay_id="+wxReturnData.getPrepay_id()); signMap.put("signType", "MD5"); signMap.put("timeStamp", timeStamp); log.debug(">>>>>>>>>>>>>>>>>根据预支付订单号再次进行签名...."); String paySign = WxSign.createSign(signMap, WeChatConst.KEY); log.debug(">>>>>>>>>>>>>>>>>签名完成!准备生成本地支付订单...."); result.setSign(paySign); } else { result.setResult_code("fail"); } log.debug(">>>>>>>>>>>>>>>>>开始生成一条新的本地支付订单。"); marketingOrder.setPaymentType(1); marketingOrder.setMemberId(Integer.parseInt(memberId)); marketingOrder.setOutTradeNo(out_trade_no); marketingOrder.setTotalFee(total_fee.toString()); marketingOrder.setPointAmount(pointAmount); marketingOrder.setOpenId(openId); AjaxResult ajaxResult = mOrder.insertMarketingOrder(marketingOrder); if(!ajaxResult.getSuccess()){ result.setResult_code("fail"); } log.debug(">>>>>>>>>>>>>>>>>开始正式支付!"); return result; } @Override public void callBack(HttpServletRequest request, HttpServletResponse response) throws Exception{ log.debug(">>>>>>>>>>>>>>>>>微信开始回调本地。校验签名。准备校验签名...."); response.setContentType("text/xml;charset=UTF-8"); try { InputStream is = request.getInputStream(); String result = IOUtils.toString(is, "UTF-8"); if("".equals(result)){ response.getWriter().write("
"); return ; } // 解析xml XStream stream = new XStream(new DomDriver()); stream.alias("xml", WxPaySendData.class); WxPaySendData wxPaySendData = (WxPaySendData)stream.fromXML(result); System.out.println(wxPaySendData.toString()); String appid = wxPaySendData.getAppid(); String mch_id =wxPaySendData.getMch_id(); String nonce_str = wxPaySendData.getNonce_str(); String out_trade_no = wxPaySendData.getOut_trade_no(); String total_fee = wxPaySendData.getTotal_fee(); String trade_type = wxPaySendData.getTrade_type(); String openid =wxPaySendData.getOpenid(); String return_code = wxPaySendData.getReturn_code(); String result_code = wxPaySendData.getResult_code(); String bank_type = wxPaySendData.getBank_type(); Integer cash_fee = wxPaySendData.getCash_fee(); String fee_type = wxPaySendData.getFee_type(); String is_subscribe = wxPaySendData.getIs_subscribe(); String time_end = wxPaySendData.getTime_end(); String transaction_id = wxPaySendData.getTransaction_id(); String sign = wxPaySendData.getSign(); //签名验证 SortedMap
parameters = new TreeMap
(); parameters.put("appid",appid); parameters.put("mch_id",mch_id); parameters.put("nonce_str",nonce_str); parameters.put("out_trade_no",out_trade_no); parameters.put("total_fee",total_fee); parameters.put("trade_type",trade_type); parameters.put("openid",openid); parameters.put("return_code",return_code); parameters.put("result_code",result_code); parameters.put("bank_type",bank_type); parameters.put("cash_fee",cash_fee); parameters.put("fee_type",fee_type); parameters.put("is_subscribe",is_subscribe); parameters.put("time_end",time_end); parameters.put("transaction_id",transaction_id); String sign2 = WxSign.createSign(parameters, WeChatConst.KEY); if(sign.equals(sign2)){ log.debug(">>>>>>>>>>>>>>>>>校验签名完成。正常交易。开始校验支付状态..."); if(return_code.equals("SUCCESS") && result_code.equals("SUCCESS")){ log.debug(">>>>>>>>>>>>>>>>>校验支付状态完成。支付成功!"); // 业务逻辑(先判断数据库中订单号是否存在,并且订单状态为未支付状态) MarketingOrder marketingOrder = new MarketingOrder(); marketingOrder.setOutTradeNo(out_trade_no); marketingOrder.setPayAmount(total_fee); AjaxResult affordOrder = mOrder.affordOrder(marketingOrder); if(!affordOrder.getSuccess()){ response.getWriter().write("
"); } // TODO:这个有什么用吗?暂未确定... request.setAttribute("out_trade_no", out_trade_no); // 通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了. log.debug(">>>>>>>>>>>>>>>>>本地异步确认支付成功!交易结束!"); response.getWriter().write("
"); }else{ log.debug(">>>>>>>>>>>>>>>>>本地异步确认支付失败!交易结束!"); response.getWriter().write("
"); } }else{ log.debug(">>>>>>>>>>>>>>>>>校验签名完成。异常交易!结束交易流程。"); response.getWriter().write("
"); } response.getWriter().flush(); response.getWriter().close(); return ; } catch (IOException e) { e.printStackTrace(); } } }
import java.util.HashMap;import java.util.Map;import org.apache.commons.lang3.StringUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.stereotype.Service;import com.alibaba.fastjson.JSONObject;import com.meihe.dto.wePay.UserInfo;import com.meihe.service.wePay.IOauthService;import com.meihe.utils.wePayUtils.HttpUtil;@Servicepublic class OauthServiceImpl implements IOauthService {        private static final Logger log = LoggerFactory.getLogger(PreparedToPayServiceImpl.class);    //    private static final String CODE_URI = "http://open.weixin.qq.com/connect/oauth2/authorize";    private static final String TOKEN_URI = "https://api.weixin.qq.com/sns/oauth2/access_token";    private static final String REFRESH_TOKEN_URI = "https://api.weixin.qq.com/sns/oauth2/refresh_token";    private static final String SNS_USER_INFO_URL = "https://api.weixin.qq.com/sns/userinfo";    public String getToken(String code, String appid, String secret) throws Exception {        log.debug(">>>>>>>>>>>>>>>>>准备根据code获取用户open_id/accessToken..",code,appid,secret);        Map
params = new HashMap
(); params.put("appid", appid); params.put("secret", secret); params.put("code", code); params.put("grant_type", "authorization_code"); String result = HttpUtil.sendGet(TOKEN_URI,params); log.debug(">>>>>>>>>>>>>>>>>获取信息完成[{}]...",result); return result; } public String getRefreshToken(String refreshToken, String appid,String secret) throws Exception { Map
params = new HashMap
(); params.put("appid", appid); params.put("grant_type", "refresh_token"); params.put("refresh_token", refreshToken); return HttpUtil.sendGet(REFRESH_TOKEN_URI, params); } public UserInfo getSnsUserInfo(String openid, String accessToken)throws Exception { Map
params = new HashMap
(); log.info(">>>>>>>>>>>>>>>>>转格式成功!准备根据open_id/accessToken获取用户信息...!"); params.put("access_token", accessToken); params.put("openid", openid); params.put("lang", "zh_CN"); log.debug(">>>>>>>>>>>>>>>>>token:[{}],openid[{}].开始连接微信获取信息..",accessToken,openid); String jsonStr = HttpUtil.sendGet(SNS_USER_INFO_URL, params); log.debug(">>>>>>>>>>>>>>>>>成功获取信息[{}]。转JSON为UserInfo..",jsonStr); if (StringUtils.isNotEmpty(jsonStr)) { JSONObject obj = JSONObject.parseObject(jsonStr); if (obj.get("errcode") != null) { throw new Exception(obj.getString("errmsg")); } UserInfo user = (UserInfo) JSONObject.toJavaObject(obj,UserInfo.class); log.debug(">>>>>>>>>>>>>>>>>转UserInfo格式完成!"); return user; } return null; }}

 

转载于:https://www.cnblogs.com/yugure/p/7090605.html

你可能感兴趣的文章
程序员如何优雅的挣零花钱?
查看>>
推荐 2 个简历模板及 2 大加分技巧
查看>>
关于伪类选择器中一个冒号和两个冒号的区别
查看>>
理解敏捷开发准则
查看>>
[beta cycle]daily scrum10_2.25
查看>>
【转载】和 Thrift 的一场美丽邂逅
查看>>
CM_RESOURCE_LIST structure 分类: wind...
查看>>
css单位pr,em,与颜色
查看>>
Angularjs笔记(三)
查看>>
@ControllerAdvice 标签为起作用
查看>>
lambda
查看>>
ubuntu16.04下使用python3开发时,安装pip3与scrapy,升级pip3
查看>>
python网络编程基础
查看>>
selenium+maven+testng+IDEA+git自动化测试环境搭建(二)
查看>>
Mini2440 UART原理及使用
查看>>
Linux学习第六篇之文件处理命令ln(链接命令)
查看>>
thinkphp5 tp5 七牛云 上传图片
查看>>
VM下Linux网卡丢失(pcnet32 device eth0 does not seem to be ...)解决方案
查看>>
第一阶段意见汇总以及改进
查看>>
再说virtual
查看>>