1. 程式人生 > >用java實現微信支付,退款,部分退款服務端

用java實現微信支付,退款,部分退款服務端

由於公司業務需要,最近搞微信退款功能,今天抽空在此記錄一下,以後用到也可以到這來看一眼。廢話不多說,進入正題。

微信支付以及付款呢,先要有個證書,不清楚的,還要有證書,可以去微信平臺看證書怎麼下載 https://pay.weixin.qq.com, APP_ID,MCH_ID,API_KEY,當然這些你可以向你boss要,

下面就是我的程式碼了

maven檔案

<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.mobile</groupId>
            <artifactId>spring-mobile-device</artifactId>
        </dependency>
        <dependency>
            <groupId>javax.inject</groupId>
            <artifactId>javax.inject</artifactId>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-search-orm</artifactId>
            <version>5.6.1.Final</version>
        </dependency>
        <dependency>
            <groupId>org.ansj</groupId>
            <artifactId>ansj_seg</artifactId>
            <version>5.1.6</version>
        </dependency>
        <dependency>
            <groupId>org.ansj</groupId>
            <artifactId>ansj_lucene5_plug</artifactId>
            <version>5.1.1.2</version>
        </dependency>
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
        </dependency>
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</artifactId>
        </dependency>
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache-web</artifactId>
            <version>2.0.4</version>
            <exclusions>
                <exclusion>
                    <groupId>net.sf.ehcache</groupId>
                    <artifactId>ehcache-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>1.3.2</version>
            <exclusions>
                <exclusion>
                    <groupId>net.sf.ehcache</groupId>
                    <artifactId>ehcache-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
        </dependency>
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
        </dependency>
        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
        </dependency>
        <dependency>
            <groupId>commons-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
        </dependency>
        <dependency>
            <groupId>commons-net</groupId>
            <artifactId>commons-net</artifactId>
        </dependency>
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
        </dependency>
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-email</artifactId>
            <version>1.4</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-compress</artifactId>
            <version>1.12</version>
        </dependency>
        <dependency>
            <groupId>org.apache.oltu.oauth2</groupId>
            <artifactId>org.apache.oltu.oauth2.client</artifactId>
            <version>1.0.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
        </dependency>
        <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
        </dependency>
        <dependency>
            <groupId>jaxen</groupId>
            <artifactId>jaxen</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-xml</artifactId>
        </dependency>
        <dependency>
            <groupId>org.jsoup</groupId>
            <artifactId>jsoup</artifactId>
            <version>1.9.2</version>
        </dependency>
        <dependency>
            <groupId>org.jxls</groupId>
            <artifactId>jxls</artifactId>
            <version>2.4.3</version>
            <exclusions>
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.jxls</groupId>
            <artifactId>jxls-poi</artifactId>
            <version>1.0.14</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
        </dependency>
        <dependency>
            <groupId>org.codehaus.groovy</groupId>
            <artifactId>groovy-all</artifactId>
        </dependency>
        <dependency>
            <groupId>org.im4java</groupId>
            <artifactId>im4java</artifactId>
            <version>1.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-jdk15on</artifactId>
            <version>1.51</version>
        </dependency>
        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>2.0.5</version>
            <exclusions>
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.perf4j</groupId>
            <artifactId>perf4j</artifactId>
            <version>0.9.16</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
        </dependency>
        <dependency>
            <groupId>com.googlecode.log4jdbc</groupId>
            <artifactId>log4jdbc</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>com.alipay</groupId>
            <artifactId>alipay-sdk</artifactId>
            <version>20171026141113</version>
        </dependency>
        <dependency>
            <groupId>com.google.code.kaptcha</groupId>
            <artifactId>kaptcha</artifactId>
            <version>2.3.2</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.microsoft.sqlserver</groupId>
            <artifactId>mssql-jdbc</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>oracle</groupId>
            <artifactId>ojdbc</artifactId>
            <version>7</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.google.zxing</groupId>
            <artifactId>core</artifactId>
            <version>3.2.0</version>
        </dependency>
        <dependency>
            <groupId>com.google.zxing</groupId>
            <artifactId>javase</artifactId>
            <version>3.2.0</version>
        </dependency>
        
        <!-- getui -->
        <dependency>
            <groupId>com.gexin.platform</groupId>
            <artifactId>gexin-rp-fastjson</artifactId>
            <version>1.0.0.1</version>
          </dependency>
        <dependency>
            <groupId>com.gexin.platform</groupId>
            <artifactId>gexin-rp-sdk-http</artifactId>
            <version>4.0.1.17</version>
          </dependency>
          <dependency>
            <groupId>com.gexin.platform</groupId>
            <artifactId>gexin-rp-sdk-template</artifactId>
            <version>4.0.0.16</version>
          </dependency>
          <dependency>
            <groupId>com.gexin.platform</groupId>
            <artifactId>gexin-rp-sdk-base</artifactId>
            <version>4.0.0.22</version>
          </dependency>
          <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
            <version>2.5.0</version>
        </dependency>
        
        <!-- 微信支付工具類需要的jar end-->
        <dependency>
            <groupId>com.github.wxpay</groupId>
            <artifactId>wxpay-sdk</artifactId>
            <version>0.0.3</version>
        </dependency>
        <dependency>
          <groupId>com.alibaba</groupId>
              <artifactId>fastjson</artifactId>
              <version>1.2.32</version>
            </dependency>
            <!-- 微信支付工具類需要的jar start-->
        <dependency>
          <groupId>jdom</groupId>
          <artifactId>jdom</artifactId>
          <version>1.1</version>
        </dependency>
        <dependency>
          <groupId>org.apache.httpcomponents</groupId>
          <artifactId>httpmime</artifactId>
          <version>4.5.3</version>
        </dependency>
        <!-- log start -->
        <dependency>
          <groupId>log4j</groupId>
          <artifactId>log4j</artifactId>
          <version>1.2.17</version>
        </dependency>
        <!-- 支付寶 -->
        <dependency>
          <groupId>commons-httpclient</groupId>
          <artifactId>commons-httpclient</artifactId>
          <version>3.1</version>
        </dependency>
    </dependencies>

工具類:

XMLUtil 類:

import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * @Author: HONGLINCHEN
 * @Description: xml 工具類
 * @Date: 2018-8-23
 */
public class XMLUtil {
   /**
    * 解析xml,返回第一級元素鍵值對。如果第一級元素有子節點,則此節點的值是子節點的xml資料。 微信支付 解析xml xml轉map  獲取prepay_id
    * @param strxml
    * @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 = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
      SAXBuilder builder = new SAXBuilder();
      Document doc = builder.build(in);
      Element root = doc.getRootElement();
      List list = root.getChildren();
      Iterator it = list.iterator();
      while(it.hasNext()) {
         Element e = (Element) it.next();
         String k = e.getName();
         String v = "";
         List children = e.getChildren();
         if(children.isEmpty()) {
            v = e.getTextNormalize();
         } else {
            v = XMLUtil.getChildrenText(children);
         }
         
         m.put(k, v);
      }
      
      //關閉流
      in.close();
      
      return m;
   }
   /**
    * @Author: HONGLINCHEN
    * @Description: 微信支付 解析xml xml轉map  獲取prepay_id
    * @param xml
    * @Date: 2017-9-8 10:13
    */
   public static Map<String,Object> getResult(String xml){
      Map<String,Object> map = new HashMap<String, Object>();
      try {
         org.dom4j.Document document = DocumentHelper.parseText(xml);
         org.dom4j.Element root = document.getRootElement();
         Iterator<org.dom4j.Element> it = root.elementIterator();
         while (it.hasNext()) {
            org.dom4j.Element element = it.next();
            map.put(element.getName(), element.getTextTrim());
         }
      } catch (DocumentException e) {
         e.printStackTrace();
      }
      return map;
   }
   /**
    * 獲取子結點的xml
    * @param children
    * @return String
    */
   public static String getChildrenText(List children) {
      StringBuffer sb = new StringBuffer();
      if(!children.isEmpty()) {
         Iterator it = children.iterator();
         while(it.hasNext()) {
            Element e = (Element) it.next();
            String name = e.getName();
            String value = e.getTextNormalize();
            List list = e.getChildren();
            sb.append("<" + name + ">");
            if(!list.isEmpty()) {
               sb.append(XMLUtil.getChildrenText(list));
            }
            sb.append(value);
            sb.append("</" + name + ">");
         }
      }
      
      return sb.toString();
   }
   
   /**
    * 獲取xml編碼字符集
    * @param strxml
    * @return
    * @throws IOException 
    * @throws JDOMException 
    */
   public static String getXMLEncoding(String strxml) throws JDOMException, IOException {
      InputStream in = String2Inputstream(strxml);
      SAXBuilder builder = new SAXBuilder();
      Document doc = builder.build(in);
      in.close();
      return (String)doc.getProperty("encoding");
   }
   public static InputStream String2Inputstream(String str) {
      return new ByteArrayInputStream(str.getBytes());
   }
   
}
 

WXPayUtil類:

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.SSLContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.*;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.util.*;

/**
 * @Author: HONGLINCHEN
 * @Description:微信支付
 * @Date: 2018-8-23
 */
public class WXPayUtil {
    public static String PostRequest(String url, String data) throws IOException {
        HttpClient client = new HttpClient();
        PostMethod post=new PostMethod(url);
        String result = "";
        post.addRequestHeader("Content-Type", "text/html; charset=utf-8");
        post.addRequestHeader("content", "text/html; charset=utf-8");
        post.setRequestBody(data);
        try {
            int status=client.executeMethod(post);
            result = post.getResponseBodyAsString();
            result = new String(result.getBytes(post.getResponseCharSet()), "utf-8");
        } catch (IOException e){
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 建立md5摘要,規則是:按引數名稱a-z排序,遇到空值的引數不參加簽名。
     */
    public static String createSign(SortedMap<String, String> packageParams, String AppKey) {
        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 (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        sb.append("key=" + AppKey);
        String sign = MD5Util.MD5Encode(sb.toString(), "UTF-8").toUpperCase();
        return sign;
    }
    /**
     * @Author: HONGLINCHEN
     * @Description:微信支付 統一下單
     * @param out_trade_no
     * @param body
     * @param detail
     * @param total_fee
     * @param ip_address
     * @Date:2018-8-23
     * @return:
     */
    public static String unifiedOrder(String out_trade_no, String body, String detail, int total_fee,String ip_address) {
        StringBuffer xml = new StringBuffer();
        String data = null;
        try{
            xml.append("</xml>");
            if (body.length() > 32) {
                body = body.substring(0, 32);
            }
            SortedMap<String, String> parameters = new TreeMap();
            parameters.put("appid", WXPayConstants.APP_ID);
            parameters.put("body", body);
            parameters.put("detail", detail);
            parameters.put("mch_id", WXPayConstants.MCH_ID);
            parameters.put("nonce_str", genNonceStr());
            parameters.put("notify_url", "http://www.aidongsports.com/wx");
            parameters.put("out_trade_no", out_trade_no);
            parameters.put("fee_type", "CNY");
            parameters.put("spbill_create_ip", ip_address);
            parameters.put("total_fee", String.valueOf(total_fee));
            parameters.put("trade_type", "APP");
            parameters.put("sign", createSign(parameters, WXPayConstants.API_KEY));
            data = PostRequest("https://api.mch.weixin.qq.com/pay/unifiedorder",SortedMaptoXml(parameters));
        }catch (Exception e){
            e.printStackTrace();
        }
        return data;
    }
    /**
     * @Author: HONGLINCHEN
     * @Description:微信退款
     * @param out_trade_no 商戶訂單號
     * @param transaction_id 微信訂單號
     * @param total_fee 訂單總金額
     * @Date: 2018-8-23
     * @return:
     */
    public static String wxPayRefund(String out_trade_no, String transaction_id,String total_fee) {
        StringBuffer xml = new StringBuffer();
        String data = null;
        try {
            String nonceStr = genNonceStr();//生成32位隨機字串
            xml.append("</xml>");
            SortedMap<String,String> parameters = new TreeMap<String,String>();
            parameters.put("appid", WXPayConstants.APP_ID);
            parameters.put("mch_id", WXPayConstants.MCH_ID);
            parameters.put("nonce_str", nonceStr);
            parameters.put("out_trade_no", out_trade_no);
            parameters.put("transaction_id", transaction_id);
            parameters.put("out_refund_no", nonceStr);
            parameters.put("fee_type", "CNY");
            parameters.put("total_fee", total_fee);
            parameters.put("refund_fee", total_fee);
            parameters.put("op_user_id", WXPayConstants.MCH_ID);
            parameters.put("sign", createSign(parameters, WXPayConstants.API_KEY));
            data =SortedMaptoXml(parameters);
        } catch (Exception e) {
            System.err.println(e.getMessage());
            return null;
        }
        return data;
    }
    
    /**
     * @Author: HONGLINCHEN
     * @Description:微信部分退款
     * @param out_trade_no 商戶訂單號
     * @param transaction_id 微信訂單號
     * @param total_fee 訂單總金額
     * @param refund_fee 退款金額
     * @Date: 2018-8-23
     * @return:
     */
    public static String wxPayRefund(String out_trade_no, String transaction_id,String total_fee,String refund_fee) {
        StringBuffer xml = new StringBuffer();
        String data = null;
        try {
            String nonceStr = genNonceStr();//生成32位隨機字串
            xml.append("</xml>");
            SortedMap<String,String> parameters = new TreeMap<String,String>();
            parameters.put("appid", WXPayConstants.APP_ID);
            parameters.put("mch_id", WXPayConstants.MCH_ID);
            parameters.put("nonce_str", nonceStr);
            parameters.put("out_trade_no", out_trade_no);
            parameters.put("transaction_id", transaction_id);
            parameters.put("out_refund_no", nonceStr);
            parameters.put("fee_type", "CNY");
            parameters.put("total_fee", total_fee);
            parameters.put("refund_fee", refund_fee);//部退款金額
            parameters.put("op_user_id", WXPayConstants.MCH_ID);
            parameters.put("sign", createSign(parameters, WXPayConstants.API_KEY));
            data =SortedMaptoXml(parameters);
        } catch (Exception e) {
            System.err.println(e.getMessage());
            return null;
        }
        return data;
    }
    
    
    /**
     * 證書使用
     * 微信退款
     */
    public static String wxPayBack(String url, String data) throws Exception {
        KeyStore keyStore  = KeyStore.getInstance("PKCS12");
//        FileInputStream instream = new FileInputStream(new File("F:\\wx\\apiclient_cert.p12"));
        
        InputStream inputStream = WXPayUtil.class.getClassLoader().getResourceAsStream("apiclient_cert.p12");
        //FileInputStream instream=(FileInputStream)inputStream;
        String result="";
        try {
            keyStore.load(inputStream, WXPayConstants.MCH_ID.toCharArray());
        } finally {
            inputStream.close();
        }

        // Trust own CA and all self-signed certs
        SSLContext sslcontext = SSLContexts.custom()
                .loadKeyMaterial(keyStore, WXPayConstants.MCH_ID.toCharArray())
                .build();
        // Allow TLSv1 protocol only
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                sslcontext,
                new String[] { "TLSv1" },
                null,
                SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        CloseableHttpClient httpclient = HttpClients.custom()
                .setSSLSocketFactory(sslsf)
                .build();
        try {
            HttpPost httppost = new HttpPost("https://api.mch.weixin.qq.com/secapi/pay/refund");
            StringEntity entitys = new StringEntity(data);
            httppost.setEntity((HttpEntity) entitys);
            CloseableHttpResponse response = httpclient.execute(httppost);
            try {
                HttpEntity entity = response.getEntity();

                if (entity != null) {
                    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent()));
                    String text="";
                    String t="";
                    while ((text=bufferedReader.readLine()) != null) {
                        t+=text;
                    }
                    byte[] temp=t.getBytes("gbk");//這裡寫原編碼方式
                    String newStr=new String(temp,"utf-8");//這裡寫轉換後的編碼方式
                    result=newStr;
                }
                EntityUtils.consume(entity);
            } finally {
                response.close();
            }
        } finally {
            httpclient.close();
        }
        return result;
    }

    /**
     * XML格式字串轉換為Map
     * 微信支付 解析xml xml轉map  獲取prepay_id
     * @param strXML XML字串
     * @return XML資料轉換後的Map
     * @throws Exception
     */
    public static Map<String, String> xmlToMap(String strXML) throws Exception {
        try {
            Map<String, String> data = new HashMap<String, String>();
            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
            InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
            org.w3c.dom.Document doc = documentBuilder.parse(stream);
            doc.getDocumentElement().normalize();
            NodeList nodeList = doc.getDocumentElement().getChildNodes();
            for (int idx = 0; idx < nodeList.getLength(); ++idx) {
                Node node = nodeList.item(idx);
                if (node.getNodeType() == Node.ELEMENT_NODE) {
                    org.w3c.dom.Element element = (org.w3c.dom.Element) node;
                    data.put(element.getNodeName(), element.getTextContent());
                }
            }
            try {
                stream.close();
            } catch (Exception ex) {
                // do nothing
            }
            return data;
        } catch (Exception ex) {
            WXPayUtil.getLogger().warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);
            throw ex;
        }

    }

    /**
     * 獲取隨機字串 Nonce Str
     *
     * @return String 隨機字串
     */
    public static String generateNonceStr() {
        return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
    }

    /**
     * 生成 MD5
     *
     * @param data 待處理資料
     * @return MD5結果
     */
    public static String MD5(String data) throws Exception {
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] array = md.digest(data.getBytes("UTF-8"));
        StringBuilder sb = new StringBuilder();
        for (byte item : array) {
            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
        }
        return sb.toString().toUpperCase();
    }

    /**
     * 生成 HMACSHA256
     * @param data 待處理資料
     * @param key 金鑰
     * @return 加密結果
     * @throws Exception
     */
    public static String HMACSHA256(String data, String key) throws Exception {
        Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
        sha256_HMAC.init(secret_key);
        byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
        StringBuilder sb = new StringBuilder();
        for (byte item : array) {
            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
        }
        return sb.toString().toUpperCase();
    }

    /**
     * @Author: HONGLINCHEN
     * @Description:通過prepay_id 生成微信支付引數
     * @param prepay_id
     * @Date: 2018-8-23
     */
    public static  SortedMap<Object,Object> genPayRequest(String prepay_id) {
        SortedMap<Object,Object> parameters = new TreeMap<Object,Object>();
        parameters.put("appid", WXPayConstants.APP_ID);
        parameters.put("noncestr", genNonceStr());
        parameters.put("package", "Sign=WXPay");
        parameters.put("partnerid", WXPayConstants.MCH_ID);
        parameters.put("prepayid", prepay_id);
        parameters.put("timestamp",getCurrentTimestamp());
        parameters.put("sign", MD5.createSign("utf-8", parameters).toUpperCase());
        return parameters;
    }
    /**
     * @Author: HONGLINCHEN
     * @Description:請求值轉換為xml格式 SortedMap轉xml
     * @param params
     * @Date:2018-8-23
     */
    private static String SortedMaptoXml(SortedMap<String,String> params) {
        StringBuilder sb = new StringBuilder();
        Set es = params.entrySet();
        Iterator it = es.iterator();
        sb.append("<xml>\n");
        while(it.hasNext()) {
            Map.Entry entry = (Map.Entry)it.next();
            String k = (String)entry.getKey();
            Object v = entry.getValue();
            sb.append("<"+k+">");
            sb.append(v);
            sb.append("</"+k+">\n");
        }
        sb.append("</xml>");
        return sb.toString();
    }
    /**
     * 日誌
     * @return
     */
    public static Logger getLogger() {
        Logger logger = LoggerFactory.getLogger("wxpay java sdk");
        return logger;
    }

    /**
     * 生成32位隨機數字
     */
    public static String genNonceStr() {
        Random random = new Random();
        return MD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes());
    }
    /**
     * 獲取當前時間戳,單位秒
     * @return
     */
    public static long getCurrentTimestamp() {
        return System.currentTimeMillis()/1000;
    }


    /**
     * 生成 uuid, 即用來標識一筆單,也用做 nonce_str
     * @return
     */
    public static String generateUUID() {
        return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
    }

}

WXPayConstants類:

/**
 * @Author: HONGLINCHEN
 * @Description:微信支付引數
 * @Date:2018-8-23
 */
public class WXPayConstants {

    //請同時修改  androidmanifest.xml裡面 .PayActivityd裡的屬性<data android:scheme="wxb4ba3c02aa476ea1"/>為新設定的appid
//    public static final String APP_ID = "替換成自己的";
     public static final String APP_ID = "";
    //商戶號
//    public static final String MCH_ID = "替換成自己的";
    public static final String MCH_ID = "";
    //API金鑰,在商戶平臺設定
//    public static final  String API_KEY="替換成自己的";
    public static final  String API_KEY="";
    //商戶號 微信小程式使用
    public static final String APPLET_MCHID = "替換成自己的";

    //appid 微信小程式使用
    public static final String APPLET_APPID = "替換成自己的";


}

WeiXinPayController類:

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import static net.shopxx.pay.utils.WXPayUtil.genPayRequest;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyStore;
import java.util.SortedMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Author: HONGLINCHEN
 * @Description:微信支付
 * @Date: 2018-8-23
 */

public class WeiXinPayController {

    private static final Logger logger = Logger.getLogger(WeiXinPayController.class);

    //@RequestMapping(value = "/weixinpay",method = {RequestMethod.GET})
    public String weixinpay(HttpServletRequest request, HttpServletResponse response, Model model){
        return "wxpay";
    }

    //@RequestMapping(value = "/wechatRefund",method = {RequestMethod.GET})
    public String wecharrefund(HttpServletRequest request, HttpServletResponse response, Model model){
        return "wechatrefund";
    }

    /**
     * @Author: HONGLINCHEN
     * @Description: 微信支付
     * @param number
     * @param money
     * @param request
     * @param response
     * @param model
     * @Date: 2018-8-23
     */
   // @RequestMapping(value = "/wxPay",method = {RequestMethod.GET,RequestMethod.POST})
    public String weixinpay(String number, double money, HttpServletRequest request, HttpServletResponse response, Model model) throws IOException {
        String ipAddress = request.getRemoteAddr();//得到來訪者的IP地址
//      String ipAddress = RequestUtils.getClientIpAddress(request);
        String bodyStr = "參與活動", detailStr = "微信支付測試 1.0*1";
        String paySignXml = WXPayUtil.unifiedOrder(number, bodyStr, detailStr, (int)(money*100), ipAddress);
        System.err.println("加密以後的支付引數\n"+paySignXml);
        try {
            String prepay_id = WXPayUtil.xmlToMap(paySignXml).get("prepay_id").toString();
            if(prepay_id!=null&&!"".equals(prepay_id)){
                SortedMap<Object,Object> pay = genPayRequest(prepay_id);
                System.err.println("實際支付引數\n"+pay);
                response.getWriter().print(JSON.toJSON(pay));
            }else{
                model.addAttribute("result","請求支付引數錯誤!");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * @Author: HONGLINCHEN
     * @Description: 微信取消訂單
     * @param number
     * @param money
     * @param request
     * @param response
     * @param model
     * @Date:2018-8-23
     */
   // @RequestMapping(value = "/wxPayCancelOrder",method = {RequestMethod.GET,RequestMethod.POST})
    public String wxPayCancelOrder(String number, double money, HttpServletRequest request, HttpServletResponse response, Model model) throws IOException {
        String ipAddress = request.getRemoteAddr();//得到來訪者的IP地址
       // String ipAddress = RequestUtils.getClientIpAddress(request);
        String bodyStr = "參與活動", detailStr = "微信支付測試 1.0*1";
        String paySignXml = WXPayUtil.unifiedOrder(number, bodyStr, detailStr, (int)(money*100), ipAddress);
        System.err.println("加密以後的支付引數\n"+paySignXml);
        try {
            String prepay_id = WXPayUtil.xmlToMap(paySignXml).get("prepay_id").toString();
            if(prepay_id!=null&&!"".equals(prepay_id)){
                SortedMap<Object,Object> pay = genPayRequest(prepay_id);
                System.err.println("實際支付引數\n"+pay);
                response.getWriter().print(JSON.toJSON(pay));
            }else{
                model.addAttribute("result","請求支付引數錯誤!");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * @Author: HONGLINCHEN  微信退款三種方式,隨便哪一個都可以
     * @Description:微信退款   注意::微信金額的單位是分 所以這裡要X100 轉成int是因為 退款的時候不能有小數點
     * @param merchantNumber 商戶這邊的訂單號
     * @param wxTransactionNumber 微信那邊的交易單號
     * @param totalFee 訂單的金額
     * @Date: 2018-8-23
     */
   // @RequestMapping(value = "/wxPayRefundTwo", method = { RequestMethod.GET, RequestMethod.POST })
    public String qxwxsign(String merchantNumber,String wxTransactionNumber,double totalFee,HttpServletResponse response, HttpServletRequest request) throws IOException {
        String param = WXPayUtil.wxPayRefund(merchantNumber,wxTransactionNumber,String.valueOf((int)(totalFee*100)));
        System.err.println("param"+param);
        String result = "";
        String url = "https://api.mch.weixin.qq.com/secapi/pay/refund";
        try {
            result = WXPayUtil.wxPayBack(url, param);
        } catch (Exception e) {
            e.printStackTrace();
        }
        String tt = "2018072001201611180592735689";
        Pattern p = Pattern.compile("\\.*(\\w{" + tt.length() + "})\\.*");
        int st = result.indexOf("<refund_id>");
        String res = "";
        if (st >= 0) {
            int en = result.indexOf("</refund_id>");
            res = result.substring(st, en);
            Matcher m = p.matcher(res);
            if (m.find()) {
                res = m.group(1);
            }
            if (res.length() > 0) {
                result = "code:1,msg:退款成功";
            } else {
                result = "code:-1,msg:退款失敗";
            }
            response.getWriter().print(JSON.toJSON(result));
        }
        return null;
    }

    /**
     * @Author: HONGLINCHEN
     * @Description:微信退款   注意::微信金額的單位是分 所以這裡要X100 轉成int是因為 退款的時候不能有小數點
     * @param merchantNumber 商戶這邊的訂單號
     * @param wxTransactionNumber 微信那邊的交易單號
     * @param totalFee 訂單的金額
     * @Date: 2018-8-23
     */
    //@RequestMapping(value = "/wxPayRefund", method = { RequestMethod.GET, RequestMethod.POST })
    public Object wxPayRefund(String merchantNumber,String wxTransactionNumber,double totalFee) {
        try{
            KeyStore keyStore = KeyStore.getInstance("PKCS12");
            FileInputStream instream = new FileInputStream(new File("D:\\apiclient_cert.p12"));
            try {
                keyStore.load(instream, WXPayConstants.MCH_ID.toCharArray());
            }finally {
                instream.close();
            }
            // Trust own CA and all self-signed certs
  SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, WXPayConstants.MCH_ID.toCharArray()).build();
            // Allow TLSv1 protocol only
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                    sslcontext, new String[] { "TLSv1" }, null,
                    SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
            CloseableHttpClient httpclient = HttpClients.custom()
                    .setSSLSocketFactory(sslsf).build();
            // HttpGet httpget = new
            // HttpGet("https://api.mch.weixin.qq.com/secapi/pay/refund");
            HttpPost httppost = new HttpPost("https://api.mch.weixin.qq.com/secapi/pay/refund");
            //微信金額的單位是分 所以這裡要X100 轉成int是因為 退款的時候不能有小數點
            String xml = WXPayUtil.wxPayRefund(merchantNumber,wxTransactionNumber,String.valueOf((int)(totalFee*100)));
            try {
                StringEntity se = new StringEntity(xml);
                httppost.setEntity(se);
                System.out.println("executing request" + httppost.getRequestLine());
                CloseableHttpResponse responseEntry = httpclient.execute(httppost);
                try {
                    HttpEntity entity = responseEntry.getEntity();
                    System.out.println(responseEntry.getStatusLine());
                    if (entity != null) {
                        System.out.println("Response content length: "+ entity.getContentLength());
                        SAXReader saxReader = new SAXReader();
                        Document document = saxReader.read(entity.getContent());
                        Element rootElt = document.getRootElement();
                        System.out.println("根節點:" + rootElt.getName());
                        System.out.println("==="+rootElt.elementText("result_code"));
                        System.out.println("==="+rootElt.elementText("return_msg"));
                        String resultCode = rootElt.elementText("result_code");
                        JSONObject result = new JSONObject();
                        Document documentXml = DocumentHelper.parseText(xml);
                        Element rootEltXml = documentXml.getRootElement();
                        if(resultCode.equals("SUCCESS")){
                            System.out.println("=================prepay_id===================="+ rootElt.elementText("prepay_id"));
                            System.out.println("=================sign===================="+ rootEltXml.elementText("sign"));
                            result.put("weixinPayUrl", rootElt.elementText("code_url"));
                            result.put("prepayId", rootElt.elementText("prepay_id"));
                            result.put("status","success");
                            result.put("msg","success");
                        }else{
                            result.put("status","false");
                            result.put("msg",rootElt.elementText("err_code_des"));
                        }
                        return result;
                    }
                    EntityUtils.consume(entity);
                }
                finally {
                    responseEntry.close();
                }
            }
            finally {
                httpclient.close();
            }
            return null;
        }catch(Exception e){
            e.printStackTrace();
            JSONObject result = new JSONObject();
            result.put("status","error");
            result.put("msg",e.getMessage());
            return result;
        }
    }


    /**
     * @Author: HONGLINCHEN
     * @Description:微信退款   注意::微信金額的單位是分 所以這裡要X100 轉成int是因為 退款的時候不能有小數點
     * @param merchantNumber 商戶這邊的訂單號
     * @param wxTransactionNumber 微信那邊的交易單號
     * @param totalFee 訂單的金額
     * @Date: 2018-8-23
     */
    //@RequestMapping(value = "/wxPayRefundone", method = { RequestMethod.GET, RequestMethod.POST })
    public Object wxPayRefundone(String merchantNumber,String wxTransactionNumber,double totalFee) {
        Object object = ClientCustomSSL.setUrl(merchantNumber,wxTransactionNumber,totalFee);
        return object;
    }
}

MD5Util類:

import java.security.MessageDigest;
public class MD5Util {
   private static String byteArrayToHexString(byte b[]) {
      StringBuffer resultSb = new StringBuffer();
      for (int i = 0; i < b.length; i++)
         resultSb.append(byteToHexString(b[i]));

      return resultSb.toString();
   }

   private static String byteToHexString(byte b) {
      int n = b;
      if (n < 0)
         n += 256;
      int d1 = n / 16;
      int d2 = n % 16;
      return hexDigits[d1] + hexDigits[d2];
   }

   public static String MD5Encode(String origin, String charsetname) {
      String resultString = null;
      try {
         resultString = new String(origin);
         MessageDigest md = MessageDigest.getInstance("MD5");
         if (charsetname == null || "".equals(charsetname))
            resultString = byteArrayToHexString(md.digest(resultString
                  .getBytes()));
         else
            resultString = byteArrayToHexString(md.digest(resultString
                  .getBytes(charsetname)));
      } catch (Exception exception) {
      }
      return resultString;
   }

   private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",
         "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };

}

MD5類:

import java.security.MessageDigest;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
/**
 * @Author: HONGLINCHEN
 * @Description: MD5 工具類
 * @Date: 2018-8-23 
 */
public class MD5 {
   public final static String getMessageDigest(byte[] buffer) {
      char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
      try {
         MessageDigest mdTemp = MessageDigest.getInstance("MD5");
         mdTemp.update(buffer);
         byte[] md = mdTemp.digest();
         int j = md.length;
         char str[] = new char[j * 2];
         int k = 0;
         for (int i = 0; i < j; i++) {
            byte byte0 = md[i];
            str[k++] = hexDigits[byte0 >>> 4 & 0xf];
            str[k++] = hexDigits[byte0 & 0xf];
         }
         return new String(str);
      } catch (Exception e) {
         return null;
      }
   }
   private static String byteArrayToHexString(byte b[]) {
        StringBuffer resultSb = new StringBuffer();  
        for (int i = 0; i < b.length; i++)  
            resultSb.append(byteToHexString(b[i]));  
  
        return resultSb.toString();  
    }  
  
    private static String byteToHexString(byte b) {  
        int n = b;  
        if (n < 0)  
            n += 256;  
        int d1 = n / 16;  
        int d2 = n % 16;  
        return hexDigits[d1] + hexDigits[d2];  
    }  
  
    public static String MD5Encode(String origin, String charsetname) {  
        String resultString = null;
        try {  
            resultString = new String(origin);  
            MessageDigest md = MessageDigest.getIns