微信V3接口商家转账到零钱

微信V3接口商家转账到零钱

来自微信官方文档v3接口说明
需要引入一下mavn包

    <!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all -->
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.8.2</version>
    </dependency>
    <dependency>
        <groupId>com.github.wechatpay-apiv3</groupId>
        <artifactId>wechatpay-apache-httpclient</artifactId>
        <version>0.4.8</version>
    </dependency>

1、下载微信平台证书

这里用到的是接口方式调用 ,一劳永逸。平台证书和微信商户证书不是同一个,商户证书在微信支付api中下载,平台证书通过接口下载,定期更新。
1、下载微信平台证书

 /**
     * 获取微信平台证书并解密
     * 公钥
     * 敏感数据需通过平台公钥加密。。
     * @param
     * @return
     */
    public static  String  plateCert() {
        String pulicKey = "";
        String vmsg = "";

        try {
            //时间戳
            long timestamp = System.currentTimeMillis() / 1000;
            //随机字符串(用UUID去掉-就行)
            String nonce = IdUtil.fastSimpleUUID().toUpperCase();
            String body = "";

            //拼接要签名的字符串
            ClassPathResource mchPrivateKeyFile = new ClassPathResource("static/cert/apiclient_key.pem");
            InputStream mchPrivateKeyInputStream = null;
            try {
                mchPrivateKeyInputStream = mchPrivateKeyFile.getInputStream();
            } catch (IOException e) {
                e.printStackTrace();
            }
            PrivateKey mchPrivateKey = PemUtil.loadPrivateKey(mchPrivateKeyInputStream);//用流的形式加载私钥key

            //拼接要签名的字符串
            String orgSignText = "GET\n"
                    + "/v3/certificates\n"
                    + timestamp + "\n"
                    + nonce + "\n"
                    + body + "\n";
            // 生成签名
            String sign = RsaKit.encryptByPrivateKey(orgSignText, mchPrivateKey);
            //要放在HttpHeader中的auth信息
            // 获取商户证书序列号  这里填写公钥路径 也就是apiclient_cert.pem这个文件的路径
            //证书序列号
            ClassPathResource mchPublicKeyFile = new ClassPathResource("static/cert/apiclient_cert.pem");
            InputStream mchPublicKeyInputStream = null;
            try {
                mchPublicKeyInputStream = mchPublicKeyFile.getInputStream();
            } catch (IOException e) {
                e.printStackTrace();
            }
            X509Certificate certificate =  PemUtil.loadCertificate(mchPublicKeyInputStream);
            String serialNo = certificate.getSerialNumber().toString(16).toUpperCase();
            String auth = "WECHATPAY2-SHA256-RSA2048 "
                    + "mchid=\"" + MyConfig.MCH_ID + "\",nonce_str=\""
                    + nonce + "\",timestamp=\"" + timestamp
                    + "\",serial_no=\"" + serialNo + "\",signature=\"" + sign + "\"";

            String url = "https://api.mch.weixin.qq.com/v3/certificates";

            HashMap<String, Object> tmap = new HashMap<String, Object>();
            tmap.put("Authorization", auth);//tmap.put("token","tonken值");
            tmap.put("Accept", "application/json");
            tmap.put("User-Agent", "https://zh.wikipedia.org/wiki/User_agent");

            vmsg = httpGet(url, tmap);//获取请求的返回结果

            System.out.println("获取平台证书" + vmsg);
            //解密
            String ciphertext = "";
            String associated_data = "";
            String nonce1 = "";
            String serial_no = "";//微信平台证书序列号
            JSONObject jsonObj = JSON.parseObject(vmsg);
            JSONArray data = jsonObj.getJSONArray("data");
            for (int i = 0; i < data.size(); i++) {
                JSONObject temp = (JSONObject) data.get(i);
                JSONObject encrypt_certificate = temp.getJSONObject("encrypt_certificate");
                ciphertext = encrypt_certificate.getString("ciphertext");
                associated_data = encrypt_certificate.getString("associated_data");
                nonce1 = encrypt_certificate.getString("nonce");
                serial_no = temp.getString("serial_no");

            }

        //解密方法
            AesUtil aesUtil = new AesUtil(MyConfig.MCH_V3KEY.getBytes("UTF-8"));
            pulicKey = aesUtil.decryptToString(associated_data.getBytes("utf-8"), nonce1.getBytes("utf-8"), ciphertext);
            //得到微信支付平台公钥(此公钥和微信商户支付公钥不一样)
            System.out.println("解密微信平台证书后数据" + pulicKey);

            // 保存证书 
 //这里可用保存证书到本地 也可以直接复制pulicKey到证书文件中然后使用 
            // 获取平台证书序列号
            X509Certificate platcertificate = PemUtil.loadCertificate(new ByteArrayInputStream(pulicKey.getBytes()));
            String platcertxlh= platcertificate.getSerialNumber().toString(16).toUpperCase();
            System.out.println("平台序号"+platcertxlh);


        } catch (Exception e) {
            e.printStackTrace();
        }
        return vmsg;
    }



    /**
     * HttpGet请求
     * @param vurl:请求地址,map:{头部信息}
     * @return 返回消息
     */
    public static String httpGet(String vurl,HashMap<String, Object> map) {
        try {
            URL url = new URL(vurl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            for (Map.Entry item : map.entrySet()) {
                connection.setRequestProperty(item.getKey().toString(),item.getValue().toString());//设置header
            }
            InputStream in = connection.getInputStream();
            InputStreamReader isr = new InputStreamReader(in, "utf-8");
            BufferedReader br = new BufferedReader(isr);
            String line;
            StringBuilder sb = new StringBuilder();
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
            br.close();
            isr.close();
            in.close();
            return sb.toString();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

上面用到的工具包
package io.cxxz.common.wechatpay.utils;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.StrUtil;

public class RsaKit {

/**
 * 加密算法RSA
 */
private static final String KEY_ALGORITHM = "RSA";

/**
 * 私钥签名
 *
 * @param data       需要加密的数据
 * @param privateKey 私钥
 * @return 加密后的数据
 * @throws Exception 异常信息
 */
public static String encryptByPrivateKey(String data, PrivateKey privateKey) throws Exception {
    java.security.Signature signature = java.security.Signature.getInstance("SHA256WithRSA");
    signature.initSign(privateKey);
    signature.update(data.getBytes(StandardCharsets.UTF_8));
    byte[] signed = signature.sign();
    return StrUtil.str(Base64.encode(signed));
}

/**
 * 从字符串中加载私钥<br>
 * 加载时使用的是PKCS8EncodedKeySpec(PKCS#8编码的Key指令)。
 *
 * @param privateKeyStr 私钥
 * @return {@link PrivateKey}
 * @throws Exception 异常信息
 */
public static PrivateKey loadPrivateKey(String privateKeyStr) throws Exception {
    try {
        byte[] buffer = Base64.decode(privateKeyStr);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        return keyFactory.generatePrivate(keySpec);
    } catch (NoSuchAlgorithmException e) {
        throw new Exception("无此算法");
    } catch (InvalidKeySpecException e) {
        throw new Exception("私钥非法");
    } catch (NullPointerException e) {
        throw new Exception("私钥数据为空");
    }
}
/**
 * 公钥验证签名
 *
 * @param data      需要加密的数据
 * @param sign      签名
 * @param publicKey 公钥
 * @return 验证结果
 * @throws Exception 异常信息
 */
public static boolean checkByPublicKey(String data, String sign, PublicKey publicKey) throws Exception {
    java.security.Signature signature = java.security.Signature.getInstance("SHA256WithRSA");
    signature.initVerify(publicKey);
    signature.update(data.getBytes(StandardCharsets.UTF_8));
    return signature.verify(Base64.decode(sign.getBytes(StandardCharsets.UTF_8)));
}

}

2、微信客户端读取证书

// An highlighted block
package io.cxxz.common.wechatpay.V3;
import cn.hutool.core.io.FileUtil;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import io.cxxz.common.wxpay.sdk.MyConfig;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.core.io.ClassPathResource;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Arrays;


public class WeChatClient {

    /**
     * 微信通讯client
     * @return CloseableHttpClient
     */
    public static CloseableHttpClient getClient() {

        /**商户私钥文件*/
        ClassPathResource mchPrivateKeyFile = new ClassPathResource("static/cert/apiclient_key.pem");
        InputStream mchPrivateKeyInputStream = null;
        try {
            mchPrivateKeyInputStream = mchPrivateKeyFile.getInputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }

        /**微信平台公钥文件,是获取平台证书接口得到的pulicKey存到wechatpay.pem中*/
        ClassPathResource platformKeyFile = new ClassPathResource("static/cert/wechatpay.pem");
        InputStream platformKeyInputStream = null;
        try {
            platformKeyInputStream = platformKeyFile.getInputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
        PrivateKey mchPrivateKey = PemUtil.loadPrivateKey(mchPrivateKeyInputStream);
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(MyConfig.MCH_ID, MyConfig.MCH_XLH, mchPrivateKey)
                .withWechatPay(Arrays.asList(PemUtil.loadCertificate(platformKeyInputStream)));
        CloseableHttpClient httpClient = builder.build();
        return httpClient;
    }

    /**
     * 微信通讯client1 读取本地文件方法用以下方法
     * @return CloseableHttpClient
     */
    public static CloseableHttpClient getClient1() {
        /**商户私钥文件*/
        File mchPrivateKeyFile = new File("C:\\cert\\apiclient_key.pem");
        InputStream mchPrivateKeyInputStream = FileUtil.getInputStream(mchPrivateKeyFile);


        /**微信平台公钥文件*/
        File platformKeyFile = new File("C:\\cert\\wechatpay.pem");
        InputStream platformKeyInputStream = FileUtil.getInputStream(platformKeyFile);


        PrivateKey mchPrivateKey = PemUtil.loadPrivateKey(mchPrivateKeyInputStream);
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(MyConfig.MCH_ID, MyConfig.MCH_XLH, mchPrivateKey)
                .withWechatPay(Arrays.asList(PemUtil.loadCertificate(platformKeyInputStream)));
        CloseableHttpClient httpClient = builder.build();
        return httpClient;
    }


    /**
     * 微信敏感数据加密公钥
     * @return
     */
    public static X509Certificate getSaveCertificates() {
        /**商户公钥证书文件*/
        File mchPublicKeyFile = new File("C:\\cert\\wechatpay.pem");
        InputStream mchPublicKeyInputStream = FileUtil.getInputStream(mchPublicKeyFile);
        return PemUtil.loadCertificate(mchPublicKeyInputStream);
    }

}

业务逻辑

 @Override
    public R paytochange(PageData pd) {
        String openId = pd.getString("account");

        CloseableHttpClient httpClient = WeChatClient.getClient();
        X509Certificate x509Certificate = WeChatClient.getSaveCertificates();
        Map<String, Object> postMap = new HashMap<String, Object>();

        //商家批次单号 长度 1~32
        String outNo = IdUtil.getSnowflake(0, 0).nextIdStr();

        postMap.put("appid",  MyConfig.APP_ID);
        postMap.put("out_batch_no", outNo);
        //该笔批量转账的名称
        postMap.put("batch_name", "测试转账");
        //转账说明,UTF8编码,最多允许32个字符
        postMap.put("batch_remark", "测试转账");
        //转账金额单位为“分”。 总金额
        postMap.put("total_amount", 100);
        //转账总笔数
        postMap.put("total_num", 1);


        List<Map> list = new ArrayList<>();
        Map<String, Object> subMap = new HashMap<>(4);
        //商家明细单号
        subMap.put("out_detail_no", outNo);
        //转账金额
        subMap.put("transfer_amount", 100);
        //转账备注
        subMap.put("transfer_remark", "明细备注1");
        //用户在直连商户应用下的用户标示
        subMap.put("openid", openId);
//		subMap.put("user_name", RsaCryptoUtil.encryptOAEP(userName, x509Certificate));
        list.add(subMap);
        postMap.put("transfer_detail_list", list);

        //发起转账操作
        try {


            String body = JSONUtil.toJsonStr(postMap);
            System.out.println("请求参数:" + body);
            HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/transfer/batches");
            httpPost.addHeader(ACCEPT, APPLICATION_JSON.toString());
            httpPost.addHeader(CONTENT_TYPE, APPLICATION_JSON.toString());
            httpPost.addHeader("Wechatpay-Serial", MyConfig.MCH_XLH);

            httpPost.setEntity(new StringEntity(body, "UTF-8"));
            CloseableHttpResponse response = httpClient.execute(httpPost);
            try {
                String bodyAsString = EntityUtils.toString(response.getEntity());
                System.out.println("返回参数:" + bodyAsString);
            } finally {
                response.close();
            }



        } catch (Exception e) {
            e.printStackTrace();
        }
        return  R.ok();

    }

以上方法中用到的常量 MyConfig 常量类中定义
public static final String APP_ID = “你自己的appid”; //app Id
public static final String MCH_KEY = “你自己设置的商户key”;//签名秘钥
public static final String MCH_ID = “你的商户号”; //商户号
public static final String MCH_XLH = “商户证书序列号”; //商户证书序列号
public static final String MCH_V3KEY = “证书的V3密钥 在微信支付平台设置的”; //证书V3秘钥

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值