SpringBoot对接微信商家转账功能

本文档介绍了如何在微信支付商户后台实现商家提现功能,包括商家申请提现、小程序运营商审批及转账的逻辑。文中详细展示了引入依赖、常量配置、姓名加密工具类的实现以及发起商家转账的代码步骤,涉及微信支付APIv3版本和RSA加密。开通此功能需满足商户号入驻和交易条件。
摘要由CSDN通过智能技术生成

最近做的一个外卖小程序时,后台涉及到商家提现的功能,就是用微信的这个功能来实现,具体逻辑:商家入驻了这个小程序,卖出商品后,用户支付的钱会到微信商户后台,之后商家会进行提现申请,小程序运营商审批通过后会对商家进行转账,就使用此功能实现。
在这里插入图片描述
官方文档:https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter4_3_1.shtml

实现步骤及代码演示

引入依赖

        <!--微信支付依赖 v3版本-->
        <dependency>
            <groupId>com.github.wechatpay-apiv3</groupId>
            <artifactId>wechatpay-apache-httpclient</artifactId>
            <version>0.2.2</version>
        </dependency>

常量类

package com.office.admin.constants;

/**
 * @InterfaceName: WechatPayConstant
 * @Description: 微信支付常量类
 * @Authror: XQD
 * @Date: 2022/1/6 15:02
 */
public interface WechatPayConstant {

    /**
     * 商户号
     */
    String MCH_ID = "***";

    /**
     * 商户证书序列号
     */
    String MCH_SERIAL_NO = "***";

    /**
     * API_V3密钥
     */
    String API_V3KEY = "***";

    /**
     * 小程序appId
     */
    String MP_APP_ID = "***";

    /**
     * 商户私钥
     */
    String PRIVATE_KEY = "-----BEGIN PRIVATE KEY-----\n" +
            "***" +
            "-----END PRIVATE KEY-----\n";

    /**
     * 发起转账url
     */
    String V3_BATCHES_URL = "https://api.mch.weixin.qq.com/v3/transfer/batches";
    

}

姓名加密工具类

注意:转账金额大于2000元时,微信真实姓名字段是必填的,所以才需要对姓名加密处理,微信要求的

package com.office.admin.utils;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Base64;

public class RsaCryptoUtil {

    /**
     * 加密
     */
    public static String encryptOAEP(String message, X509Certificate certificate)
            throws IllegalBlockSizeException {
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
            cipher.init(Cipher.ENCRYPT_MODE, certificate.getPublicKey());

            byte[] data = message.getBytes(StandardCharsets.UTF_8);
            byte[] ciphertext = cipher.doFinal(data);
            return Base64.getEncoder().encodeToString(ciphertext);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e);
        } catch (InvalidKeyException e) {
            throw new IllegalArgumentException("无效的证书", e);
        } catch (IllegalBlockSizeException | BadPaddingException e) {
            throw new IllegalBlockSizeException("加密原串的长度不能超过214字节");
        }
    }

    public static String decryptOAEP(String ciphertext, PrivateKey privateKey)
            throws BadPaddingException {
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
            cipher.init(Cipher.DECRYPT_MODE, privateKey);

            byte[] data = Base64.getDecoder().decode(ciphertext);
            return new String(cipher.doFinal(data), StandardCharsets.UTF_8);
        } catch (NoSuchPaddingException | NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e);
        } catch (InvalidKeyException e) {
            throw new IllegalArgumentException("无效的私钥", e);
        } catch (BadPaddingException | IllegalBlockSizeException e) {
            throw new BadPaddingException("解密失败");
        }
    }
}

发起商家转账代码

 public Object batches() throws Exception {
        // 加载商户私钥(privateKey:私钥字符串)
        PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(
                new ByteArrayInputStream(WechatPayConstant.PRIVATE_KEY.getBytes("utf-8")));
        //使用自动更新的签名验证器,不需要传入证书
        AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
                new WechatPay2Credentials(WechatPayConstant.MCH_ID, new PrivateKeySigner(WechatPayConstant.MCH_SERIAL_NO, merchantPrivateKey)),
                WechatPayConstant.API_V3KEY.getBytes("utf-8"));
        // 初始化httpClient
        CloseableHttpClient httpClient = WechatPayHttpClientBuilder.create()
                .withMerchant(WechatPayConstant.MCH_ID, WechatPayConstant.MCH_SERIAL_NO, merchantPrivateKey)
                .withValidator(new WechatPay2Validator(verifier))
                .build();
        // 获取微信支付平台证书序列号
        URIBuilder uriBuilder = new URIBuilder("https://api.mch.weixin.qq.com/v3/certificates");
        uriBuilder.setParameter("p", "1&2");
        uriBuilder.setParameter("q", "你好");
        HttpGet httpGet = new HttpGet(uriBuilder.build());
        httpGet.addHeader("Accept", "application/json");

        CloseableHttpResponse response1 = httpClient.execute(httpGet);
        String badyString = EntityUtils.toString(response1.getEntity());
        if (response1.getStatusLine().getStatusCode() != 200){
            return "获取平台序列号失败";
        }
        //assertEquals(200, response1.getStatusLine().getStatusCode());
        try {
            HttpEntity entity1 = response1.getEntity();
            // do something useful with the response body
            // and ensure it is fully consumed
            EntityUtils.consume(entity1);
        } finally {
            response1.close();
        }

        JSONObject jsonObject1 = JSON.parseObject(badyString);
        JSONArray data = jsonObject1.getJSONArray("data");
        JSONObject jsonObject2 = data.getJSONObject(0);
        //获取到的微信支付平台公钥
        String serialNo = jsonObject2.getString("serial_no");

        verifier.getValidCertificate().getSerialNumber().toString(16);
        // 对姓名进行Rsa加密 名字根据业务逻辑传参
        String userName = RsaCryptoUtil.encryptOAEP("张三", verifier.getValidCertificate());
        HttpPost httpPost = new HttpPost(WechatPayConstant.V3_BATCHES_URL);
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");
        //httpPost.addHeader("Wechatpay-Serial", WechatPayConstant.MCH_SERIAL_NO);
        httpPost.addHeader("Wechatpay-Serial", serialNo);
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectMapper objectMapper = new ObjectMapper();
        ObjectNode rootNode = objectMapper.createObjectNode();
        rootNode.put("appid", WechatPayConstant.MP_APP_ID)
                .put("out_batch_no", System.currentTimeMillis() + "")
                .put("batch_name", "批次名称")
                .put("batch_remark", "批次备注")
                .put("total_amount", 100)
                .put("total_num", 1);
        List<ObjectNode> list = new ArrayList<>();
        ObjectNode rootNode1 = objectMapper.createObjectNode();
        rootNode1.put("out_detail_no", System.currentTimeMillis() + "")
                .put("transfer_amount", 100)
                .put("transfer_remark", "转账备注")
                .put("openid", "用户在直连商户应用下的用户标示")
                .put("user_name", userName);
        list.add(rootNode1);
        rootNode.putArray("transfer_detail_list")
                .addAll(list);
        objectMapper.writeValue(bos, rootNode);
        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
        //完成签名并执行请求
        CloseableHttpResponse response = httpClient.execute(httpPost);
        String bodyAsString = EntityUtils.toString(response.getEntity());
        log.info("转账返回的报文:{}", bodyAsString);
        //自行根据业务修改
        return bodyAsString;
    }

完成!!!

注意:该功能的开通条件是
1、商户号已入驻90日且截止今日回推30天商户号保持连续不间的交易。
2、登录微信支付商户平台-产品中心,开通商家转账到零钱。

  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值