1.在微信网站上的设置
(你必须是服务号并且申请各种认证通过后才能往下走,否则请回炉!)
微信公众平台,进行如下配置,谢谢。
1.1微信支付-开发配置
1.2公众号设置-功能设置
1.3开发者中心-配置项-接口权限
1.4 微信支付-商户平台
(坑1:此处为最大的巨坑,要不是大神告诉我要配置这里,我死也想不出这里也需要配置,这里不配置的话,后面的预订单提交会一直报错,并且那个错也许你永远都不知道原因。。。阿西)
将这个秘钥设置成和AppSecret(应用密钥)设置成一样的,just相信我。
2.调用网页微支付
各位童鞋,经过上面的配置,大家可以进入第二阶段,its time to coding.
因为我们是要从网页上调用微支付么,对吧,那么我们就要通过一个http get请求来告诉微信服务器,我们要请求啦。肿么写?
2.1 第一个http请求,我们要支付!!!
https://open .weixin.qq.c om/connect/o auth2/author ize?appid=11111111&redirect_uri=http%3A%2F%2Ftest.aaa.com /wxtest%2Fwxtest&response_type=code&scope=snsapi_base&state=12345#wechat_redirect
您第一眼看到这坨,您会想,嗯。。。这尼玛是什么?
简单的说,这坨就是你要告诉微信服务器你要进行支付的一个http get请求的url,当然了这只是第一步,你需要给他问号传参,下面请允许我来解释一下这几个参数:
appid:这就不用说了,你应该把它弄成全局变量,很多地方要用。
redirect_uri:跳转地址,写成你自己url,切记这里的写法要与上面的配置在一个域名里,否则这步就够你卡一阵子的。你可以配置成/xxx.jsp,xxx.do,xxx.action,啥都可以,我这里是用了spring 的restful的url格式。(坑2:
你要把你的url进行encode转码,切记你的url是带http://的,例如我的是http://
test.aaaa.co m/wxtest,转码之后是
http%3A%2F%2Ftest.aaa.com /wxtest%2Fwxtest。
这不是演习!)
response_type:
code,
固定写法,不要问为什么。
scope:
snsapi_base
,
固定写法,不要问为什么。
state:
state是你自己的业务id,随便写啥都行,在后面会接到你自己传的参数,名字就叫state,别改。后面的照抄(
#wechat_redirect
),别乱写。
这坨url只能在微信浏览器中使用,因此我们的测试方法是把这个url用微信随便发给一个人,然后你自己在聊天框里点击这个url。
如果上面的配置都木有错误的话,那么按照套路出牌,微信服务器会回调你的url,说白了就是请求你的服务器,然后给你传递2个参数:code和state。如果没有拿到这2个参数或者没有被请求到,反省吧,凡人,前面的配置你肯定是配错了。好的,休息一下,我们进入下一小节。
(什么?你说你不会写如何提供web服务让微信访问你的代码?
就是controller,action里的方法呗,或者一个jsp,servlet也行呀。
什么?你还是不懂?
凡人,你需要立即马上放下手头的工作,去买一本java web速成。。。)
2.2 第一次微信来请求你,还不错。
好的,欢迎回来,前文书说到我们会拿到2个参数,下面是2个参数的含义:
state:你自定义的业务id,前面提到过。
code:
code是微信返回的用户auth校验后的code码,后面要用到。
你接到参数的时候需要做判断:
如果code是空,则不能往下继续进行,因为可能用户鉴权失败了。
2.3 第一个签名(config配置签名),坑王之王。
注意!一大波大坑正在靠近。你的脑海里要有一个印象,在亚瑞特巨坑中,呃,不是,在微信jsapi巨坑中,所有的签名,都是手机中的战斗机。坑王之王。只要是带签名的地方,我们基本都被卡住很长时间,各种试啊,你懂我的意思。
那么好,你拿到的那个code和state,是要干什么用?答案是你要做第一个config签名(配置签名),什么是配置签名?
简单的说,微信服务器为了防止第三方程序or个人进行抓包篡改请求参数,就是做坏事,的一种防范措施。
签名里放的是你要给微信服务器请求的参数以及加密,再使用ticket,把他们揉在一起,捏出来一个签名,发给微信服务器。
不太懂对吗?
别怕,跟着杨老师一步一步带你升仙。
这样,我先告诉你,这个config签名里都需要什么吧,然后我在下面一步一步带你升仙,肿么样?
这个签名里还需要
nonce_str32位随机码,
timestamp生成时间,
url微支付页面url,
jsapiTicket门票。就这四样任务物品,得到后就可以交任务,拿经验了。
其中jsapiTicket是比较坑的,获取
jsapiTicket需要
accessToken,而获取
accessToken又需要appid和
AppSecret(应用密钥)。
WTF?我们继续。。。
2.3.1 获取
accessToken
简单的说你要给微信的一个url发http get请求,参数是appid和AppSecret(应用秘钥),
这是我的代码,当然了我把appid和
AppSecret(应用密钥)弄成了全局变量。
(坑3:这个请求不要试图在页面上写,不要用jquery或者ajax,会有跨域的问题,即使你解决了跨域的问题,也会有拿不到返回值的问题。乖乖的拿到java代码里去写,在java代码里写一个get请求,并且用json来接返回值,不会写的话,QQ里管我要。)
请求后,微信服务器会返回给你一个json,没错,你没听错,的确是json。
json里取"access_token",并且要把这个值全局缓存起来,7200秒吧。频繁的请求微信服务器会把你的账号封掉。
什么?你说你不会取json里的值?
凡人。。。。。
2.3.2 获取
jsapiTicket
好的,我们现在已经有了
accessToken,下一步就是去取
jsapiTicket了哟。
同上,你需要给微信的另一个url发http get请求,参数是我们刚才拿到的
access_token,
返回值依然是json,你需要在返回的json中取"
ticket
",并缓存7200秒。
2.3.3 获取url
这里的url是
微支付页面url,也是你之前在2.1里配置的那个url,好了,坑来了。
(坑4:这个url必须是带"http://",并且不用encode转码,记住,是不能转码!并且必须带上code和state参数,否则出错!当时就因为没有传code和state试了多少遍,阿西!)
例如:http://test. aaa.com/wxte st
?code="+code+"&state="+state;
code和state是微信给你传的,还记得吗?
2.3.4 获取nonce_str+timestamp
String nonce_str = UUID.randomUUID().toString().replaceAll("-", "");//32位随机码
String timestamp = Long.toString(System.currentTimeMillis() / 1000);//生成时间
String timestamp = Long.toString(System.currentTimeMillis() / 1000);//生成时间
能看懂吧?
2.3.5 生成第一个config配置签名
有了上面的4样任务物品,咱们就可以交任务,拿经验了。
(坑5:下面的写法顺序也有严格的要求,照我的抄吧,至少我的写法是最后支付成功了。)
下面我们就来把这4个东西揉在一块,捏出一个签名出来,我直接放java代码了,有疑问请加我QQ。
/**
* @category 第一个签名,前台的config配置签名
* @param code
* @param state
* @return 返回签名map,前台要用
*/
public static Map<String, Object> configSign(String code,String state) {
String nonce_str = UUID.randomUUID().toString().replaceAll("-", "");//32位随机码
String timestamp = Long.toString(System.currentTimeMillis() / 1000);//生成时间
String url = WxConfig.getUrl()+"/wxtest?code="+code+"&state="+state;//微支付页面url
String jsapiTicket;
try {
jsapiTicket = WxpayUtil.getJsapiTicket();
//注意这里参数名必须全部小写,且必须有序
String string1 = "jsapi_ticket=" + jsapiTicket +
"&noncestr=" + nonce_str +
"×tamp=" + timestamp +
"&url=" + url;
//加密算法
MessageDigest crypt = MessageDigest.getInstance("SHA-1");
crypt.reset();
crypt.update(string1.getBytes("UTF-8"));
String signature = byteToHex(crypt.digest());//生成签名
Map<String, Object> returnMap = new HashMap<String, Object>();
returnMap.put("url", url);
returnMap.put("jsapi_ticket", jsapiTicket);
returnMap.put("nonceStr", nonce_str);
returnMap.put("timestamp", timestamp);
returnMap.put("signature", signature); //这是万恶的config配置签名,就是他!!!
return returnMap;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
* @category 第一个签名,前台的config配置签名
* @param code
* @param state
* @return 返回签名map,前台要用
*/
public static Map<String, Object> configSign(String code,String state) {
String nonce_str = UUID.randomUUID().toString().replaceAll("-", "");//32位随机码
String timestamp = Long.toString(System.currentTimeMillis() / 1000);//生成时间
String url = WxConfig.getUrl()+"/wxtest?code="+code+"&state="+state;//微支付页面url
String jsapiTicket;
try {
jsapiTicket = WxpayUtil.getJsapiTicket();
//注意这里参数名必须全部小写,且必须有序
String string1 = "jsapi_ticket=" + jsapiTicket +
"&noncestr=" + nonce_str +
"×tamp=" + timestamp +
"&url=" + url;
//加密算法
MessageDigest crypt = MessageDigest.getInstance("SHA-1");
crypt.reset();
crypt.update(string1.getBytes("UTF-8"));
String signature = byteToHex(crypt.digest());//生成签名
Map<String, Object> returnMap = new HashMap<String, Object>();
returnMap.put("url", url);
returnMap.put("jsapi_ticket", jsapiTicket);
returnMap.put("nonceStr", nonce_str);
returnMap.put("timestamp", timestamp);
returnMap.put("signature", signature); //这是万恶的config配置签名,就是他!!!
return returnMap;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
我的方法返回了一个MAP,MAP里放的是config签名信息,我们姑且叫他config签名MAP,我在前台页面中需要拿这个MAP进行下一步操作。
我的页面中是这么写的,当然你可以有你更好的写法,我并没有对代码进行优化和封装:
<%
String code = request.getParameter("code");//这个code码在下面会用到,到时不要问我code码从哪里来,他的故乡在远方。。。
String state = request.getParameter("state");//报名ID
//第一个签名,用于第一个JS配置
Map<String,Object> signatureMap = WxpaySign.configSign(code,state);
%>
String code = request.getParameter("code");//这个code码在下面会用到,到时不要问我code码从哪里来,他的故乡在远方。。。
String state = request.getParameter("state");//报名ID
//第一个签名,用于第一个JS配置
Map<String,Object> signatureMap = WxpaySign.configSign(code,state);
%>
我,尼玛,这都是神马跟神马?
这只是一个开始,休息一会,抽根烟,放松放松,我们接着来。
2.4 页面中配置config对象以及ready+error监听方法
前文书说到,我们拿到了万恶的config配置签名,以及从后台返回了一个
config签名MAP,下面,我们要把这个MAP里的东西用起来,来配置天杀的config对象。
2.4.1 创建一个jsp页面,并引入微信的js包+jquery包
<script src="http://res.w x.qq.com/ope n/js/jweixin -1.0.0.js"></script>
<script type="text/javascript" src="${ctx}/js/jquery/jquery-1.9.1.min.js"></script>
<script type="text/javascript" src="${ctx}/js/jquery/jquery-1.9.1.min.js"></script>
什么?你不用jquery?厉害!
2.4.2 配置config对象
<script>
wx.config({
debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来appId: '<%=WxConfig.getAppID()%>', // 必填,公众号的唯一标识
timestamp: '<%=signatureMap.get("timestamp")%>', // 必填,生成签名的时间戳
nonceStr: '<%=signatureMap.get("nonceStr")%>', // 必填,生成签名的随机串
signature: '<%=signatureMap.get("signature")%>',// 必填,签名,见附录1
sApiList: ['chooseWXPay'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
});
(坑6,记住全部要带单引号!!!)
signatureMap就是我上面从后台返回的
config签名MAP,不用客气,从里面拿东西往config里放吧。
2.4.3 配置error监听
<script>
wx.error(function(res){
for(i in res){
alert(i+":"+res[i]);
}
alert("微支付调用失败,请截图给管理人员,谢谢");
window.close();
});
wx.error(function(res){
for(i in res){
alert(i+":"+res[i]);
}
alert("微支付调用失败,请截图给管理人员,谢谢");
window.close();
});
你在这里可以做你想要做的任何逻辑。
2.4.4 配置ready监听
<script>
wx.ready(function(){
alert("hello world...");
});
wx.ready(function(){
alert("hello world...");
});
(坑7:如果上述的配置or写法有一点错误,那么页面上就会触发error监听,坑的是触发完error监听,依然会进入ready监听,大家要写好自己的逻辑。触发error监听后,你能看到微信服务器给你返回的错误提示信息,OK,但凡触发了错误监听,您就别往下走了,老老实实的回去反思吧。)
呃。。。如果上面的配置or写法完全100%没问你的话,会打出hello world,前提是没有进error监听方法的情况下,好!!!
第一场战役胜利了。HOLY SHIT!!!
庆祝一下!!!你可以试想当时没有人指点的情况下,要摸清这么多坑,是多么恶心的事情。
你下面几乎全部的写法都要在ready监听方法里实现,OK,我们进入下一章,万恶的预订单提交。
2.5 万恶的预订单提交
该配置的配置完啦,我们该准备提交订单啦,等等!先要有一个预订单,就是告诉微信服务器你准备要提交订单了,我,尼玛。。。
预订单说白了就是一个表单,微信官方叫统一下单接口,你要给微信服务器一个http post请求,参数是xml(尼玛刚才不还是json呢么,肿么现在是xml了?),这里我是在java里写的,在ready里用ajax调用的。您随意。
我先大概说一下这个预订单提交,你需要拿到通过code码(之前有)拿到openid。然后提交一坨参数给微信,是xml格式,还包括万恶的预订单签名,在一切木有问题的情况下,微信会给你返回一个xml,你通过解析后能拿到
prepay_id,传说中的预订单ID,有了这个ID我们才好去打下一关的boss对不对?
2.5.1 取得openid
简单的说你要给微信的一个url再发http get请求(恩?我为什么要说一个再字),参数是appid,AppSecret(应用秘钥),code码。
这个url给你返回的是一个json对象,木有错,就是一会xml一会json玩死你。
在返回的json对象中取"
openid
"
什么?你问我openid是什么?
对不起,我也不知道,用,就对了。
2.5.2 构造预订单参数
像我刚才说的,预订单需要一个xml格式的参数,那么好,我们现在来搞这个xml。
我这里是用一个MAP里放一坨参数,然后再用代码转成xml的,当然,你也可以有你自己的实现方式。
直接粘代码了啊:
(坑7:Map的顺序一定不能错,不然会报签名错误,GOD!)
//此map用于生成签名和XML
Map<String,Object> paramSignMap = new LinkedHashMap();//按顺序来的
paramSignMap.put("appid", WxConfig.getAppID());
paramSignMap.put("attach", "aaa");// 附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
paramSignMap.put("body", "逐风者祝福之剑"); //支付的时候给用户看的商品描述,直接写中文,不用转义。
paramSignMap.put("mch_id", WxConfig.getMchID());//商户号
paramSignMap.put("nonce_str", UUID.randomUUID().toString().replaceAll("-", ""));//随机码
paramSignMap.put("notify_url", WxConfig.getUrl()+"/wxnotify");//监听地址,支付成功或失败微信会回调这个url通知你。
paramSignMap.put("openid",openId); //之前拿到的oepnid
paramSignMap.put("out_trade_no", "1231231231231");//自己的订单号,这里跟你的业务逻辑要挂钩了。
paramSignMap.put("spbill_create_ip", request.getRemoteAddr());//请求者IP
paramSignMap.put("total_fee", signMap.get("money")+"00");//钱数
paramSignMap.put("trade_type", "JSAPI");//固定写法
paramSignMap.put("sign", WxpaySign.preFormSign(paramSignMap));//生成预订单签名
Map<String,Object> paramSignMap = new LinkedHashMap();//按顺序来的
paramSignMap.put("appid", WxConfig.getAppID());
paramSignMap.put("attach", "aaa");// 附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
paramSignMap.put("body", "逐风者祝福之剑"); //支付的时候给用户看的商品描述,直接写中文,不用转义。
paramSignMap.put("mch_id", WxConfig.getMchID());//商户号
paramSignMap.put("nonce_str", UUID.randomUUID().toString().replaceAll("-", ""));//随机码
paramSignMap.put("notify_url", WxConfig.getUrl()+"/wxnotify");//监听地址,支付成功或失败微信会回调这个url通知你。
paramSignMap.put("openid",openId); //之前拿到的oepnid
paramSignMap.put("out_trade_no", "1231231231231");//自己的订单号,这里跟你的业务逻辑要挂钩了。
paramSignMap.put("spbill_create_ip", request.getRemoteAddr());//请求者IP
paramSignMap.put("total_fee", signMap.get("money")+"00");//钱数
paramSignMap.put("trade_type", "JSAPI");//固定写法
paramSignMap.put("sign", WxpaySign.preFormSign(paramSignMap));//生成预订单签名
(坑8:paramSignMap的notify_url,必须是带http://的,例如:http://test. aaa.com/wxno tify)
(坑9:paramSignMap的total_fee,后面要加00,因为你传过去1,默认是1分钱)
(坑10:paramSignMap的sign,详见2.5.3,一点都不能错,不然就会报天杀的"签名错误")
2.5.3 生成预订单签名
好的,大家看到了刚才的这段代码:
paramSignMap.put("sign", WxpaySign.preFormSign(paramSignMap));//生成预表单签名
preFormSign是我自己封的构造预订单签名的方法,大概的逻辑说一下。
把paramSignMap里除了sign以外的所有参数都累加,并且使用MD5加密后再加上一个秘钥的大写,就形成了预订单签名。
(坑11:这个秘钥就是用户必须要在商户平台去设置的那个,同坑1,我们为了方便,把这个秘钥跟AppSecret(应用密钥)设置成一个了。这个地方是坑王之王,坑断肠。)
话不多说,粘代码:
/**
* @category 预提交表单签名
* @param url
* @return
*/
public static String preFormSign(Map<String,Object> paramSignMap) {
StringBuffer tempStr = new StringBuffer(1024);
for (String key : paramSignMap.keySet()) {
tempStr.append(key+"="+paramSignMap.get(key)+"&");
}
System.out.println(tempStr.toString());
String returnStr = MD5.GetMD5Code(tempStr.substring(0,tempStr.length()-1)+"&key="+WxConfig.getSecret()).toUpperCase();
logger.info("preFormSign==========================================>"+returnStr);
return returnStr;
}
* @category 预提交表单签名
* @param url
* @return
*/
public static String preFormSign(Map<String,Object> paramSignMap) {
StringBuffer tempStr = new StringBuffer(1024);
for (String key : paramSignMap.keySet()) {
tempStr.append(key+"="+paramSignMap.get(key)+"&");
}
System.out.println(tempStr.toString());
String returnStr = MD5.GetMD5Code(tempStr.substring(0,tempStr.length()-1)+"&key="+WxConfig.getSecret()).toUpperCase();
logger.info("preFormSign==========================================>"+returnStr);
return returnStr;
}
(坑12,此处的MD5校验,完全复制我的代码吧,之前我们自己的MD5交易,微信服务器不认!)
代码如下:
public class MD5 {
// 全局数组
private final static String[] strDigits = { "0", "1", "2", "3", "4", "5",
"6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
public MD5() {
}
// 返回形式为数字跟字符串
private static String byteToArrayString(byte bByte) {
int iRet = bByte;
// System.out.println("iRet="+iRet);
if (iRet < 0) {
iRet += 256;
}
int iD1 = iRet / 16;
int iD2 = iRet % 16;
return strDigits[iD1] + strDigits[iD2];
}
// 返回形式只为数字
private static String byteToNum(byte bByte) {
int iRet = bByte;
System.out.println("iRet1=" + iRet);
if (iRet < 0) {
iRet += 256;
}
return String.valueOf(iRet);
}
// 转换字节数组为16进制字串
private static String byteToString(byte[] bByte) {
StringBuffer sBuffer = new StringBuffer();
for (int i = 0; i < bByte.length; i++) {
sBuffer.append(byteToArrayString(bByte[i]));
}
return sBuffer.toString();
}
public static String GetMD5Code(String strObj) {
String resultString = null;
try {
resultString = new String(strObj);
MessageDigest md = MessageDigest.getInstance("MD5");
// md.digest() 该函数返回值为存放哈希值结果的byte数组
resultString = byteToString(md.digest(strObj.getBytes()));
} catch (NoSuchAlgorithmException ex) {
ex.printStackTrace();
}
return resultString;
}
public static void main(String[] args) {
System.out.println(MD5.GetMD5Code("qqqqqq"));
}
}
// 全局数组
private final static String[] strDigits = { "0", "1", "2", "3", "4", "5",
"6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
public MD5() {
}
// 返回形式为数字跟字符串
private static String byteToArrayString(byte bByte) {
int iRet = bByte;
// System.out.println("iRet="+iRet);
if (iRet < 0) {
iRet += 256;
}
int iD1 = iRet / 16;
int iD2 = iRet % 16;
return strDigits[iD1] + strDigits[iD2];
}
// 返回形式只为数字
private static String byteToNum(byte bByte) {
int iRet = bByte;
System.out.println("iRet1=" + iRet);
if (iRet < 0) {
iRet += 256;
}
return String.valueOf(iRet);
}
// 转换字节数组为16进制字串
private static String byteToString(byte[] bByte) {
StringBuffer sBuffer = new StringBuffer();
for (int i = 0; i < bByte.length; i++) {
sBuffer.append(byteToArrayString(bByte[i]));
}
return sBuffer.toString();
}
public static String GetMD5Code(String strObj) {
String resultString = null;
try {
resultString = new String(strObj);
MessageDigest md = MessageDigest.getInstance("MD5");
// md.digest() 该函数返回值为存放哈希值结果的byte数组
resultString = byteToString(md.digest(strObj.getBytes()));
} catch (NoSuchAlgorithmException ex) {
ex.printStackTrace();
}
return resultString;
}
public static void main(String[] args) {
System.out.println(MD5.GetMD5Code("qqqqqq"));
}
}
2.5.4 用一坨参数构造
成
xml
书接上文2.5.2,我们已经有了
paramSignMap,下面该干什么啦?对!我们应该把
paramSignMap转成xml,然后一脚踢给微信服务器,
抽出她裤衩里的猴皮筋做个弹弓打他们家玻璃。
粘代码:
String xmlInfo = XMLUtil.getRequestXml(paramSignMap);//生成xml,这个xml要post请求到微信的服务端
/**
* @category 生成请求xml,用于微支付
* @param parameters
* @return
*/
public static String getRequestXml(Map<String,Object> parameters){
StringBuffer sb = new StringBuffer(1024);
sb.append("<xml>");
for (String key : parameters.keySet()) {
System.out.println("key= "+ key + " and value= " + parameters.get(key));
String k = key;
String v = parameters.get(key)+"";
sb.append("<"+k+">"+"<![CDATA["+v+"]]></"+k+">");
}
sb.append("</xml>");
return sb.toString();
}
* @category 生成请求xml,用于微支付
* @param parameters
* @return
*/
public static String getRequestXml(Map<String,Object> parameters){
StringBuffer sb = new StringBuffer(1024);
sb.append("<xml>");
for (String key : parameters.keySet()) {
System.out.println("key= "+ key + " and value= " + parameters.get(key));
String k = key;
String v = parameters.get(key)+"";
sb.append("<"+k+">"+"<![CDATA["+v+"]]></"+k+">");
}
sb.append("</xml>");
return sb.toString();
}
(坑13:我们当时测试的时候,有的加了CDATA,有的没加,各种问题,后来都加上CDATA就好了。不管了,这么用吧。什么?你不知道什么是CDATA?恩,做得好!)
2.5.5 提交xml给微信服务器,拿prepay_id
准备工作都做好了,下面可以砸他们家玻璃了,下面的写法也是我从网上找的,至少他是好用的对吧?
(坑14:这里的提交方式你也可以自己写,不过切记编码集是UTF-8,且是POST提交)
提交成功后,需要拿到返回的prepay_id,他给你返回的也是xml,需要解析。
另外此处你可以跟你的业务挂钩了,进行你的表操作。
粘代码:
//提交XML给微信服务器,微信服务器返回PREID
URL url = new URL("https://api. mch.weixin.q q.com/pay/un ifiedorder");
URLConnection con = url.openConnection();
con.setDoOutput(true); // POST方式
out = new OutputStreamWriter(con.getOutputStream(), "UTF-8");
out.write(xmlInfo);
if(out!=null){
out.flush();
out.close();
}
StringBuffer returnStr = new StringBuffer(1024);
String sCurrentLine = "";
InputStream l_urlStream = con.getInputStream();
BufferedReader l_reader = new BufferedReader(new InputStreamReader(l_urlStream));
while ((sCurrentLine = l_reader.readLine()) != null) {
returnStr.append(sCurrentLine);
}
// System.out.println("xmlInfo=" + xmlInfo);
// System.out.println("-------------预订单返回--------------"+returnStr.toString());
Map<String, String> returnMap = XMLUtil.doXMLParse(returnStr.toString());//解析微信返回的信息,以Map形式存储便于取值
// System.out.println("最后的结果111----->"+returnMap.get("return_code"));
// System.out.println("最后的结果222----->"+returnMap.get("return_msg"));
if("SUCCESS".equals(returnMap.get("return_code"))){
URL url = new URL("https://api.
URLConnection con = url.openConnection();
con.setDoOutput(true); // POST方式
out = new OutputStreamWriter(con.getOutputStream(), "UTF-8");
out.write(xmlInfo);
if(out!=null){
out.flush();
out.close();
}
StringBuffer returnStr = new StringBuffer(1024);
String sCurrentLine = "";
InputStream l_urlStream = con.getInputStream();
BufferedReader l_reader = new BufferedReader(new InputStreamReader(l_urlStream));
while ((sCurrentLine = l_reader.readLine()) != null) {
returnStr.append(sCurrentLine);
}
// System.out.println("xmlInfo=" + xmlInfo);
// System.out.println("-------------预订单返回--------------"+returnStr.toString());
Map<String, String> returnMap = XMLUtil.doXMLParse(returnStr.toString());//解析微信返回的信息,以Map形式存储便于取值
// System.out.println("最后的结果111----->"+returnMap.get("return_code"));
// System.out.println("最后的结果222----->"+returnMap.get("return_msg"));
if("SUCCESS".equals(returnMap.get("return_code"))){
其中
doXMLParse的代码:
/**
* 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
* @param strxml
* @return
* @throws JDOMException
* @throws IOException
*/
public static Map doXMLParse(String strxml) throws JDOMException, IOException {
strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
if(null == strxml || "".equals(strxml)) {
return null;
}
Map m = new HashMap();
InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(in);
Element root = doc.getRootElement();
List list = root.getChildren();
Iterator it = list.iterator();
while(it.hasNext()) {
Element e = (Element) it.next();
String k = e.getName();
String v = "";
List children = e.getChildren();
if(children.isEmpty()) {
v = e.getTextNormalize();
} else {
v = XMLUtil.getChildrenText(children);
}
m.put(k, v);
}
//关闭流
in.close();
return m;
}
* 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
* @param strxml
* @return
* @throws JDOMException
* @throws IOException
*/
public static Map doXMLParse(String strxml) throws JDOMException, IOException {
strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
if(null == strxml || "".equals(strxml)) {
return null;
}
Map m = new HashMap();
InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(in);
Element root = doc.getRootElement();
List list = root.getChildren();
Iterator it = list.iterator();
while(it.hasNext()) {
Element e = (Element) it.next();
String k = e.getName();
String v = "";
List children = e.getChildren();
if(children.isEmpty()) {
v = e.getTextNormalize();
} else {
v = XMLUtil.getChildrenText(children);
}
m.put(k, v);
}
//关闭流
in.close();
return m;
}
(坑15:关于xml解析等等部分最好是按照我的代码写,因为不保证别的代码是否有问题。)
需要判断返回值:
if("SUCCESS".equals(returnMap.get("return_code"))){
//自己的业务
return 返回给前台 prepay_id以及你自己的业务id ;
//自己的业务
return 返回给前台 prepay_id以及你自己的业务id ;
}else{
return 错误信息;
}
return 错误信息;
}
艾玛,这里你只要能拿到prepay_id,恭喜,一个里程碑似的胜利,然后我把这个prepay_id返回到前台(我们依然是在ready函数里,还记得吗?)。
2.6 快要胜利了,支付订单提交
我知道你现在的感受,我很同情你,但是,坚持,坚持。。。
我
知道你现在也很痛苦,但是你想想我们淌水的时候那种艰辛,你换位思考一下,就释然了。
书接上文2.5.5,我们已经拿到了prepay_id了对吧,好,我们要用这个prepay_id来干嘛?
对!我们要来拿prepay_id 来做订单提交的签名呀。
我从前台给传过来了
timestamp,
prepay_id,
nonceStr,其中
timestamp和
nonceStr是从之前的
signatureMap里get出来的,我真的懒得再生成了,代码如下:
/**
* @category 真实表单签名生成
* @param request
* @param response
* @return String
*/
@ResponseBody
@RequestMapping("getRealFormSignature")
public JsonMessage getRealFormSignature(HttpServletRequest request, String prepay_id,String timestamp,String nonceStr) throws Exception{
Map<String,Object> readFormParamMap = new HashMap();
readFormParamMap.put("appId",WxConfig.getAppID());
readFormParamMap.put("timeStamp",timestamp);
readFormParamMap.put("nonceStr",nonceStr);
readFormParamMap.put("package","prepay_id="+prepay_id);
readFormParamMap.put("signType","MD5");
String realFormSign = WxpaySign.getSign(readFormParamMap);
return 把签名返回给前台
}
* @category 真实表单签名生成
* @param request
* @param response
* @return String
*/
@ResponseBody
@RequestMapping("getRealFormSignature")
public JsonMessage getRealFormSignature(HttpServletRequest request, String prepay_id,String timestamp,String nonceStr) throws Exception{
Map<String,Object> readFormParamMap = new HashMap();
readFormParamMap.put("appId",WxConfig.getAppID());
readFormParamMap.put("timeStamp",timestamp);
readFormParamMap.put("nonceStr",nonceStr);
readFormParamMap.put("package","prepay_id="+prepay_id);
readFormParamMap.put("signType","MD5");
String realFormSign = WxpaySign.getSign(readFormParamMap);
return 把签名返回给前台
}
WxpaySign.getSign代码:
//官方的代码
public static String getSign(Map<String,Object> map){
ArrayList<String> list = new ArrayList<String>();
for(Map.Entry<String,Object> entry:map.entrySet()){
if(entry.getValue()!=""){
list.add(entry.getKey() + "=" + entry.getValue() + "&");
}
}
int size = list.size();
String [] arrayToSort = list.toArray(new String[size]);
Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
StringBuilder sb = new StringBuilder();
for(int i = 0; i < size; i ++) {
sb.append(arrayToSort[i]);
}
String result = sb.toString();
result += "key=" + WxConfig.getKey();
//Util.log("Sign Before MD5:" + result);
result = MD5Util.MD5Encode(result).toUpperCase();
//Util.log("Sign Result:" + result);
return result;
}
public static String getSign(Map<String,Object> map){
ArrayList<String> list = new ArrayList<String>();
for(Map.Entry<String,Object> entry:map.entrySet()){
if(entry.getValue()!=""){
list.add(entry.getKey() + "=" + entry.getValue() + "&");
}
}
int size = list.size();
String [] arrayToSort = list.toArray(new String[size]);
Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
StringBuilder sb = new StringBuilder();
for(int i = 0; i < size; i ++) {
sb.append(arrayToSort[i]);
}
String result = sb.toString();
result += "key=" + WxConfig.getKey();
//Util.log("Sign Before MD5:" + result);
result = MD5Util.MD5Encode(result).toUpperCase();
//Util.log("Sign Result:" + result);
return result;
}
(坑15:这个getSign是官方的通用签名写法,必须这么写,你自己写的一定会出错。)
拿到支付订单签名了吧,下面开始真正的提交支付订单:
需要几个参数:
写法如下:
timestamp,
nonceStr,
package,
signType。就照着我的写法就可以。
//提交支付订单
wx.chooseWXPay({
timestamp: '<%=signatureMap.get("timestamp")%>',
nonceStr: '<%=signatureMap.get("nonceStr")%>',
package: 'prepay_id='+prepay_id,
signType: 'MD5',//SHA1
paySign: realFormSignature,
success:function(res1) {
//alert(res1);
//修改交易状态,这是我自己的逻辑,你换成你的。就是需要更新你的逻辑,告诉自己的数据库,交易成功了。
$.ajax({
type:"POST", //post提交方式默认是get
dataType:'json',
url:'${ctx}/wxPaySaveSuccess',
data : {
key_id : payLogUUID, //这是我自己的业务ID
trade_status :'TRADE_SUCCESS'//我自己的业务参数
},
error:function(data) {// 设置表单提交出错
alert('系统出现异常,请联系管理员');
},
success:function(result) {//提交成功
if(result.success){
alert("哇!成功啦,快去领取您的号码牌吧~");
window.location.href="${ctx}/sign/phoneSignSuccess/<%=state%>"; //跳转到成功页面
}else{
alert(result.msg);
alert("更新交易记录失败!");
}
}
});
},fail:function(res) {
//alert(res);
alert("本次支付失败,请联系客服人员,谢谢您的支持与理解。");
}
});
wx.chooseWXPay({
timestamp: '<%=signatureMap.get("timestamp")%>',
nonceStr: '<%=signatureMap.get("nonceStr")%>',
package: 'prepay_id='+prepay_id,
signType: 'MD5',//SHA1
paySign: realFormSignature,
success:function(res1) {
//alert(res1);
//修改交易状态,这是我自己的逻辑,你换成你的。就是需要更新你的逻辑,告诉自己的数据库,交易成功了。
$.ajax({
type:"POST", //post提交方式默认是get
dataType:'json',
url:'${ctx}/wxPaySaveSuccess',
data : {
key_id : payLogUUID, //这是我自己的业务ID
trade_status :'TRADE_SUCCESS'//我自己的业务参数
},
error:function(data) {// 设置表单提交出错
alert('系统出现异常,请联系管理员');
},
success:function(result) {//提交成功
if(result.success){
alert("哇!成功啦,快去领取您的号码牌吧~");
window.location.href="${ctx}/sign/phoneSignSuccess/<%=state%>"; //跳转到成功页面
}else{
alert(result.msg);
alert("更新交易记录失败!");
}
}
});
},fail:function(res) {
//alert(res);
alert("本次支付失败,请联系客服人员,谢谢您的支持与理解。");
}
});
2.7 支付成功后的闭环回调
微支付会有一个异步的通知给你,告诉你支付成功了,你还需要给他返一个成功标识,不然他还会不听的报警。
这个异步通知的url是在
2.5.2配置的notify_url。
(坑16:返回给微支付的必须是一个xml)
写法如下,我的是springmvc的controller,不过都大同小异了:
/**
* @category 监听通知,微信服务器调用闭环
* @param request
* @param response
* @return String
*/
@RequestMapping("wxnotify")
public void wxnotify(HttpServletRequest request, HttpServletResponse response) throws Exception{
System.out.println("微支付回调.1111111111111111111111111111111...");
InputStream inputStream = null;
try {
// 解析结果存储在HashMap
Map<String, String> map = new HashMap<String, String>();
inputStream = request.getInputStream();
// 读取输入流
SAXReader reader = new SAXReader();
Document document = reader.read(inputStream);
// 得到xml根元素
Element root = document.getRootElement();
// 得到根元素的所有子节点
List<Element> elementList = root.elements();
// 遍历所有子节点
for (Element e : elementList){
map.put(e.getName(), e.getText());
}
//遍历MAP
for (String key : map.keySet()) {
System.out.println("key= "+ key + " and value= " + map.get(key));
}
if(map!=null&&"SUCCESS".equals(map.get("return_code"))){
Map logMap = tPayLogService.findOneByUUID(map.get("out_trade_no"));
if(logMap!=null&&"".equals(logMap.get("trade_status"))){//如果已经状态是成功则无视,否则UPDATE一下状态
Map paramMap = new HashMap();
logMap.put("trade_status", "TRADE_SUCCESS");
Map signMap = signService.findOneByKeyId(logMap.get("sign_id"));
Map userMap = tClientService.findOneByKeyId(signMap.get("client_id"));
Map activityMap = tActivityService.findOneByKeyId(signMap.get("activity_id"));
paramMap.put("social_type", userMap.get("social_type"));
paramMap.put("price_model", signMap.get("price_model"));
//支付成功
signMap.put("s_status", 2);//状态(0:免费;1:未付费;2:已付费)
signMap.put("sign_num", signService.findMaxSignNum(paramMap, activityMap));
signService.saveLogAndSignTransaction(logMap,signMap);
Map<String,Object> paramSignMap = new LinkedHashMap();//按顺序来的
paramSignMap.put("return_code","SUCCESS");
// paramSignMap.put("return_msg",);
String xmlInfo = XMLUtil.getRequestXml(paramSignMap);//生成xml,这个xml要post请求到微信的服务端
PrintWriter out = null;
try {
// 转码
// response.setContentType("text/html;charset=UTF-8");
out = response.getWriter();
out.write(xmlInfo);
out.flush();
} catch (final IOException e) {
e.printStackTrace();
} finally {
if (out != null) {
out.close();
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}finally{
if(inputStream!=null){
// 释放资源
inputStream.close();
inputStream = null;
}
}
}
* @category 监听通知,微信服务器调用闭环
* @param request
* @param response
* @return String
*/
@RequestMapping("wxnotify")
public void wxnotify(HttpServletRequest request, HttpServletResponse response) throws Exception{
System.out.println("微支付回调.1111111111111111111111111111111...");
InputStream inputStream = null;
try {
// 解析结果存储在HashMap
Map<String, String> map = new HashMap<String, String>();
inputStream = request.getInputStream();
// 读取输入流
SAXReader reader = new SAXReader();
Document document = reader.read(inputStream);
// 得到xml根元素
Element root = document.getRootElement();
// 得到根元素的所有子节点
List<Element> elementList = root.elements();
// 遍历所有子节点
for (Element e : elementList){
map.put(e.getName(), e.getText());
}
//遍历MAP
for (String key : map.keySet()) {
System.out.println("key= "+ key + " and value= " + map.get(key));
}
if(map!=null&&"SUCCESS".equals(map.get("return_code"))){
Map logMap = tPayLogService.findOneByUUID(map.get("out_trade_no"));
if(logMap!=null&&"".equals(logMap.get("trade_status"))){//如果已经状态是成功则无视,否则UPDATE一下状态
Map paramMap = new HashMap();
logMap.put("trade_status", "TRADE_SUCCESS");
Map signMap = signService.findOneByKeyId(logMap.get("sign_id"));
Map userMap = tClientService.findOneByKeyId(signMap.get("client_id"));
Map activityMap = tActivityService.findOneByKeyId(signMap.get("activity_id"));
paramMap.put("social_type", userMap.get("social_type"));
paramMap.put("price_model", signMap.get("price_model"));
//支付成功
signMap.put("s_status", 2);//状态(0:免费;1:未付费;2:已付费)
signMap.put("sign_num", signService.findMaxSignNum(paramMap, activityMap));
signService.saveLogAndSignTransaction(logMap,signMap);
Map<String,Object> paramSignMap = new LinkedHashMap();//按顺序来的
paramSignMap.put("return_code","SUCCESS");
// paramSignMap.put("return_msg",);
String xmlInfo = XMLUtil.getRequestXml(paramSignMap);//生成xml,这个xml要post请求到微信的服务端
PrintWriter out = null;
try {
// 转码
// response.setContentType("text/html;charset=UTF-8");
out = response.getWriter();
out.write(xmlInfo);
out.flush();
} catch (final IOException e) {
e.printStackTrace();
} finally {
if (out != null) {
out.close();
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}finally{
if(inputStream!=null){
// 释放资源
inputStream.close();
inputStream = null;
}
}
}
最后,我只能以一句“GOOD LUCK”来做结束。
有技术交流请加我的QQ。
下面附上我全部的页面代码:
wxtest.jsp:
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%@ include file="/jsp/common/taglib.jsp"%>
<%@ page import="com.mingyisoft.bean.wxpay.WxpayUtil"%>
<%@ page import="com.mingyisoft.bean.wxpay.WxConfig"%>
<%@ page import="com.mingyisoft.bean.wxpay.WxpaySign"%>
<%@ page import="java.util.Map"%>
<html>
<head>
<meta charset="utf-8">
<script src=" http://res.w x.qq.com/ope n/js/jweixin -1.0.0.js"></script>
<script type="text/javascript" src="${ctx}/js/jquery/jquery-1.9.1.min.js"></script>
<script>
<%
String code = request.getParameter("code");//页面获取的code码
String state = request.getParameter("state");//报名ID
//第一个签名,用于第一个JS配置
Map<String,Object> signatureMap = WxpaySign.configSign(code,state);
%>
wx.config({
debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: '<%=WxConfig.getAppID()%>', // 必填,公众号的唯一标识
timestamp: '<%=signatureMap.get("timestamp")%>', // 必填,生成签名的时间戳
nonceStr: '<%=signatureMap.get("nonceStr")%>', // 必填,生成签名的随机串
signature: '<%=signatureMap.get("signature")%>',// 必填,签名,见附录1
jsApiList: ['chooseWXPay'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
});
wx.error(function(res){
for(i in res){
alert(i+":"+res[i]);
}
// config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
alert("微支付调用失败,请截图给管理人员,谢谢");
window.close();
});
// config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
wx.ready(function(){
//调用统一下单接口
$.ajax({
type:"POST", //post提交方式默认是get
dataType:'json',
url:basePath+'/wxpostpreform',
data : {
openId :'<%=WxpayUtil.getOpenid(code)%>',
state : '<%=state%>'
},error:function(data) {// 设置表单提交出错
alert('系统出现异常,请联系管理员');
},success:function(result) {//提交成功
if(result.success){
var prepay_id = result.msg;//这个就是预订单ID
var payLogUUID = result.data;//自己的业务逻辑ID
//alert("预订单编号===>"+prepay_id);
$.ajax({
type:"POST", //post提交方式默认是get
dataType:'json',
url:basePath+'/getRealFormSignature',
data : {
prepay_id :prepay_id,
timestamp :'<%=signatureMap.get("timestamp")%>',
nonceStr :'<%=signatureMap.get("nonceStr")%>'
},error:function(data) {// 设置表单提交出错
alert('系统出现异常,请联系管理员');
},success:function(result3) {//提交成功
if(result3.success){
var realFormSignature = result3.msg;
//alert("最后订单的签名===>"+result3.msg);
//提交支付订单
wx.chooseWXPay({
timestamp: '<%=signatureMap.get("timestamp")%>',
nonceStr: '<%=signatureMap.get("nonceStr")%>',
package: 'prepay_id='+prepay_id,
signType: 'MD5',//SHA1
paySign: realFormSignature,
success:function(res1) {
//alert(res1);
//修改交易状态
$.ajax({
type:"POST", //post提交方式默认是get
dataType:'json',
url:'${ctx}/wxPaySaveSuccess',
data : {
key_id : payLogUUID,
trade_status :'TRADE_SUCCESS'//参数
},
error:function(data) {// 设置表单提交出错
alert('系统出现异常,请联系管理员');
},
success:function(result) {//提交成功
if(result.success){
alert("哇!成功啦,快去领取您的号码牌吧~");
window.location.href="${ctx}/sign/phoneSignSuccess/<%=state%>";
}else{
alert(result.msg);
alert("更新交易记录失败!");
}
}
});
},fail:function(res) {
//alert(res);
alert("本次支付失败,请联系客服人员,谢谢您的支持与理解。");
}
});
}else{
alert(result3.msg);
}
}
});
}else{
alert(result.msg);
}
}
});
});
</script>
</head>
<body>
<h1>系统正在提交订单,请勿跳转页面。</h1>
</body>
</html>
<%@ include file="/jsp/common/taglib.jsp"%>
<%@ page import="com.mingyisoft.bean.wxpay.WxpayUtil"%>
<%@ page import="com.mingyisoft.bean.wxpay.WxConfig"%>
<%@ page import="com.mingyisoft.bean.wxpay.WxpaySign"%>
<%@ page import="java.util.Map"%>
<html>
<head>
<meta charset="utf-8">
<script src=" http://res.w
<script type="text/javascript" src="${ctx}/js/jquery/jquery-1.9.1.min.js"></script>
<script>
<%
String code = request.getParameter("code");//页面获取的code码
String state = request.getParameter("state");//报名ID
//第一个签名,用于第一个JS配置
Map<String,Object> signatureMap = WxpaySign.configSign(code,state);
%>
wx.config({
debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: '<%=WxConfig.getAppID()%>', // 必填,公众号的唯一标识
timestamp: '<%=signatureMap.get("timestamp")%>', // 必填,生成签名的时间戳
nonceStr: '<%=signatureMap.get("nonceStr")%>', // 必填,生成签名的随机串
signature: '<%=signatureMap.get("signature")%>',// 必填,签名,见附录1
jsApiList: ['chooseWXPay'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
});
wx.error(function(res){
for(i in res){
alert(i+":"+res[i]);
}
// config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
alert("微支付调用失败,请截图给管理人员,谢谢");
window.close();
});
// config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
wx.ready(function(){
//调用统一下单接口
$.ajax({
type:"POST", //post提交方式默认是get
dataType:'json',
url:basePath+'/wxpostpreform',
data : {
openId :'<%=WxpayUtil.getOpenid(code)%>',
state : '<%=state%>'
},error:function(data) {// 设置表单提交出错
alert('系统出现异常,请联系管理员');
},success:function(result) {//提交成功
if(result.success){
var prepay_id = result.msg;//这个就是预订单ID
var payLogUUID = result.data;//自己的业务逻辑ID
//alert("预订单编号===>"+prepay_id);
$.ajax({
type:"POST", //post提交方式默认是get
dataType:'json',
url:basePath+'/getRealFormSignature',
data : {
prepay_id :prepay_id,
timestamp :'<%=signatureMap.get("timestamp")%>',
nonceStr :'<%=signatureMap.get("nonceStr")%>'
},error:function(data) {// 设置表单提交出错
alert('系统出现异常,请联系管理员');
},success:function(result3) {//提交成功
if(result3.success){
var realFormSignature = result3.msg;
//alert("最后订单的签名===>"+result3.msg);
//提交支付订单
wx.chooseWXPay({
timestamp: '<%=signatureMap.get("timestamp")%>',
nonceStr: '<%=signatureMap.get("nonceStr")%>',
package: 'prepay_id='+prepay_id,
signType: 'MD5',//SHA1
paySign: realFormSignature,
success:function(res1) {
//alert(res1);
//修改交易状态
$.ajax({
type:"POST", //post提交方式默认是get
dataType:'json',
url:'${ctx}/wxPaySaveSuccess',
data : {
key_id : payLogUUID,
trade_status :'TRADE_SUCCESS'//参数
},
error:function(data) {// 设置表单提交出错
alert('系统出现异常,请联系管理员');
},
success:function(result) {//提交成功
if(result.success){
alert("哇!成功啦,快去领取您的号码牌吧~");
window.location.href="${ctx}/sign/phoneSignSuccess/<%=state%>";
}else{
alert(result.msg);
alert("更新交易记录失败!");
}
}
});
},fail:function(res) {
//alert(res);
alert("本次支付失败,请联系客服人员,谢谢您的支持与理解。");
}
});
}else{
alert(result3.msg);
}
}
});
}else{
alert(result.msg);
}
}
});
});
</script>
</head>
<body>
<h1>系统正在提交订单,请勿跳转页面。</h1>
</body>
</html>