java微信公众号支付案例

来源:互联网 发布:同步带传动计算软件 编辑:程序博客网 时间:2024/06/06 01:53
一、基本设置
1、进入微信公众平台->微信支付->开发配置 设置授权目录,这个url精确到支付地址的上一级即可
2、公众号设置->功能设置  设置js接口安全域名,网页授权域名
二、获取网页授权
1、用户同意授权,获取code
  1.             String APPID = "";
  2. String uri="";
  3. String REDIRECT_URI =URLEncoder.encode(uri);;
  4. String code = "code";
  5. String SCOPE = "snsapi_base";
  6. String url = "https://open.weixin.qq.com/connect/oauth2/authorize?" +
  7. "appid=" + APPID +
  8. "&redirect_uri=" +
  9. REDIRECT_URI+
  10. "&response_type="+code+"&scope="+SCOPE+"&state=123#wechat_redirect";
  11. response.sendRedirect(url);
code state 作为参数跳转到了设置的REDIRECT_URI中
2、通过code换取网页授权access_token
  1.         SortedMap<String, String> paramMap1=new TreeMap<String, String>();
  2. paramMap1.put("appid",Global.getResource("tuji.official_accounts.app_id"));
  3. paramMap1.put("secret",Global.getResource("tuji.official_accounts.app_secret") );
  4. paramMap1.put("code",code);
  5. paramMap1.put("grant_type","authorization_code");
  6. Map<String, String> resultMap=WeiXinUtil.getAccessToken(paramMap1);
String openId=resultMap.get("openid"); 
获取到 openid;
三、统一下单 
  1.         SortedMap<String,Object> parmMap=new TreeMap<String, Object>();
  2.         parmMap.put("openid",openId );         Map<String, Object> resultMap=new HashMap<String,Object>();
  3. String urlStr="https://api.mch.weixin.qq.com/pay/unifiedorder";
  4. //参数
  5. parmMap.put("out_trade_no",hOrder.getOrderNumber());
  6. parmMap.put("appid",Global.getResource("tuji.official_accounts.app_id"));
  7. parmMap.put("mch_id",Global.getResource("weixin.mac_id"));//商户号
  8. //parmMap.put("device_info",WeiXinUtil.DEVICE_INFO_WEB );//设备号
  9. parmMap.put("nonce_str",create_nonce_str());//随机字符串
  10. //parmMap.put("sign_type","MD5" );//签名类型,默认为MD5,支持HMAC-SHA256和MD5。
  11. parmMap.put("body",hOrder.getGoodsName());//商品简单描述,这里放的是商品名称
  12. int costPrice=0;
  13. JSONArray jsonArray=new JSONArray();
  14. JSONObject subJson=new JSONObject();
  15. Double d =hOrder.getTotalPrice();
  16. costPrice=HotelUtil.yuan2fen(d);
  17. subJson.put("goods_id",hOrder.getGoodId()+"");
  18. subJson.put("goods_name",hOrder.getGoodsName());
  19. subJson.put("quantity",hOrder.getGoodsNumber().intValue());//商品数量
  20. subJson.put("price",costPrice);
  21. jsonArray.add(subJson);
  22. JSONObject jsonObject=new JSONObject();
  23. //jsonObject.put("cost_price",608800);//cost_price Int 可选 32 订单原价,商户侧一张小票订单可能被分多次支付,订单原价            用于记录整张小票的支付金额。当订单原价与支付金额不相等则被判定为拆单,无法享受优惠
  24. //jsonObject.put("receipt_id","wx123" );//receipt_id String 可选 32 商家小票ID
  25. jsonObject.put("goods_detail", jsonArray);
  26. parmMap.put("detail",jsonObject.toString());//商品详细列表,使用Json格式,传输签名前请务必使用CDATA标签将JSON文本串保            护起来。
  27. //parmMap.put("attach","lalala" );//附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用。
  28. parmMap.put("total_fee",costPrice );//订单总金额,单位为分
  29. //parmMap.put("fee_type","CNY");//符合ISO 4217标准的三位字母代码,默认人民币:CNY
  30. //parmMap.put("goods_tag", );//商品标记,使用代金券或立减优惠功能时需要的参数
  31. parmMap.put("trade_type","JSAPI");//取值如下:JSAPI,NATIVE,APP
  32. //parmMap.put("product_id", );//rade_type=NATIVE时(即扫码支付),此参数必传。此参数为二维码中包含的商品ID,商户自行定义。
  33. //parmMap.put("limit_pay","no_credit" );//上传此参数no_credit--可限制用户不能使用信用卡支付
  34. parmMap.put("notify_url",Global.getResource("weixin.notify_url"));//异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。
  35. String spbill_create_ip =getIp2(request);
  36. parmMap.put("spbill_create_ip",spbill_create_ip );//APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。
  37. //传入的参数
  38. /*parmMap.put("time_start", );//订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。
  39. parmMap.put("time_expire", );//订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。
  40. parmMap.put("out_trade_no", );//商户系统内部订单号,要求32个字符内、且在同一个商户号下唯一。
  41. parmMap.put("openid", );//trade_type=JSAPI时(即公众号支付),此参数必传,此参数为微信用户在商户对应appid下的唯一标识。
  42. */
  43. //做签名
  44. //String sign=getSign(parmMap);
  45. String sign=WeiXinUtil.createSign(parmMap,Global.getResource("weixin.key"));//做签名的key是商户平台设置的
  46. parmMap.put("sign",sign);//签名
  47. String xmlInfo="";
  48. try {
  49. // xmlInfo=unifiedorderMap2Xml(parmMap);
  50. xmlInfo=XMLUtil.arraySortToXml(parmMap, true);
  51. xmlInfo=new String(xmlInfo.toString().getBytes(), "ISO8859-1");//此处解决body为中文的问题
  52. } catch (Exception e) {
  53. throw new WxPayException("unifiedorderMap2Xml错误");
  54. }
  55. String resultStr="";
  56. try {
  57. resultStr=XmlPostUtil.post(urlStr, xmlInfo);
  58. } catch (Exception e) {
  59. throw new WxPayException("XmlPostUtil.post错误");
  60. }
  61. try {
  62. //resultMap=PayCommonUtil.getMapFromXML(resultStr);
  63. resultMap=XMLUtil.doXMLParse(resultStr);
  64. } catch (Exception e) {
  65. throw new WxPayException("getMapFromXML错误");
  66. }
  67. return resultMap;
下单用到的工具类
  1. /**
  2. * 签名 sortMap。
  3. */
  4. public static String createSign(SortedMap<String, Object> paramMap,String key) {
  5. List list = new ArrayList(paramMap.keySet());
  6. Object[] ary = list.toArray();
  7. Arrays.sort(ary);
  8. list = Arrays.asList(ary);
  9. String str = "";
  10. for(int i=0;i<list.size();i++){
  11. str+=list.get(i)+"="+paramMap.get(list.get(i)+"")+"&";
  12. }
  13. str+="key="+key;
  14. str = MD5Util.MD5Encode(str, "UTF-8").toUpperCase();
  15. return str;
  16. }
  1. /**
  2. * SortedMap转成xml
  3. * @param parm
  4. * @param isAddCDATA isAddCDATA true 带<![CDATA[]];false 不带
  5. * @return
  6. */
  7. public static String arraySortToXml(SortedMap<String, Object> parm, boolean isAddCDATA) {
  8. StringBuffer strbuff = new StringBuffer("<xml>");
  9. if (parm != null && !parm.isEmpty()) {
  10. for (Entry<String, Object> entry : parm.entrySet()) {
  11. strbuff.append("<").append(entry.getKey()).append(">");
  12. if (isAddCDATA) {
  13. strbuff.append("<![CDATA[");
  14. if (StringUtils.isNotEmpty(entry.getValue())) {
  15. strbuff.append(entry.getValue());
  16. }
  17. strbuff.append("]]>");
  18. } else {
  19. if (StringUtils.isNotEmpty(entry.getValue())) {
  20. strbuff.append(entry.getValue());
  21. }
  22. }
  23. strbuff.append("</").append(entry.getKey()).append(">");
  24. }
  25. }
  26. return strbuff.append("</xml>").toString();
  27. }
  1. /**
  2. * 发送xml数据请求到server端
  3. *
  4. * @param url
  5. * xml请求数据地址
  6. * @param xmlString
  7. * 发送的xml数据流
  8. * @return null发送失败,否则返回响应内容
  9. */
  10. public static String post(String url, String xmlString) {
  11. // 关闭
  12. System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.SimpleLog");
  13. System.setProperty("org.apache.commons.logging.simplelog.showdatetime", "true");
  14. System.setProperty("org.apache.commons.logging.simplelog.log.org.apache.commons.httpclient", "stdout");
  15. // 创建httpclient工具对象
  16. HttpClient client = new HttpClient();
  17. // 创建post请求方法
  18. PostMethod myPost = new PostMethod(url);
  19. // 设置请求超时时间
  20. client.setConnectionTimeout(300 * 1000);
  21. String responseString = null;
  22. try {
  23. // 设置请求头部类型
  24. myPost.setRequestHeader("Content-Type", "text/xml");
  25. myPost.setRequestHeader("charset", "UTF-8");
  26. // 设置请求体,即xml文本内容,注:这里写了两种方式,一种是直接获取xml内容字符串,一种是读取xml文件以流的形式
  27. myPost.setRequestBody(xmlString);
  28. // InputStream
  29. // body=this.getClass().getResourceAsStream(xmlFileName);
  30. // myPost.setRequestBody(body);
  31. // myPost.setRequestEntity(new
  32. // StringRequestEntity(xmlString,"text/xml","utf-8"));
  33. int statusCode = client.executeMethod(myPost);
  34. if (statusCode == HttpStatus.SC_OK) {
  35. BufferedInputStream bis = new BufferedInputStream(myPost.getResponseBodyAsStream());
  36. byte[] bytes = new byte[1024];
  37. ByteArrayOutputStream bos = new ByteArrayOutputStream();
  38. int count = 0;
  39. while ((count = bis.read(bytes)) != -1) {
  40. bos.write(bytes, 0, count);
  41. }
  42. byte[] strByte = bos.toByteArray();
  43. responseString = new String(strByte, 0, strByte.length, "UTF-8");
  44. bos.close();
  45. bis.close();
  46. }
  47. } catch (Exception e) {
  48. e.printStackTrace();
  49. }
  50. myPost.releaseConnection();
  51. return responseString;
  52. }
  1. /**
  2. * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
  3. *
  4. * @param strxml
  5. * @return
  6. * @throws JDOMException
  7. * @throws IOException
  8. */
  9. public static Map doXMLParse(String strxml) throws Exception {
  10. if (null == strxml || "".equals(strxml)) {
  11. return null;
  12. }
  13. Map m = new HashMap();
  14. InputStream in = String2Inputstream(strxml);
  15. SAXBuilder builder = new SAXBuilder();
  16. Document doc = builder.build(in);
  17. Element root = doc.getRootElement();
  18. List list = root.getChildren();
  19. Iterator it = list.iterator();
  20. while (it.hasNext()) {
  21. Element e = (Element) it.next();
  22. String k = e.getName();
  23. String v = "";
  24. List children = e.getChildren();
  25. if (children.isEmpty()) {
  26. v = e.getTextNormalize();
  27. } else {
  28. v = getChildrenText(children);
  29. }
  30. m.put(k, v);
  31. }
  32. // 关闭流
  33. in.close();
  34. return m;
  35. }
获取到 prepay_id=resultMap.get("prepay_id");
四、调用支付控件
  1. $(".submit")
  2. .bind(
  3. "click",
  4. function() {
  5. var whitch = $(".selected div").html();
  6. if (whitch == "微信支付") {
  7. if (typeof WeixinJSBridge == "undefined") {
  8. if (document.addEventListener) {
  9. document.addEventListener('WeixinJSBridgeReady',
  10. onBridgeReady, false);
  11. } else if (document.attachEvent) {
  12. document.attachEvent('WeixinJSBridgeReady',
  13. onBridgeReady);
  14. document.attachEvent('onWeixinJSBridgeReady',
  15. onBridgeReady);
  16. }
  17. } else {
  18. onBridgeReady();
  19. }
  20. }
  21. })
  22. function onBridgeReady() {
  23. var appId = "${appId}";
  24. var timeStamp = "${timeStamp}";
  25. var nonceStr = "${nonceStr}";
  26. var package1 = "${package}";
  27. var signType = "${signType}";
  28. var paySign = "${paySign}";
  29. WeixinJSBridge.invoke('getBrandWCPayRequest', {
  30. "appId" : appId,//公众号名称,由商户传入
  31. "timeStamp" : timeStamp,//时间戳,自1970年以来的秒数
  32. "nonceStr" : nonceStr,//随机串
  33. "package" : package1,
  34. "signType" : signType,//微信签名方式:
  35. "paySign" : paySign
  36. //微信签名
  37. }, function(res) {
  38. if (res.err_msg == "get_brand_wcpay_request:ok") {
  39. window.location.href="${ctx}/api/v1/hotel/paySuccess";
  40. }else if(res.err_msg == "get_brand_wcpay_request:fail"){
  41. alert('支付失败');
  42. }else if(res.err_msg == "get_brand_wcpay_request:cancel"){
  43. alert('支付取消');
  44. }else{
  45. alert(res.err_msg);
  46. }
  47. });
  48. }

五、支付结果通知

  1. /**
  2. * 下单回掉地址
  3. * @return
  4. */
  5. @RequestMapping(value="notifyUrl",method=RequestMethod.POST)
  6. @ResponseBody
  7. public String notifyUrl(HttpServletRequest request,HttpServletResponse response,Model model){
  8. System.out.println("==开始进入h5支付回调方法==");
  9. String xml_review_result = WeiXinUtil.getXmlRequest(request);
  10. System.out.println("微信支付结果:"+xml_review_result);
  11. Map<String, String> resultMap = null;
  12. try {
  13. resultMap = XMLUtil.doXMLParse(xml_review_result);
  14. System.out.println("resultMap:"+resultMap);
  15. if(resultMap != null && resultMap.size() > 0){
  16. String sign_receive = (String)resultMap.get("sign");//返回的签名
  17. System.out.println("sign_receive:"+sign_receive);
  18. resultMap.remove("sign");//删除签名
  19. String checkSign = WeiXinUtil.getSign(resultMap,Global.getResource("weixin.key"));//生成新签名
  20. System.out.println("checkSign:"+checkSign);
  21. //签名校验成功
  22. if(checkSign != null && sign_receive != null &&
  23. checkSign.equals(sign_receive.trim())){
  24. System.out.println("weixin receive check Sign sucess");
  25. try{
  26. //获得返回结果
  27. String return_code = (String)resultMap.get("return_code");
  28. if("SUCCESS".equals(return_code)){//交易成功
  29. String out_trade_no = (String)resultMap.get("out_trade_no");//获取商户的订单号
  30. System.out.println("weixin pay sucess,out_trade_no:"+out_trade_no);
  31. //处理支付成功以后的逻辑,处理订单,相关的商品 减去相应数量
  32. String resultXml="<xml><return_code><![CDATA[SUCCESS]]></return_code>"+
  33. "<return_msg><![CDATA[OK]]></return_msg></xml>";
  34. return resultXml;
  35. }else{//交易失败
  36. String resultXml="<xml><return_code><![CDATA[FAIL]]></return_code>"+
  37. "<return_msg><![CDATA[FAIL]]></return_msg></xml>";
  38. return resultXml;
  39. }
  40. }catch(Exception e){
  41. e.printStackTrace();
  42. }
  43. }else{
  44. //签名校验失败
  45. System.out.println("weixin receive check Sign fail");
  46. String checkXml = "<xml><return_code><![CDATA[FAIL]]></return_code>"+
  47. "<return_msg><![CDATA[check sign fail]]></return_msg></xml>";
  48. return checkXml;
  49. }
  50. }
  51. } catch (Exception e) {
  52. e.printStackTrace();
  53. }
  54. return null;
  55. }