1.微信官方支付地址
2.注册微信商户开发平台和商户平台,创建移动应用。
在这里插入图片描述
3.应用审核通过后,开通APP支付能力,这里需要关联商户平台
在这里插入图片描述
4.APP支付能力开通后,进入编码阶段。


public class WeiChartConfig {

   /**
    * 预支付请求地址
    */
   public static final String  PrepayUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";

   /**
    * 查询订单地址
    */
   public static final String  OrderUrl = "https://api.mch.weixin.qq.com/pay/orderquery";

   /**
    * 关闭订单地址
    */
   public static final String  CloseOrderUrl = "https://api.mch.weixin.qq.com/pay/closeorder";

   /**
    * 申请退款地址
    */
   public static final String  RefundUrl = "https://api.mch.weixin.qq.com/secapi/pay/refund";

   /**
    * 查询退款地址
    */
   public static final String  RefundQueryUrl = "https://api.mch.weixin.qq.com/pay/refundquery";

   /**
    * 下载账单地址
    */
   public static final String  DownloadBillUrl = "https://api.mch.weixin.qq.com/pay/downloadbill";

   /**
    * 商户APPID
    */
   public static final String  AppId = "wx270xxxx";

   /**
    * 商户账户
    */
   public static final String  MchId = "160xxxx";

   /**
    * 商户秘钥
    */
   public static final String  AppSercret = "d6d5xxx";

   /**
    * 服务器异步通知页面路径
    */
   public static String notify_url = "xxxPay/notify";

   /**
    * 退款通知地址
    */
   public static String refund_notify_url = "";

   /**
    * 退款需要证书文件,证书文件的地址
    */
   public static final String refund_file_path = "xxx";

}

预下单:

Map<String, String> reqMap = new HashMap<String, String>();
        reqMap.put("appid", WeiChartConfig.AppId);//appid
        reqMap.put("mch_id", WeiChartConfig.MchId);//商户id
        reqMap.put("nonce_str", WeiChartUtil.getRandomString());//随机字符串

        reqMap.put("body", commodityName);//商品名称
        reqMap.put("out_trade_no", paySignParams.getOrderNo()); //商户系统内部的订单号,
        if(payPrice.doubleValue() <= 0){
            throw new BusinessException("支付金额需要大于0");
        }
        reqMap.put("total_fee", WeiChartUtil.changeToFen(payPrice.doubleValue())); //订单总金额,单位为分
        reqMap.put("spbill_create_ip", WeiChartUtil.getHostIp()); //用户端实际ip
        reqMap.put("notify_url", SYS_HOST+WeiChartConfig.notify_url); //通知地址
        //交易类型
        reqMap.put("trade_type", "APP");
        //指定支付方式,no_credit 指定不能使用信用卡支  非必填
        //reqMap.put("limit_pay", "no_credit");
        reqMap.put("sign", WeiChartUtil.getSign(reqMap));//签名
        String reqStr = WeiChartUtil.creatXml(reqMap);//生产xml字符串
        String retStr = HttpClientUtil.postHtpps(WeiChartConfig.PrepayUrl, reqStr);
        log.info("=======微信预支付返回结果:{}",reqStr);

WeiChartUtil工具类:


import com.alipay.api.AlipayApiException;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;

@Slf4j
public class WeiChartUtil{

   /**
    * 关闭订单
    * @param orderId  商户自己的订单号
    * @return
    */
   public static Map<String, String> closeOrder(String orderId){
      Map<String, String> reqMap = new HashMap<String, String>();
      reqMap.put("appid", WeiChartConfig.AppId);
      reqMap.put("mch_id", WeiChartConfig.MchId);
      reqMap.put("nonce_str", getRandomString());
      reqMap.put("out_trade_no", orderId); //商户系统内部的订单号,
      reqMap.put("sign", getSign(reqMap));

      String reqStr = creatXml(reqMap);
      String retStr = HttpClientUtil.postHtpps(WeiChartConfig.CloseOrderUrl, reqStr);
      return getInfoByXml(retStr);
   }

   /**
    * 查询订单
    * @param orderId 商户自己的订单号
    * @return
    */
   public static String getOrder(String orderId){
      Map<String, String> reqMap = new HashMap<String, String>();
      reqMap.put("appid", WeiChartConfig.AppId);
      reqMap.put("mch_id", WeiChartConfig.MchId);
      reqMap.put("nonce_str", getRandomString());
      reqMap.put("out_trade_no", orderId); //商户系统内部的订单号,
      reqMap.put("sign", getSign(reqMap));

      String reqStr = creatXml(reqMap);
      String retStr = HttpClientUtil.postHtpps(WeiChartConfig.OrderUrl, reqStr);
      return retStr;
   }

   /**
    * 退款
    * @param orderId  商户订单号
    * @param refundId  退款单号
    * @param totralFee 总金额(分)
    * @param refundFee 退款金额(分)
    * @param opUserId 操作员ID
    * @return
    */
   public static Map<String, String> refundWei(String orderId,String refundId,String totralFee,String refundFee,String opUserId){
      Map<String, String> reqMap = new HashMap<String, String>();
      reqMap.put("appid", WeiChartConfig.AppId);
      reqMap.put("mch_id", WeiChartConfig.MchId);
      reqMap.put("nonce_str", getRandomString());
      reqMap.put("out_trade_no", orderId); //商户系统内部的订单号,
      reqMap.put("out_refund_no", refundId); //商户退款单号
      reqMap.put("total_fee", totralFee); //总金额
      reqMap.put("refund_fee", refundFee); //退款金额
      reqMap.put("op_user_id", opUserId); //操作员
      reqMap.put("sign", getSign(reqMap));

      String reqStr = creatXml(reqMap);
      String retStr = "";
      try{
         retStr = HttpClientUtil.postHttplientNeedSSL(WeiChartConfig.RefundUrl, reqStr, WeiChartConfig.refund_file_path, WeiChartConfig.MchId);
      }catch(Exception e){
         e.printStackTrace();
         return null;
      }
      return getInfoByXml(retStr);
   }

   /**
    * 退款查询
    * @param refundId  退款单号
    * @return
    */
   public static Map<String, String> getRefundWeiInfo(String refundId){
      Map<String, String> reqMap = new HashMap<String, String>();
      reqMap.put("appid", WeiChartConfig.AppId);
      reqMap.put("mch_id", WeiChartConfig.MchId);
      reqMap.put("nonce_str", getRandomString());
      reqMap.put("out_refund_no", refundId); //商户退款单号
      reqMap.put("sign", getSign(reqMap));

      String reqStr = creatXml(reqMap);
      String retStr = HttpClientUtil.postHtpps(WeiChartConfig.RefundQueryUrl, reqStr);
      return getInfoByXml(retStr);
   }

   /**
    * 传入map  生成头为XML的xml字符串,例:<xml><key>123</key></xml>
    * @param reqMap
    * @return
    */
   public static String creatXml(Map<String, String> reqMap){
      Set<String> set = reqMap.keySet();
      StringBuffer b = new StringBuffer();
      b.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
      b.append("<xml>");
      for(String key : set){
         b.append("<"+key+">").append(reqMap.get(key)).append("</"+key+">");
      }
      b.append("</xml>");
      return b.toString();
   }

   /**
    * 得到加密值
    * @param map
    * @return
    */
   public static String getSign(Map<String, String> map){
      String[] keys = map.keySet().toArray(new String[0]);
      Arrays.sort(keys);
      StringBuffer reqStr = new StringBuffer();
      for(String key : keys){
         String v = map.get(key);
         if(v != null && !v.equals("")){
            reqStr.append(key).append("=").append(v).append("&");
         }
      }
      reqStr.append("key").append("=").append(WeiChartConfig.AppSercret);

      return WeiMd5.encode(reqStr.toString()).toUpperCase();
   }

   /**
    * 得到10 位的时间戳
    * 如果在JAVA上转换为时间要在后面补上三个0 
    * @return
    */
   public static String getTenTimes(){
      String t = new Date().getTime()+"";
      t = t.substring(0, t.length()-3);
      return t;
   }

   /**
    * 得到随机字符串
    * @return
    */
   public static String getRandomString(){
      int length = 32;
      String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
      Random random = new Random();
      StringBuffer sb = new StringBuffer();

      for(int i = 0; i < length; ++i){
         int number = random.nextInt(62);//[0,62)  
         sb.append(str.charAt(number));
      }
      return sb.toString();
   }

   /**
    * 得到本地机器的IP
    * @return
    */
   public static String getHostIp(){
      String ip = "";
      try{
         ip = InetAddress.getLocalHost().getHostAddress();
      }catch(UnknownHostException e){
         e.printStackTrace();
      }
      return ip;
   }

   public static Map<String, String> getInfoByXml(String xmlStr){
      Map<String, String> m = new HashMap<String, String>();
      try{
         Document d = DocumentHelper.parseText(xmlStr);
         Element root = d.getRootElement();
         for ( Iterator<?> i = root.elementIterator(); i.hasNext(); ) {
            Element element = (Element) i.next();
            String name = element.getName();
            if(!element.isTextOnly()){
               //不是字符串 跳过。确定了微信放回的xml只有根目录
               continue;
            }else{
               m.put(name, element.getTextTrim());
            }
         }
         //对返回结果做校验.去除sign 字段再去加密
         String retSign = m.get("sign");
         m.remove("sign");
         String rightSing = getSign(m);
         if(rightSing.equals(retSign)){
            return m;
         }
      }catch(DocumentException e){
         e.printStackTrace();
      }
      return m;
}

   /**
    * 将金额转换成分
    * @param fee 元格式的
    * @return 分
    */
   public static String changeToFen(Double fee){
      String priceStr = "";
      if(fee != null){
          int p = (int)(fee * 100); //价格变为分
          priceStr = Integer.toString(p);
      }
      return priceStr;
   }

   /**
    * 将map解析成string
    * @param param
    * @return
    */
   public static String GetMapToXML(Map<String,String> param){
      StringBuffer sb = new StringBuffer();
      sb.append("<xml>");
      for (Map.Entry<String,String> entry : param.entrySet()) {
         sb.append("<"+ entry.getKey() +">");
         sb.append(entry.getValue());
         sb.append("</"+ entry.getKey() +">");
      }
      sb.append("</xml>");
      return sb.toString();
   }
   /**
    * xml解析
    * @param strxml
    * @return
    * @throws JDOMException
    * @throws IOException
    */
   public static Map doXMLParse(String strxml) throws JDOMException, IOException {
      strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
      if(null == strxml || "".equals(strxml)) {
         return null;
      }
      Map m = new HashMap();
      InputStream in = null;
      try {
         in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
         SAXBuilder builder = new SAXBuilder();
         org.jdom.Document doc = builder.build(in);
         org.jdom.Element root = doc.getRootElement();
         List list = root.getChildren();
         Iterator it = list.iterator();
         while(it.hasNext()) {
            org.jdom.Element e = (org.jdom.Element) it.next();
            String k = e.getName();
            String v = "";
            List children = e.getChildren();
            if(children.isEmpty()) {
               v = e.getTextNormalize();
            } else {
               v = getChildrenText(children);
            }

            m.put(k, v);
         }
      }finally {
         if(in != null){
            in.close();
         }
      }
      return m;
   }

   public static String getChildrenText(List children) {
      StringBuffer sb = new StringBuffer();
      if(!children.isEmpty()) {
         Iterator it = children.iterator();
         while(it.hasNext()) {
            org.jdom.Element e = (org.jdom.Element) it.next();
            String name = e.getName();
            String value = e.getTextNormalize();
            List list = e.getChildren();
            sb.append("<" + name + ">");
            if(!list.isEmpty()) {
               sb.append(getChildrenText(list));
            }
            sb.append(value);
            sb.append("</" + name + ">");
         }
      }
      return sb.toString();
   }


   /**
    * 验证回调签名
    * @return
    */
   public static boolean isTenpaySign(Map<String, String> map) {
      String characterEncoding="utf-8";
      String charset = "utf-8";
      String signFromAPIResponse = map.get("sign");
      if (signFromAPIResponse == null || signFromAPIResponse.equals("")) {
         System.out.println("API返回的数据签名数据不存在,有可能被第三方篡改!!!");
         return false;
      }
      System.out.println("服务器回包里面的签名是:" + signFromAPIResponse);
      //过滤空 设置 TreeMap
      SortedMap<String,String> packageParams = new TreeMap();

      for (String parameter : map.keySet()) {
         String parameterValue = map.get(parameter);
         String v = "";
         if (null != parameterValue) {
            v = parameterValue.trim();
         }
         packageParams.put(parameter, v);
      }
      StringBuffer sb = new StringBuffer();
      Set es = packageParams.entrySet();
      Iterator it = es.iterator();

      while(it.hasNext()) {
         Map.Entry entry = (Map.Entry)it.next();
         String k = (String)entry.getKey();
         String v = (String)entry.getValue();
         if(!"sign".equals(k) && null != v && !"".equals(v)) {
            sb.append(k + "=" + v + "&");
         }
      }
      sb.append("key=" + WeiChartConfig.AppSercret);
      //将API返回的数据根据用签名算法进行计算新的签名,用来跟API返回的签名进行比较
      //算出签名
      String resultSign = null;
      String tobesign = sb.toString();
      try{
         resultSign = MD5Util.MD5Encode(tobesign, characterEncoding).toUpperCase();
      }catch (Exception e) {
         resultSign = MD5Util.MD5Encode(tobesign, characterEncoding).toUpperCase();
      }
      String tenpaySign = packageParams.get("sign").toUpperCase();
      return tenpaySign.equals(resultSign);
   }

uniapp支付微信和支付宝的orderInfo不同,支付宝传一个字符串,微信需要时一个object官网地址
微信支付后的回调方法请求RequestMapping

@RequestMapping(value="notify",produces = {"application/json;charset=UTF-8" },method = RequestMethod.POST)

微信回调方法

InputStream inStream = null;
        ByteArrayOutputStream outSteam = null;
        try {
            inStream = request.getInputStream();
            outSteam = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len = 0;
            while ((len = inStream.read(buffer)) != -1) {
                outSteam.write(buffer, 0, len);
            }
            String resultxml = new String(outSteam.toByteArray(), "utf-8");
            Map<String, String> params = WeiChartUtil.doXMLParse(resultxml);
            log.info("=====微信回调返回数据:{}",JSON.toJSONString(params));
            Map<String,String> return_data = new HashMap<String,String>();
            if (!WeiChartUtil.isTenpaySign(params)) {
                log.info("=====微信支付失败。。。");
                return_data.put("return_code", "FAIL");
                return_data.put("return_msg", "支付失败");
                return WeiChartUtil.GetMapToXML(return_data);
            } else {
               //TODO 业务处理...
                log.info("=====微信支付成功。。。");
                return_data.put("return_code", "SUCCESS");
                return_data.put("return_msg", "OK");
                return WeiChartUtil.GetMapToXML(return_data);
            }
        }finally {
            if(outSteam != null){
                outSteam.close();
            }
            if(inStream != null){
                inStream.close();
            }
        }
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐