JAVA 微信商户转账V3(保姆级)

 2024.09.24更新说明

许多人对两个微信支付依赖不清楚是什么关系区别
这里解释一下

<!--  微信官方封装SDK -->
<dependency>
    <groupId>com.github.wechatpay-apiv3</groupId>
    <artifactId>wechatpay-java</artifactId>
    <version>0.2.14</version>
</dependency>

官方原封maven依赖



 <!-- 微信官方封装的Apache HttpClient扩展SDK-->
<dependency>
    <groupId>com.github.wechatpay-apiv3</groupId>
    <artifactId>wechatpay-apache-httpclient</artifactId>
    <version>0.4.9</version>
</dependency>

官方扩展封装maven依赖 使用HttpClient构造内部请求


​​​​​​​如果你是使用Apache HttpClient的商户开发者
可以使用它构造HttpClient。得到的HttpClient在执行请求时将自动携带身份认证信息,
并检查应答的微信支付签名。

上述为微信支付github库原述 
所以有人疑惑微信要求传入认证信息 
我这里不需要填写因为全部已经封装进HttpClient里了  
这里我又通过官方封装的源码实例分离出验证方法简化验证过程 
只要商户证书秘钥等正确一致 基本上直接通
这里放上微信maven库可仔细查看各个不同支付封装的解释 wechatpay-apiv3 · GitHub

为了你们能看懂我操碎了心!

2024.05.09更新方法

更新WXPaySignatureCertificateUtil.java 工具类 详细看3.1

许多人对AutoUpdateCertificatesVerifier验证有疑问 

所以此次更新使用Verifier验证

两者自选使用

不废话  如何开通微信转账我就不发了 网上一大堆 直接上主代码

主打一个复制粘贴直接用 依然没有其它依赖

加入Maven依赖

 <!-- 微信支付V3 目前新版本-->
        <dependency>
            <groupId>com.github.wechatpay-apiv3</groupId>
            <artifactId>wechatpay-apache-httpclient</artifactId>
            <version>0.4.9</version>
        </dependency>

创建 WxV3PayConfig.java

/**
 * implements WXPayConfig
 */
@Data
public class WxV3PayConfig {

    //平台证书序列号
    public static String mchSerialNo = "xxxxxxxxxxxxxxxxxxxxxx";

    //appID
    public static String APP_ID = "xxxxxxxxxxx";

    //商户id
    public static String Mch_ID = "xxxxxxxxxxxx";

    // API V3密钥
    public static String apiV3Key = "xxxxxxxxxxxxxxxxxxxxxx";
    
}

创建WXPayConstants.java

/**
 * 常量
 */
public class WXPayConstants {


    public static final String DOMAIN_API = "https://api.mch.weixin.qq.com/v3";

    //微信转账
    public static final String REFUND_DOMESTIC_REFUNDS = "/refund/domestic/refunds";


}

 2.创建WXPaySignatureCertificateUtil.java 工具类

复制粘贴即可

/***
*
*包都在这
*
*
*
*/
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import lombok.SneakyThrows;
import org.apache.http.impl.client.CloseableHttpClient;
import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class WXPaySignatureCertificateUtil {




    /**
     * 证书验证
     * 自动更新的签名验证器
     */
    public static CloseableHttpClient checkSign() throws IOException {
        //验签
        CloseableHttpClient httpClient = null;
        PrivateKey merchantPrivateKey = WXPaySignatureCertificateUtil.getPrivateKey();
        httpClient = WechatPayHttpClientBuilder.create()
                .withMerchant(WxV3PayConfig.Mch_ID, WxV3PayConfig.mchSerialNo, merchantPrivateKey)
                .withValidator(new WechatPay2Validator(WXPaySignatureCertificateUtil.getVerifier(WxV3PayConfig.mchSerialNo)))
                .build();

        return httpClient;
    }




    /**
     * 保存微信平台证书
     */
    private static final ConcurrentHashMap<String, AutoUpdateCertificatesVerifier> verifierMap = new ConcurrentHashMap<>();

    /**
     * 功能描述:获取平台证书,自动更新
     * 注意:这个方法内置了平台证书的获取和返回值解密
     */
    static AutoUpdateCertificatesVerifier getVerifier() {
        String mchSerialNo = WxV3PayConfig.mchSerialNo;
        AutoUpdateCertificatesVerifier verifier = null;
        if (verifierMap.isEmpty() || !verifierMap.containsKey(mchSerialNo)) {
            verifierMap.clear();
            try {
                 //传入证书
                 PrivateKey privateKey = getPrivateKey();
                //刷新
                PrivateKeySigner signer = new PrivateKeySigner(mchSerialNo, privateKey);
                WechatPay2Credentials credentials = new WechatPay2Credentials(WxV3PayConfig.Mch_ID, signer);
                verifier = new AutoUpdateCertificatesVerifier(credentials
                        , WxV3PayConfig.apiV3Key.getBytes("utf-8"));
                verifierMap.put(verifier.getValidCertificate().getSerialNumber()+"", verifier);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        } else {
            verifier = verifierMap.get(mchSerialNo);
        }
        return verifier;
    }



    /**
     * app生成带签名支付信息
     *
     * @param timestamp 时间戳
     * @param nonceStr  随机数
     * @param prepayId  预付单
     * @return 支付信息
     * @throws Exception
     */
    public static String appPaySign(String timestamp, String nonceStr, String prepayId) throws Exception {
        //上传私钥
        PrivateKey privateKey = getPrivateKey();
        String signatureStr = Stream.of(WxV3PayConfig.APP_ID, timestamp, nonceStr, prepayId)
                .collect(Collectors.joining("\n", "", "\n"));
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(privateKey);
        sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(sign.sign());
    }

    /**
     * 小程序及其它支付生成带签名支付信息
     *
     * @param timestamp 时间戳
     * @param nonceStr  随机数
     * @param prepayId  预付单
     * @return 支付信息
     * @throws Exception
     */
    public static String jsApiPaySign(String timestamp, String nonceStr, String prepayId) throws Exception {
        //上传私钥
        PrivateKey privateKey = getPrivateKey();
        String signatureStr = Stream.of(WxV3PayConfig.APP_ID, timestamp, nonceStr, "prepay_id="+prepayId)
                .collect(Collectors.joining("\n", "", "\n"));
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(privateKey);
        sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(sign.sign());
    }


    /**
     * 获取私钥。
     * 证书路径 本地使用如: D:\\微信平台证书工具\\7.9\\apiclient_key.pem
     * 证书路径 线上使用如: /usr/apiclient_key.pem
     * String filename 私钥文件路径  (required)
     * @return 私钥对象
     */
    public static PrivateKey getPrivateKey() throws IOException {
        String content = new String(Files.readAllBytes(Paths.get("D:\\微信平台证书工具\\7.9\\apiclient_key.pem")), "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.getDecoder().decode(privateKey)));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持RSA", e);
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException("无效的密钥格式");
        }
    }

}

3.1.创建WXPaySignatureCertificateUtil.java

/***
*
*包都在这
*
*
*
*/
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import lombok.SneakyThrows;
import org.apache.http.impl.client.CloseableHttpClient;
import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class WXPaySignatureCertificateUtil {




    private static final ConcurrentHashMap<String, Verifier> verifierMaps = new ConcurrentHashMap<>();


   /**
     * 证书验证
     * 自动更新的签名验证器
     */
    public static CloseableHttpClient checkSign() throws IOException {
        //验签
        CloseableHttpClient httpClient = null;
        PrivateKey merchantPrivateKey = WXPaySignatureCertificateUtil.getPrivateKey();
        httpClient = WechatPayHttpClientBuilder.create()
                .withMerchant(WxV3PayConfig.Mch_ID, WxV3PayConfig.mchSerialNo, merchantPrivateKey)
                .withValidator(new WechatPay2Validator(WXPaySignatureCertificateUtil.getVerifier(WxV3PayConfig.mchSerialNo)))
                .build();

        return httpClient;
    }
        


    /**
     * 功能描述:获取平台证书,自动更新
     * 注意:这个方法内置了平台证书的获取和返回值解密
     */
    public static Verifier getVerifiers(String mchSerialNo) {
        Verifier verifier = null;
        if (verifierMaps.isEmpty() || !verifierMaps.containsKey(mchSerialNo)) {
            verifierMaps.clear();
            try {
                PrivateKey privateKey = getPrivateKey();
                //刷新
                PrivateKeySigner signer = new PrivateKeySigner(mchSerialNo, privateKey);
                WechatPay2Credentials credentials = new WechatPay2Credentials(WxV3PayConfig.Mch_ID, signer);
                verifier = new AutoUpdateCertificatesVerifier(credentials
                        , apiV3Key.getBytes("utf-8"));
                verifierMaps.put(verifier.getValidCertificate().getSerialNumber()+"", verifier);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        } else {
            verifier = verifierMaps.get(mchSerialNo);
        }
        return verifier;
    }


    /**
     * app生成带签名支付信息
     *
     * @param timestamp 时间戳
     * @param nonceStr  随机数
     * @param prepayId  预付单
     * @return 支付信息
     * @throws Exception
     */
    public static String appPaySign(String timestamp, String nonceStr, String prepayId) throws Exception {
        //上传私钥
        PrivateKey privateKey = getPrivateKey();
        String signatureStr = Stream.of(WxV3PayConfig.APP_ID, timestamp, nonceStr, prepayId)
                .collect(Collectors.joining("\n", "", "\n"));
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(privateKey);
        sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(sign.sign());
    }

    /**
     * 小程序及其它支付生成带签名支付信息
     *
     * @param timestamp 时间戳
     * @param nonceStr  随机数
     * @param prepayId  预付单
     * @return 支付信息
     * @throws Exception
     */
    public static String jsApiPaySign(String timestamp, String nonceStr, String prepayId) throws Exception {
        //上传私钥
        PrivateKey privateKey = getPrivateKey();
        String signatureStr = Stream.of(WxV3PayConfig.APP_ID, timestamp, nonceStr, "prepay_id="+prepayId)
                .collect(Collectors.joining("\n", "", "\n"));
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(privateKey);
        sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(sign.sign());
    }


    /**
     * 获取私钥。
     * 证书路径 本地使用如: D:\\微信平台证书工具\\7.9\\apiclient_key.pem
     * 证书路径 线上使用如: /usr/apiclient_key.pem
     * String filename 私钥文件路径  (required)
     * @return 私钥对象
     */
    public static PrivateKey getPrivateKey() throws IOException {
        String content = new String(Files.readAllBytes(Paths.get("D:\\微信平台证书工具\\7.9\\apiclient_key.pem")), "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.getDecoder().decode(privateKey)));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持RSA", e);
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException("无效的密钥格式");
        }
    }

}

创建WechatPaymentService.java

/**
 * 微信支付 WechatPaymentService
 * @author 影子
 */
public interface WechatPaymentService{

    /**
     * 微信转账提现
     * @param payParam
     * @return
     */
    public Map<String, Object> weChatWithdraw();

}

创建WeChatPaymentServiceImpl.java

/**
 * 微信支付 WeChatPaymentServiceImpl
 */
@Service
@Slf4j
public class WeChatPaymentServiceImpl implements WechatPaymentService {



/**
     * 微信提现转账
     * @param 
     * @return 影子
     */
    @Override
    public Map<String, Object> weChatWithdraw() {
        Map<String,Object> ajaxResult = new HashMap<>();
        try {

            //支付总金额
            BigDecimal totalPrice = BigDecimal.ZERO;
            totalPrice = totalPrice.add(BigDecimal.valueOf(500));
            //转换金额
            Integer money = new BigDecimal(String.valueOf(totalPrice)).movePointRight(2).intValue();
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectMapper objectMapper = new ObjectMapper();
            ObjectNode rootNode = objectMapper.createObjectNode();
            //微信商户批次号订单号
            rootNode.put("appid", "appid")
                    //微信商户批次号订单号
                    .put("out_batch_no","51213215612")
                    .put("batch_name","分佣提现")
                    .put("batch_remark","商品订单分佣奖励")
                    .put("total_amount",money)
                    .put("total_num",1);
            ArrayNode transferDetailList = objectMapper.createArrayNode();
            ObjectNode transferDetail = objectMapper.createObjectNode();
            //微信批次号
            transferDetail.put("out_detail_no","55312168545132")
                    .put("transfer_amount",money)
                    .put("transfer_remark","商品订单分佣奖励")
                    .put("openid","1223115612");
            transferDetailList.add(transferDetail);
            rootNode.set("transfer_detail_list", transferDetailList);
            objectMapper.writeValue(bos, rootNode);
            //验证证书
            CloseableHttpClient httpClient = WXPaySignatureCertificateUtil.checkSign();
            //申请退款接口
            HttpPost httpPost = new HttpPost(DOMAIN_API + REFUND_DOMESTIC_REFUNDS);
            httpPost.addHeader("Accept", "application/json");
            httpPost.addHeader("Content-type", "application/json");
            httpPost.addHeader("Wechatpay-Serial", WxV3PayConfig.mchSerialNo);
            httpPost.setEntity(new StringEntity(bos.toString(), "UTF-8"));
            CloseableHttpResponse response = httpClient.execute(httpPost);
            //退款成功返回消息
            String bodyAsString = EntityUtils.toString(response.getEntity());
            JSONObject jsonObject = JSONObject.parseObject(bodyAsString);
            if (jsonObject.containsKey("batch_status")) {
                String batchStatus = jsonObject.getString("batch_status");
                if ("ACCEPTED".equals(batchStatus)) {
                    // 退款成功
                    ajaxResult.put("code", "200");
                    ajaxResult.put("msg", "提现成功");
                    return ajaxResult;
                }
            } else if (jsonObject.containsKey("code")) {
                String code = jsonObject.getString("code");
                if ("NOT_ENOUGH".equals(code)) {

                    ajaxResult.put("code", "500");
                    ajaxResult.put("msg", "资金不足");
                    return ajaxResult;
                } else {
                    ajaxResult.put("code", "500");
                    ajaxResult.put("msg", "申请提现失败");
                    return ajaxResult;

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

        }
        ajaxResult.put("code", "500");
        ajaxResult.put("msg", "申请提现失败");
        return ajaxResult;
    }


}

你学费了吗?

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值