diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/QueryTransferBatchesRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/QueryTransferBatchesRequest.java new file mode 100644 index 0000000000..48401ce9c8 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/QueryTransferBatchesRequest.java @@ -0,0 +1,50 @@ +package com.github.binarywang.wxpay.bean.transfer; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 查询微信批次单号查询批次单API参数 + * + * @author zhongjun + * @date 2022/6/17 + **/ +@Data +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +public class QueryTransferBatchesRequest implements Serializable { + private static final long serialVersionUID = -2175582517588397426L; + + /** + * 微信批次单号 + */ + private String batchId; + + /** + * 是否查询转账明细单 + */ + private Boolean needQueryDetail; + + private Integer offset; + + private Integer limit; + + /** + * 明细状态 + * 查询指定状态的转账明细单,当need_query_detail为true时,该字段必填 + * ALL:全部。需要同时查询转账成功和转账失败的明细单 + * SUCCESS:转账成功。只查询转账成功的明细单 + * FAIL:转账失败。只查询转账失败的明细单 + */ + private String detailStatus; + + /** + * 商家批次单号 + */ + private String outBatchNo; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/QueryTransferBatchesResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/QueryTransferBatchesResult.java new file mode 100644 index 0000000000..59a2f30fc4 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/QueryTransferBatchesResult.java @@ -0,0 +1,101 @@ +package com.github.binarywang.wxpay.bean.transfer; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + * 查询微信批次单号查询批次单API响应 + * + * @author zhongjun + * @date 2022/6/17 + **/ +@Data +@NoArgsConstructor +public class QueryTransferBatchesResult implements Serializable { + private static final long serialVersionUID = -2175582517588397426L; + + @SerializedName("offset") + private Integer offset; + + @SerializedName("limit") + private Integer limit; + + @SerializedName("transfer_batch") + private TransferBatch transferBatch; + + @SerializedName("transfer_detail_list") + private List transferDetailList; + + @NoArgsConstructor + @Data + public static class TransferBatch { + @SerializedName("mchid") + private String mchid; + + @SerializedName("out_batch_no") + private String outBatchNo; + + @SerializedName("batch_id") + private String batchId; + + @SerializedName("appid") + private String appid; + + @SerializedName("batch_status") + private String batchStatus; + + @SerializedName("batch_type") + private String batchType; + + @SerializedName("batch_name") + private String batchName; + + @SerializedName("batch_remark") + private String batchRemark; + + @SerializedName("close_reason") + private String closeReason; + + @SerializedName("total_amount") + private Integer totalAmount; + + @SerializedName("total_num") + private Integer totalNum; + + @SerializedName("create_time") + private String createTime; + + @SerializedName("update_time") + private String updateTime; + + @SerializedName("success_amount") + private Integer successAmount; + + @SerializedName("success_num") + private Integer successNum; + + @SerializedName("fail_amount") + private Integer failAmount; + + @SerializedName("fail_num") + private Integer failNum; + } + + @NoArgsConstructor + @Data + public static class TransferDetail { + + @SerializedName("detail_id") + private String detailId; + + @SerializedName("out_detail_no") + private String outDetailNo; + + @SerializedName("detail_status") + private String detailStatus; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBatchDetailResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBatchDetailResult.java new file mode 100644 index 0000000000..8b76e445ed --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBatchDetailResult.java @@ -0,0 +1,60 @@ +package com.github.binarywang.wxpay.bean.transfer; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 微信明细单号查询明细单API + * + * @author zhongjun + */ +@NoArgsConstructor +@Data +public class TransferBatchDetailResult implements Serializable { + private static final long serialVersionUID = -2175582517588397426L; + + @SerializedName("mchid") + private String mchid; + + @SerializedName("out_batch_no") + private String outBatchNo; + + @SerializedName("batch_id") + private String batchId; + + @SerializedName("appid") + private String appid; + + @SerializedName("out_detail_no") + private String outDetailNo; + + @SerializedName("detail_id") + private String detailId; + + @SerializedName("detail_status") + private String detailStatus; + + @SerializedName("transfer_amount") + private Integer transferAmount; + + @SerializedName("transfer_remark") + private String transferRemark; + + @SerializedName("fail_reason") + private String failReason; + + @SerializedName("openid") + private String openid; + + @SerializedName("user_name") + private String userName; + + @SerializedName("initiate_time") + private String initiateTime; + + @SerializedName("update_time") + private String updateTime; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBatchesRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBatchesRequest.java new file mode 100644 index 0000000000..56f358494d --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBatchesRequest.java @@ -0,0 +1,106 @@ +package com.github.binarywang.wxpay.bean.transfer; + +import com.github.binarywang.wxpay.v3.SpecEncrypt; +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + * 发起商家转账API参数 + * + * @author zhongjun + * @date 2022/6/17 + **/ +@Data +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +public class TransferBatchesRequest implements Serializable { + private static final long serialVersionUID = -2175582517588397426L; + + /** + * 直连商户的appid + */ + @SerializedName("appid") + private String appid; + + /** + * 商家批次单号 + */ + @SerializedName("out_batch_no") + private String outBatchNo; + + /** + * 批次名称 + */ + @SerializedName("batch_name") + private String batchName; + + /** + * 批次备注 + */ + @SerializedName("batch_remark") + private String batchRemark; + + /** + * 转账总金额 + */ + @SerializedName("total_amount") + private Integer totalAmount; + + /** + * 转账总笔数 + */ + @SerializedName("total_num") + private Integer totalNum; + + /** + * 转账明细列表 + */ + @SerializedName("transfer_detail_list") + private List transferDetailList; + + + @Data + @Builder(builderMethodName = "newBuilder") + @AllArgsConstructor + @NoArgsConstructor + public static class TransferDetail { + + /** + * 商家明细单号 + */ + @SerializedName("out_detail_no") + private String outDetailNo; + + /** + * 转账金额 + */ + @SerializedName("transfer_amount") + private Integer transferAmount; + + /** + * 转账备注 + */ + @SerializedName("transfer_remark") + private String transferRemark; + + /** + * 用户在直连商户应用下的用户标示 + */ + @SerializedName("openid") + private String openid; + + /** + * 收款用户姓名 + */ + @SpecEncrypt + @SerializedName("user_name") + private String userName; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBatchesResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBatchesResult.java new file mode 100644 index 0000000000..70b9a279d1 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/TransferBatchesResult.java @@ -0,0 +1,37 @@ +package com.github.binarywang.wxpay.bean.transfer; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 商家转账结果 + * + * @author zhongjun + * @date 2022/6/17 + **/ +@Data +@NoArgsConstructor +public class TransferBatchesResult implements Serializable { + private static final long serialVersionUID = -2175582517588397426L; + + /** + * 商家批次单号 + */ + @SerializedName("out_batch_no") + private String outBatchNo; + + /** + * 微信批次单号 + */ + @SerializedName("batch_id") + private String batchId; + + /** + * 批次创建时间 + */ + @SerializedName("create_time") + private String createTime; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/TransferService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/TransferService.java new file mode 100644 index 0000000000..b2516ebe61 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/TransferService.java @@ -0,0 +1,102 @@ +package com.github.binarywang.wxpay.service; + +import com.github.binarywang.wxpay.bean.transfer.*; +import com.github.binarywang.wxpay.exception.WxPayException; + +/** + * 商家转账到零钱 + * + * @author zhongjun + * @date 2022/6/17 + **/ +public interface TransferService { + + /** + *
+   *
+   * 发起商家转账API
+   *
+   * 请求方式:POST(HTTPS)
+   * 请求地址:请求地址
+   *
+   * 文档地址:发起商家转账API
+   * 
+ * + * @param request 转账请求参数 + * @return TransferBatchesResult 转账结果 + * @throws WxPayException . + */ + TransferBatchesResult transferBatches(TransferBatchesRequest request) throws WxPayException; + + /** + *
+   *
+   * 微信批次单号查询批次单API
+   *
+   * 请求方式:GET(HTTPS)
+   * 请求地址:请求地址
+   *
+   * 文档地址:微信批次单号查询批次单API
+   * 
+ * + * @param request 查询请求参数 + * @return TransferBatchesResult 查询结果 + * @throws WxPayException . + */ + QueryTransferBatchesResult transferBatchesBatchId(QueryTransferBatchesRequest request) throws WxPayException; + + /** + *
+   *
+   * 微信明细单号查询明细单API
+   *
+   * 请求方式:GET(HTTPS)
+   * 请求地址:请求地址
+   *
+   * 文档地址:微信明细单号查询明细单API
+   * 
+ * + * @param batchId 微信批次单号 + * @param detailId 微信明细单号 + * @return TransferBatchDetailResult 查询结果 + * @throws WxPayException . + */ + TransferBatchDetailResult transferBatchesBatchIdDetail(String batchId, String detailId) throws WxPayException; + + /** + *
+   *
+   * 商家批次单号查询批次单API
+   *
+   * 请求方式:GET(HTTPS)
+   * 请求地址:请求地址
+   *
+   * 文档地址:商家批次单号查询批次单API
+   * 
+ * + * @param request 查询请求参数 + * @return TransferBatchesResult 查询结果 + * @throws WxPayException . + * @throws WxPayException . + */ + QueryTransferBatchesResult transferBatchesOutBatchNo(QueryTransferBatchesRequest request) throws WxPayException; + + /** + *
+   *
+   * 商家明细单号查询明细单API
+   *
+   * 请求方式:GET(HTTPS)
+   * 请求地址:请求地址
+   *
+   * 文档地址:商家明细单号查询明细单API
+   * 
+ * + * @param outBatchNo 商家明细单号 + * @param outDetailNo 商家批次单号 + * @return TransferBatchDetailResult 查询结果 + * @throws WxPayException . + */ + TransferBatchDetailResult transferBatchesOutBatchNoDetail(String outBatchNo, String outDetailNo) throws WxPayException; + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java index 74f353e838..29735a00c4 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java @@ -1352,7 +1352,15 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri /** * 获取银行组件服务 - * @return 银行组件服务 + * + * @return 银行组件服务 */ BankService getBankService(); + + /** + * 获取商家转账到零钱服务类. + * + * @return the transfers service + */ + TransferService getTransferService(); } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java index 27fe2bfa27..6753f6d1f6 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java @@ -79,6 +79,7 @@ public abstract class BaseWxPayServiceImpl implements WxPayService { private final PayrollService payrollService = new PayrollServiceImpl(this); private final ComplaintService complaintsService = new ComplaintServiceImpl(this); private final BankService bankService = new BankServiceImpl(this); + private final TransferService transferService = new TransferServiceImpl(this); protected Map configMap; @@ -1269,4 +1270,9 @@ public ComplaintService getComplaintsService() { public BankService getBankService() { return bankService; } + + @Override + public TransferService getTransferService() { + return transferService; + } } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/TransferServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/TransferServiceImpl.java new file mode 100644 index 0000000000..c0a8f76184 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/TransferServiceImpl.java @@ -0,0 +1,61 @@ +package com.github.binarywang.wxpay.service.impl; + +import com.github.binarywang.wxpay.bean.transfer.*; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.TransferService; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.v3.util.RsaCryptoUtil; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import lombok.RequiredArgsConstructor; + +/** + * 商家转账到零钱 + * + * @author zhongjun + * @date 2022/6/17 + **/ +@RequiredArgsConstructor +public class TransferServiceImpl implements TransferService { + + private static final Gson GSON = new GsonBuilder().create(); + private final WxPayService payService; + + @Override + public TransferBatchesResult transferBatches(TransferBatchesRequest request) throws WxPayException { + String url = String.format("%s/v3/transfer/batches", this.payService.getPayBaseUrl()); + RsaCryptoUtil.encryptFields(request, this.payService.getConfig().getVerifier().getValidCertificate()); + String result = this.payService.postV3WithWechatpaySerial(url, GSON.toJson(request)); + return GSON.fromJson(result, TransferBatchesResult.class); + } + + @Override + public QueryTransferBatchesResult transferBatchesBatchId(QueryTransferBatchesRequest request) throws WxPayException { + String url = String.format("%s/v3/transfer/batches/batch-id/%s?need_query_detail=%s&offset=%s&limit=%s&detail_status=%s", + this.payService.getPayBaseUrl(), request.getBatchId(), request.getNeedQueryDetail(), request.getOffset(), request.getLimit(), request.getDetailStatus()); + String result = this.payService.getV3(url); + return GSON.fromJson(result, QueryTransferBatchesResult.class); + } + + @Override + public TransferBatchDetailResult transferBatchesBatchIdDetail(String batchId, String detailId) throws WxPayException { + String url = String.format("%s/v3/transfer/batches/batch-id/%s/details/detail-id/%s", this.payService.getPayBaseUrl(), batchId, detailId); + String result = this.payService.getV3(url); + return GSON.fromJson(result, TransferBatchDetailResult.class); + } + + @Override + public QueryTransferBatchesResult transferBatchesOutBatchNo(QueryTransferBatchesRequest request) throws WxPayException { + String url = String.format("%s/v3/transfer/batches/out-batch-no/%s?need_query_detail=%s&offset=%s&limit=%s&detail_status=%s", + this.payService.getPayBaseUrl(), request.getOutBatchNo(), request.getNeedQueryDetail(), request.getOffset(), request.getLimit(), request.getDetailStatus()); + String result = this.payService.getV3(url); + return GSON.fromJson(result, QueryTransferBatchesResult.class); + } + + @Override + public TransferBatchDetailResult transferBatchesOutBatchNoDetail(String outBatchNo, String outDetailNo) throws WxPayException { + String url = String.format("%s/v3/transfer/batches/out-batch-no/%s/details/out-detail-no/%s", this.payService.getPayBaseUrl(), outBatchNo, outDetailNo); + String result = this.payService.getV3(url); + return GSON.fromJson(result, TransferBatchDetailResult.class); + } +} diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/TransferServiceImplTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/TransferServiceImplTest.java new file mode 100644 index 0000000000..654276a278 --- /dev/null +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/TransferServiceImplTest.java @@ -0,0 +1,76 @@ +package com.github.binarywang.wxpay.service.impl; + +import com.github.binarywang.wxpay.bean.transfer.QueryTransferBatchesRequest; +import com.github.binarywang.wxpay.bean.transfer.TransferBatchesRequest; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.testbase.ApiTestModule; +import com.google.inject.Inject; +import lombok.extern.slf4j.Slf4j; +import org.testng.annotations.Guice; +import org.testng.annotations.Test; + +import java.util.ArrayList; +import java.util.List; + +/** + * 获取商家转账到零钱服务类API测试 + * + * @author zhongjun + * @date 2022/6/17 + **/ +@Slf4j +@Test +@Guice(modules = ApiTestModule.class) +public class TransferServiceImplTest { + + @Inject + private WxPayService payService; + + @Test + public void testTransferBatches() throws WxPayException { + List transferDetailList = new ArrayList<>(); + transferDetailList.add(TransferBatchesRequest.TransferDetail.newBuilder() + .outDetailNo("1655447989156") + .transferAmount(100) + .transferRemark("测试转账") + .openid("oX_7Jzr9gSZz4X_Xc9-_7HGf8XzI") + .userName("测试用户").build()); + TransferBatchesRequest batchesRequest = TransferBatchesRequest.newBuilder() + .appid("wxf636efh5xxxxx") + .outBatchNo("1655447999520") + .batchName("测试批次") + .batchRemark("测试批次备注") + .totalAmount(100) + .totalNum(1) + .transferDetailList(transferDetailList).build(); + log.info("发起商家转账:{}", this.payService.getTransferService().transferBatches(batchesRequest)); + } + + @Test + public void testTransferBatchesBatchId() throws WxPayException { + log.info("微信批次单号查询批次单:{}", this.payService.getTransferService().transferBatchesBatchId(QueryTransferBatchesRequest.newBuilder() + .batchId("1655448154148") + .needQueryDetail(true) + .build())); + + } + + @Test + public void testTransferBatchesBatchIdDetail() throws WxPayException { + log.info("微信明细单号查询明细单:{}", this.payService.getTransferService().transferBatchesBatchIdDetail("1030000071100999991182020050700019480001", "1040000071100999991182020050700019500100")); + } + + @Test + public void testTransferBatchesOutBatchNo() throws WxPayException { + log.info("商家批次单号查询批次单:{}", this.payService.getTransferService().transferBatchesOutBatchNo(QueryTransferBatchesRequest.newBuilder() + .outBatchNo("1655447999520") + .needQueryDetail(true) + .build())); + } + + @Test + public void testTransferBatchesOutBatchNoDetail() throws WxPayException { + log.info("商家明细单号查询明细单:{}", this.payService.getTransferService().transferBatchesOutBatchNoDetail("1655447999520", "1655447989156")); + } +}