官网文档和demo代码下载地址
https://mp.weixin.qq.com/htmledition/res/bussiness-faq/wx_app_pay.zip
-
基础常量代码
package com.xiaoshuai.util; /** * 配置类 * @author 小帅帅 * @date 2016-7-5下午01:40:53 */ public class ConfigUtil { /** * 服务号相关信息 */ public final static String APPID = "";//服务号的应用号 public final static String APP_SECRECT = "";//服务号的应用密码 public final static String TOKEN = "weixinCourse";//服务号的配置token public final static String MCH_ID = "";//商户号 public final static String API_KEY = "";//API密钥 public final static String SIGN_TYPE = "MD5";//签名加密方式 public final static String CERT_PATH = "";//微信支付证书存放路径地址 //微信支付统一接口的回调action public final static String NOTIFY_URL = ""; //微信支付成功支付后跳转的地址 public final static String SUCCESS_URL = "http://www.xiaoshuaishuai.com"; //oauth2授权时回调action public final static String REDIRECT_URI = ""; /** * 微信基础接口地址 */ //获取token接口(GET) public final static String TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET"; //oauth2授权接口(GET) public final static String OAUTH2_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code"; //刷新access_token接口(GET) public final static String REFRESH_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN"; /** * 微信支付接口地址 */ //微信支付统一接口(POST) public final static String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder"; //微信退款接口(POST) public final static String REFUND_URL = "https://api.mch.weixin.qq.com/secapi/pay/refund"; //订单查询接口(POST) public final static String CHECK_ORDER_URL = "https://api.mch.weixin.qq.com/pay/orderquery"; //关闭订单接口(POST) public final static String CLOSE_ORDER_URL = "https://api.mch.weixin.qq.com/pay/closeorder"; //退款查询接口(POST) public final static String CHECK_REFUND_URL = "https://api.mch.weixin.qq.com/pay/refundquery"; //对账单接口(POST) public final static String DOWNLOAD_BILL_URL = "https://api.mch.weixin.qq.com/pay/downloadbill"; //短链接转换接口(POST) public final static String SHORT_URL = "https://api.mch.weixin.qq.com/tools/shorturl"; //接口调用上报接口(POST) public final static String REPORT_URL = "https://api.mch.weixin.qq.com/payitil/report"; }
-
支付工具类代码
package com.xiaoshuai.util; import java.util.Date; import java.util.Iterator; import java.util.Map; import java.util.Random; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.alibaba.fastjson.*; /** * 支付类 * @author 小帅帅 * @date 2016-7-5下午01:41:54 */ public class PayCommonUtil { @SuppressWarnings("unused") private static Logger log = LoggerFactory.getLogger(PayCommonUtil.class); public static String CreateNoncestr(int length) { String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; String res = ""; for (int i = 0; i < length; i++) { Random rd = new Random(); res += chars.indexOf(rd.nextInt(chars.length() - 1)); } return res; } public static String CreateNoncestr() { String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; String res = ""; for (int i = 0; i < 16; i++) { Random rd = new Random(); res += chars.charAt(rd.nextInt(chars.length() - 1)); } return res; } /** * @author 小帅帅 * @date 2016-7-5下午01:41:54 * @Description:sign签名 * @param characterEncoding 编码格式 * @param parameters 请求参数 * @return */ @SuppressWarnings("unchecked") public static String createSign(String characterEncoding,SortedMap<Object,Object> parameters){ StringBuffer sb = new StringBuffer(); 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=" + ConfigUtil.API_KEY); String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase(); return sign; } /** * @author 小帅帅 * @date 2016-7-5下午01:41:54 * @Description:将请求参数转换为xml格式的string * @param parameters 请求参数 * @return */ @SuppressWarnings("unchecked") public static String getRequestXml(SortedMap<Object,Object> parameters){ StringBuffer sb = new StringBuffer(); sb.append("<xml>"); Set es = parameters.entrySet(); Iterator it = es.iterator(); while(it.hasNext()) { Map.Entry entry = (Map.Entry)it.next(); String k = (String)entry.getKey(); String v = (String)entry.getValue(); if ("attach".equalsIgnoreCase(k)||"body".equalsIgnoreCase(k)||"sign".equalsIgnoreCase(k)) { sb.append("<"+k+">"+"<![CDATA["+v+"]]></"+k+">"); }else { sb.append("<"+k+">"+v+"</"+k+">"); } } sb.append("</xml>"); return sb.toString(); } /** * @author 小帅帅 * @date 2016-7-5下午01:41:54 * @Description:返回给微信的参数 * @param return_code 返回编码 * @param return_msg 返回信息 * @return */ public static String setXML(String return_code, String return_msg) { return "<xml><return_code><![CDATA[" + return_code + "]]></return_code><return_msg><![CDATA[" + return_msg + "]]></return_msg></xml>"; } /** * * getPrepayId(调用微信统一下单接口,生成微信预支付id) * @param totalFee 支付金额 * @param ipAddress ip地址 * @param orderNumber 订单号 * @param body 商品或支付单简要描述 * @param openid trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识 * @return * @throws Exception * @throws * @author 小帅帅 * @date 2016-7-5下午01:41:54 */ @SuppressWarnings("unchecked") public static Map<String, String> getPrepayId(String totalFee,String ipAddress,String orderNumber,String body,String openid) throws Exception{ SortedMap<Object,Object> parameters = new TreeMap<Object,Object>(); parameters.put("appid", ConfigUtil.APPID); parameters.put("mch_id", ConfigUtil.MCH_ID); parameters.put("nonce_str", CreateNoncestr()); parameters.put("body", body); parameters.put("out_trade_no", orderNumber); parameters.put("total_fee", totalFee); parameters.put("spbill_create_ip",ipAddress); parameters.put("notify_url", ConfigUtil.NOTIFY_URL); parameters.put("trade_type", "JSAPI"); parameters.put("openid", openid); String sign = PayCommonUtil.createSign("UTF-8", parameters); parameters.put("sign", sign); String requestXML = PayCommonUtil.getRequestXml(parameters); String result =CommonUtil.httpsRequest(ConfigUtil.UNIFIED_ORDER_URL, "POST", requestXML); Map<String, String> map = XMLUtil.doXMLParse(result);//解析微信返回的信息,以Map形式存储便于取值 return map; } /** * * queryWeiXinOrder(微信订单查询) * @param orderNumber 订单号 * @return 当返回 return_code="SUCCESS" * 和result_code="SUCCESS" 时 获取交易状态SUCCESS—支付成功 REFUND—转入退款 NOTPAY—未支付 CLOSED—已关闭 REVOKED—已撤销(刷卡支付) USERPAYING--用户支付中 PAYERROR--支付失败(其他原因,如银行返回失败) * @throws Exception * @throws * @author 小帅帅 * @date 2016-7-5下午01:41:54 */ @SuppressWarnings("unchecked") public static Map<String, String> queryWeiXinOrder(String orderNumber) throws Exception{ SortedMap<Object,Object> parameters = new TreeMap<Object,Object>(); parameters.put("appid", ConfigUtil.APPID); parameters.put("mch_id", ConfigUtil.MCH_ID); parameters.put("nonce_str", CreateNoncestr()); parameters.put("out_trade_no", orderNumber); String sign = PayCommonUtil.createSign("UTF-8", parameters); parameters.put("sign", sign); String requestXML = PayCommonUtil.getRequestXml(parameters); String result =CommonUtil.httpsRequest(ConfigUtil.CHECK_ORDER_URL, "POST", requestXML); Map<String, String> map = XMLUtil.doXMLParse(result);//解析微信返回的信息,以Map形式存储便于取值 return map; } /** * * createPackageValue(调起支付) * @param appid * @param appKey * @param prepay_id * @return * @throws * @author 小帅帅 * @date 2016-7-5下午01:41:54 */ public static String createPackageValue(String prepay_id) { SortedMap<Object,Object> params = new TreeMap<Object,Object>(); params.put("appId", ConfigUtil.APPID); params.put("timeStamp", Long.toString(new Date().getTime())); params.put("nonceStr", PayCommonUtil.CreateNoncestr()); params.put("package", "prepay_id="+prepay_id); params.put("signType", ConfigUtil.SIGN_TYPE); String paySign = PayCommonUtil.createSign("UTF-8", params); params.put("packageValue", "prepay_id="+prepay_id); //这里用packageValue是预防package是关键字在js获取值出错 params.put("paySign", paySign); //paySign的生成规则和Sign的生成规则一致 params.put("sendUrl", ConfigUtil.SUCCESS_URL); //付款成功后跳转的页面 String json = JSONObject.toJSONString(params); return json; } }
-
请求业务代码
package com.xiaoshuai.controller; import java.io.IOException; import java.io.PrintWriter; import java.math.BigDecimal; import java.util.Map; import java.util.Random; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.jdom.JDOMException; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import com.xiaoshuai.util.CommonUtil; import com.xiaoshuai.util.PayCommonUtil; /** * 支付请求Controller * @author 小帅帅 * @date 2016-7-5下午02:08:47 */ @Controller @RequestMapping(value="pay") public class WeiXinPayController { /** * * index(支付首页) * @return */ @RequestMapping(value="index") public String index(){ return "pay"; } /** * * payOrder(调起微信支付) * @param request * @param totalFee //支付金额 * @param body //支付描述 * @return * @throws Exception */ @RequestMapping(value="payOrder") @ResponseBody public String payOrder(HttpServletRequest request,double totalFee,String body) throws Exception{ String resultString=null; BigDecimal fee = new BigDecimal(totalFee); // 微信支付参数以分为单位。 fee = fee.multiply(new BigDecimal(100)); String payFee=fee.longValue()+""; //1 生成预支付id String ipAddress=request.getRemoteAddr(); String orderNumber=System.currentTimeMillis()+new Random().nextInt(100)+""; String openid="o2VKNju8JqCeGVoEWJ1S8Ue_up8E"; //调用网页授权接口获取 try { Map<String, String> map=PayCommonUtil.getPrepayId(payFee, ipAddress, orderNumber, body, openid); String prepay_id=map.get("prepay_id"); //2 给H5页面传递参数 调起微信支付 resultString=PayCommonUtil.createPackageValue(prepay_id); } catch (JDOMException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return resultString; } /** * * updateOrderState(异步回调通知) * * @param request */ @RequestMapping(value="noticePay") public void updateOrderState(HttpServletRequest request,HttpServletResponse response) { try { PrintWriter out= response.getWriter(); // xml请求解析 Map<String, Object> requestMap = CommonUtil.parseXml(request); String orderNumber = (String) requestMap.get("out_trade_no"); if (orderNumber != null) { // 调用微信查询订单接口, Map<String, String> orderMap = PayCommonUtil.queryWeiXinOrder(orderNumber); if (orderMap.get("return_code") != null&& orderMap.get("return_code").equalsIgnoreCase("SUCCESS")) { if (orderMap.get("result_code") != null&& orderMap.get("result_code").equalsIgnoreCase("SUCCESS")) { if (orderMap.get("trade_state") != null&& orderMap.get("trade_state").equalsIgnoreCase("SUCCESS")) { // 支付成功。。。。开始更新你的订单状态吧 根据自己的业务去修改 String resultXml="<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>"; out.println(resultXml); } } } } } catch (Exception e) { e.printStackTrace(); } } }
- 项目结构。只是简单的基础代码
5.pay.jsp
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport"
content="initial-scale=1, maximum-scale=3, minimum-scale=1, user-scalable=no">
<title>微信支付</title>
</head>
<body>
<div style="text-align: center; padding: 10px 10px 10px 10px">
<span
style="color: red; font-size: 20px; font-weight: bold; display: none">请在微信浏览器中打开</span>
</div>
支付金额:
<input type="text" id="totalFee" style="width: 80%; height: 30px;" />
<br>
<br>
支付描述:
<input type="text" id="body" style="width: 80%; height: 30px;" />
<br>
<br>
<input type="button" value="确定" id="pay"
style="width: 150px; height: 80px;">
<script src="<%=basePath%>resources/js/jquery.min.js"
type="text/javascript"></script>
<script type="text/javascript">
var basePath = "<%=basePath%>";
$(function(){
$("#pay").click(payOrder);
if(!isWeiXin()){
$("span").show();
alert("请在微信浏览器中打开");
}else{
$("span").hide();
}
})
function isWeiXin(){
var ua = window.navigator.userAgent.toLowerCase();
if(ua.match(/MicroMessenger/i) == 'micromessenger'){
return true;
}else{
return false;
}
}
function payOrder(){
if(!isWeiXin()){
alert("请在微信浏览器中打开")
$("span").show();
return;
}else{
$("span").hide();
}
var totalFee=$("#totalFee").val();
var body=$("#body").val();
$.ajax({
type: "post",
url:basePath+"pay/payOrder" ,
data: {'totalFee':totalFee, 'body':body},
dataType: "json",
success: function(data){
WeixinJSBridge.invoke('getBrandWCPayRequest',{
"appId" : data.appId, //公众号名称,由商户传入
"timeStamp":data.timeStamp, //时间戳,自 1970 年以来的秒数
"nonceStr" : data.nonceStr, //随机串
"package" : data.packageValue, //<span style="font-family:微软雅黑;">商品包信息</span>
"signType" : data.signType, //微信签名方式:
"paySign" : data.paySign //微信签名
},function(res){
if(res.err_msg == "get_brand_wcpay_request:ok" ) {
window.location.href=data.sendUrl;
}else{
alert("fail");
// window.location.href="http://183.45.18.197:8016/wxweb/config/oauth!execute.action";
}
});
}
});
}
</script>
</body>
</html>