首页 > 学院 > 开发设计 > 正文

微信公众号支付(二):统一下单

2019-11-14 15:41:35
字体:
来源:转载
供稿:网友

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

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

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

看文档,主要流程就是把20个左右的参数封装为xml格式发送到微信给的接口地址,然后就可以获取到返回的内容了,如果成功里面就有支付所需要的预支付ID

请求参数就不解释了。

其中,随机字符串:我用的是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      * 创建统一下单的xml的java对象 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中的其他数据转成XML格式,当做参数传递给统一下单地址。

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

对象转XML

 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 payInfoToXML(PayInfo pi) {29         xstream.alias("xml", pi.getClass());30         return xstream.toXML(pi);31     }

xml转Map

 1     @SuppressWarnings("unchecked") 2     public static Map<String, String> parseXml(String xml) throws Exception { 3         Map<String, String> map = new HashMap<String, String>(); 4  5         Document document = DocumentHelper.parseText(xml); 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.payInfoToXML(pi).replace("__", "_"));2   Map<String, String> map = CommonUtil.httpsRequestToXML("https://api.mch.weixin.qq.com/pay/unifiedorder", "POST", MessageUtil.payInfoToXML(pi).replace("__", "_").replace("<![CDATA[", "").replace("]]>", ""));3    log.info(map);
 1 public static Map<String, String> httpsRequestToXML(String requestUrl, String requestMethod, String outputStr) { 2         Map<String, String> result = new HashMap<>(); 3         try { 4              StringBuffer buffer = httpsRequest(requestUrl, requestMethod, outputStr); 5              result = MessageUtil.parseXml(buffer.toString()); 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。后面支付需要它。


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表