微信支付的准备工作

最近忙于研究微信支付的相关内容,自己有踏入了不少的坑。在这里说一下吧,

微信提供的网站有:
微信开放平台:https://open.weixin.qq.com/
用于微信中绑定开发的App,小程序等,实现App支付。
微信公众平台:https://mp.weixin.qq.com/
用于申请微信公众号,小程序的,
微信支付平台:https://pay.weixin.qq.com/
用于查询微信公众号微信支付的订单,可以申请h5支付

一、申请微信服务号
想要开始微信支付,必须要有一个微信服务号,
然后配置微信服务器,等等。

对于微信支付的类型:
扫码支付、公众号支付、App支付、h5支付。
对于扫码支付、公众号支付只需要申请公众号就可以获取,
对于App支付、h5支付,则需要进一步的去申请权限。

关于支付的一些字段:
appid 开发者ID
mch_id 商户号
AppSecret 开发者密码,API秘钥
这些在申请时,会都发到你的邮箱中。

二、开发时,准备的工具类:
1.微信的传输数据为xml
这里借用的是com.thoughtworks.xstream.jar包

public static <T> String beanToXml(T xml){
        XStream stream = new XStream();
        stream.alias("xml", xml.getClass());
        return stream.toXML(xml).replace("__", "_");
    }

    public static <T> void beanToXml(T xml,OutputStream out) throws UnsupportedEncodingException, IOException{
        String content = beanToXml(xml);
        out.write(content.getBytes("utf-8"));
    }

    public static <T> T xmlToBean(String xmlStr,Class<T> clazz){
        XStream stream = new XStream();
        stream.alias("xml", clazz);
        stream.alias("root", clazz);
        stream.ignoreUnknownElements();
        @SuppressWarnings("unchecked")
        T t = (T) stream.fromXML(xmlStr);
        return t;
    }

    @SuppressWarnings("unchecked")
    public static <T> T xmlToBean(String xmlStr,T obj){
        XStream stream = new XStream();
        stream.alias("xml", obj.getClass());
        stream.alias("root", obj.getClass());
        stream.ignoreUnknownElements();
        return (T) stream.fromXML(xmlStr, obj);
    }

    public static <T> T xmlToBean(InputStream in,Class<T> clazz){
        XStream stream = new XStream();
        stream.alias("xml", clazz);
        stream.alias("root", clazz);
        stream.ignoreUnknownElements();
        @SuppressWarnings("unchecked")
        T t = (T) stream.fromXML(in);
        return t;
    }

2.对传输的数据按照属性ASCII顺序排列形成键值对的格式,
再加上秘钥,获取签名

public static <T> String getSignTemp(T t){
        if(t == null){
            throw new RuntimeException("参数不能为空");
        }
        StringBuilder sb = new StringBuilder();
        //1.对参数按照key=value的格式,并按照参数名ASCII字典序排序
        try {
            Class<?> clazz = t.getClass();
            Field[] fields = clazz.getDeclaredFields();
            List<Field> list = new ArrayList<>(Arrays.asList(fields)); 
            Collections.sort(list, new Comparator<Field>(){

                @Override
                public int compare(Field o1, Field o2) {
                    return o1.getName().compareTo(o2.getName());
                }

            });
            for(Field field : list){
                String name = field.getName();
                //序列号省略
                if(name.contains("serialVersionUID")){
                    continue;
                }
                //签名省略
                if(name.equals("sign")){
                    continue;
                }
                field.setAccessible(true);
                if(field.get(t) == null){
                    continue;
                }
                if(field.getType() == String.class && StringUtils.isEmpty(field.get(t))){
                    continue;
                }
                if(name.equals("packageValue")){
                    name = "package";
                }
                sb.append(name).append("=").append(field.get(t)).append("&");
            }
            //2.拼接API密钥
//      sb.append("key=").append(PayConstant.KEY);

            sb.append("key=").append(PayConstant.APP_SECRET);
        } catch (Exception e) {
            throw new RuntimeException("获取参数字符串失败", e);
        }
        //3.返回拼接文本
        return sb.toString();
    }
public static String Md5Digest(String str) {
        if(StringUtils.isEmpty(str)){
            throw new RuntimeException("字符串不能为空");
        }
        byte[] data = DigestUtils.md5(str);

        //将字节数组转为16进制字符串
        StringBuffer sb = new StringBuffer();
        for(byte b:data){
            String sHex = Integer.toHexString(b&0xff);
            if(sHex.length()==1){
                sHex = "0"+sHex;
            }
            sb.append(sHex);
        }
        String content = sb.toString().toUpperCase();
        return content;
    }

3.对于支付退款时,需要使用https请求,并且要使用证书验证
构造https请求

String pwd = PayConstant.MCH_ID;
            // 1.获取证书 输入流, 获取证书密码
//          InputStream in = new FileInputStream(PayConstant.API_FILE_PATH);
            InputStream in = HttpUtil.class.getClassLoader().getResourceAsStream("apiclient_cert.p12");

            char[] password = pwd.toCharArray();

            // 2.实例化密钥库 & 初始化密钥工厂
            KeyStore key = KeyStore.getInstance("PKCS12");
            key.load(in, password);

            KeyManagerFactory keyManager = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            keyManager.init(key, password);

            // 3.创建 SSLContext
            SSLContext ssl = SSLContext.getInstance("TLS");;
            ssl.init(keyManager.getKeyManagers(), null, new SecureRandom());
            SSLConnectionSocketFactory sslFactory = new SSLConnectionSocketFactory(
                    ssl,
                    new String[]{"TLSv1"},
                    null,
                    new DefaultHostnameVerifier());
            // 4.创建连接管理类
BasicHttpClientConnectionManager connManager = new BasicHttpClientConnectionManager(
                    RegistryBuilder.<ConnectionSocketFactory>create()
                            .register("http", PlainConnectionSocketFactory.getSocketFactory())
                            .register("https", sslFactory)
                            .build(),
                    null,
                    null,
                    null
            );
HttpClient httpClient = HttpClientBuilder.create()
                .setConnectionManager(connManager)
                .build();           

4.对于申请退款时,返回的数据需要进行AES解密。

注意:这里可能会报秘钥的长度非法,
这里因为java当前jre只支持秘钥128位的,而秘钥 192、256位的,则会报错。

因此这里需要从官网下载对秘钥长度没有限制的jar,把之前的替换了就可以了。
把里面的两个jar包:local_policy.jar 和 US_export_policy.jar 替换掉原来 Jdk 安装目录 $\Java\jre{6|7|8}\lib\security 下的两个jar 包接可以了

下载地址:
jdk1.7:http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html

jdk1.8:http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html

/**
     * 密钥算法
     */
    private static final String ALGORITHM = "AES";
    /**
     * 加解密算法/工作模式/填充方式
     */
    private static final String ALGORITHM_MODE_PADDING = "AES/ECB/PKCS5Padding";
    /**
     * 生成Key
     */
    public static SecretKey key = new SecretKeySpec(
            MyDigestUtils.Md5Digest("2IBtBXdrqC3kCBs4gaceL7nl2nnFadQv")
            .toLowerCase().getBytes(), ALGORITHM);


    /**
     * 加密
     * @param data
     * @return
     * @throws Exception
     */
    public static String encryptData(String data) throws Exception{
        Cipher cipher = Cipher.getInstance(ALGORITHM_MODE_PADDING);
        cipher.init(Cipher.ENCRYPT_MODE, key);
        byte[] s = cipher.doFinal(data.getBytes("utf-8"));
        return Base64Utils.encodeToString(s);
    }

    /**
     * 解密
     * @param base64Data
     * @return
     * @throws Exception
     */
    public static String decryptData(String base64Data) throws Exception{
        Cipher cipher = Cipher.getInstance(ALGORITHM_MODE_PADDING);
        cipher.init(Cipher.DECRYPT_MODE, key);
        byte[] data = cipher.doFinal(Base64Utils.decodeFromString(base64Data));

        return new String(data, "utf-8");
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值