评论

微信转账到零钱2种实现方式,SDK和API对接

微信转账到零钱

微信官方文档地址

https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter4_3_1.shtml#doc-main

第一种:SDK方式

首先引入微信官方的sdk包

        <dependency>
            <groupId>com.github.wechatpay-apiv3</groupId>
            <artifactId>wechatpay-java</artifactId>
            <version>0.2.11</version>
        </dependency>

核心代码

 
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.deliyun.transfer.util.UUIDUtil;
import com.deliyun.transfer.util.WechatPayConstant;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.service.transferbatch.TransferBatchService;
import com.wechat.pay.java.service.transferbatch.model.InitiateBatchTransferRequest;
import com.wechat.pay.java.service.transferbatch.model.InitiateBatchTransferResponse;
import com.wechat.pay.java.service.transferbatch.model.TransferDetailInput;
 
import java.util.ArrayList;
import java.util.List;
 
public class BatchTransfer {
 
    /**
     * 商户号
     */
    public static String merchantId = "1********";
    /**
     * 商户API私钥路径
     */
    public static String privateKeyPath = "F:apiclient_key.pem";
    /**
     * 商户证书序列号
     */
    public static String merchantSerialNumber = "3860*********************E7447";
    /**
     * 商户APIV3密钥
     */
    public static String apiV3Key = "V**********************************V";
    public static TransferBatchService service;
 
    public static void main(String[] args) {
 
        Config config = new RSAAutoCertificateConfig.Builder()
                .merchantId(merchantId)
                .privateKeyFromPath(privateKeyPath)
                .merchantSerialNumber(merchantSerialNumber)
                .apiV3Key(apiV3Key)
                .build();
        service = new TransferBatchService.Builder().config(config).build();
 
        String out_batch_no = RandomUtil.randomNumbers(10);
        out_batch_no = StrUtil.concat(true, "sc", out_batch_no);
 
        InitiateBatchTransferRequest initiateBatchTransferRequest = new InitiateBatchTransferRequest();
        initiateBatchTransferRequest.setAppid(WechatPayConstant.APP_ID);
        initiateBatchTransferRequest.setOutBatchNo(out_batch_no);
        initiateBatchTransferRequest.setBatchName("2019年1月深圳分部报销单");
        initiateBatchTransferRequest.setBatchRemark("2019年1月深圳分部报销单");
        initiateBatchTransferRequest.setTotalAmount(100l);
        initiateBatchTransferRequest.setTotalNum(1);
 
        List transferDetailListList = new ArrayList<>();
        TransferDetailInput transferDetailInput = new TransferDetailInput();
        transferDetailInput.setOutDetailNo(UUIDUtil.getUUID());
        transferDetailInput.setTransferAmount(100l);
        transferDetailInput.setTransferRemark("2020年4月报销");
        transferDetailInput.setOpenid("o_***************************");
//        transferDetailInput.setUserName(
//                "757b340b45ebef5467rter35gf464344v3542sdf4t6re4tb4f54ty45t4yyry45");
        transferDetailListList.add(transferDetailInput);
        initiateBatchTransferRequest.setTransferDetailList(
                transferDetailListList);
        initiateBatchTransferRequest.setTransferSceneId("1000");
        InitiateBatchTransferResponse response =
                service.initiateBatchTransfer(initiateBatchTransferRequest);
        System.out.println(response);
    }
}


第二种:API方式

协议对象

public class Transfer {
    private String appid; // 直连商户的appid
 
    private String out_batch_no; // 商家批次单号
    private String batch_name = "分润红包"; // 该笔批量转账的名称:
    private String batch_remark = "分润红包"; // 转账说明,UTF8编码,最多允许32个字符
 
    private int total_amount; // 转账总金额:分
    private int total_num = 1; // 转账总笔数
 
    private List transfer_detail_list; // 发起批量转账的明细列表,最多三千笔
 
    public Transfer(String appid, String out_batch_no, int total_amount, List transfer_detail_list) {
        this.appid = appid;
        this.out_batch_no = out_batch_no;
        this.total_amount = total_amount;
        this.transfer_detail_list = transfer_detail_list;
    }
 
    public String getAppid() {
        return appid;
    }
 
    public void setAppid(String appid) {
        this.appid = appid;
    }
 
    public String getOut_batch_no() {
        return out_batch_no;
    }
 
    public void setOut_batch_no(String out_batch_no) {
        this.out_batch_no = out_batch_no;
    }
 
    public String getBatch_name() {
        return batch_name;
    }
 
    public void setBatch_name(String batch_name) {
        this.batch_name = batch_name;
    }
 
    public String getBatch_remark() {
        return batch_remark;
    }
 
    public void setBatch_remark(String batch_remark) {
        this.batch_remark = batch_remark;
    }
 
    public int getTotal_amount() {
        return total_amount;
    }
 
    public void setTotal_amount(int total_amount) {
        this.total_amount = total_amount;
    }
 
    public int getTotal_num() {
        return total_num;
    }
 
    public void setTotal_num(int total_num) {
        this.total_num = total_num;
    }
 
    public List getTransfer_detail_list() {
        return transfer_detail_list;
    }
 
    public void setTransfer_detail_list(List transfer_detail_list) {
        this.transfer_detail_list = transfer_detail_list;
    }
 
    public static class TransferDetailList {
 
        private String out_detail_no = UUIDUtil.getUUID(); // 商户系统内部区分转账批次单下不同转账明细单的唯一标识,要求此参数只能由数字、大小写字母组成
        private int transfer_amount; // 转账金额单位为分
        private String transfer_remark = "分润红包"; //转账备注
        private String openid; // 用户在直连商户应用下的用户标示
 
        public TransferDetailList( int transfer_amount, String openid) {
            this.transfer_amount = transfer_amount;
            this.openid = openid;
        }
 
        public String getOut_detail_no() {
            return out_detail_no;
        }
 
        public void setOut_detail_no(String out_detail_no) {
            this.out_detail_no = out_detail_no;
        }
 
        public int getTransfer_amount() {
            return transfer_amount;
        }
 
        public void setTransfer_amount(int transfer_amount) {
            this.transfer_amount = transfer_amount;
        }
 
        public String getTransfer_remark() {
            return transfer_remark;
        }
 
        public void setTransfer_remark(String transfer_remark) {
            this.transfer_remark = transfer_remark;
        }
 
        public String getOpenid() {
            return openid;
        }
 
        public void setOpenid(String openid) {
            this.openid = openid;
        }
    }
}


工具类 CertUtil.java

public class CertUtil {
 
    public static String APICLIENT_KEY = "conf/cert/apiclient_key.pem";
    public static String APICLIENT_CERT = "conf/cert/apiclient_cert.pem";
 
    /**
     * 获取私钥。
     *
     * @return 私钥对象
     */
    public static PrivateKey getPrivateKey() throws IOException {
        InputStream instream = CertUtil.class.getClassLoader().getResourceAsStream(APICLIENT_KEY);
        String content = new String(ByteStreams.toByteArray(instream), StandardCharsets.UTF_8);
 
//		String content = new String(Files.readAllBytes(Paths.get(APICLIENT_KEY)), StandardCharsets.UTF_8);
        try {
            String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "").replace("-----END PRIVATE KEY-----", "").replaceAll("\\s+", "");
            KeyFactory kf = KeyFactory.getInstance("RSA");
            return kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey)));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持RSA", e);
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException("无效的密钥格式");
        }
    }
 
    /**
     * 获取商户证书。
     *
     * @return X509证书
     */
    public static X509Certificate getCertificate() throws IOException {
//		InputStream fis = new FileInputStream(APICLIENT_CERT);
        InputStream fis = CertUtil.class.getClassLoader().getResourceAsStream(APICLIENT_CERT);
        ;
        try (BufferedInputStream bis = new BufferedInputStream(fis)) {
            CertificateFactory cf = CertificateFactory.getInstance("X509");
            X509Certificate cert = (X509Certificate) cf.generateCertificate(bis);
            cert.checkValidity();
            return cert;
        } catch (CertificateExpiredException e) {
            throw new RuntimeException("证书已过期", e);
        } catch (CertificateNotYetValidException e) {
            throw new RuntimeException("证书尚未生效", e);
        } catch (CertificateException e) {
            throw new RuntimeException("无效的证书文件", e);
        }
    }
 
    /**
     * 获取商户证书序列号
     *
     * @return
     * @throws IOException
     */
    public static String getSerialNo() throws IOException {
        X509Certificate certificate = getCertificate();
        return certificate.getSerialNumber().toString(16).toUpperCase();
    }
 
    public static void main(String[] args) {
        try {
            System.out.println(CertUtil.getCertificate());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


HttpUrl工具类

public class HttpUrlUtil {
 
    public static String SCHEMA = "WECHATPAY2-SHA256-RSA2048";
    public static String merchantId = WechatPayConstant.MCH_ID; // 服务商
 
    public static String POST = "POST";
    public static String GET = "GET";
 
    public static String TRANSFER_URL = "https://api.mch.weixin.qq.com/v3/transfer/batches"; // 转账
 
    public static String postTransfer(String body) {
        String authorization = getToken(POST, TRANSFER_URL, body);
        String returnMsg = HttpRequest.post(TRANSFER_URL)
                .header("Authorization", authorization)
                .header("Wechatpay-Serial", WechatPayConstant.merchantSerialNumber)
                .body(body)
                .execute().body();
        JSONObject returnTransferInfo = JSON.parseObject(returnMsg);
        return returnTransferInfo.toJSONString();
    }
 
    /**
     * 获取加密串
     *
     * @param method
     * @param url
     * @param body
     * @return
     */
    public static String getToken(String method, String url, String body) {
        String nonceStr = UUIDUtil.getUUID();
        long timestamp = System.currentTimeMillis() / 1000;
        HttpUrl httpUrl = HttpUrl.parse(url);
        String message = buildMessage(method, httpUrl, timestamp, nonceStr, body);
        String signature = null;
        String certificateSerialNo = null;
        try {
            signature = sign(message.getBytes("utf-8"));
            certificateSerialNo = CertUtil.getSerialNo();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return SCHEMA + " mchid=\"" + merchantId + "\"," + "nonce_str=\"" + nonceStr + "\"," + "timestamp=\"" + timestamp + "\"," + "serial_no=\""
                + certificateSerialNo + "\"," + "signature=\"" + signature + "\"";
    }
 
    /**
     * 得到签名字符串
     */
    public static String sign(byte[] message) throws Exception {
        Signature sign = Signature.getInstance("SHA256withRSA");
        PrivateKey privateKey = CertUtil.getPrivateKey();
        sign.initSign(privateKey);
        sign.update(message);
        return Base64.getEncoder().encodeToString(sign.sign());
    }
 
    public static String buildMessage(String method, HttpUrl url, long timestamp, String nonceStr, String body) {
        String canonicalUrl = url.encodedPath();
        if (url.encodedQuery() != null) {
            canonicalUrl += "?" + url.encodedQuery();
        }
        return method + "\n" + canonicalUrl + "\n" + timestamp + "\n" + nonceStr + "\n" + body + "\n";
    }


UUID工具类

public class UUIDUtil {
 
    public static String getUUID () {
        return UUID.randomUUID().toString().replace("-", "");
    }
 
    public static void main(String[] args) {
        System.out.println(UUIDUtil.getUUID());
    }
}


常量类 WechatPayConstant.java

public class WechatPayConstant {
 
    /**
     * appId
     */
    public static String APP_ID = "wx64************";
 
    /**
     * 商户号
     */
    public static String MCH_ID = "1***********";
 
    /**
     * API_V3密钥
     */
    public static String API_V3KEY = "V*****************************";
 
    /**
     * 证书序列号
     */
    public static String merchantSerialNumber = "************************************";
}


转账业务类

public class TransferService {
 
    public static void exe(String openId) {
 
        String out_batch_no = RandomUtil.randomNumbers(10);
        out_batch_no = StrUtil.concat(true, "sc", out_batch_no);
 
 
        int transfer_amount = 100;
        Transfer.TransferDetailList transferDetailList = new Transfer.TransferDetailList(transfer_amount, openId);
        List list = new ArrayList<>();
        list.add(transferDetailList);
        Transfer transfer = new Transfer(WechatPayConstant.APP_ID, out_batch_no, transfer_amount, list);
        String str = HttpUrlUtil.postTransfer(JSONObject.toJSONString(transfer));
        System.out.println("转账结果返回:" + str);
    }
 
    public static void main(String[] args) {
 
        String openId = "o_F*******************************";
        TransferService.exe(openId);
    }
 
}


执行结果:

{"create_time":"2023-11-22T18:56:39+08:00","batch_id":"131000110008101808*********************3342","batch_status":"ACCEPTED","out_batch_no":"592147839212385514533287"}


最后一次编辑于  2023-11-23  
点赞 0
收藏
评论

1 个评论

  • @天翼
    @天翼
    06-21

    那么使用官方skd,怎么才能知道转账的结果呢

    06-21
    赞同
    回复
登录 后发表内容