微信支付(青蛙刷臉支付、小程序)

目錄

 

DTO

Json對象或字符串轉xml

PathUtil

通用http發送方法

微信工具類

服務商賬戶信息配置

微信支付請求參數配置

微信支付工具類


DTO

package com.slf.utils.dto.wechat;

import lombok.Data;

import java.math.BigDecimal;

/**
 * @Author: LiuYong
 * @Date:2019/12/17 16:18
 * @Description: TODO 描述
 */
@Data
public class WechatDto {

    /**設備編號*/
    private String dcode;

   /**訂單編號*/
    private String orderNumber;

    /**總金額(單位:分)*/
    private BigDecimal totalFee;

    /**設備ip地址*/
    private String ip;

   /**用戶標識*/
    private String openid;

    /**人臉憑證*/
    private String faceCode;
    /**授權碼*/
    private String authCode;
    /***/
    private Long faceCodeType;
    /**特約商戶appid*/
    private String subAppid;
    /**特約商戶號*/
    private String subMchId;

    /**服務商公衆賬號ID*/
    private String appId;
    /**服務商商戶號*/
    private String mchId;
    /**服務商商戶支付密鑰*/
    private String key;
    /**商戶名稱*/
    private String bName;

}

Json對象或字符串轉xml

package com.slf.utils.utils;


import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;

import java.util.logging.Logger;

/**
 * json對象或字符串轉xml
 * @Author: LiuYong
 * @Date:2019/12/11 11:21
 * @Description: TODO json對象或字符串轉xml
 */
public class JsonAndXmlUtils {
    private static Logger logger = Logger.getLogger(JsonAndXmlUtils.class.getName());


    public static void main(String[] args) throws Exception
    {
        String jsonInput = "{\"nonce_str\":\"b927722419c52622651a871d1d9ed8b2\",\"device_info\":\"1000\",\"out_trade_no\":\"1403213376\",\"appid\":\"wxE421b1c4370ec43b\",\"total_fee\":\"1\",\"sign\":\"3CA89B5870F944736C657979192E1CF4\",\"trade_type\":\"JSAPI\",\"attach\":\"att1\",\"body\":\"JSAPI支付測試\",\"mch_id\":\"10000100\",\"notify_url\":\"http://wxpay.weixin.qq.com/pub_v2/pay/notify.php\",\"spbill_create_ip\":\"127.0.0.1\"}\n";
        String jsonToXml = JsonAndXmlUtils.jsonToXml(jsonInput);
        System.out.println("jsonToXml:\n"+jsonToXml);
        JSONObject jsonObject = xmlToJson(jsonToXml);
        System.out.println("xmlToJson:\n"+jsonObject.toJSONString());
        System.out.println("WxJsonToXml:\n"+WxJsonToXml(jsonObject));
    }


    /**
     * xml字符串轉json對象
     * @Author LiuYong
     * @Date 2019/12/11 11:40
     * @Description TODO xml字符串轉json對象
     * @param xmlStr
     * @return JSONObject
     **/
    public static JSONObject xmlToJson(String xmlStr){
        XmlMapper xmlMapper = new XmlMapper();
        JSONObject jsonObject1=null;
        try{
            jsonObject1 = xmlMapper.readValue(xmlStr, JSONObject.class);
        }catch (Exception e){
            logger.info("ERROR com.slf.utils.utils.JsonAndXmlUtils.xmlToJson 異常:"+e.getMessage());
        }
        return jsonObject1;
    }

    /**
     * json字符串轉xml字符串
     * @Author LiuYong
     * @Date 2019/12/11 11:45
     * @Description TODO json字符串轉xml字符串
     * @param json
     * @return String
     **/
    public static String jsonToXml(String json){
        JSONObject jsonObject = JSONObject.parseObject(json);
        XmlMapper xmlMapper = new XmlMapper();
        String s = null;
        try{
            s = xmlMapper.writeValueAsString(jsonObject);
        }catch (Exception e){
            logger.info("ERROR com.slf.utils.utils.JsonAndXmlUtils.jsonToXml 異常:"+e.getMessage());
        }
        return s;
    }

    public static String WxJsonToXml(JSONObject jsonObject){
        return jsonToXml(jsonObject.toJSONString()).replace("JSONObject","xml");
    }

}

PathUtil

package com.slf.utils.utils;

import org.springframework.util.ResourceUtils;

import java.io.*;

/**
 * @Author: LiuYong
 * @Date:2019/12/16 11:29
 * @Description: TODO 描述
 */
public class PathUtil {

    public static void main(String[] args) throws Exception{
        System.out.println(ResourceUtils.getURL("classpath:").getPath());
        System.out.println(PathUtil.class.getClassLoader().getResource(".").getPath());
//        String readfile = readfile("D:\\Software\\IdeaProjects\\slf-app-api\\slf-pay\\slf-pay-service\\src\\main\\resources\\static\\sq\\success.html");
//        System.out.println(readfile);
    }

    public static String getPath(String url) throws Exception {
        //路徑
        File path = new File(ResourceUtils.getURL("classpath:").getPath());
        if (!path.exists()) {
            path = new File("");
        }
//如果上傳目錄爲/static/images/upload/,則可以如下獲取
        File upload = new File(path.getAbsolutePath(), "static/"+url+"/");

        if (!upload.exists()) {
            upload.mkdirs();
//            System.out.println(upload.getAbsolutePath());
            //在開發測試模式時,得到地址爲:{項目跟目錄}/target/static/images/upload/
            //在打成jar正式發佈時,得到的地址爲:{發佈jar包目錄}/static/images/upload/
        }
        return upload.getAbsolutePath();
    }

    public static String readfile(String filePath){
        File file = new File(filePath);
        InputStream input = null;
        try {
            input = new FileInputStream(file);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        StringBuffer buffer = new StringBuffer();
        byte[] bytes = new byte[1024];
        try {
            for(int n ; (n = input.read(bytes))!=-1 ; ){
                buffer.append(new String(bytes,0,n,"UTF-8"));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return buffer.toString();
    }
}

通用http發送方法

package com.slf.utils.utils.http;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.*;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLConnection;
import java.security.cert.X509Certificate;

/**
 * 通用http發送方法
 *
 */
public class HttpUtils
{
    private static final Logger log = LoggerFactory.getLogger(HttpUtils.class);

    public static String getIpAddr(HttpServletRequest request) {
       String ip = request.getHeader("x-forwarded-for");
      if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
          ip = request.getHeader("Proxy-Client-IP");
      }
      if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
          ip = request.getHeader("WL-Proxy-Client-IP");
       }
     if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
           ip = request.getRemoteAddr();
      }
     return ip;
    }
    /**
     * 向指定 URL 發送GET方法的請求
     *
     * @param url 發送請求的 URL
     * @param param 請求參數,請求參數應該是 name1=value1&name2=value2 的形式。
     * @return 所代表遠程資源的響應結果
     */
    public static String sendGet(String url, String param)
    {
        StringBuilder result = new StringBuilder();
        BufferedReader in = null;
        try
        {
            String urlNameString = url + "?" + param;
            log.info("sendGet - {}", urlNameString);
            System.out.println(urlNameString);
            URL realUrl = new URL(urlNameString);
            URLConnection connection = realUrl.openConnection();
            connection.setRequestProperty("accept", "*/*");
            connection.setRequestProperty("connection", "Keep-Alive");
            connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            connection.connect();
            in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            String line;
            while ((line = in.readLine()) != null)
            {
                result.append(line);
            }
            log.info("recv - {}", result);
        }
        catch (ConnectException e)
        {
            log.error("調用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e);
        }
        catch (SocketTimeoutException e)
        {
            log.error("調用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e);
        }
        catch (IOException e)
        {
            log.error("調用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e);
        }
        catch (Exception e)
        {
            log.error("調用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e);
        }
        finally
        {
            try
            {
                if (in != null)
                {
                    in.close();
                }
            }
            catch (Exception ex)
            {
                log.error("調用in.close Exception, url=" + url + ",param=" + param, ex);
            }
        }
        return result.toString();
    }

    /**
     * 向指定 URL 發送POST方法的請求
     *
     * @param url 發送請求的 URL
     * @param param 請求參數,請求參數應該是 name1=value1&name2=value2 的形式。
     * @return 所代表遠程資源的響應結果
     */
    public static String sendPost(String url, String param)
    {
        PrintWriter out = null;
        BufferedReader in = null;
        StringBuilder result = new StringBuilder();
        try
        {
            String urlNameString = url + "?" + param;
            log.info("sendPost - {}", urlNameString);
            URL realUrl = new URL(urlNameString);
            URLConnection conn = realUrl.openConnection();
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            conn.setRequestProperty("Accept-Charset", "utf-8");
            conn.setRequestProperty("contentType", "utf-8");
            conn.setDoOutput(true);
            conn.setDoInput(true);
            out = new PrintWriter(conn.getOutputStream());
            out.print(param);
            out.flush();
            in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
            String line;
            while ((line = in.readLine()) != null)
            {
                result.append(line);
            }
            log.info("recv - {}", result);
        }
        catch (ConnectException e)
        {
            log.error("調用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e);
        }
        catch (SocketTimeoutException e)
        {
            log.error("調用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e);
        }
        catch (IOException e)
        {
            log.error("調用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e);
        }
        catch (Exception e)
        {
            log.error("調用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e);
        }
        finally
        {
            try
            {
                if (out != null)
                {
                    out.close();
                }
                if (in != null)
                {
                    in.close();
                }
            }
            catch (IOException ex)
            {
                log.error("調用in.close Exception, url=" + url + ",param=" + param, ex);
            }
        }
        return result.toString();
    }

    public static String sendSSLPost(String url, String param)
    {
        StringBuilder result = new StringBuilder();
        String urlNameString = url + "?" + param;
        try
        {
            log.info("sendSSLPost - {}", urlNameString);
            SSLContext sc = SSLContext.getInstance("SSL");
            sc.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom());
            URL console = new URL(urlNameString);
            HttpsURLConnection conn = (HttpsURLConnection) console.openConnection();
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            conn.setRequestProperty("Accept-Charset", "utf-8");
            conn.setRequestProperty("contentType", "utf-8");
            conn.setDoOutput(true);
            conn.setDoInput(true);

            conn.setSSLSocketFactory(sc.getSocketFactory());
            conn.setHostnameVerifier(new TrustAnyHostnameVerifier());
            conn.connect();
            InputStream is = conn.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            String ret = "";
            while ((ret = br.readLine()) != null)
            {
                if (ret != null && !ret.trim().equals(""))
                {
                    result.append(new String(ret.getBytes("ISO-8859-1"), "utf-8"));
                }
            }
            log.info("recv - {}", result);
            conn.disconnect();
            br.close();
        }
        catch (ConnectException e)
        {
            log.error("調用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e);
        }
        catch (SocketTimeoutException e)
        {
            log.error("調用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e);
        }
        catch (IOException e)
        {
            log.error("調用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e);
        }
        catch (Exception e)
        {
            log.error("調用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e);
        }
        return result.toString();
    }

    private static class TrustAnyTrustManager implements X509TrustManager
    {
        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType)
        {
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType)
        {
        }

        @Override
        public X509Certificate[] getAcceptedIssuers()
        {
            return new X509Certificate[] {};
        }
    }

    private static class TrustAnyHostnameVerifier implements HostnameVerifier
    {
        @Override
        public boolean verify(String hostname, SSLSession session)
        {
            return true;
        }
    }
}

微信工具類

package com.slf.utils.utils.wechat;

import org.apache.commons.lang3.RandomStringUtils;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
import java.util.logging.Logger;

/**
 * 微信工具類
 * @Author: LiuYong
 * @Date:2019/12/11 11:06
 * @Description: TODO 微信工具類
 */
public class WeChatTool {
    private static Logger logger = Logger.getLogger(WeChatTool.class.getName());


    /**
     * 隨機字符串生成
     * @Author LiuYong
     * @Date 2019/12/12 9:43
     * @Description TODO
     * @param length 生成的字符串長度
     * @param useLetters 生成的字符串可以包括字母字符
     * @param useNumbers 生成的字符串可以包含數字字符
     * @return
     **/
    public static String generatedString(int length,boolean useLetters,boolean useNumbers){
        return RandomStringUtils.random(length, useLetters, useNumbers);
    }

    /**
     * Sign簽名生成方法
     * @Author LiuYong
     * @Date 2019/12/11 11:13
     * @Description TODO Sign簽名生成方法
     * @param map 自定義參數
     * @param key 商戶KEY
     * @param type 簽名類型
     * @return
     **/
    public static String getSign(Map<String,Object> map,String key,String type){
        StringBuilder sb = new StringBuilder();
        String result =null;
        try{
            ArrayList<String> list = new ArrayList<String>();
            for(Map.Entry<String,Object> entry:map.entrySet()){
                if(entry.getValue()!=""){
                    list.add(entry.getKey() + "=" + entry.getValue() + "&");
                }
            }
            int size = list.size();
            String [] arrayToSort = list.toArray(new String[size]);
            Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
            for(int i = 0; i < size; i ++) {
                sb.append(arrayToSort[i]);
            }
            sb.append("key=" + key);
            result = sb.toString();
            if(type.equals(WeChatConstants.MD5)){
                result=MD5(result);
            }else if(type.equals(WeChatConstants.HMACSHA256)){
                result=HMACSHA256(result,key);
            }
        }catch (Exception e){
            logger.info("ERROR com.slf.utils.utils.wechat.WeChatTool.getSign :"+e.getMessage());
            return null;
        }
        return result;
    }


    /**
     * 生成 MD5
     * @Author LiuYong
     * @Date 2019/12/11 17:08
     * @Description TODO 生成 MD5
     * @param  data 待處理數據
     * @return 加密結果
     **/
    public static String MD5(String data) {
        StringBuilder sb = new StringBuilder();
        try{
            java.security.MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] array = md.digest(data.getBytes("UTF-8"));
            for (byte item : array) {
                sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
            }
        }catch (Exception e){
            logger.info("ERROR com.slf.utils.utils.wechat.WeChatTool.MD5 :"+e.getMessage());
            return null;
        }
        return sb.toString().toUpperCase();
    }

    /**
     * 生成 HMACSHA256
     * @Author LiuYong
     * @Date 2019/12/11 17:00
     * @Description TODO 生成 HMACSHA256
     * @param data 待處理數據
     * @param key 密鑰
     * @return 加密結果
     **/
    public static String HMACSHA256(String data, String key)  {
        StringBuilder sb = new StringBuilder();
        try{
            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"));
            for (byte item : array) {
                sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
            }
        }catch (Exception e){
            logger.info("ERROR com.slf.utils.utils.wechat.WeChatTool.HMACSHA256 :"+e.getMessage());
            return null;
        }
        return sb.toString().toUpperCase();
    }

}

服務商賬戶信息配置

package com.slf.utils.utils.wechat;

/**
 * 微信參數配置
 * @Author: LiuYong
 * @Date:2019/12/11 10:45
 * @Description: TODO 服務商賬戶信息配置
 */
public class WeChatConstants {

    /**
     * 服務商公衆賬號ID
     * appid是商戶在微信申請公衆號成功後分配的帳號ID
     */
    protected static final String APPID="";

    /**
     * 特約商戶公衆賬號ID
     * 服務商模式專有參數
     */
    protected static final String SUB_APPID="SUB_APPID";
    /**
     * 服務商商戶號
     * 微信支付分配的商戶號
     */
    protected static final String MCH_ID="";
    /**
     * 服務商商戶支付密鑰
     */
    protected static final String KEY="";

    /**簽名類型*/
    public static final String MD5="MD5";
    public static final String HMACSHA256="HMACSHA256";


    /*************************************************/
    /**
     * 小程序密鑰
     */
    protected static final String APP_SECRET="";
    /**
     * 小程序appid
     */
    protected static final String APP_APPID="";


}

微信支付請求參數配置

package com.slf.utils.utils.wechat;

/**
 * @Author: LiuYong
 * @Date:2019/12/12 12:02
 * @Description: TODO 微信支付請求參數配置
 */
public class WeChatPayConstants {

    /**
     * 主要的API
     */
    public static final String DOMAIN_API = "https://api.mch.weixin.qq.com/";


    /**----------------↓↓↓↓↓↓刷臉支付後端接口↓↓↓↓↓↓----------------*/
    /**接口文檔: https://pay.weixin.qq.com/wiki/doc/wxfacepay/develop/backend.html#%E5%88%B7%E8%84%B8%E6%94%AF%E4%BB%98%E5%90%8E%E7%AB%AF%E6%8E%A5%E5%8F%A3
     * */


    /**
     * 刷臉支付接口
     * 是否需要證書:不需要
     */
    public static final String PAY_FACEPAY = "pay/facepay";
    /**
     * 刷臉支付查詢接口
     * 是否需要證書:不需要
     */
    public static final String PAY_FACEPAYQUERY = "pay/facepayquery";

    /**
     *  刷臉支付退款查詢接口
     * 是否需要證書:不需要
     */
    public static final String PAY_REFUNDQUERY = "pay/refundquery";
    /**
     * 刷臉支付撤銷訂單接口
     * 是否需要證書:需要
     */
    public static final String SECAPI_PAY_FACEPAYREVERSE = "secapi/pay/facepayreverse";

    /**----------------↑↑↑↑↑↑刷臉支付後端接口↑↑↑↑↑↑----------------*/

    /**----------------↓↓↓↓↓↓掃碼支付接口↓↓↓↓↓↓----------------*/
    /**https://pay.weixin.qq.com/wiki/doc/api/micropay_sl.php?chapter=9_2*/
    /**
     * 掃碼支付
     * 是否需要證書:不需要
     */
    public static final String PAY_MICROPAY = "pay/micropay";
    /**
     * 掃碼支付查詢訂單
     * 是否需要證書:不需要
     */
    public static final String PAY_ORDERQUERY = "pay/orderquery";
    /**
     * 掃碼支付撤銷訂單
     * 是否需要證書:請求需要雙向證書
     */
    public static final String SECAPI_PAY_REVERSE = "secapi/pay/reverse";
    /**
     * 申請退款:服務商模式下,退款接口需要單獨申請權限
     * 是否需要證書:請求需要雙向證書
     */
    public static final String SECAPI_PAY_REFUND = "secapi/pay/refund";
    /**
     * 下載對賬單
     * 是否需要證書:不需要
     */
    public static final String PAY_DOWNLOADBILL = "pay/downloadbill";
    /**
     * 授權碼查詢openid
     */
    public static final String TOOLS_AUTHCIDETOOPENID = "tools/authcodetoopenid";
    /**----------------↑↑↑↑↑↑掃碼支付接口↑↑↑↑↑↑----------------*/






     /**----------------↓↓↓↓↓↓人臉付押金接口↓↓↓↓↓↓----------------*/
    /**接口文檔:https://pay.weixin.qq.com/wiki/doc/api/deposit_sl.php?chapter=27_0&index=1*/

    /**
     * 支付押金(人臉支付)
     * 是否需要證書:不需要
     * 簽名方式:HMAC-SHA256
     */
    public static final String DEPOSIT_FACEPAY = "deposit/facepay";
    /**
     * 支付押金(付款碼支付)
     * 是否需要證書:不需要
     * 簽名方式:HMAC-SHA256
     */
    public static final String DEPOSIT_MICROPAY = "deposit/micropay";
    /**
     * 查詢訂單
     * 是否需要證書:不需要
     * 簽名方式:HMAC-SHA256
     */
    public static final String DEPOSIT_ORDERQUERY = "deposit/orderquery";

    /**
     * 撤銷訂單
     * 是否需要證書:請求需要雙向證書
     * 簽名方式:HMAC-SHA256
     */
    public static final String DEPOSIT_REVERSE = "deposit/reverse";

    /**
     * 消費押金
     * 是否需要證書:請求需要雙向證書
     * 簽名方式:HMAC-SHA256
     */
    public static final String DEPOSIT_CONSUME = "deposit/consume";
    /**
     * 申請退款(押金)
     * 是否需要證書:請求需要雙向證書
     * 簽名方式:HMAC-SHA256
     */
    public static final String DEPOSIT_REFUND = "deposit/refund";
    /**
     * 查詢退款(押金)
     * 是否需要證書:請求需要雙向證書
     * 簽名方式:HMAC-SHA256
     */
    public static final String DEPOSIT_REFUNDQUERY = "deposit/refundquery";

}

微信支付工具類

package com.slf.utils.utils.wechat;

import com.alibaba.fastjson.JSONObject;
import com.slf.utils.dto.wechat.WechatDto;
import com.slf.utils.utils.JsonAndXmlUtils;
import com.slf.utils.utils.PathUtil;
import com.slf.utils.utils.http.HttpUtils;
import com.slf.utils.utils.text.Convert;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
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.TrustStrategy;
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.SSLContextBuilder;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.math.BigDecimal;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;


/**
 * 微信小程序支付工具類
 *
 * @Author: LiuYong
 * @Date:2019/12/12 11:11
 * @Description: TODO 微信小程序支付工具類
 */

public class WxPayUtil {
    private static Logger log = Logger.getLogger(WxPayUtil.class.getName());


    public static void main(String[] args) {
        Map<String, Object> map = new HashMap<>();
        map.put("appid", "服務商的APPID");
        map.put("mch_id", "商戶號");
        map.put("sub_appid", "子商戶公衆賬號ID");
        map.put("sub_mch_id", "子商戶號");
        map.put("nonce_str", WeChatTool.generatedString(32, true, true));
        map.put("out_trade_no", "663742984239583232");
        map.put("out_refund_no", "tk01");
        map.put("total_fee", "1");
        map.put("refund_fee", "1");
        map.put("sign", WeChatTool.getSign(map, "商戶支付MD5字符串", WeChatConstants.MD5));
        JSONObject json = new JSONObject(map);
        JSONObject jsonObject1 = WxPayUtil.doRefundRequest("商戶號", json);
        System.out.println(jsonObject1);
    }


    /**
     * 登錄憑證校驗
     *
     * @param jsCode
     * @param appAppId  小程序appid
     * @param appSecret 小程序密鑰
     * @return
     */
    public static JSONObject openid(String jsCode, String appAppId, String appSecret) {
        String s = HttpUtils.sendGet("https://api.weixin.qq.com/sns/jscode2session",
                "appid=" + appAppId + "&secret=" + appSecret + "&js_code=" + jsCode + "&grant_type=authorization_code");
        System.out.println(s);
        return JSONObject.parseObject(s);
    }


    /**
     * 刷臉支付接口
     *
     * @param jsonObject 參數對象
     * @param key        商戶支付密鑰
     * @return
     * @Author LiuYong
     * @Date 2019/12/16 10:47
     * @Description TODO  刷臉支付接口
     **/
    public static JSONObject facepay(JSONObject jsonObject, String key) {
        log.info("Submit payment:" + jsonObject);
        String data = JsonAndXmlUtils.WxJsonToXml(jsonObject);
        try {
            CloseableHttpClient httpClient = notReadCertificate();
            String s = HttpPost(WeChatPayConstants.PAY_FACEPAY, data, httpClient);
            log.info("Paying with Your Face:" + s);
            JSONObject result = JsonAndXmlUtils.xmlToJson(s);
            System.out.println(result);
            log.info("status:" + result.toJSONString());
            if (result.getString("return_code").equalsIgnoreCase("SUCCESS") &&
                    result.getString("result_code").equalsIgnoreCase("SUCCESS")) {
                return result;
            } else {
                payRevocation(jsonObject, key, false);
                return result;
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 刷臉支付接口
     *
     * @param wechatDto
     * @return
     */
    public static JSONObject facepay(WechatDto wechatDto) {
        JSONObject pay = new JSONObject();
        pay.put("appid", wechatDto.getAppId());
        pay.put("mch_id", wechatDto.getMchId());
        pay.put("sub_appid", wechatDto.getSubAppid());
        pay.put("sub_mch_id", wechatDto.getSubMchId());
        pay.put("nonce_str", WeChatTool.generatedString(32, true, true));
        pay.put("body", wechatDto.getBName());
        pay.put("out_trade_no", wechatDto.getOrderNumber());
        pay.put("total_fee", getTotalFee(wechatDto.getTotalFee()));
        pay.put("spbill_create_ip", wechatDto.getIp());
        pay.put("openid", wechatDto.getOpenid());
        pay.put("face_code", wechatDto.getFaceCode());
        pay.put("sign", WeChatTool.getSign(pay, wechatDto.getKey(), WeChatConstants.MD5));
        return facepay(pay, wechatDto.getKey());
    }

    /**
     * 付款碼支付
     *
     * @param wechatDto
     * @return
     */
    public static JSONObject micropay(WechatDto wechatDto) {
        System.out.println(wechatDto);
        JSONObject pay = new JSONObject();
        pay.put("appid", wechatDto.getAppId());
        pay.put("sub_appid", wechatDto.getSubAppid());
        pay.put("mch_id", wechatDto.getMchId());
        pay.put("sub_mch_id", wechatDto.getSubMchId());
        pay.put("nonce_str", WeChatTool.generatedString(32, true, true));
        pay.put("body", wechatDto.getBName());
        pay.put("out_trade_no", wechatDto.getOrderNumber());
        pay.put("total_fee", getTotalFee(wechatDto.getTotalFee()));
        pay.put("spbill_create_ip", wechatDto.getIp());
        pay.put("auth_code", wechatDto.getAuthCode());
        pay.put("sign", WeChatTool.getSign(pay, wechatDto.getKey(), WeChatConstants.MD5));
        log.info("Scan the payment parameters:" + pay.toJSONString());
        return micropay(pay, wechatDto.getKey());
    }

    /**
     * @param
     * @return
     * @Author LiuYong
     * @Date 2020-01-02 13:45
     * @Description TODO 微信金額計算,元轉分
     **/
    public static Long getTotalFee(BigDecimal bigDecimal) {
        BigDecimal b1 = new BigDecimal(bigDecimal.toString());
        BigDecimal b2 = new BigDecimal("100");
        return Convert.toLong(b1.multiply(b2));
    }

    /**
     * 付款碼支付
     *
     * @param jsonObject 參數對象
     * @param key        商戶支付密鑰
     * @return
     * @Author LiuYong
     * @Date 2019/12/23 9:24
     * @Description TODO
     **/
    public static JSONObject micropay(JSONObject jsonObject, String key) {
        log.info("Submit payment:" + jsonObject);
        String data = JsonAndXmlUtils.WxJsonToXml(jsonObject);
        try {
            CloseableHttpClient httpClient = notReadCertificate();
            String s = HttpPost(WeChatPayConstants.PAY_MICROPAY, data, httpClient);
            JSONObject result = JsonAndXmlUtils.xmlToJson(s);
            log.info("Payment code payment results:" + result.toJSONString());
            if (result.getString("return_code").equalsIgnoreCase("SUCCESS") &&
                    result.getString("result_code").equalsIgnoreCase("SUCCESS")) {
                return result;
            } else {
                payRevocation(jsonObject, key, true);
                return result;
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 撤銷訂單
     *
     * @param jsonObject 參數
     * @param  key 服務商支付KEY
     * @param b true爲掃碼,false爲刷臉
     * @return
     * @Author LiuYong
     * @Date 2019/12/23 9:23
     * @Description TODO 撤銷訂單
     **/
    public static void payRevocation(JSONObject jsonObject, String key, Boolean b) {
        log.info("Cancel order parameter JSONObject:" + jsonObject);
        log.info("Cancel order parameter key:" + key);
        if (jsonObject.getString("out_trade_no") != null) {
            JSONObject jsonObject2 = facepayQuery(jsonObject, key, b);
            if (!jsonObject2.getString("result_code").equalsIgnoreCase("FAIL")
                    || jsonObject2.getString("err_code") != null) {
                log.info("View order results:" + jsonObject2);
                /**Cancel the order*/
                JSONObject json = new JSONObject();
                json.put("appid", jsonObject.getString("appid"));
                json.put("sub_mch_id", jsonObject.getString("sub_mch_id"));
                json.put("mch_id", jsonObject.getString("mch_id"));
                json.put("out_trade_no", jsonObject.getString("out_trade_no"));
                json.put("nonce_str", WeChatTool.generatedString(32, true, true));
                json.put("sign", WeChatTool.getSign(json, key, WeChatConstants.MD5));
                JSONObject jsonObject1 = payReverse(jsonObject.getString("mch_id"), json, b);
                log.info("Cancel the order results:" + jsonObject1);
            }
        }

    }

    /**
     * 查詢訂單接口
     *
     * @param jsonObject 參數對象
     * @return JSONObject
     * @Author LiuYong
     * @Date 2019/12/16 10:47
     * @Description TODO  查詢訂單
     **/
    public static JSONObject facepayQuery(JSONObject jsonObject, String key, Boolean b) {
        JSONObject json = new JSONObject();
        json.put("appid", jsonObject.getString("appid"));
        json.put("mch_id", jsonObject.getString("mch_id"));
        json.put("sub_mch_id", jsonObject.getString("sub_mch_id"));
        json.put("out_trade_no", jsonObject.getString("out_trade_no"));
        json.put("nonce_str", WeChatTool.generatedString(32, true, true));
        json.put("sign", WeChatTool.getSign(json, key, WeChatConstants.MD5));
        String data = JsonAndXmlUtils.WxJsonToXml(json);
        try {
            CloseableHttpClient httpClient = notReadCertificate();
            String s = HttpPost(b ? WeChatPayConstants.PAY_ORDERQUERY : WeChatPayConstants.PAY_FACEPAYQUERY, data, httpClient);
            JSONObject jsonObject1 = JsonAndXmlUtils.xmlToJson(s);
            log.info("view an order:" + jsonObject1.toJSONString());
            return jsonObject1;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 退款訂單(需要雙向證書)
     *
     * @param mchId      微信支付分配的商戶號
     * @param jsonObject 參數對象
     * @return JSONObject
     * @Author LiuYong
     * @Date 2019/12/12 11:19
     * @Description TODO 退款訂單(需要雙向證書)
     **/
    public static JSONObject doRefundRequest(String mchId, JSONObject jsonObject) {
        String data = JsonAndXmlUtils.WxJsonToXml(jsonObject);
        try {
            CloseableHttpClient httpClient = readCertificate(mchId);
            String s = HttpPost(WeChatPayConstants.SECAPI_PAY_REFUND, data, httpClient);
            return JsonAndXmlUtils.xmlToJson(s);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 查詢退款接口
     *
     * @param jsonObject 參數對象
     * @return JSONObject
     * @Author LiuYong
     * @Date 2019/12/16 10:47
     * @Description TODO  查詢退款接口
     **/
    public static JSONObject refundQuery(JSONObject jsonObject) {
        String data = JsonAndXmlUtils.WxJsonToXml(jsonObject);
        try {
            CloseableHttpClient httpClient = notReadCertificate();
            String s = HttpPost(WeChatPayConstants.PAY_REFUNDQUERY, data, httpClient);
            return JsonAndXmlUtils.xmlToJson(s);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 刷臉支付撤銷訂單接口(需要雙向證書)
     *
     * @param mchId      微信支付分配的商戶號
     * @param jsonObject 參數對象
     * @param b true爲掃碼,false爲刷臉
     * @return JSONObject
     * @Author LiuYong
     * @Date 2019/12/12 11:19
     * @Description TODO 撤銷訂單(需要雙向證書)
     **/
    public static JSONObject payReverse(String mchId, JSONObject jsonObject, Boolean b) {
        String data = JsonAndXmlUtils.WxJsonToXml(jsonObject);
        try {
            CloseableHttpClient httpClient = readCertificate(mchId);
            String s = HttpPost(b ? WeChatPayConstants.SECAPI_PAY_REVERSE : WeChatPayConstants.SECAPI_PAY_FACEPAYREVERSE, data, httpClient);
            JSONObject jsonObject1 = JsonAndXmlUtils.xmlToJson(s);
            log.info("Cancellation result:" + jsonObject1.toJSONString());
            return jsonObject1;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 下載對賬單
     *
     * @param jsonObject 請求參數
     * @return String
     * @Author LiuYong
     * @Date 2019/12/12  11:18
     * @Description TODO 下載對賬單
     **/
    public static JSONObject doDownloadbill(JSONObject jsonObject) {
        try {
            String data = JsonAndXmlUtils.WxJsonToXml(jsonObject);
            CloseableHttpClient httpClient = notReadCertificate();
            String s = HttpPost(WeChatPayConstants.PAY_DOWNLOADBILL, data, httpClient);
            return JsonAndXmlUtils.xmlToJson(s);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    /**-----------------------------------PAIVATE----------------------------------------------------*/
    /**
     * 雙向證書請求
     *
     * @param mchId      微信支付分配的商戶號
     * @param url        請求地址
     * @param jsonObject 參數
     * @return 結果
     * @Author LiuYong
     * @Date 2019/12/12 11:58
     * @Description TODO 雙向證書請求,請求地址及參數處理
     **/
    private static String twoWayRequest(String mchId, String url, JSONObject jsonObject) {
        try {
            String data = JsonAndXmlUtils.WxJsonToXml(jsonObject);
            /**小程序下載資金賬單需要調用雙向證書的認證*/
            CloseableHttpClient httpClient = readCertificate(mchId);
            return HttpPost(url, data, httpClient);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 請求
     *
     * @param url
     * @param data
     * @param httpClient
     * @return
     * @throws Exception
     */
    protected static String HttpPost(String url, String data, CloseableHttpClient httpClient) throws IOException {
        try {
            /**設置響應頭信息*/
            HttpPost httpost = new HttpPost(WeChatPayConstants.DOMAIN_API + url);
            httpost.addHeader("Connection", "keep-alive");
            httpost.addHeader("Accept", "*/*");
            httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
            httpost.addHeader("Host", "api.mch.weixin.qq.com");
            httpost.addHeader("X-Requested-With", "XMLHttpRequest");
            httpost.addHeader("Cache-Control", "max-age=0");
            httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
            httpost.setEntity(new StringEntity(data, "UTF-8"));
            /**超時時間設置*/
            RequestConfig requestConfig = RequestConfig.custom()
                    .setConnectTimeout(5000).setSocketTimeout(5000).build();
            httpost.setConfig(requestConfig);
            CloseableHttpResponse response = httpClient.execute(httpost);
            try {
                HttpEntity entity = response.getEntity();
                String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
                EntityUtils.consume(entity);
                return jsonStr;
            } finally {
                response.close();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            httpClient.close();
        }

    }

    /**
     * 微信小程序讀取雙向證書
     *
     * @param mchId 微信支付分配的商戶號
     * @return CloseableHttpClient
     * @Author LiuYong
     * @Date 2019/12/12  11:21
     * @Description TODO 微信小程序讀取雙向證書
     **/
    private static CloseableHttpClient readCertificate(String mchId) throws Exception {
        /**
         * Note that the PKCS12 certificate is downloaded from WeChat merchant platform - "account Settings -" API security
         */
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        /**P12 file directory certificate path, here you need to modify, Linux or Windows under the root path*/
        log.info("P12文件目錄filepath->" + PathUtil.getPath("P12"));
        FileInputStream instream = new FileInputStream(PathUtil.getPath("P12") + "/apiclient_cert.p12");
        try {
            /**這裏寫密碼..默認是你的MCHID*/
            keyStore.load(instream, mchId.toCharArray());
        } finally {
            instream.close();
        }
        // Trust own CA and all self-signed certs
        /**這裏也是寫密碼的*/
        SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, mchId.toCharArray()).build();
        // Allow TLSv1 protocol only
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, SSLConnectionSocketFactory.getDefaultHostnameVerifier());
        return HttpClients.custom().setSSLSocketFactory(sslsf).build();
    }

    /**
     * 微信小程序不讀取雙向證書
     *
     * @param
     * @return
     * @Author LiuYong
     * @Date 2019/12/12 11:20
     * @Description TODO 微信小程序不讀取雙向證書
     **/
    protected static CloseableHttpClient notReadCertificate() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {

        CloseableHttpClient httpClient = null;
        SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
            /**信任所有*/
            @Override
            public boolean isTrusted(X509Certificate[] chain, String authType) {
                return true;
            }
        }).build();
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);
        httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
        return httpClient;
    }

}

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章