你的位置:首页 > Java教程

[Java教程]微信公众号支付(二):统一下单

上一篇已经获取到了用户的OpenId

这篇主要是调用微信公众支付的统一下单API

API地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1

看文档,主要流程就是把20个左右的参数封装为

请求参数就不解释了。

其中,随机字符串:我用的是UUID去中划线

1 public static String create_nonce_str() {2    return UUID.randomUUID().toString().replace("-","");3 }

商户订单号:每个订单号只能使用一次,所以用的是系统的订单号加的时间戳。

总金额:不能为0

通知地址:微信支付成功或失败回调给系统的地址

签名:

 1 import java.io.Serializable; 2  3 public class PayInfo implements Serializable{ 4    5   private static final long serialVersionUID = 5637164279924222380L; 6   private String appid; 7   private String mch_id; 8   private String device_info; 9   private String nonce_str;10   private String sign;11   private String body;12   private String attach;13   private String out_trade_no;14   private int total_fee;15   private String spbill_create_ip;16   private String notify_url;17   private String trade_type;18   private String openid;19     20   //下面是get,set方法    21 }

 1   /** 2    * 创建统一下单的 3    * @param bizOrder 系统中的业务单号 4    * @param ip 用户的ip地址 5    * @param openId 用户的openId 6    * @return 7   */ 8   public PayInfo createPayInfo(BizOrder bizOrder,String ip,String openId) { 9     PayInfo payInfo = new PayInfo();10     payInfo.setAppid(Constants.appid);11     payInfo.setDevice_info("WEB");12     payInfo.setMch_id(Constants.mch_id);13     payInfo.setNonce_str(CommonUtil.create_nonce_str().replace("-", ""));14     payInfo.setBody("这里是某某白米饭的body");15     payInfo.setAttach(bizOrder.getId());16     payInfo.setOut_trade_no(bizOrder.getOrderCode().concat("A").concat(DateFormatUtils.format(new Date(), "MMddHHmmss")));17     payInfo.setTotal_fee((int)bizOrder.getFeeAmount());18     payInfo.setSpbill_create_ip(ip);19     payInfo.setNotify_url(Constants.notify_url);20     payInfo.setTrade_type("JSAPI");21     payInfo.setOpenid(openId);22     return payInfo;23   }

获取签名:

 1   /** 2    * 获取签名 3    * @param payInfo 4    * @return 5    * @throws Exception 6   */ 7   public String getSign(PayInfo payInfo) throws Exception { 8     String signTemp = "appid="+payInfo.getAppid() 9         +"&attach="+payInfo.getAttach()10         +"&body="+payInfo.getBody()11         +"&device_info="+payInfo.getDevice_info()12         +"&mch_id="+payInfo.getMch_id()13         +"&nonce_str="+payInfo.getNonce_str()14         +"&notify_url="+payInfo.getNotify_url()15         +"&openid="+payInfo.getOpenid()16         +"&out_trade_no="+payInfo.getOut_trade_no()17         +"&spbill_create_ip="+payInfo.getSpbill_create_ip()18         +"&total_fee="+payInfo.getTotal_fee()19         +"&trade_type="+payInfo.getTrade_type()20         +"&key="+Constants.key; //这个key注意21     22    MessageDigest md5 = MessageDigest.getInstance("MD5");23     md5.reset();24    md5.update(signTemp.getBytes("UTF-8"));25    String sign = CommonUtil.byteToStr(md5.digest()).toUpperCase();26    return sign;27   }

注意:上面的Constants.key取值在商户号API安全的API密钥中。

一些工具方法:获取ip地址,将字节数组转换为十六进制字符串,将字节转换为十六进制字符串

 1  /** 2    * 将字节数组转换为十六进制字符串 3    *  4    * @param byteArray 5    * @return 6   */ 7   public static String byteToStr(byte[] byteArray) { 8     String strDigest = ""; 9     for (int i = 0; i < byteArray.length; i++) {10       strDigest += byteToHexStr(byteArray[i]);11     }12     return strDigest;13   }14 15   /**16    * 将字节转换为十六进制字符串17    * 18    * @param btyes19    * @return20   */21   public static String byteToHexStr(byte bytes) {22     char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };23     char[] tempArr = new char[2];24     tempArr[0] = Digit[(bytes >>> 4) & 0X0F];25     tempArr[1] = Digit[bytes & 0X0F];26 27     String s = new String(tempArr);28     return s;29   }30   31   /**32    * 获取ip地址33    * @param request34    * @return35   */36   public static String getIpAddr(HttpServletRequest request) { 37     InetAddress addr = null; 38     try { 39       addr = InetAddress.getLocalHost(); 40     } catch (UnknownHostException e) { 41       return request.getRemoteAddr(); 42     } 43     byte[] ipAddr = addr.getAddress(); 44     String ipAddrStr = ""; 45     for (int i = 0; i < ipAddr.length; i++) { 46       if (i > 0) { 47         ipAddrStr += "."; 48       } 49       ipAddrStr += ipAddr[i] & 0xFF; 50     } 51     return ipAddrStr; 52   } 

这样就获取了签名,把签名与PayInfo中的其他数据转成

1 PayInfo pi = pu.createPayInfo(bo,"10.204.3.32","22");2 String sign = pu.getSign(pi);3 pi.setSign(sign);

对象转

 1  /** 2    * 扩展xstream使其支持CDATA 3   */ 4   private static XStream xstream = new XStream(new XppDriver() { 5     public HierarchicalStreamWriter createWriter(Writer out) { 6       return new PrettyPrintWriter(out) { 7         //增加CDATA标记 8         boolean cdata = true; 9 10         @SuppressWarnings("rawtypes")11         public void startNode(String name, Class clazz) {12           super.startNode(name, clazz);13         }14 15         protected void writeText(QuickWriter writer, String text) {16           if (cdata) {17             writer.write("<![CDATA[");18             writer.write(text);19             writer.write("]]>");20           } else {21             writer.write(text);22           }23         }24       };25     }26   });27   28   public static String payInfoTo29     xstream.alias(", pi.getClass());30     return xstream.to31   }

 1   @SuppressWarnings("unchecked") 2   public static Map<String, String> parsethrows Exception { 3     Map<String, String> map = new HashMap<String, String>(); 4  5     Document document = DocumentHelper.parseText( 6     Element root = document.getRootElement(); 7     List<Element> elementList = root.elements(); 8  9     for (Element e : elementList)10       map.put(e.getName(), e.getText());11     return map;12   }  

下面就是调用统一下单的URL了

1   log.info(MessageUtil.payInfoTo));2   Map<String, String> map = CommonUtil.httpsRequestTo));3  log.info(map);

 1 public static Map<String, String> httpsRequestTo 2     Map<String, String> result = new HashMap<>(); 3     try { 4       StringBuffer buffer = httpsRequest(requestUrl, requestMethod, outputStr); 5       result = MessageUtil.parse 6     } catch (ConnectException ce) { 7       log.error("连接超时:"+ce.getMessage()); 8     } catch (Exception e) { 9       log.error("https请求异常:"+ece.getMessage());10     }11     return result;12   }

httpsRequest()这个方法在第一篇中
上面获取到的Map如果成功的话,里面就会有
 1  String return_code = map.get("return_code"); 2  if(StringUtils.isNotBlank(return_code) && return_code.equals("SUCCESS")){ 3    String return_msg = map.get("return_msg"); 4   if(StringUtils.isNotBlank(return_msg) && !return_msg.equals("OK")) { 5     return "统一下单错误!"; 6   } 7 }else{ 8   return "统一下单错误!"; 9  }10     11 String prepay_Id = map.get("prepay_id");

这个prepay_id就是预支付的ID。后面支付需要它。