最近开发app支付,支付宝按照开发文档很快搞定,本想微信支付开发也一样的容易,结果我错了,一路踩坑不断,到最后终于完成,耗了不少时间和精力,所以想写一篇关于微信统一支付的开发过程,希望大家能少走弯路

本文章适用于微信公众号支付开发,用的方式是统一支付接口,请对号入座,因为有好几种支付接口

只能是在手机App内的微信公众号内微信浏览器使用;PC电脑、手机浏览器(但不在微信公众号内的浏览器)是无法适用

另外说下

H5的支付方式(适用于任意浏览器),无法申请,不够资格;

Wap的支付方式(适用于任意浏览器),无法申请,不够资格;

:(


准备工作

一、微信公众号这部分的配置

申请公众号   https://mp.weixin.qq.com

  1. 申请好后,做下图配置,看1,2点

申请支付商户

  1. 申请好后,得到下图,看1点,记下商户号

点击上图中的“开发配置”,做以下配置,如图

授权目录url的注意地方

比如:您准备要写的支付调用页面是 http://xxx.xxx.xxx/abc/pay/weixin_pay.jsp 这个

则 授权目录就得写成

http://xxx.xxx.xxx/abc/pay/

至此,微信公众号就配置好了

二、微信支付的配置

登陆微信支付平台

https://pay.weixin.qq.com

在您申请成功微信支付审核通过后,会有一封邮件发给你,里边有帐号和密码什么的。

登陆进去后,点击顶部的“账户中心”,再点左边菜单的“api安全”,按下图配置

这样,微信支付这部分也大概设置好了。

三、到这里,我们总共记下了这些信息,如下图

四、以上准备好后,就可以准备写代码了

顺序一般是

pay_type.jsp (A支付类型选择) => pay_order_info.jsp(B商品订单信息) => wexinpay_oauth2.jsp(C统一支付)

a)支付类型选择,就是指选择支付宝、微信、银联等支付方式;

b)A页面提交后(选择微信支付),到B页面显示商品订单的详情,包括订单编号(order_no)、数量及金额等,这里开始就得写微信支付相关代码了,就从这里说起;

B页面,提交后,就到C统一支付页面。

以下是详细说明

B页面的订单信息,有表单提交按钮

<button οnclick="onpay();">确认提交订单</button>

这里要先取得用户信息,用 oauth2授权

注意:url的位置在前面的授权目录内

appid=你的微信公众号appid

订单提交代码如下

<script>
function onpay(){
var return_url = encodeURIComponent('http://xxx.xxx.xxx/abc/pay/wexinpay_oauth2.jsp');
var oauth2_url = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=微信公众号appid&redirect_uri='+return_url+'&response_type=code&scope=snsapi_base&state='+state+'#wechat_redirect';document.getElementById("jtform").target = '_blank';document.getElementById("jtform").action = oauth2_url;}</script>

这样,在点击“确认提交订单”按钮后,表单的订单内容信息,就会post到(统一支付页面)

http://xxx.xxx.xxx/abc/pay/wexinpay_oauth2.jsp

重要

下面,我们再看这个C统一支付页面的写法

在徽信公众号浏览器下,可以直接带回code参数,而不需要微信用户点击授权确认;

wexinpay_oauth2.jsp 内关键代码如下
<html>
<head><meta charset="utf-8" /><script src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script><script src="jquery/jquery.min.js"></script>  <!--引入你的jquery-->
<script>//执行支付function executePay(){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();  }  }//执行支付 function onBridgeReady(){   var result = document.getElementById("payString").value; var payJson = eval('('+result+')'); //就是统一支付最终返回的json字符串     if(result!=''&&payJson['paySign']!=undefined&&payJson['paySign']!=null&&payJson['paySign']!=''){ WeixinJSBridge.invoke(    'getBrandWCPayRequest',{"appId":payJson['appId'], "timeStamp":payJson['timeStamp'],//时间戳,自1970年以来的秒数"nonceStr":payJson['nonceStr'], //随机串"package":payJson['package'],"signType":"MD5",  //微信签名方式:"paySign":payJson['paySign'] //微信签名},function(res){if(res.err_msg == "get_brand_wcpay_request:ok" ) {alert('支付成功!');
/*在这里其实是不保险的,有可能在支付时,用户提前关闭或断线什么的,不能保证真正的支付结果,
应该在统一支付的回调url中处理,支付后的状态与订单状态同步,
请看第五点 ,统一支付的回调处理//回调的url本例设置为
String notify_url = "http://xxx.xxx.xxx/abc/pay/wexin_order_pay.jsp"; //通知回调地址,不能有参数*/var curr_url = '商户展示界面'; window.location.href = curr_url; }else{   alert('支付失败!');var curr_url = '商户失败界面';window.location.href = curr_url;}    /* 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回    ok,但并不保证它绝对可靠。 */});      }else{alert('支付校验未能通过!');  var curr_url = '商户失败界面'; window.location.href = curr_url;           }}
</script></head><body οnlοad="executePay();"> //页面加载完成后,调用executePay()执行支付...略<%String code = String.valueOf(request.getParameter("code")).replaceAll("null", ""); //取得传过来的code
String order_no = String.valueOf(request.getParameter("order_no")).replaceAll("null", ""); //取得传过来的订单编号String userIp = '取得用户端的访问ip'; //代码略....
WeiXinPayDao  wxPayDao = new WeiXinPayDao();   //做了一个微信支付业务类String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="+appid+"&secret="+secret+"&code="+code+"&grant_type=authorization_code";
//用公众号的appid,key,刚接收的code组成新的url,来获取微信用户的openid
String info = wxPayDao.httpRequest(url);if(info!=null&&!info.equals("")){if(info.indexOf("errcode")==-1){JSONObject joInfo = new JSONObject(info);access_token = joInfo.getString("access_token"); refresh_token = joInfo.getString("refresh_token");openid = joInfo.getString("openid");unionid = joInfo.getString("unionid");}}/*
如果以上没有什么问题的话,就可以取得当前微信用户的openid;
接下来,支付准备工作就差不多完成,终于到了调用统一支付接口这里了,统一支付接口调用就是为了取得 prepay_id 的值,有了这个值,就能发起支付
*//**
根据订单编号order_no,查询订单信息放到 Map orderMap = new HashMap();orderMap  就是存放订单信息的,代码略....*//**
接下来, 调用统一支付接口....*/ String payJson = wxPayDao.pay(openid,orderMap,userIp); //参数openid,orderMap(订单信息),userIp(微信用户端的真实ip),得到一个json字符串//把最终带 prepay_id 参数的字符串的值,放到界面的一个隐藏域内,这个值给executePay(),页面初始化时,发起支付
if(!payJson .equals("")){                payJson = payJson.replaceAll("\"", "'");
%>  <input id="payString" name="payString" value="<%=payJson%>" type="hidden">
<%
}
%>
</body>
</html>

以下是代码补充

补方法:
WeiXinPayDao

pay(openid,orderMap,userIp),是一个方法,代码如下

  /*** 统一下单接口* openid 微信用户id* orderMap 订单信息map* user_ip  微信用户ip * */
public String pay(String openid,Map orderMap,String user_ip){String result = getResult("0","pass",null);    //本方法最终返回的字符串
String order_no = String.valueOf(orderMap.get("order_no")); //订单号
String device_info = "WEB";
String nonce_str = UUID.randomUUID().toString().replaceAll("-", ""); //随机字串,32位
String body = String.valueOf(orderMap.get("order_name")).replaceAll("null", ""); //商品描述,我这里用订单名称
String total_fee = "2" ;//总金额 ,单位是分
String fee_type = "CNY"; //币种
String notify_url = "http://xxx.xxx.xxx/abc/pay/wexin_order_pay.jsp"; //通知回调地址,不能有参数
String trade_type = "JSAPI"; //交易类型 公众号=JSAPI
String limit_pay = "no_credit"; //上传此参数no_credit--可限制用户不能使用信用卡支付
//然后把以上参数放到一个map中
Map paramsMap = new HashMap();
paramsMap.put("appid", appid); //微信公众号
paramsMap.put("mch_id", mch_id); //商户号,前面就是你记下来的
paramsMap.put("device_info", device_info);
paramsMap.put("nonce_str", nonce_str);
paramsMap.put("body", body);
paramsMap.put("total_fee", total_fee);
paramsMap.put("spbill_create_ip", user_ip);
paramsMap.put("notify_url", notify_url);
paramsMap.put("trade_type", trade_type);
paramsMap.put("limit_pay", limit_pay);
paramsMap.put("attach", order_no);
paramsMap.put("fee_type", fee_type);
paramsMap.put("openid", openid);
paramsMap.put("out_trade_no",order_no);//然后用以上参数排个序,连成一个参数字符串
String[] arr = new String[]{"appid","mch_id","device_info","nonce_str","total_fee",
"attach","fee_type","spbill_create_ip","notify_url","trade_type",
"limit_pay","body","openid","out_trade_no"};
Arrays.sort(arr);
String stringA = "";
int arr_len = arr.length;
for(int i=0;i<arr_len;i++){
stringA = stringA + "&"+arr[i]+"=" + paramsMap.get(arr[i]);
}
if(!stringA.equals("")){
stringA = stringA.substring(1);
}
String stringSignTemp = stringA+"&key=支付的key,注意不是公众号的key了";
String sign = Md5Encode.md5Encoding(stringSignTemp).toUpperCase();
//做一次md5加密,结果转成大写//然后,用sign的值,再拼一次xml格式的字符串
StringBuffer payXmlBuff = new StringBuffer();
payXmlBuff.append("<xml>")
.append("<appid>").append(appid).append("</appid>")
.append("<attach>").append(order_no).append("</attach>")
.append("<body><![CDATA[").append(body).append("]]></body>")
.append("<device_info>").append(device_info).append("</device_info>")
.append("<fee_type>").append(fee_type).append("</fee_type>")
.append("<limit_pay>").append(limit_pay).append("</limit_pay>")
.append("<mch_id>").append(mch_id).append("</mch_id>")
.append("<nonce_str>").append(nonce_str).append("</nonce_str>")
.append("<notify_url>").append(notify_url).append("</notify_url>")
.append("<openid>").append(openid).append("</openid>")
.append("<out_trade_no>").append(order_no).append("</out_trade_no>")
.append("<spbill_create_ip>").append(user_ip).append("</spbill_create_ip>")
.append("<total_fee>").append(total_fee).append("</total_fee>")
.append("<trade_type>").append(trade_type).append("</trade_type>")
.append("<sign>").append(sign).append("</sign>")
.append("</xml>");/**然后,把带sign的xml拼成的字符串,发送到统一支付接口*/
String payUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder"; //统一支付接口的url
//然后,把上面拼接好的xml字符串,发到统一支付接口HttpClient httpClient = new DefaultHttpClient();HttpPost post = new  HttpPost(payUrl);post.setEntity(new StringEntity(payXmlBuff.toString(),"UTF-8")); //注意utf-8try {HttpResponse execute = httpClient.execute(post);HttpEntity entity = execute.getEntity();  String responseContent = EntityUtils.toString(entity,"utf-8");  //注意utf-8 ,responseContent 的结果也是一个xml字符串//System.out.println("PayController index获取到的总数据:"+responseContent);
/*判断是否成功,返回的字符串结果中,标志有SUCCESS及prepay_id参数,为成功*/if(responseContent.indexOf("SUCCESS")!=-1&&responseContent.indexOf("prepay_id")!=-1){try {
//对返回的xml字符串做解析,这里用的是dom4j包Document document = DocumentHelper.parseText(responseContent);Element root = document.getRootElement();result = String.valueOf(root.elementText("prepay_id")).replaceAll("null", "");//System.out.println("prepay_id:="+result);String payTimeStamp =  Long.toString(System.currentTimeMillis()/1000); //去掉三位,精确到秒String payNonceStr = UUID.randomUUID().toString().replaceAll("-", ""); //随机串//跟前面一样,把参数放到map String payarr[] = new String[]{"appId","timeStamp","nonceStr","package","signType"};Map payParamMap = new HashMap();payParamMap.put("appId", appid);payParamMap.put("timeStamp",payTimeStamp);payParamMap.put("nonceStr", payNonceStr);payParamMap.put("package", "prepay_id="+result);payParamMap.put("signType", "MD5");Arrays.sort(payarr); //排序String payA = "";int pay_len = payarr.length;for(int i=0;i<pay_len;i++){payA = payA + "&"+payarr[i]+"=" + payParamMap.get(payarr[i]);}if(!payA.equals("")){payA = payA.substring(1);}String paySignTemp = payA+"&key=支付的key,注意不是公众号的key";//System.out.println("paySignTemp:"+paySignTemp);String paySign = Md5Encode.md5Encoding(paySignTemp).toUpperCase();//System.out.println("paysign:"+paySign);payParamMap.put("paySign", paySign);JSONObject payJson = new JSONObject(payParamMap);result = payJson.toString();
//做成一个json字符串,返回给 http://xxx.xxx.xxx/abc/pay/wexinpay_oauth2.jsp 界面的隐藏域内} catch (DocumentException e) {// TODO Auto-generated catch blocke.printStackTrace();
result = getResult("999","支付出错!",null);} }} catch (ClientProtocolException e) {// TODO Auto-generated catch blocke.printStackTrace();result = getResult("999","支付出错!",null);} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();result = getResult("999","支付出错!",null);}  }

补方法:
WeiXinPayDao  
getResult(code,msg,infoMap)是一个方法,代码如下

  /*** 生成返回信息* */private String getResult(String code,String msg,Map infoMap){JSONObject result = new JSONObject();result.put("code",code);result.put("msg",msg);if(infoMap!=null&&!infoMap.isEmpty()){result.put("info", infoMap);}return result.toString();}

补方法:
WeiXinPayDao  
httpRequest(url),是一个方法,代码如下

 /** * 发起http请求获取返回结果 * @param req_url 请求地址 * @return */ private String httpRequest(String req_url) {StringBuffer buffer = new StringBuffer();  try {  URL url = new URL(req_url);  HttpURLConnection httpUrlConn = (HttpURLConnection) url.openConnection();  httpUrlConn.setDoOutput(false);  httpUrlConn.setDoInput(true);  httpUrlConn.setUseCaches(false);  httpUrlConn.setRequestMethod("GET");  httpUrlConn.connect();  // 将返回的输入流转换成字符串  InputStream inputStream = httpUrlConn.getInputStream();  InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");  BufferedReader bufferedReader = new BufferedReader(inputStreamReader);  String str = null;  while ((str = bufferedReader.readLine()) != null) {  buffer.append(str);  }  bufferedReader.close();  inputStreamReader.close();  // 释放资源  inputStream.close();  inputStream = null;  httpUrlConn.disconnect();  } catch (Exception e) {  System.out.println(e.getStackTrace());  }  return buffer.toString();  }  

五、统一支付回调处理

http://xxx.xxx.xxx/abc/pay/wexin_order_pay.jsp
回调处理 ,主要就是同步徽信支付状态和商户订单的支付状态,成功或失败

关键代码如下

<html>
<head>
<body>略...
<%String return_xml = "";//获取徽信那边post过来的数据流String acceptjson=null;try {BufferedReader br = new BufferedReader(new InputStreamReader( (ServletInputStream) request.getInputStream(), "utf-8"));  StringBuffer sb = new StringBuffer("");  String temp;  while ((temp = br.readLine()) != null) {  sb.append(temp);  }  br.close();  acceptjson = sb.toString();  //  System.out.print("acceptjson="+acceptjson);} catch (Exception e) {  e.printStackTrace();    } //判断处理 ,根据你自己实际的业务来写 ,acceptjson 实际也是一个xml字符串,用dom4j解析if(acceptjson!=null&&!acceptjson.equals("")){    String source = "eva";String appid = "";String attach = "";String mch_id = "";String out_trade_no = "";String result_code = "";String return_code = "";String time_end = "";String transaction_id = "";try {Document document = DocumentHelper.parseText(acceptjson);Element elRoot = document.getRootElement();appid = String.valueOf(elRoot.elementText("appid")).replaceAll("null", "");attach = String.valueOf(elRoot.elementText("attach")).replaceAll("null", "");mch_id = String.valueOf(elRoot.elementText("mch_id")).replaceAll("null", "");out_trade_no = String.valueOf(elRoot.elementText("out_trade_no")).replaceAll("null", "");result_code = String.valueOf(elRoot.elementText("result_code")).replaceAll("null", "");return_code = String.valueOf(elRoot.elementText("return_code")).replaceAll("null", "");time_end = String.valueOf(elRoot.elementText("time_end")).replaceAll("null", "");transaction_id = String.valueOf(elRoot.elementText("transaction_id")).replaceAll("null", "");//合法性校验if(appid.equals("您的公众号")&&mch_id.equals("您的商户号")){//以下订单的处理只做参考if(!out_trade_no.equals("")){//订单同步处理JdbcHelp jdbc = JdbcHelpUtil.getInstance();String querySQL = "select 1 from hq_order_head where payment_status=8 and order_no="+out_trade_no;//是否已经支付过了Form orForm = new Form();orForm.setValue(JdbcConstant.SQL, querySQL);orForm.setValue(JdbcConstant.SOURCE, source);Map qMap = jdbc.queryMap(orForm);if(qMap.isEmpty()){if(result_code.equals("SUCCESS")){time_end = UtilTools.getInstance().formatDate("yyyyMMddHHmmss", "yyyy-MM-dd HH:mm:ss", time_end);String upSQL = "update hq_order_head set payment_status=8,status=8,payment_date=?,payment_no=? where order_no="+out_trade_no;orForm.setValue(JdbcConstant.PARAMES_FIELDS, "payment_date,payment_no");orForm.setValue("payment_date", time_end);orForm.setValue("payment_no", transaction_id);orForm.setValue(JdbcConstant.SQL, upSQL);jdbc.update(orForm);jdbc.commit(source);}}return_xml = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[]]></return_msg></xml>";}}else{return_xml = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[参数格式校验错误]]></return_msg></xml>";}} catch (DocumentException e1) {return_xml = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[参数格式校验错误]]></return_msg></xml>";} }else{return_xml = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[参数格式校验错误]]></return_msg></xml>";}//返回处理结果给徽信那边,值是xml字符串, SUCCESS或FAILresponse.getWriter().print(return_xml);%>
</body>
</html>

到此,整个微信公众号统一支付介绍完了

以上代码是关键代码,非完整,但不影响阅读和参考。

希望对需要的人有用:)

微信统一支付详解,坑太多,不得不写相关推荐

  1. app接入微信第三方支付详解以及坑

    上一篇文章我们介绍了支付宝的接入,这一篇我们正式介绍一下app怎么接入微信支付功能 正文 微信sdk下载 微信支付文档 微信签名工具app 首先我们微信和支付宝一样,都需要导入相应的sdk 微信sdk ...

  2. 微信jsapi支付详解

    首先:需要先判断环境,如果不是微信环境下就切换成H5浏览器环境下支付. 前提配置:公众号要设置好支付域名,网页授权域名,企业账号,商户号,开通jsapi支付权限. H5通过调用微信提供的JSbridg ...

  3. 微信支付 php详解,微信支付之公众号支付详解

    本文主要和大家分享微信支付之公众号支付详解,随着微信支付的流行,大多产品都开发了自己的公众号.小程序等,产品的营销需要支付的支撑,最近做了个微信公号号支付,采坑无数,今天给大家分享一下,希望能帮助到大 ...

  4. Java=微信支付详解与日志记录详解

    一.二维码: (1)什么是二维码 二维码又称QR Code,QR全称Quick Response,是一个近几年来移动设备上超流行的一种编码方式,它比传统的Bar Code条形码能存更多的信息,也能表示 ...

  5. 对接微信支付之网页支付详解

    对接微信支付之网页支付详解 声明:转载请注明出处 阅读对象:本文针对的是网页中的扫码支付 温馨提示:微信支付坑比较多,阅读时请仔细一些,不要放过所有需要注意的内容 , 本人一路踩坑过来,希望大家引以为 ...

  6. 服务器中调试微信支付免预充,微信支付-统一支付接口被坑经历

    今天来写点什么,就写微信支付吧.哥可是被微信支付接口给折腾惨了. 大概从5月22好开始,进行微信开发,到现在也有个吧月了,老总要求3个月拿出微信会员系统,感觉有点悬,这两天又被微信支付给卡住了. 为了 ...

  7. 字节跳动小程序支付详解

    微信支付详解 1.为什么要写这篇文章 参考了字节跳动官方的文档之后发现写的太简单,完全一头雾水摸不清头脑,后来在百度了别人的实现方案,才得以总结出来. 2.背景 我司要开发一个头条小程序,需要支持支付 ...

  8. 微信App支付源码坑注释

    微信App支付源码&坑注释 部分的代码,因为代码是copy的我自己代码,然后再进行部分的编辑和注释,所以在使用的时候有可能有欠缺,不过整体来说,应该不影响使用的.如果有疑问,可以留言.在微信A ...

  9. 第三篇、记录微信统一支付、扫码支付开发

    项目中使用了微信的统一支付以及扫码支付,记录下学习记录 目录 项目中使用了微信的统一支付以及扫码支付,记录下学习记录 1.配置wechat4j.properties 文件 2.编写支付工具类 3.统一 ...

最新文章

  1. datagrid底部显示水平滚动_DevExpress WPF v19.1:Data Grid/Tree List等控件功能增强
  2. js中Object类型和Array类型的变量被赋值(复制)给其他变量后,修改被赋值(复制)的新变量的值,会影响原始变量的值,这是为什么呢?
  3. Ant Design Pro 组件事件绑定 Input onChange
  4. 驳斥《沙盒用于数据防泄密是重大技术原理性失误》
  5. python官网 中文版 新闻-用python看新闻
  6. (八)ThreadLocal的使用及原理分析
  7. html怎么给表格加a链接地址,html基础02-图片标签、绝/相对地址、表格的属性、链接的属性及链接的分类、name定义锚点的名称、编码...
  8. python 自动填excel_使用python自动填充文字.docx从excel fi
  9. 【C语言】九九乘法口诀表
  10. 算法分析与设计实验报告一——分治算法
  11. java实现webservice调用
  12. 压力测试流程及测试步骤
  13. 计算机应用技术在医院的应用,计算机应用技术对医院信息化的影响探讨
  14. 员工年终绩效考核表模板
  15. Google Guava简介
  16. 服务器硬盘红灯常亮_硬盘指示灯一直亮
  17. 勇者斗恶龙10 android,《勇者斗恶龙》系列35周年纪念直播情报汇总
  18. vue中使用vue-awesome-swiper的方法(实现一屏展示多个图片,点击左右滚动一张)
  19. 当新三板公司踏入币圈 |链捕手
  20. 按揭贷款买房流程及注意事项详解 你准备好买房了吗

热门文章

  1. Python_RuntimeError问题的解决
  2. 邢台计算机编程培训学校,邢台有几家计算机培训中心
  3. openEuler-21.09 dnf update时EPOL仓库报错解决
  4. JavsScript基础语法01
  5. 2015-8-29阿里校园招聘研发project师笔试题
  6. 思科曹图强:勒索软件将打破安全防御平衡
  7. redis事件通知(notify-keyspace-events Ex)
  8. DAO层常用的查询方法
  9. Unable to ping server at localhost:1099
  10. ERR_CONNECTION_CLOSED