From 03114b990040a3a2210e7b1396739ad8b2416a49 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 28 Nov 2025 11:22:31 +0800 Subject: [PATCH] =?UTF-8?q?:art:=20#3753=20=E3=80=90=E5=BE=AE=E4=BF=A1?= =?UTF-8?q?=E6=94=AF=E4=BB=98=E3=80=91=E6=B7=BB=E5=8A=A0=E5=95=86=E5=AE=B6?= =?UTF-8?q?=E8=BD=AC=E8=B4=A6=E7=94=A8=E6=88=B7=E6=8E=88=E6=9D=83=E5=85=8D?= =?UTF-8?q?=E7=A1=AE=E8=AE=A4=E6=A8=A1=E5=BC=8F=E7=9B=B8=E5=85=B3=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ReservationTransferBatchGetResult.java | 170 +++++++++++++++++ .../ReservationTransferBatchRequest.java | 148 +++++++++++++++ .../ReservationTransferBatchResult.java | 51 ++++++ .../ReservationTransferNotifyResult.java | 173 ++++++++++++++++++ .../UserAuthorizationStatusResult.java | 63 +++++++ .../wxpay/constant/WxPayConstants.java | 65 +++++++ .../wxpay/service/TransferService.java | 119 ++++++++++++ .../service/impl/TransferServiceImpl.java | 81 ++++++++ 8 files changed, 870 insertions(+) create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/ReservationTransferBatchGetResult.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/ReservationTransferBatchRequest.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/ReservationTransferBatchResult.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/ReservationTransferNotifyResult.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/UserAuthorizationStatusResult.java diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/ReservationTransferBatchGetResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/ReservationTransferBatchGetResult.java new file mode 100644 index 000000000..d32db8c7c --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/ReservationTransferBatchGetResult.java @@ -0,0 +1,170 @@ +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; + +/** + *
+ * 预约转账批次单号查询接口响应结果
+ * 通过预约批次单号查询批量预约商家转账批次单基本信息。
+ * 文档地址:https://pay.weixin.qq.com/doc/v3/merchant/4015901167
+ * 
+ * + * @author wanggang + * created on 2025/11/28 + */ +@Data +@NoArgsConstructor +public class ReservationTransferBatchGetResult implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 【商户号】 微信支付分配的商户号 + */ + @SerializedName("mch_id") + private String mchId; + + /** + * 【商户预约批次单号】 商户系统内部的商家预约批次单号 + */ + @SerializedName("out_batch_no") + private String outBatchNo; + + /** + * 【微信预约批次单号】 微信预约批次单号,微信商家转账系统返回的唯一标识 + */ + @SerializedName("reservation_batch_no") + private String reservationBatchNo; + + /** + * 【商户AppID】 商户在微信申请公众号或移动应用成功后分配的账号ID + */ + @SerializedName("appid") + private String appid; + + /** + * 【批次备注】 批次备注 + */ + @SerializedName("batch_remark") + private String batchRemark; + + /** + * 【转账场景ID】 商户在商户平台-产品中心-商家转账中申请的转账场景ID + */ + @SerializedName("transfer_scene_id") + private String transferSceneId; + + /** + * 【批次状态】 + * ACCEPTED: 批次已受理 + * PROCESSING: 批次处理中 + * FINISHED: 批次处理完成 + * CLOSED: 批次已关闭 + */ + @SerializedName("batch_state") + private String batchState; + + /** + * 【转账总金额】 转账金额单位为"分" + */ + @SerializedName("total_amount") + private Integer totalAmount; + + /** + * 【转账总笔数】 转账总笔数 + */ + @SerializedName("total_num") + private Integer totalNum; + + /** + * 【转账成功金额】 转账成功金额单位为"分" + */ + @SerializedName("success_amount") + private Integer successAmount; + + /** + * 【转账成功笔数】 转账成功笔数 + */ + @SerializedName("success_num") + private Integer successNum; + + /** + * 【转账失败金额】 转账失败金额单位为"分" + */ + @SerializedName("fail_amount") + private Integer failAmount; + + /** + * 【转账失败笔数】 转账失败笔数 + */ + @SerializedName("fail_num") + private Integer failNum; + + /** + * 【批次创建时间】 批次受理成功时返回 + * 遵循rfc3339标准格式,格式为yyyy-MM-DDTHH:mm:ss+TIMEZONE + */ + @SerializedName("create_time") + private String createTime; + + /** + * 【批次更新时间】 批次最后更新时间 + * 遵循rfc3339标准格式,格式为yyyy-MM-DDTHH:mm:ss+TIMEZONE + */ + @SerializedName("update_time") + private String updateTime; + + /** + * 【批次关闭原因】 批次关闭原因 + * MERCHANT_REVOCATION: 商户主动撤销 + * OVERDUE_CLOSE: 系统超时关闭 + */ + @SerializedName("close_reason") + private String closeReason; + + /** + * 【是否需要查询明细】 + */ + @SerializedName("need_query_detail") + private Boolean needQueryDetail; + + /** + * 【转账明细列表】 + */ + @SerializedName("transfer_detail_list") + private List transferDetailList; + + /** + * 转账明细 + */ + @Data + @NoArgsConstructor + public static class TransferDetail implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 【商户明细单号】 商户系统内部区分转账批次单下不同转账明细单的唯一标识 + */ + @SerializedName("out_detail_no") + private String outDetailNo; + + /** + * 【微信转账单号】 微信转账单号,微信商家转账系统返回的唯一标识 + */ + @SerializedName("transfer_bill_no") + private String transferBillNo; + + /** + * 【明细状态】 + * PROCESSING: 转账处理中 + * SUCCESS: 转账成功 + * FAIL: 转账失败 + */ + @SerializedName("detail_state") + private String detailState; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/ReservationTransferBatchRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/ReservationTransferBatchRequest.java new file mode 100644 index 000000000..82e3d6f32 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/ReservationTransferBatchRequest.java @@ -0,0 +1,148 @@ +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; + +/** + *
+ * 批量预约商家转账请求参数
+ * 商户可以通过批量预约接口一次发起批量转账请求,最多可以同时向50个用户发起转账。
+ * 文档地址:https://pay.weixin.qq.com/doc/v3/merchant/4015901167
+ * 
+ * + * @author wanggang + * created on 2025/11/28 + */ +@Data +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +public class ReservationTransferBatchRequest implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 【商户AppID】 商户在微信申请公众号或移动应用成功后分配的账号ID + */ + @SerializedName("appid") + private String appid; + + /** + * 【商户预约批次单号】 商户系统内部的商家预约批次单号,要求此参数只能由数字、大小写字母组成,在商户系统内部唯一 + */ + @SerializedName("out_batch_no") + private String outBatchNo; + + /** + * 【转账场景ID】 商户在商户平台-产品中心-商家转账中申请的转账场景ID + */ + @SerializedName("transfer_scene_id") + private String transferSceneId; + + /** + * 【批次备注】 批次备注 + */ + @SerializedName("batch_remark") + private String batchRemark; + + /** + * 【转账总金额】 转账金额单位为"分",转账总金额必须与批次内所有转账明细金额之和保持一致,否则无法发起转账操作 + */ + @SerializedName("total_amount") + private Integer totalAmount; + + /** + * 【转账总笔数】 转账总笔数,需要与批次内所有转账明细笔数保持一致,否则无法发起转账操作 + */ + @SerializedName("total_num") + private Integer totalNum; + + /** + * 【转账明细列表】 转账明细列表,最多50条 + */ + @SerializedName("transfer_detail_list") + private List transferDetailList; + + /** + * 【异步回调地址】 异步接收微信支付结果通知的回调地址,通知url必须为公网可访问的url,必须为https,不能携带参数 + */ + @SerializedName("notify_url") + private String notifyUrl; + + /** + * 转账明细 + */ + @Data + @Builder(builderMethodName = "newBuilder") + @NoArgsConstructor + @AllArgsConstructor + public static class TransferDetail implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 【商户明细单号】 商户系统内部区分转账批次单下不同转账明细单的唯一标识,要求此参数只能由数字、大小写字母组成 + */ + @SerializedName("out_detail_no") + private String outDetailNo; + + /** + * 【转账金额】 转账金额单位为"分" + */ + @SerializedName("transfer_amount") + private Integer transferAmount; + + /** + * 【转账备注】 单条转账备注(微信用户会收到该备注),UTF8编码,最多允许32个字符 + */ + @SerializedName("transfer_remark") + private String transferRemark; + + /** + * 【收款用户OpenID】 商户AppID下,某用户的OpenID + */ + @SerializedName("openid") + private String openid; + + /** + * 【收款用户姓名】 收款方真实姓名。支持标准RSA算法和国密算法,公钥由微信侧提供 + */ + @SpecEncrypt + @SerializedName("user_name") + private String userName; + + /** + * 【转账场景报备信息】 + */ + @SerializedName("transfer_scene_report_infos") + private List transferSceneReportInfos; + } + + /** + * 转账场景报备信息 + */ + @Data + @Builder(builderMethodName = "newBuilder") + @NoArgsConstructor + @AllArgsConstructor + public static class TransferSceneReportInfo implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 【信息类型】 信息类型编码 + */ + @SerializedName("info_type") + private String infoType; + + /** + * 【信息内容】 信息内容 + */ + @SerializedName("info_content") + private String infoContent; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/ReservationTransferBatchResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/ReservationTransferBatchResult.java new file mode 100644 index 000000000..ef762cee5 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/ReservationTransferBatchResult.java @@ -0,0 +1,51 @@ +package com.github.binarywang.wxpay.bean.transfer; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + *
+ * 批量预约商家转账响应结果
+ * 文档地址:https://pay.weixin.qq.com/doc/v3/merchant/4015901167
+ * 
+ * + * @author wanggang + * created on 2025/11/28 + */ +@Data +@NoArgsConstructor +public class ReservationTransferBatchResult implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 【商户预约批次单号】 商户系统内部的商家预约批次单号 + */ + @SerializedName("out_batch_no") + private String outBatchNo; + + /** + * 【微信预约批次单号】 微信预约批次单号,微信商家转账系统返回的唯一标识 + */ + @SerializedName("reservation_batch_no") + private String reservationBatchNo; + + /** + * 【批次创建时间】 批次受理成功时返回 + * 遵循rfc3339标准格式,格式为yyyy-MM-DDTHH:mm:ss+TIMEZONE + */ + @SerializedName("create_time") + private String createTime; + + /** + * 【批次状态】 + * ACCEPTED: 批次已受理 + * PROCESSING: 批次处理中 + * FINISHED: 批次处理完成 + * CLOSED: 批次已关闭 + */ + @SerializedName("batch_state") + private String batchState; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/ReservationTransferNotifyResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/ReservationTransferNotifyResult.java new file mode 100644 index 000000000..438354e7b --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/ReservationTransferNotifyResult.java @@ -0,0 +1,173 @@ +package com.github.binarywang.wxpay.bean.transfer; + +import com.github.binarywang.wxpay.bean.notify.OriginNotifyResponse; +import com.github.binarywang.wxpay.bean.notify.WxPayBaseNotifyV3Result; +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; + +/** + *
+ * 预约商家转账通知回调结果
+ * 预约批次单中的明细单在转账成功或转账失败时,微信会把相关结果信息发送给商户。
+ * 文档地址:https://pay.weixin.qq.com/doc/v3/merchant/4015901167
+ * 
+ * + * @author wanggang + * created on 2025/11/28 + */ +@Data +public class ReservationTransferNotifyResult implements Serializable, WxPayBaseNotifyV3Result { + private static final long serialVersionUID = 1L; + + /** + * 源数据 + */ + private OriginNotifyResponse rawData; + + /** + * 解密后的数据 + */ + private ReservationTransferNotifyResult.DecryptNotifyResult result; + + @Data + @NoArgsConstructor + public static class DecryptNotifyResult implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 【商户号】 微信支付分配的商户号 + */ + @SerializedName("mch_id") + private String mchId; + + /** + * 【商户预约批次单号】 商户系统内部的商家预约批次单号 + */ + @SerializedName("out_batch_no") + private String outBatchNo; + + /** + * 【微信预约批次单号】 微信预约批次单号,微信商家转账系统返回的唯一标识 + */ + @SerializedName("reservation_batch_no") + private String reservationBatchNo; + + /** + * 【批次状态】 + * ACCEPTED: 批次已受理 + * PROCESSING: 批次处理中 + * FINISHED: 批次处理完成 + * CLOSED: 批次已关闭 + */ + @SerializedName("batch_state") + private String batchState; + + /** + * 【转账总金额】 转账金额单位为"分" + */ + @SerializedName("total_amount") + private Integer totalAmount; + + /** + * 【转账总笔数】 转账总笔数 + */ + @SerializedName("total_num") + private Integer totalNum; + + /** + * 【转账成功金额】 转账成功金额单位为"分" + */ + @SerializedName("success_amount") + private Integer successAmount; + + /** + * 【转账成功笔数】 转账成功笔数 + */ + @SerializedName("success_num") + private Integer successNum; + + /** + * 【转账失败金额】 转账失败金额单位为"分" + */ + @SerializedName("fail_amount") + private Integer failAmount; + + /** + * 【转账失败笔数】 转账失败笔数 + */ + @SerializedName("fail_num") + private Integer failNum; + + /** + * 【批次创建时间】 批次受理成功时返回 + * 遵循rfc3339标准格式,格式为yyyy-MM-DDTHH:mm:ss+TIMEZONE + */ + @SerializedName("create_time") + private String createTime; + + /** + * 【批次更新时间】 批次最后更新时间 + * 遵循rfc3339标准格式,格式为yyyy-MM-DDTHH:mm:ss+TIMEZONE + */ + @SerializedName("update_time") + private String updateTime; + + /** + * 【转账明细列表】 + */ + @SerializedName("transfer_detail_list") + private List transferDetailList; + } + + /** + * 转账明细通知 + */ + @Data + @NoArgsConstructor + public static class TransferDetailNotify implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 【商户明细单号】 商户系统内部区分转账批次单下不同转账明细单的唯一标识 + */ + @SerializedName("out_detail_no") + private String outDetailNo; + + /** + * 【微信转账单号】 微信转账单号,微信商家转账系统返回的唯一标识 + */ + @SerializedName("transfer_bill_no") + private String transferBillNo; + + /** + * 【明细状态】 + * PROCESSING: 转账处理中 + * SUCCESS: 转账成功 + * FAIL: 转账失败 + */ + @SerializedName("detail_state") + private String detailState; + + /** + * 【收款用户OpenID】 商户AppID下,某用户的OpenID + */ + @SerializedName("openid") + private String openid; + + /** + * 【转账金额】 转账金额单位为"分" + */ + @SerializedName("transfer_amount") + private Integer transferAmount; + + /** + * 【失败原因】 转账失败原因 + */ + @SerializedName("fail_reason") + private String failReason; + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/UserAuthorizationStatusResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/UserAuthorizationStatusResult.java new file mode 100644 index 000000000..e0bab8215 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/transfer/UserAuthorizationStatusResult.java @@ -0,0 +1,63 @@ +package com.github.binarywang.wxpay.bean.transfer; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + *
+ * 商户查询用户授权信息接口响应结果
+ * 商户通过此接口可查询用户是否对商户的商家转账场景进行了授权。
+ * 文档地址:https://pay.weixin.qq.com/doc/v3/merchant/4015901167
+ * 
+ * + * @author wanggang + * created on 2025/11/28 + */ +@Data +@NoArgsConstructor +public class UserAuthorizationStatusResult implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 【商户AppID】 商户在微信申请公众号或移动应用成功后分配的账号ID + */ + @SerializedName("appid") + private String appid; + + /** + * 【商户号】 微信支付分配的商户号 + */ + @SerializedName("mch_id") + private String mchId; + + /** + * 【用户标识】 用户在直连商户应用下的用户标识 + */ + @SerializedName("openid") + private String openid; + + /** + * 【授权状态】 用户授权状态 + * UNAUTHORIZED: 未授权 + * AUTHORIZED: 已授权 + */ + @SerializedName("authorization_state") + private String authorizationState; + + /** + * 【授权时间】 用户授权时间,遵循rfc3339标准格式 + * 格式为yyyy-MM-DDTHH:mm:ss+TIMEZONE + */ + @SerializedName("authorize_time") + private String authorizeTime; + + /** + * 【取消授权时间】 用户取消授权时间,遵循rfc3339标准格式 + * 格式为yyyy-MM-DDTHH:mm:ss+TIMEZONE + */ + @SerializedName("deauthorize_time") + private String deauthorizeTime; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/constant/WxPayConstants.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/constant/WxPayConstants.java index b1a57ccc0..ec9e14ff2 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/constant/WxPayConstants.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/constant/WxPayConstants.java @@ -401,6 +401,71 @@ public class WxPayConstants { } + + /** + * 用户授权状态 + * + * @see 商户查询用户授权信息 + */ + @UtilityClass + public static class AuthorizationState { + /** + * 未授权 + */ + public static final String UNAUTHORIZED = "UNAUTHORIZED"; + + /** + * 已授权 + */ + public static final String AUTHORIZED = "AUTHORIZED"; + } + + /** + * 预约转账批次状态 + * + * @see 批量预约商家转账 + */ + @UtilityClass + public static class ReservationBatchState { + /** + * 批次已受理 + */ + public static final String ACCEPTED = "ACCEPTED"; + + /** + * 批次处理中 + */ + public static final String PROCESSING = "PROCESSING"; + + /** + * 批次处理完成 + */ + public static final String FINISHED = "FINISHED"; + + /** + * 批次已关闭 + */ + public static final String CLOSED = "CLOSED"; + } + + /** + * 预约转账批次关闭原因 + * + * @see 预约转账批次单号查询 + */ + @UtilityClass + public static class ReservationBatchCloseReason { + /** + * 商户主动撤销 + */ + public static final String MERCHANT_REVOCATION = "MERCHANT_REVOCATION"; + + /** + * 系统超时关闭 + */ + public static final String OVERDUE_CLOSE = "OVERDUE_CLOSE"; + } + /** * 【转账场景ID】 该笔转账使用的转账场景,可前往“商户平台-产品中心-商家转账”中申请。 */ 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 index 01113c950..e48e32750 100644 --- 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 @@ -189,4 +189,123 @@ public interface TransferService { * @throws WxPayException the wx pay exception */ TransferBillsNotifyResult parseTransferBillsNotifyResult(String notifyData, SignatureHeader header) throws WxPayException; + + // ===================== 用户授权免确认模式相关接口 ===================== + + /** + *
+   * 商户查询用户授权信息接口
+   *
+   * 商户通过此接口可查询用户是否对商户的商家转账场景进行了授权。
+   *
+   * 请求方式:GET(HTTPS)
+   * 请求地址:请求地址
+   *
+   * 文档地址:商户查询用户授权信息
+   * 
+ * + * @param openid 用户在直连商户应用下的用户标识 + * @param transferSceneId 转账场景ID + * @return UserAuthorizationStatusResult 用户授权信息 + * @throws WxPayException . + */ + UserAuthorizationStatusResult getUserAuthorizationStatus(String openid, String transferSceneId) throws WxPayException; + + /** + *
+   * 批量预约商家转账接口
+   *
+   * 商户可以通过批量预约接口一次发起批量转账请求,最多可以同时向50个用户发起转账。
+   * 批量预约接口适用于用户已授权免确认的场景,在转账时无需用户确认即可完成转账。
+   *
+   * 请求方式:POST(HTTPS)
+   * 请求地址:请求地址
+   *
+   * 文档地址:批量预约商家转账
+   * 
+ * + * @param request 批量预约商家转账请求参数 + * @return ReservationTransferBatchResult 批量预约商家转账结果 + * @throws WxPayException . + */ + ReservationTransferBatchResult reservationTransferBatch(ReservationTransferBatchRequest request) throws WxPayException; + + /** + *
+   * 商户预约批次单号查询批次单接口
+   *
+   * 通过商户预约批次单号查询批量预约商家转账批次单基本信息。
+   *
+   * 请求方式:GET(HTTPS)
+   * 请求地址:请求地址
+   *
+   * 文档地址:商户预约批次单号查询批次单
+   * 
+ * + * @param outBatchNo 商户预约批次单号 + * @param needQueryDetail 是否需要查询明细 + * @param offset 分页偏移量 + * @param limit 分页大小 + * @param detailState 明细状态(PROCESSING/SUCCESS/FAIL) + * @return ReservationTransferBatchGetResult 批量预约商家转账批次查询结果 + * @throws WxPayException . + */ + ReservationTransferBatchGetResult getReservationTransferBatchByOutBatchNo(String outBatchNo, Boolean needQueryDetail, + Integer offset, Integer limit, String detailState) throws WxPayException; + + /** + *
+   * 微信预约批次单号查询批次单接口
+   *
+   * 通过微信预约批次单号查询批量预约商家转账批次单基本信息。
+   *
+   * 请求方式:GET(HTTPS)
+   * 请求地址:请求地址
+   *
+   * 文档地址:微信预约批次单号查询批次单
+   * 
+ * + * @param reservationBatchNo 微信预约批次单号 + * @param needQueryDetail 是否需要查询明细 + * @param offset 分页偏移量 + * @param limit 分页大小 + * @param detailState 明细状态(PROCESSING/SUCCESS/FAIL) + * @return ReservationTransferBatchGetResult 批量预约商家转账批次查询结果 + * @throws WxPayException . + */ + ReservationTransferBatchGetResult getReservationTransferBatchByReservationBatchNo(String reservationBatchNo, Boolean needQueryDetail, + Integer offset, Integer limit, String detailState) throws WxPayException; + + /** + *
+   * 解析预约商家转账通知回调结果
+   *
+   * 预约批次单中的明细单在转账成功或转账失败时,微信会把相关结果信息发送给商户。
+   *
+   * 文档地址:预约商家转账通知
+   * 
+ * + * @param notifyData 通知数据 + * @param header 通知头部数据,不传则表示不校验头 + * @return ReservationTransferNotifyResult 预约商家转账通知结果 + * @throws WxPayException the wx pay exception + */ + ReservationTransferNotifyResult parseReservationTransferNotifyResult(String notifyData, SignatureHeader header) throws WxPayException; + + /** + *
+   * 关闭预约商家转账批次接口
+   *
+   * 商户可以通过此接口关闭预约商家转账批次单。关闭后,该批次内所有未成功的转账将被取消。
+   *
+   * 请求方式:POST(HTTPS)
+   * 请求地址:请求地址
+   *
+   * 文档地址:关闭预约商家转账批次
+   * 
+ * + * @param outBatchNo 商户预约批次单号 + * @throws WxPayException . + */ + void closeReservationTransferBatch(String outBatchNo) throws WxPayException; } 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 index 038af32b8..fe05ab89a 100644 --- 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 @@ -124,4 +124,85 @@ public class TransferServiceImpl implements TransferService { public TransferBillsNotifyResult parseTransferBillsNotifyResult(String notifyData, SignatureHeader header) throws WxPayException { return this.payService.baseParseOrderNotifyV3Result(notifyData, header, TransferBillsNotifyResult.class, TransferBillsNotifyResult.DecryptNotifyResult.class); } + + // ===================== 用户授权免确认模式相关接口实现 ===================== + + @Override + public UserAuthorizationStatusResult getUserAuthorizationStatus(String openid, String transferSceneId) throws WxPayException { + String url = String.format("%s/v3/fund-app/mch-transfer/authorization/openid/%s?transfer_scene_id=%s", + this.payService.getPayBaseUrl(), openid, transferSceneId); + String result = this.payService.getV3(url); + return GSON.fromJson(result, UserAuthorizationStatusResult.class); + } + + @Override + public ReservationTransferBatchResult reservationTransferBatch(ReservationTransferBatchRequest request) throws WxPayException { + String url = String.format("%s/v3/fund-app/mch-transfer/reservation/transfer-batches", this.payService.getPayBaseUrl()); + List transferDetailList = request.getTransferDetailList(); + if (transferDetailList != null && !transferDetailList.isEmpty()) { + X509Certificate validCertificate = this.payService.getConfig().getVerifier().getValidCertificate(); + for (ReservationTransferBatchRequest.TransferDetail detail : transferDetailList) { + if (detail.getUserName() != null && !detail.getUserName().isEmpty()) { + RsaCryptoUtil.encryptFields(detail, validCertificate); + } + } + } + String result = this.payService.postV3WithWechatpaySerial(url, GSON.toJson(request)); + return GSON.fromJson(result, ReservationTransferBatchResult.class); + } + + @Override + public ReservationTransferBatchGetResult getReservationTransferBatchByOutBatchNo(String outBatchNo, Boolean needQueryDetail, + Integer offset, Integer limit, String detailState) throws WxPayException { + String url = buildReservationBatchQueryUrl("out-batch-no", outBatchNo, needQueryDetail, offset, limit, detailState); + String result = this.payService.getV3(url); + return GSON.fromJson(result, ReservationTransferBatchGetResult.class); + } + + @Override + public ReservationTransferBatchGetResult getReservationTransferBatchByReservationBatchNo(String reservationBatchNo, Boolean needQueryDetail, + Integer offset, Integer limit, String detailState) throws WxPayException { + String url = buildReservationBatchQueryUrl("reservation-batch-no", reservationBatchNo, needQueryDetail, offset, limit, detailState); + String result = this.payService.getV3(url); + return GSON.fromJson(result, ReservationTransferBatchGetResult.class); + } + + private String buildReservationBatchQueryUrl(String batchNoType, String batchNo, Boolean needQueryDetail, + Integer offset, Integer limit, String detailState) { + StringBuilder url = new StringBuilder(); + url.append(this.payService.getPayBaseUrl()) + .append("/v3/fund-app/mch-transfer/reservation/transfer-batches/") + .append(batchNoType).append("/").append(batchNo); + + boolean hasParams = false; + if (needQueryDetail != null) { + url.append("?need_query_detail=").append(needQueryDetail); + hasParams = true; + } + if (offset != null) { + url.append(hasParams ? "&" : "?").append("offset=").append(offset); + hasParams = true; + } + if (limit != null) { + url.append(hasParams ? "&" : "?").append("limit=").append(limit); + hasParams = true; + } + if (detailState != null && !detailState.isEmpty()) { + url.append(hasParams ? "&" : "?").append("detail_state=").append(detailState); + } + return url.toString(); + } + + @Override + public ReservationTransferNotifyResult parseReservationTransferNotifyResult(String notifyData, SignatureHeader header) throws WxPayException { + return this.payService.baseParseOrderNotifyV3Result(notifyData, header, ReservationTransferNotifyResult.class, + ReservationTransferNotifyResult.DecryptNotifyResult.class); + } + + @Override + public void closeReservationTransferBatch(String outBatchNo) throws WxPayException { + String url = String.format("%s/v3/fund-app/mch-transfer/reservation/transfer-batches/out-batch-no/%s/close", + this.payService.getPayBaseUrl(), outBatchNo); + this.payService.postV3(url, ""); + } }