前段时间做了微信支付,遇到了很多问题,现在总结一下:
1.前端页面js:
1 var totalprice = document.getElementById("total").innerHTML; //支付总金额
2 var appId = null;
3 var nonceStr =null;
4 var package1 = "prepay_id="; //流水号
5 var signType = "MD5";
6 var timeStamp = null;
7 var paySign =null;
8
9 function is_weixn(){
10 var ua = navigator.userAgent.toLowerCase();
11 if(ua.match(/MicroMessenger/i)=="micromessenger") {
12 return true;
13 } else {
14 location.href = "javascript:history.go(-1);";
15 }
16 }
17 is_weixn();
18
19 //获取时间戳
20 function getTimeStamp(){
21 var timestamp=new Date().getTime();
22 var timestampstring = timestamp.toString();//一定要转换字符串
23 oldTimeStamp = timestampstring;
24 return timestampstring;
25 };
26 //随机字符串
27 function getNonceStr(){
28 var $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
29 var maxPos = $chars.length;
30 var noceStr = "";
31 for (var i = 0; i < 32; i++) {
32 noceStr += $chars.charAt(Math.floor(Math.random() * maxPos));
33 }
34 oldNonceStr = noceStr;
35 return noceStr;
36 };
37
38 function md5(string){
39 return hex_md5(string).toUpperCase();
40 };
41
42 function onBridgeReady(){
43 // alert("appId="+appId+"--timeStamp="+timeStamp+"--nonceStr="+nonceStr+"--package="+package1+"--signType="+signType+"--paySign="+paySign);
44 WeixinJSBridge.invoke(
45 'getBrandWCPayRequest', {
46 "appId" : appId, //公众号名称,由商户传入
47 "timeStamp": timeStamp, //时间戳,自1970年以来的秒数
48 "nonceStr" : nonceStr, //随机串
49 "package" : package1,
50 "signType" : signType, //微信签名方式:
51 "paySign" : paySign //微信签名
52 },
53 function(res){
54 // alert(res.err_msg)
55 if(res.err_msg == "get_brand_wcpay_request:ok" ) {
56 // getjuan();//支付成功后,调用的后台的方法
57 location.href = "gift.html";
58 } // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。
59 }
60 );
61 };
62
63 //调用支付接口
64 function getTotal() {
65 //alert(code+total);
66 $.ajax({
67 type : "post",
68 url : "../jf/shoplist/pay",
69 dataType : "json",
70 data : {
71 money : <%=totalprice%>,
72 user_id : <%=uid%>,
73 num : <%=num%>,
74 id : <%=id%>
75 },
76 success : function(data) {
77 if(data[0]=="eFAIL"){
78 alert("支付金额有误!");
79 }else{
80 appId = data[1];
81 nonceStr =getNonceStr();
82 package1 = "prepay_id="+data[0];
83 signType = "MD5";
84 timeStamp = getTimeStamp().substring(0,10) ;
85 paySign = md5("appId="+appId+"&nonceStr="+nonceStr+"&package="+package1+"&signType=MD5"+"&timeStamp="+timeStamp+"&key=f4pjDRy09D0urJG4uySYwdd1J6Pr9hhD");
86 if (typeof WeixinJSBridge == "undefined"){
87 if( document.addEventListener ){
88 document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
89 }else if (document.attachEvent){
90 document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
91 document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
92 }
93 }else{
94 // alert("onBridgeReady--------");
95 onBridgeReady();
96 }
97 }
98 },
99 error : function(data) {
100 alert("支付失败");
101 }
102 });
103
104 }
105
2.后台:
appid:公众号id
body:商品描述
mch_id:商户编号
nonce_str:随机字符串(自己生成的)
notify_url : 成功返回url (自己配置的)
openid : 客户openid
out_trade_no: 商户内部订单号(自己生成的)
spbill_create_ip:客户ip(自己获取的:getRequest().getRemoteAddr())
total_fee:订单金额 (自己计算的)
trade_type:支付方式(JSAPI)
sign:按照文档的说明生成sign(自己生成的)
(1)随机数的生成:
public static String getNonceStr() {
Random random = new Random();
return MD5util.MD5Encode(UUID.randomUUID().toString().replace("-",""), "").toUpperCase();
}(2)sign的生成方式:签名将通过API 密匙 先按照accsii升序排序,再通过md5加密,最后转换为大写字母
public static String createSign(SortedMap<String, Object> payParams,String key){
StringBuffer sb = new StringBuffer();
Set es = payParams.entrySet();//所有参与传参的参数按照accsii排序(升序)
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);
System.out.println("XML源串+key:"+sb.toString());
String sign = MD5util.MD5Encode(sb.toString(), "").toUpperCase();
System.out.println("md5后的字符串"+sign);
return sign;
}
(3)生成xml格式时的方法:
public static String buildUnifiedOrderReq(Map<String,Object> payParams){
String payStr = "";
payStr += "<xml>";//<![CDATA[你的appid]]>
payStr += "<appid><![CDATA["+payParams.get("appid")+"]]></appid>";
payStr += "<body><![CDATA["+payParams.get("body")+"]]></body>";
payStr += "<mch_id><![CDATA["+payParams.get("mch_id")+"]]></mch_id>";
payStr += "<nonce_str><![CDATA["+payParams.get("nonce_str")+"]]></nonce_str>";
payStr += "<notify_url><![CDATA["+payParams.get("notify_url")+"]]></notify_url>";
payStr += "<openid><![CDATA["+payParams.get("openid")+"]]></openid>";
payStr += "<out_trade_no><![CDATA["+payParams.get("out_trade_no")+"]]></out_trade_no>";
payStr += "<spbill_create_ip><![CDATA["+payParams.get("spbill_create_ip")+"]]></spbill_create_ip>";
payStr += "<total_fee><![CDATA["+payParams.get("total_fee")+"]]></total_fee>";
payStr += "<trade_type><![CDATA[JSAPI]]></trade_type>";
payStr += "<sign><![CDATA["+payParams.get("sign")+"]]></sign>";
payStr +="</xml>";
System.out.println("最终提交的xml:"+payStr);
return payStr;
}
(4)成功返回url(支付流水---最重要)
//处理微信支付返回逻辑
public void payresponse() throws Exception{
System.out.println("支付通知url");
// 解析结果存储在HashMap
Map<String, String> map = new HashMap<String, String>();
InputStream inputStream = getRequest().getInputStream();
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
boolean firstLine = true;
String line = null; ;
while((line = bufferedReader.readLine()) != null){
if(!firstLine){
stringBuilder.append(System.getProperty("line.separator"));
}else{
firstLine = false;
}
stringBuilder.append(line);
}
HashMap<String,String> zhifutongzhi = new HashMap<String, String>();
zhifutongzhi = (HashMap<String, String>) payutil.parseXml(stringBuilder.toString());
System.out.println("返回支付通知:【业务结果】"+zhifutongzhi.get("result_code"));
System.out.println("返回支付通知:【内部订单】"+zhifutongzhi.get("out_trade_no"));
System.out.println("返回支付通知:【流水】"+zhifutongzhi.get("transaction_id"));
// 释放资源
inputStream.close();
inputStream = null;
getResponse().setContentType("text/xml; charset=UTF-8");
String resXml= null;
if("SUCCESS".equals(zhifutongzhi.get("result_code"))){
System.out.println("进入支付-------------------------------------------"+new Date());
//支付成功
double money=(Double.valueOf(zhifutongzhi.get("total_fee"))/100);
//int money=(int) (Double.valueOf(zhifutongzhi.get("total_fee"))/100);
String sql="select user_id,num,gift_id from cbb_orders where order_code =?";
Record re= Db.findFirst(sql,zhifutongzhi.get("out_trade_no"));
int user_id = re.getInt("user_id");
int num = re.getInt("num");
int id = re.getInt("gift_id"); //商品id
//修改订单状态
String bigorder="update cbb_orders set total = ?,state=1,payflowcode=?,update_time=now() where order_code=?";
int flag = Db.update(bigorder, money,zhifutongzhi.get("transaction_id"),zhifutongzhi.get("out_trade_no"));
/* if(flag<=0){
//try {
// DbKit.getConfig().getConnection().rollback();
String sql1="INSERT INTO order_pay_log VALUES (NULL,?,?,?,?,NOW(),0)";
Db.update(sql1,re.getInt("user_id"),money,zhifutongzhi.get("transaction_id"),zhifutongzhi.get("out_trade_no"));
//} catch (SQLException e) {
// e.printStackTrace();
// renderJson("修改order_state错误");
// return ;
//}
}*/
//生成支付流水
String payflow = "insert into payflow (ordertype,paytype,payflowcode,paymoney,user_id,pay_time,create_time)values(1,2,?,?,?,now(),now())";
int payfplwfalg = Db.use("mysql").update(payflow, zhifutongzhi.get("transaction_id"), money, re.getInt("user_id"));
if (payfplwfalg == 0) {
try {
DbKit.getConfig().getConnection().rollback();
} catch (SQLException e) {
e.printStackTrace();
renderJson("生成支付流水失败");
return ;
}
}
resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
+ "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
}else{
resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
+ "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
}
System.out.println("微信支付回调数据结束");
BufferedOutputStream out = new BufferedOutputStream(getResponse().getOutputStream());
out.write(resXml.getBytes());
out.flush();
out.close();
System.out.println("支付处理结束!");
renderJson(zhifutongzhi.get("result_code"));
}
(5)返回消息的方法:
public static Map<String, String> parseXml(String xml) throws Exception {
Map<String, String> map = new HashMap<String, String>();
Document document = DocumentHelper.parseText(xml);
Element root = document.getRootElement();
List<Element> elementList = root.elements();
for (Element e : elementList)
map.put(e.getName(), e.getText());
return map;
}
(6)传值调用支付的方法,并返回消息:
public static HashMap<String, String> tyjk(String body,int money,String openid,String out_trade_no,String spbill_create_ip){
System.out.println("统一接口money:"+money);
System.out.println("key:"+Config.KEY1);
SortedMap<String,Object> payParams = new TreeMap<String,Object>();
String payStr = "";
payParams.put("appid", Config.APPID1);//公众号id
payParams.put("body", body);//商品描述
payParams.put("mch_id", Config.MCH_ID2);//商户编号
payParams.put("nonce_str", payutil.getNonceStr());//随机字符串
payParams.put("notify_url", Config.NOTIFY_URl3);//成功返回url
payParams.put("openid", openid);//客户openid
payParams.put("out_trade_no", out_trade_no);//商户内部订单号
payParams.put("spbill_create_ip", spbill_create_ip);//客户ip
payParams.put("total_fee", money);//订单金额
payParams.put("trade_type", "JSAPI");//支付方式
String sign = payutil.createSign(payParams, Config.KEY1);//按照文档的说明生成sign
payParams.put("sign", sign);
payStr=payutil.buildUnifiedOrderReq(payParams);//生成请求xml字符串
HttpUtil hutil = new HttpUtil();//调用统一接口
HashMap<String,String> tongjifh = new HashMap<String, String>();
String rs = hutil.sendHttpsPOST("https://api.mch.weixin.qq.com/pay/unifiedorder", payStr);//发送http请求
try {
tongjifh = (HashMap<String, String>) payutil.parseXml(rs);
return tongjifh;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return tongjifh;
}
(7)在controller中传参数
public void pay() {
// 4个参数依次 :订单描述, 金额[单位为 元],用户openid,内部订单号,客户ip地址
// 返回结果:如果统一接口成功 则返回 预支付id 否则返回"FAIL"
HashMap<String, String> tongjifh = new HashMap<String, String>();
System.out.println("code+++++++++++++++++++++++++++++++" + code);
QianJiJuan", (int) (money * 100),
openid, code, getRequest().getRemoteAddr());
System.out.println("fanhui=" + tongjifh);
if ("FAIL".equalsIgnoreCase(tongjifh.get("return_code"))) {
obj[0] = "FAIL";
} else {
System.out.println("prepay_id----APPID1"
+ tongjifh.get("prepay_id"));
obj[0] = tongjifh.get("prepay_id");
obj[1] = Config.APPID1;
}renderJson(obj);
}
(8)商户平台的参数配置:
最后说一下,我遇到的坑吧:
(1)fanhui={return_msg=受理机构必须传入sub_mch_id,return_code_FAIL}
商户id写错了,微信公众号支付,只需要普通的商户id就可以了,但是我传入的是服务商的商户id;
(2)fanhui={return_msg=xml格式错误,return_code_FAIL}
此时应仔细核对xml中传入的参数,商户id写错;
(3)fanhui={return_msg=xml格式错误,return_code_FAIL}
xml里面的body是中文,此时报错,改成英文或字母就好;