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


    上一篇已经获取到了用户的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 btyes
    19      * @return
    20      */
    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 request
    34      * @return
    35      */
    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。后面支付需要它。

  • 相关阅读:
    HDU 1015(字符运算 **)
    IOS7中自动计算label的宽度和高度的方法
    IOS开发UI基础文本属性Attributes
    IOS开发UI基础UIControl事件
    IOS开发UI基础UIImagePickerController的属性
    IOS开发UI基础UITableView的属性
    IOS开发UI基础UIActivityIndicatorView的属性
    IOS开发UI基础 UIAlertView的属性
    IOS开发UI基础UIImageView属性属性
    IOS开发UI基础 UIDatePicker的属性
  • 原文地址:https://www.cnblogs.com/imeng/p/4792043.html
Copyright © 2020-2023  润新知