通联支付工具类

1,183 阅读6分钟

引言:更多相关请看 Java其它
代码:

package com.lebaoxun.allinpay;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.lebaoxun.commons.utils.Date8Util;
import lombok.extern.slf4j.Slf4j;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import java.io.IOException;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

/**
 * @Auther: Administrator
 * @Date: 2020/1/16 10:46
 * @Description: 通联支付工具类
 */

@Slf4j
public class AllInPayUtils {

    // 当前类名
    private static String currentClassName = Thread.currentThread().getStackTrace()[1].getClassName();

    // 分配的系统编号
    public static String sysid = "100000000002";

    public static String publicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA18UsDXe3kEFk5bAV2NVmdpiTtc/ayoa8xeboL312fiOKhhP9YukApmSkZCyiVUhGSjcY8JHE6TUZgyTpUMlrtEGZmt593YuVjG81VB0GXFE9J0mzOjdkIzy+h8b0HNNZYuz1athm65aDVB8aOw3SIBPQxHK7j9X0VEwdr+dSLJlYxkimKrRBXEQUPxrt/MLhnSRXDJTN09g3zNaPhDqHAGP8GP59fQb6/QwN4UgDUhtpjEbgzbgbzZplVRkedsSM6781WlkXrjne0kJrPE0cfEAJDpohZOc/flnBa0To7JuWgEz/2o24ROjfdnFOY0+hByTzs4oZNkgY6cWJ+rQmUwIDAQAB";
    public static String privateKey = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDXxSwNd7eQQWTlsBXY1WZ2mJO1z9rKhrzF5ugvfXZ+I4qGE/1i6QCmZKRkLKJVSEZKNxjwkcTpNRmDJOlQyWu0QZma3n3di5WMbzVUHQZcUT0nSbM6N2QjPL6HxvQc01li7PVq2GbrloNUHxo7DdIgE9DEcruP1fRUTB2v51IsmVjGSKYqtEFcRBQ/Gu38wuGdJFcMlM3T2DfM1o+EOocAY/wY/n19Bvr9DA3hSANSG2mMRuDNuBvNmmVVGR52xIzrvzVaWReuOd7SQms8TRx8QAkOmiFk5z9+WcFrROjsm5aATP/ajbhE6N92cU5jT6EHJPOzihk2SBjpxYn6tCZTAgMBAAECggEAP4EcEJCqGY5tnD5hh7JzGDeazblxuwbB23s03flOnRxnwkNy4aATHWXkjkkms/P0IBasQ+2bJ0oGCreb5beKeACQrWiLYd+Zsb3vU6TBQP12ArCpy9oOomJX701KfotjtP5p0rsDO5ZOa71FspjEPcWnPZJJCrHdWcwXNrTd1hr3jksV+XNz3ToAMgoW16wxMVVIJm/pP5PqBkqylBuUXGn7tfBSF3XDUipHfbLUQ5N55zOGF96Y9Fd1kuCIsZlTB3XdPNQze5hS3jwQSABYOUSHckWnYzzVKp4Ol9SGgONc2W5n1tkKRhoG1jqItQvlbyOjMgrBPvCjOJTklo7yOQKBgQDuaySeBgvuGqJQYJXirI1a9N6/ckZPuAmoKXr1RBagEuiKb4U//BSOongiMBeUVH214cgnjdB/aG8rog0UfZ7iBvAewqX4wdhZiFx8IdnDiJRaE9s+cLBSyOicv7wv9BGMV0gtN0mPNR4tld5vhAC4eOxCVva3tFAl1e11bZ30WQKBgQDnrnmIvJQHJRJu+SxNLfdqAk98n8s4zTNLn0l2Er9TeV29P3tS1cBX+2Usj2Msazbc36oL8ymfUN0Ufjqi9am3mRzTyNxOijj+nzgaCbMOtmjeXs9LogYXTL3YzR/UCK/DPI8HYMEi1BpXhFqVj/4RZm/tVgvlF4cDvOByNO0KiwKBgQCA8FruR1DeZXMQyxtkanNMFI/+s5uI83vtgPlpeXhO+8rdS3Ch6P9pID6eJqop4wPVgJTRp2Vx8hXlYA943HM3xA7KY/SEsrkBurhhvaDamZu5l3GDtEf3jYYXd64btQBkiLOVGIhktZJrjtisMJR/L45+Vi+5q/+IZsi0MCMziQKBgQCrwQv2U58F4PTWckDmgqI947KbuDbHSFCzykWClAlkz7+vkcVSNoaecw+hRjeGcYE9o06EB2Oe6SrMi3va3XZRVuApHWo9i3kPQmgr3W7yBEagO/c/ocEUOt8vP3pODZaHjXiXRqJHuKhCoysoX5RBwAhQEY9F6fg8zCQ0ZoooWwKBgQCOqp9sTBEWpd0cXdD5rORxuLJTvfM6xZEPjx71RGgamX2z4nWkVWhVcX7y+6z4E+Sp9lTmQlhmkqcTo/eKkuvjIcI6SWPqcyjoRdORaQLKN++x4NBx7wFfG/0gHAnLtpQHAZYaqlryQ6CzJwP8S42KSDyE/LEJXfVWAX5D4v15rw==";
    public static Provider provider = new com.sun.security.sasl.Provider();

    // 测试环境接口地址:http://116.228.64.55:6900/service/soa
    public static String testInterfaceURL = "http://116.228.64.55:6900/service/soa";
    // 测试环境网关地址:http://116.228.64.55:6900/yungateway/frontTrans.do
    public static String testGatewayURL = "http://116.228.64.55:6900/yungateway/frontTrans.do";
    // 测试环境门户地址:http://116.228.64.55:6901/portal
    public static String testPortalURL = "http://116.228.64.55:6901/portal";
    // 生产环境接口地址:https://fintech.allinpay.com/service/soa
    public static String procInterfaceURL = "https://fintech.allinpay.com/service/soa";
    // 生产环境网关地址:https://fintech.allinpay.com /yungateway/frontTrans.do
    public static String procGatewayURL = "http://fintech.allinpay.com/yungateway/frontTrans.do";
    // 生产环境门户地址:https://fintech.allinpay.com/portal
    public static String procPortalURL = "https://fintech.allinpay.com/portal";


    /**
     * 签名
     * 步骤 1:先对签名源串用 MD5 算法进行摘要计算;
     * 步骤 2:对步骤 1 的返回数据进行 Base64 编码;
     * 步骤 3:对步骤 2 的返回数据使用商户私钥进行 SHA1WithRSA 算法签名;
     * 步骤 4:对步骤 3 的返回数据进行 Base64 编码。
     *
     * @param text 签名源串:sysid(系统编号) + req(服务请求的JSON对象) + timestamp(请求时间戳)
     * @return
     * @throws Exception
     */
    public static String sign(String text) {
        log.info("当前类名:{}", currentClassName);
        log.info("当前方法名称:{}", Thread.currentThread().getStackTrace()[1].getMethodName());
        String encode = null;
        try {
//            Signature signature = Signature.getInstance("SHA1WithRSA", provider);
            Signature signature = Signature.getInstance("SHA1WithRSA");
            // 步骤1和2
            text = md5(text);
            signature.initSign(getPrivateKey());
            // 步骤 3:对步骤2的返回数据使用商户私钥进行SHA1WithRSA算法签名; ==> Signature.getInstance("SHA1WithRSA")
            signature.update(text.getBytes(Charset.forName("UTF-8")));
            byte[] data = signature.sign();
            // 步骤 4:对步骤 3 的返回数据进行 Base64 编码。
            encode = new BASE64Encoder().encode(data);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return encode;
    }

    /**
     * MD5验签
     *
     * @param src
     * @return
     */
    public static String md5(String src) {
        log.info("当前类名:{}", currentClassName);
        log.info("当前方法名称:{}", Thread.currentThread().getStackTrace()[1].getMethodName());
        String result = "";
        try {
            // 步骤 1:先对签名源串用 MD5 算法进行摘要计算;
            MessageDigest md = MessageDigest.getInstance("MD5");
//            result = Base64.encode(md.digest(src.getBytes("utf-8")));
            // 步骤 2:对步骤 1 的返回数据进行 Base64 编码;
            result = new BASE64Encoder().encode(md.digest(src.getBytes("utf-8")));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 验签
     * 步骤 1:先对签名源串用 MD5 算法进行摘要计算;
     * 步骤 2:对步骤 1 的返回数据进行 Base64 编码;
     * 步骤 3:对响应报文中的签名串 sign 进行 Base64 解码;
     * 步骤 4:对步骤 2、3 的返回数据使用通联公钥进行 SHA1WithRSA 算法验签。
     * 验签结果为 true 则说明验证签名成功,否则验证签名失败。
     * 注:验签前需要 urldecode 解码,部分语言在获取参数时会自动解码,请注意。
     *
     * @param publicKey
     * @param text
     * @param sign
     * @return
     * @throws Exception
     */
    public static boolean verify(PublicKey publicKey, String text, String sign) {
        log.info("当前类名:{}", currentClassName);
        log.info("当前方法名称:{}", Thread.currentThread().getStackTrace()[1].getMethodName());
        boolean flag = false;
        try {
//            Signature signature = Signature.getInstance("SHA1WithRSA", provider);
            Signature signature = Signature.getInstance("SHA1WithRSA");
            signature.initVerify(publicKey);
            // 步骤1和2
            text = md5(text);
            signature.update(text.getBytes("UTF-8"));
            // 步骤 3:对响应报文中的签名串 sign 进行 Base64 解码;
            byte[] signed = new BASE64Decoder().decodeBuffer(sign);
            // 步骤 4:对步骤 2、3 的返回数据使用通联公钥进行 SHA1WithRSA 算法验签。==> Signature.getInstance("SHA1WithRSA")
            flag = signature.verify(signed);
        } catch (Exception e) {
            e.printStackTrace();
        }
        log.info("验证状态:{}", flag);
        return flag;
    }

    /**
     * JSON转字符串
     *
     * @param object
     * @return
     */
    public static String jsonToStr(Object object) {
        log.info("当前类名:{}", currentClassName);
        log.info("当前方法名称:{}", Thread.currentThread().getStackTrace()[1].getMethodName());
        return JSON.toJSONString(object);
    }

    /**
     * 字符串转json
     *
     * @param str
     * @return
     */
    public static Map<String, Object> strToJson(String str) {
        log.info("当前类名:{}", currentClassName);
        log.info("当前方法名称:{}", Thread.currentThread().getStackTrace()[1].getMethodName());
        return JSON.parseObject(str, new TypeReference<TreeMap<String, Object>>() {});
//        return JSON.parseObject(str, TreeMap.class);
    }

    /**
     * 获取公钥对象
     *
     * @return
     */
    public static PublicKey getPublicKey() {
        log.info("当前类名:{}", currentClassName);
        log.info("当前方法名称:{}", Thread.currentThread().getStackTrace()[1].getMethodName());
        PublicKey publicKeyObj = null;
        X509EncodedKeySpec keySpec = null;
        try {
            keySpec = new X509EncodedKeySpec(new BASE64Decoder().decodeBuffer(publicKey));
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            publicKeyObj = keyFactory.generatePublic(keySpec);
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return publicKeyObj;
    }

    /**
     * 获取私钥对象
     *
     * @return
     */
    public static PrivateKey getPrivateKey() {
        log.info("当前类名:{}", currentClassName);
        log.info("当前方法名称:{}", Thread.currentThread().getStackTrace()[1].getMethodName());
        PrivateKey privateKeyObj = null;
        PKCS8EncodedKeySpec priPKCS8 = null;
        try {
            priPKCS8 = new PKCS8EncodedKeySpec(new BASE64Decoder().decodeBuffer(privateKey));
            KeyFactory keyf = KeyFactory.getInstance("RSA");
            privateKeyObj = keyf.generatePrivate(priPKCS8);
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return privateKeyObj;
    }

    /**
     * String 转换为publickey
     *
     * @param key
     * @return
     * @throws Exception
     */
    public static PublicKey gettoPublicKey(String key) throws Exception {
        log.info("当前类名:{}", currentClassName);
        log.info("当前方法名称:{}", Thread.currentThread().getStackTrace()[1].getMethodName());
        byte[] keyBytes = (new BASE64Decoder()).decodeBuffer(key);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicKey = keyFactory.generatePublic(keySpec);
        return publicKey;
    }

    /**
     * String转私钥PrivateKey
     *
     * @param key
     * @return
     * @throws Exception
     */
    public static PrivateKey gettoPrivateKey(String key) throws Exception {
        log.info("当前类名:{}", currentClassName);
        log.info("当前方法名称:{}", Thread.currentThread().getStackTrace()[1].getMethodName());
        byte[] keyBytes;
        keyBytes = (new BASE64Decoder()).decodeBuffer(key);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
        return privateKey;
    }

    // ==========================接口定义三连杀==============================
    // 请求报文reqMsg --> 服务请求的JSON req --> 请求参数param

    /**
     * 初始化请求报文
     *
     * @return
     */
    public static Map<String, Object> reqMsg(Map<String, Object> req) {
        log.info("当前类名:{}", currentClassName);
        log.info("当前方法名称:{}", Thread.currentThread().getStackTrace()[1].getMethodName());
        Map<String, Object> result = new HashMap<>();

        String timestamp = Date8Util.datetimeFormat(new Date());
        // 分配的系统编号
        result.put("sysid", sysid);

        // text签名源串:sysid(系统编号) + req(服务请求的JSON对象) + timestamp(请求时间戳)
//        String text = AllInPayUtils.sysid + req + timestamp;
        // req应该要从Map转为JSON字符串
        String text = sysid + jsonToStr(req) + timestamp;
        log.info("text签名源串:{}", text);
        // 签名
        String sign = sign(text);
        // 验签
        boolean verify = verify(getPublicKey(), text, sign);
        if (verify == false) {
            throw new RuntimeException("验签失败");
        }
        log.info("签名sign:{}", sign);
        result.put("sign", sign);

        // 请求时间戳  格式:yyyy-MM-ddHH:mm:ss
        result.put("timestamp", timestamp);
        // 接口版本(现为 2.0)
        result.put("v", "2.0");
        // 服务请求的 JSON 对象
        result.put("req", req);
        Map<String, Object> reqmap = (Map<String, Object>) result.get("req");
        log.info("sysid:{},sign:{},timestamp:{},v:{},req:{},param:{}",
                result.get("sysid"), result.get("sign"), result.get("timestamp"),
                result.get("v"), result.get("req"), reqmap.get("param"));
        log.info("result:{}", result);
        return result;
    }

    /**
     * 构建服务请求的 JSON req对象,参与签名
     *
     * @param service 服务对象
     * @param method  调用方法
     * @param param   请求参数
     * @return
     */
    public static Map<String, Object> req(String service, String method, Map<String, Object> param) {
        log.info("当前类名:{}", currentClassName);
        log.info("当前方法名称:{}", Thread.currentThread().getStackTrace()[1].getMethodName());
        // 服务请求的 JSON 对象,参与签名
        Map<String, Object> req = new HashMap<>();
        // 服务对象
        req.put("service", service);
        // 调用方法
        req.put("method", method);
        // 请求参数
        req.put("param", param);
        log.info("service:{},method:{},param:{}", service, method, param);
        log.info("req:{}", req);
        return req;
    }

    /**
     * 请求参数,嵌套的 JSON 对象
     *
     * @param bizUserId 商户系统用户标识,商户系统中唯一编号。注意:1.不能输入“中文” 2.不要使用系统保留用户标识:#yunBizUserId_B2C#
     * @return
     */
    public static Map<String, Object> param(String bizUserId) {
        log.info("当前类名:{}", currentClassName);
        log.info("当前方法名称:{}", Thread.currentThread().getStackTrace()[1].getMethodName());
        // 请求参数,嵌套的 JSON 对象
        Map<String, Object> param = new HashMap<>();
        // 商户系统用户标识,商户系统中唯一编号
        param.put("bizUserId", bizUserId);
        log.info("param:{}", param);
        return param;
    }

}