Java实现微信小程序商家转账到零钱功能V3

Java实现微信小程序商家转账到零钱功能V3版本

工具类

该工具类主要用于发送请求签名等

@Slf4j
public class WechatPayV3Util {


    /**
     * @param method       请求方法 post
     * @param canonicalUrl 请求地址
     * @param body         请求参数
     * @param merchantId   商户号
     * @param certSerialNo 商户证书序列号
     * @param keyPath      私钥商户证书地址
     * @return
     * @throws Exception
     */
    public static String getToken(
            String method,
            String canonicalUrl,
            String body,
            String merchantId,
            String certSerialNo,
            String keyPath) throws Exception {
        String signStr = "";
        //获取32位随机字符串
        String nonceStr = getRandomString(32);
        //当前系统运行时间
        long timestamp = System.currentTimeMillis() / 1000;
        if (StringUtils.isEmpty(body)) {
            body = "";
        }
        String message = buildMessage(method, canonicalUrl, timestamp, nonceStr, body);
        //签名操作
        //签名操作
        String signature = sign(message.getBytes(StandardCharsets.UTF_8), keyPath);
        //组装参数
        signStr = "mchid=\"" + merchantId + "\",timestamp=\"" +  timestamp+ "\",nonce_str=\"" + nonceStr
                + "\",serial_no=\"" + certSerialNo + "\",signature=\"" + signature + "\"";

        return signStr;
    }

    public static String buildMessage(String method, String canonicalUrl, long timestamp, String nonceStr, String body) {
        return method + "\n" + canonicalUrl + "\n" + timestamp + "\n" + nonceStr + "\n" + body + "\n";
    }

    public static String sign(byte[] message, String keyPath) throws Exception {
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(getPrivateKey(keyPath));
        sign.update(message);
        return Base64.encodeBase64String(sign.sign());
    }

    /**
     * @param filename 私钥文件路径  (required)
     * @return 私钥对象
     */
    public static PrivateKey getPrivateKey(String filename) throws IOException {

        //todo 我放在项目里的  也可以放在服务器上 都可以 看自己需求了
        String content = new String(Files.readAllBytes(Paths.get("src/main/resources" + filename)), 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("不支持RSA", e);
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException("无效的密钥格式");
        }
    }

    /**
     * 获取随机位数的字符串
     * @param length  随机数长度
     * @return
     */
    public static String getRandomString(int length) {
        String base = "abcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }

    /**
     * 微信敏感数据加密-公钥
     * @return
     */
    public static X509Certificate getSaveCertificates(String platformCertPath) throws IOException {
        InputStream inputStream = Files.newInputStream(new File(platformCertPath).toPath());
        BufferedInputStream bis = new BufferedInputStream(inputStream);
        return PemUtil.loadCertificate(bis);
    }
}

请求地址信息

public class WxApiUrl {

    /**
     * 商家转账到零钱
     */
    public static String WECHAT_TRANSFERS_URL = "https://api.mch.weixin.qq.com/v3/transfer/batches";
    /**
     * 商户批次号查询
     */
    public static String SELECT_ORDER_URL = "https://api.mch.weixin.qq.com/v3/transfer/batches/out-batch-no";
}

配置文件中值填充到属性中

@Configuration
@ConfigurationProperties(prefix = "wxpay") //读取wxpay节点
@Data //使用set方法将wxpay节点中的值填充到当前类的属性中
@Slf4j
public class WxPayConfig {

    // 商户号
    private String mchId;

    // 商户API证书序列号
    private String mchSerialNo;

    // 商户私钥文件
    private String privateKeyName;

    // APIv3密钥
    private String apiV3Key;

    // APPID
    private String appid;

    // 微信服务器地址
    private String domain;

    // 接收结果通知地址
    private String notifyDomain;
}

配置文件

# 微信支付相关参数
wxpay:
  # 商户号
  mch-id: xxxxxxx
  # 商户API证书序列号
  mch-serial-no: xxxxxxxxxxxxxxxxxxxxxxxxxxx
  # 商户私钥文件
  # 注意:该文件放在resource/file
  private-key-name: file/apiclient_key.pem
  # APIv3密钥
  api-v3-key: xxxxxxxxxxxxxxxxxxxxx
  # APPID
  appid: xxxxxxxxxxxxxxx
  # 微信服务器地址
  domain: https://api.mch.weixin.qq.com
  # 接收结果通知地址
  # 注意:每次重新启动ngrok,都需要根据实际情况修改这个配置
  notify-domain: http://tozzn.e3.luyouxia.net:10062

提现与账单查询

public interface WeChatApiDao {

    /**
     * 提現
     * @return
     */
    boolean transfers(String realName,String openId ,String outTradeNo, BigDecimal amount);


    /**
     * 状态 0待确认 1已受理 2转账中 3已完成 4已关闭
     * @param outTradeNo
     * @return
     */
    Integer queryTransfersStatus(String outTradeNo);
}

@Slf4j
@Component
public class WeChatApiDaoImpl implements WeChatApiDao {


    @Resource
    private WxPayConfig wxPayConfig;

    @Override
    @SneakyThrows
    public boolean transfers(String realName,String openId ,String outTradeNo, BigDecimal amount) {

//        if (StringUtils.isBlank(realName)) {
//            throw new NingException(500, "请确认是否设置真实姓名");
//        }

        int value = amount.multiply(new BigDecimal(100)).intValue();

        Map<String, Object> postMap = new HashMap<String, Object>();

        //小程序 id
        postMap.put("appid", wxPayConfig.getAppid()); // 需要跟商户ID 绑定的奥
        postMap.put("out_batch_no", outTradeNo);
        //该笔批量转账的名称
        postMap.put("batch_name", "提现");
        //转账说明
        postMap.put("batch_remark", "提现");
        //转账金额单位为“分”。 总金额

        postMap.put("total_amount", value);
        //转账总笔数
        postMap.put("total_num", 1);

        // 转账批次集合
        List<Map<String, Object>> list = new ArrayList<>();
        Map<String, Object> subMap = new HashMap<>(4);
        //商家明细单号 (系统内唯一)
        subMap.put("out_detail_no", CommUtils.getUUID());
        //转账金额
        subMap.put("transfer_amount", value);
        //转账备注
        subMap.put("transfer_remark", "提现到账");
        //用户使用小程序时 记录了用户的微信opneId  必须是在Appid 下的openId  不然不会成功的奥
        subMap.put("openid", openId);
        //大金额需要传入真实姓名 根据自己需求设置
//        subMap.put("user_name", RsaCryptoUtil.encryptOAEP(realName, WechatPayV3Util.getSaveCertificates(wxPayConfig.getPrivateKeyName())));
        list.add(subMap);
        postMap.put("transfer_detail_list", list);

        log.info("请求参数:{}",JSONObject.toJSONString(postMap));

        String result = HttpUtil.postTransBatRequest(
                WxApiUrl.WECHAT_TRANSFERS_URL,// 请求地址
                JSONObject.toJSONString(postMap),// 请求参数
                wxPayConfig.getMchSerialNo(),// 支付证书序列号
                wxPayConfig.getMchId(),// 商户号
                wxPayConfig.getPrivateKeyName()); //证书私钥地址
        JSONObject jsonObject = JSONObject.parseObject(result);

        log.info("返回结果:{}",jsonObject);
        /*正常返回
        *{
			 "out_batch_no" : "plfk2020042013",
			  "batch_id" : "1030000071100999991182020050700019480001",
			  "create_time" : "2015-05-20T13:29:35.120+08:00",
			  "batch_status" : "ACCEPTED"
		}
        */

        return "ACCEPTED".equals(jsonObject.getString("batch_status"));

    }

    @Override
    public Integer queryTransfersStatus(String outTradeNo) {

        String result = HttpUtil.getTransBatRequest(
                WxApiUrl.SELECT_ORDER_URL + "/" + outTradeNo + "?need_query_detail=true&detail_status=ALL",// 请求地址
                wxPayConfig.getMchSerialNo(),// 支付证书序列号
                wxPayConfig.getMchId(),// 商户号
                wxPayConfig.getPrivateKeyName(),
                "/v3/transfer/batches/out-batch-no/" + outTradeNo + "?need_query_detail=true&detail_status=ALL"
                ); //证书私钥地址
        JSONObject jsonObject = JSONObject.parseObject(result);

        JSONArray orderArr = jsonObject.getJSONArray("transfer_detail_list");
        if(orderArr == null){
            return 5;
        }

        JSONObject transferBatch = (JSONObject) orderArr.get(0);


        String status = transferBatch.getString("detail_status");

        if ("WAIT_PAY".equals(status)) {
            return 1;
        }else if ("PROCESSING".equals(status)) {
            return 2;
        }else if ("SUCCESS".equals(status)) {
            return 3;
        }else if ("FAIL".equals(status)) {
            return 4;
        }else if ("INIT".equals(status)) {
            return 0;
        }
        return 5;
    }

ok,大功告成,亲测可用~

微信支付商家转账零钱功能通常涉及到第三方支付API的集成,比如微信支付提供的商户SDK。在Java实现这个功能,你需要按照以下步骤操作: 1. **注册并获取API权限**: - 首先,需要在微信公众平台上注册成为服务商,并申请获取支付相关的API密钥和证书。 2. **引入微信支付库**: - 下载官方提供的微信支付Java SDK(如Wechat Pay Java SDK),将它添加到项目依赖中。 3. **创建订单**: - 创建一个包含交易信息的对象,如金额、订单描述等,然后通过SDK生成支付请求的统一下单接口调用。 ```java WxPayUnifiedOrder unifiedOrder = new WxPayUnifiedOrder(); unifiedOrder.setBody("转账说明"); unifiedOrder.setOutTradeNo("交易订单号"); unifiedOrder.setTotalFee("转账金额"); // 设置其他必要的参数,如openId、notifyUrl等 Map<String, String> result = unifiedOrder统一支付下单(); ``` 4. **用户授权并发起转账**: - 用户点击支付链接,会跳转到微信客户端进行身份验证并确认转账操作。 5. **处理回调通知**: - 商家服务器需要设置接收微信支付异步通知的URL,当转账完成或有状态变更时,微信会发送通知到该地址。 6. **检查结果和更新数据库**: - 接收到通知后,解析返回的数据,判断支付状态,成功则更新用户账户余额和交易记录。 注意:这只是一个基础流程概述,实际开发中还需处理异常、加密传输敏感数据以及遵循微信支付的安全策略。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿小张丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值