All Downloads are FREE. Search and download functionalities are using the official Maven repository.

tech.mhuang.pacebox.springboot.payment.wechat.util.PayCommonUtil Maven / Gradle / Ivy

package tech.mhuang.pacebox.springboot.payment.wechat.util;

import tech.mhuang.pacebox.core.util.CollectionUtil;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.time.Instant;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.concurrent.ThreadLocalRandom;

/**
 * 支付通用工具类
 *
 * @author mhuang
 * @since 1.0.0
 */
public class PayCommonUtil {

    /**
     * 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
     *
     * @param characterEncoding 验证签名的编码
     * @param packageParams     验证param
     * @param apiKey            API_KEY
     * @return boolean
     */
    @SuppressWarnings({"rawtypes"})
    public static boolean isTenpaySign(String characterEncoding, SortedMap packageParams, String apiKey) {
        StringBuilder sb = new StringBuilder();
        Set es = packageParams.entrySet();
        for (Object e : es) {
            Map.Entry entry = (Map.Entry) e;
            String k = (String) entry.getKey();
            String v = (String) entry.getValue();
            if (!"sign".equals(k) && null != v && !"".equals(v)) {
                sb.append(k).append("=").append(v).append("&");
            }
        }
        sb.append("key=").append(apiKey);
        //算出摘要  
        String mysign = MD5Util.md5Encode(sb.toString(), characterEncoding).toLowerCase();
        String tenpaySign = ((String) packageParams.get("sign")).toLowerCase();
        return tenpaySign.equals(mysign);
    }

    private final static String CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    private final static ThreadLocalRandom LOCAL_RANDOM = ThreadLocalRandom.current();

    public static String createNoncestr() {
        StringBuilder res = new StringBuilder();
        for (int i = 0; i < 16; i++) {
            res.append(CHARS.charAt(LOCAL_RANDOM.nextInt(CHARS.length() - 1)));
        }
        return res.toString();
    }

    /**
     * sign签名
     *
     * @param characterEncoding 签名的编码
     * @param packageParams     签名的map
     * @param apiKey            签名需要的key
     * @return String
     */
    @SuppressWarnings({"rawtypes"})
    public static String createSign(String characterEncoding, SortedMap packageParams, String apiKey) {
        StringBuilder sb = new StringBuilder();
        Set es = packageParams.entrySet();
        for (Object e : es) {
            Map.Entry entry = (Map.Entry) e;
            String k = (String) entry.getKey();
            String v = (String) entry.getValue();
            if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k).append("=").append(v).append("&");
            }
        }
        sb.append("key=").append(apiKey);
        return MD5Util.md5Encode(sb.toString(), characterEncoding).toUpperCase();
    }

    public static Map sign(String jsapiTicket, String url) throws Exception {
        Map ret = CollectionUtil.capacity(HashMap.class, 6);
        String nonceStr = createNoncestr(), timestamp = String.valueOf(Instant.now().getEpochSecond()), string1, signature;
        //注意这里参数名必须全部小写,且必须有序
        string1 = "jsapi_ticket=" + jsapiTicket + "&noncestr=" + nonceStr + "×tamp=" + timestamp + "&url=" + url;
        MessageDigest crypt = MessageDigest.getInstance("SHA-1");
        crypt.reset();
        crypt.update(string1.getBytes(StandardCharsets.UTF_8));
        signature = byteToHex(crypt.digest());

        ret.put("url", url);
        ret.put("jsapi_ticket", jsapiTicket);
        ret.put("nonceStr", nonceStr);
        ret.put("timestamp", timestamp);
        ret.put("signature", signature);

        return ret;
    }

    private static String byteToHex(final byte[] hash) {
        Formatter formatter = new Formatter();
        for (byte b : hash) {
            formatter.format("%02x", b);
        }
        String result = formatter.toString();
        formatter.close();
        return result;
    }

    /**
     * 将请求参数转换为xml格式的string
     *
     * @param parameters 参数
     * @return String
     */
    @SuppressWarnings({"rawtypes"})
    public static String getRequestXml(SortedMap parameters) {
        StringBuilder sb = new StringBuilder();
        sb.append("");
        Set es = parameters.entrySet();
        for (Object e : es) {
            Map.Entry entry = (Map.Entry) e;
            String k = (String) entry.getKey();
            String v = (String) entry.getValue();
            if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) {
                sb.append("<").append(k).append(">").append("");
            } else {
                sb.append("<").append(k).append(">").append(v).append("");
            }
        }
        sb.append("");
        return sb.toString();
    }

    /**
     * 取出一个指定长度大小的随机正整数.
     *
     * @param length 长度
     * @return int
     */
    public static int buildRandom(int length) {

        int num = 1;
        double random = LOCAL_RANDOM.nextDouble();
        if (random < 0.1) {
            random = random + 0.1;
        }
        for (int i = 0; i < length; i++) {
            num = num * 10;
        }
        return (int) ((random * num));
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy