From d184ff830321a3096e4cc63b6ac5bd2b9802cf58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=B9=BF=E9=91=AB?= <2717764887@qq.com> Date: Thu, 24 Oct 2019 09:23:56 +0800 Subject: [PATCH] =?UTF-8?q?:sparkles:=09#1010=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E5=88=86=E8=B4=A6=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 * 微信单次分账接口 * - 微信多次分账 - 微信完结分账 - 添加分账接受方 - 删除分账接受方 - 查询分账结果【未能完成单元测试,微信返回签名失败】 - 分账回退【未能完成单元测试,使用真实数据返回“参数不正确”,我对比官方文档除了缺少`sub_mch_id`和`sub_appid`之外其他相同,当我随便填了一个商户id的时候,提示“回退方没有开通分账回退功能”】 - 回退结果查询【未能完成单元测试,因分账回退无法进行,模拟数据返回”记录不存在“】 --- .../ProfitSharingFinishRequest.java | 70 +++++++++ .../ProfitSharingQueryRequest.java | 54 +++++++ .../ProfitSharingQueryResult.java | 114 +++++++++++++++ .../ProfitSharingReceiverRequest.java | 47 +++++++ .../ProfitSharingReceiverResult.java | 25 ++++ ...Request.java => ProfitSharingRequest.java} | 2 +- .../ProfitSharingReturnQueryRequest.java | 72 ++++++++++ .../ProfitSharingReturnRequest.java | 133 ++++++++++++++++++ .../ProfitSharingReturnResult.java | 74 ++++++++++ .../wxpay/bean/profitsharing/Receiver.java | 81 +++++++++++ .../request/WxPayUnifiedOrderRequest.java | 11 ++ .../wxpay/service/ProfitSharingService.java | 120 +++++++++++++++- .../impl/ProfitSharingServiceImpl.java | 85 ++++++++++- .../impl/ProfitSharingServiceImplTest.java | 105 +++++++++++++- 14 files changed, 977 insertions(+), 16 deletions(-) create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingFinishRequest.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingQueryRequest.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingQueryResult.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReceiverRequest.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReceiverResult.java rename weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/{ProfitsharingRequest.java => ProfitSharingRequest.java} (97%) create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReturnQueryRequest.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReturnRequest.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReturnResult.java diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingFinishRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingFinishRequest.java new file mode 100644 index 000000000..22cd54508 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingFinishRequest.java @@ -0,0 +1,70 @@ +package com.github.binarywang.wxpay.bean.profitsharing; + +import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest; +import com.github.binarywang.wxpay.constant.WxPayConstants; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.*; +import me.chanjar.weixin.common.annotation.Required; + +/** + * @author Wang GuangXin 2019/10/23 14:02 + * @version 1.0 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +@XStreamAlias("xml") +public class ProfitSharingFinishRequest extends BaseWxPayRequest { + + private static final long serialVersionUID = -4265779954583596627L; + + /** + *
+   * 字段名:微信订单号.
+   * 变量名:transaction_id
+   * 是否必填:是
+   * String(32)
+   * 示例值:4208450740201411110007820472
+   * 描述:微信支付订单号
+   * 
+ */ + @XStreamAlias("transaction_id") + @Required + private String transactionId; + + /** + *
+   * 字段名:商户分账单号.
+   * 变量名:out_order_no
+   * 是否必填:是
+   * String(64)
+   * 示例值:P20150806125346
+   * 描述:商户系统内部的分账单号,在商户系统内部唯一(单次分账、多次分账、完结分账应使用不同的商户分账单号),同一分账单号多次请求等同一次。只能是数字、大小写字母_-|*@
+   * 
+ */ + @XStreamAlias("out_order_no") + @Required + private String outOrderNo; + + /** + *
+   * 字段名:分账完结描述.
+   * 变量名:out_order_no
+   * 是否必填:是
+   * String(80)
+   * 示例值:分账已完成
+   * 描述:分账完结的原因描述
+   * 
+ */ + @XStreamAlias("description") + @Required + private String description; + + @Override + protected void checkConstraints() throws WxPayException { + this.setSignType(WxPayConstants.SignType.HMAC_SHA256); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingQueryRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingQueryRequest.java new file mode 100644 index 000000000..353170b01 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingQueryRequest.java @@ -0,0 +1,54 @@ +package com.github.binarywang.wxpay.bean.profitsharing; + +import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest; +import com.github.binarywang.wxpay.constant.WxPayConstants; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.*; +import me.chanjar.weixin.common.annotation.Required; + +/** + * @author Wang GuangXin 2019/10/22 15:44 + * @version 1.0 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +@XStreamAlias("xml") +public class ProfitSharingQueryRequest extends BaseWxPayRequest { + private static final long serialVersionUID = 3566332883053157102L; + /** + *
+   * 字段名:微信支付订单号.
+   * 变量名:transaction_id
+   * 是否必填:是
+   * String(32)
+   * 示例值:4208450740201411110007820472
+   * 描述:微信支付订单号
+   * 
+ */ + @XStreamAlias("transaction_id") + @Required + private String transactionId; + + /** + *
+   * 字段名:商户分账单号.
+   * 变量名:out_order_no
+   * 是否必填:是
+   * String(64)
+   * 示例值:P20150806125346
+   * 描述:查询分账结果,输入申请分账时的商户分账单号; 查询分账完结的执行结果,输入发起分账完结时的商户分账单号
+   * 
+ */ + @XStreamAlias("out_order_no") + @Required + private String outOrderNo; + + @Override + protected void checkConstraints() throws WxPayException { + this.setSignType(WxPayConstants.SignType.HMAC_SHA256); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingQueryResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingQueryResult.java new file mode 100644 index 000000000..6affffe67 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingQueryResult.java @@ -0,0 +1,114 @@ +package com.github.binarywang.wxpay.bean.profitsharing; + +import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * @author Wang GuangXin 2019/10/22 15:51 + * @version 1.0 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@XStreamAlias("xml") +public class ProfitSharingQueryResult extends BaseWxPayResult { + private static final long serialVersionUID = 2548673608075775067L; + /** + * 微信订单号 + */ + @XStreamAlias("transaction_id") + private String transactionId; + /** + * 商户分账单号 + */ + @XStreamAlias("out_order_no") + private String outOrderNo; + /** + * 微信分账单号 + */ + @XStreamAlias("orderId") + private String orderId; + /** + * 分账单状态 + */ + @XStreamAlias("status") + private String status; + /** + * 关单原因 + */ + @XStreamAlias("close_reason") + private String closeReason; + /** + * 分账接收方列表 + */ + @XStreamAlias("receivers") + private String receivers; + /** + * 分账金额 + */ + @XStreamAlias("amount") + private Integer amount; + /** + * 分账描述 + */ + @XStreamAlias("description") + private String description; + + public ProfitSharingQueryResult.Receivers formatReceivers() { + GsonBuilder gsonBuilder = new GsonBuilder(); + gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES); + Gson gson = gsonBuilder.create(); + return gson.fromJson(receivers, Receivers.class); + } + + @Data + public class Receivers { + /** + * 分账接收方类型 + */ + private String type; + /** + * 分账接收方帐号 + */ + private String account; + /** + * 分账金额 + */ + private Integer amount; + /** + * 分账描述 + */ + private String description; + /** + * 分账结果 + */ + private String result; + /** + * 分账完成时间 + */ + private String finishTime; + /** + * 分账失败原因 + */ + private String failReason; + + @Override + public String toString() { + return "Receivers{" + + "type='" + type + '\'' + + ", account='" + account + '\'' + + ", amount=" + amount + + ", description='" + description + '\'' + + ", result='" + result + '\'' + + ", finishTime='" + finishTime + '\'' + + ", failReason='" + failReason + '\'' + + '}'; + } + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReceiverRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReceiverRequest.java new file mode 100644 index 000000000..3d00d5dd7 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReceiverRequest.java @@ -0,0 +1,47 @@ +package com.github.binarywang.wxpay.bean.profitsharing; + +import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest; +import com.github.binarywang.wxpay.constant.WxPayConstants; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.*; +import me.chanjar.weixin.common.annotation.Required; + +/** + * 添加/删除分账接受方请求对象 + * + * @author Wang GuangXin 2019/10/22 13:41 + * @version 1.0 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +@XStreamAlias("xml") +public class ProfitSharingReceiverRequest extends BaseWxPayRequest { + private static final long serialVersionUID = 2628263563539120323L; + /** + *
+   * 字段名:分账接收方.
+   * 变量名:receiver
+   * 是否必填:是
+   * String(2048)
+   * 示例值:{
+   *    "type": "MERCHANT_ID",
+   *    "account": "190001001",
+   *    "name": "示例商户全称",
+   *    "relation_type": "STORE_OWNER"
+   *    }
+   * 描述:分账接收方对象,json格式
+   * 
+ */ + @XStreamAlias("receiver") + @Required + private String receiver; + + @Override + protected void checkConstraints() throws WxPayException { + this.setSignType(WxPayConstants.SignType.HMAC_SHA256); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReceiverResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReceiverResult.java new file mode 100644 index 000000000..0b9f53881 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReceiverResult.java @@ -0,0 +1,25 @@ +package com.github.binarywang.wxpay.bean.profitsharing; + +import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest; +import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * @author Wang GuangXin 2019/10/22 14:54 + * @version 1.0 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@XStreamAlias("xml") +public class ProfitSharingReceiverResult extends BaseWxPayResult { + private static final long serialVersionUID = 876204163877798066L; + /** + * 分账接收方. + */ + @XStreamAlias("receiver") + private String receiver; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitsharingRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingRequest.java similarity index 97% rename from weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitsharingRequest.java rename to weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingRequest.java index 10ed0d93b..aeb2e8175 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitsharingRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingRequest.java @@ -17,7 +17,7 @@ import me.chanjar.weixin.common.annotation.Required; @NoArgsConstructor @AllArgsConstructor @XStreamAlias("xml") -public class ProfitsharingRequest extends BaseWxPayRequest { +public class ProfitSharingRequest extends BaseWxPayRequest { private static final long serialVersionUID = 212049937430575842L; /** diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReturnQueryRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReturnQueryRequest.java new file mode 100644 index 000000000..e2a2da473 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReturnQueryRequest.java @@ -0,0 +1,72 @@ +package com.github.binarywang.wxpay.bean.profitsharing; + +import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest; +import com.github.binarywang.wxpay.constant.WxPayConstants; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.*; +import me.chanjar.weixin.common.annotation.Required; +import org.apache.commons.lang3.StringUtils; + +/** + * @author Wang GuangXin 2019/10/23 15:32 + * @version 1.0 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +@XStreamAlias("xml") +public class ProfitSharingReturnQueryRequest extends BaseWxPayRequest { + private static final long serialVersionUID = -8838464614726086009L; + /** + *
+   * 字段名:微信分账单号.
+   * 变量名:order_id
+   * 是否必填:二选一
+   * string(64)
+   * 示例值:3008450740201411110007820472
+   * 描述:原发起分账请求时,微信返回的微信分账单号,与商户分账单号一一对应。
+   * 微信分账单号与商户分账单号二选一填写
+   * 
+ */ + @XStreamAlias("order_id") + private String orderId; + + /** + *
+   * 字段名:商户分账单号.
+   * 变量名:out_order_no
+   * 是否必填:二选一
+   * Sstring(64)
+   * 示例值:P20180806125346
+   * 描述:原发起分账请求时使用的商户后台系统的分账单号。
+   * 微信分账单号与商户分账单号二选一填写
+   * 
+ */ + @XStreamAlias("out_order_no") + private String outOrderNo; + + /** + *
+   * 字段名:商户回退单号.
+   * 变量名:out_return_no
+   * 是否必填:是
+   * string(64)
+   * 示例值:R20190516001
+   * 描述:调用回退接口提供的商户系统内部的回退单号
+   * 
+ */ + @Required + @XStreamAlias("out_return_no") + private String outReturnNo; + + @Override + protected void checkConstraints() throws WxPayException { + if (StringUtils.isBlank(orderId) && StringUtils.isBlank(outOrderNo)) { + throw new WxPayException("order_id 和 outOrderNo 必须有一个存在"); + } + this.setSignType(WxPayConstants.SignType.HMAC_SHA256); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReturnRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReturnRequest.java new file mode 100644 index 000000000..6bdc73aa6 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReturnRequest.java @@ -0,0 +1,133 @@ +package com.github.binarywang.wxpay.bean.profitsharing; + +import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest; +import com.github.binarywang.wxpay.constant.WxPayConstants; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.*; +import me.chanjar.weixin.common.annotation.Required; +import org.apache.commons.lang3.StringUtils; + +/** + * @author Wang GuangXin 2019/10/23 14:27 + * @version 1.0 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Builder(builderMethodName = "newBuilder") +@NoArgsConstructor +@AllArgsConstructor +@XStreamAlias("xml") +public class ProfitSharingReturnRequest extends BaseWxPayRequest { + private static final long serialVersionUID = 5926280401474809744L; + /** + *
+   * 字段名:微信分账单号.
+   * 变量名:order_id
+   * 是否必填:二选一
+   * string(64)
+   * 示例值:3008450740201411110007820472
+   * 描述:原发起分账请求时,微信返回的微信分账单号,与商户分账单号一一对应。
+   * 微信分账单号与商户分账单号二选一填写
+   * 
+ */ + @XStreamAlias("order_id") + private String orderId; + + /** + *
+   * 字段名:商户分账单号.
+   * 变量名:out_order_no
+   * 是否必填:二选一
+   * Sstring(64)
+   * 示例值:P20180806125346
+   * 描述:原发起分账请求时使用的商户后台系统的分账单号。
+   * 微信分账单号与商户分账单号二选一填写
+   * 
+ */ + @XStreamAlias("out_order_no") + private String outOrderNo; + + /** + *
+   * 字段名:商户回退单号.
+   * 变量名:out_return_no
+   * 是否必填:是
+   * string(64)
+   * 示例值:R20190516001
+   * 描述:此回退单号是商户在自己后台生成的一个新的回退单号,在商户后台唯一
+   * 只能是数字、大小写字母_-|*@ ,同一回退单号多次请求等同一次。
+   * 
+ */ + @Required + @XStreamAlias("out_return_no") + private String outReturnNo; + + /** + *
+   * 字段名:回退方类型.
+   * 变量名:return_account_type
+   * 是否必填:是
+   * String(32)
+   * 示例值:MERCHANT_ID
+   * 描述:枚举值:
+   * MERCHANT_ID:商户ID
+   * 暂时只支持从商户接收方回退分账金额
+   * 
+ */ + @Required + @XStreamAlias("return_account_type") + private String returnAccountType; + + /** + *
+   * 字段名:回退方账号.
+   * 变量名:return_account
+   * 是否必填:是
+   * String(64)
+   * 示例值:86693852
+   * 描述:回退方类型是MERCHANT_ID时,填写商户ID
+   * 只能对原分账请求中成功分给商户接收方进行回退
+   * 
+ */ + @Required + @XStreamAlias("return_account") + private String returnAccount; + + /** + *
+   * 字段名:回退金额.
+   * 变量名:return_amount
+   * 是否必填:是
+   * int
+   * 示例值:888
+   * 描述:需要从分账接收方回退的金额,单位为分,只能为整数,不能超过原始分账单分出给该接收方的金额
+   * 
+ */ + @Required + @XStreamAlias("return_amount") + private Integer returnAmount; + + /** + *
+   * 字段名:回退描述.
+   * 变量名:description
+   * 是否必填:是
+   * String(80)
+   * 示例值:用户退款
+   * 描述:分账回退的原因描述
+   * 
+ */ + @Required + @XStreamAlias("description") + private String description; + + + @Override + protected void checkConstraints() throws WxPayException { + if (StringUtils.isBlank(orderId) && StringUtils.isBlank(outOrderNo)) { + throw new WxPayException("order_id 和 outOrderNo 必须有一个存在"); + } + this.setSignType(WxPayConstants.SignType.HMAC_SHA256); + } +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReturnResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReturnResult.java new file mode 100644 index 000000000..814bcf2d5 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/ProfitSharingReturnResult.java @@ -0,0 +1,74 @@ +package com.github.binarywang.wxpay.bean.profitsharing; + +import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * @author Wang GuangXin 2019/10/23 14:41 + * @version 1.0 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@XStreamAlias("xml") +public class ProfitSharingReturnResult extends BaseWxPayResult { + private static final long serialVersionUID = 718554909816994568L; + /** + * 微信分账单号 + */ + @XStreamAlias("order_id") + private String orderId; + /** + * 商户分账单号 + */ + @XStreamAlias("out_order_no") + private String outOrderNo; + /** + * 商户回退单号 + */ + @XStreamAlias("out_return_no") + private String outReturnNo; + /** + * 微信回退单号 + */ + @XStreamAlias("return_no") + private String returnNo; + /** + * 回退方类型 + */ + @XStreamAlias("return_account_type") + private String returnAccountType; + /** + * 回退方账号 + */ + @XStreamAlias("return_account") + private String returnAccount; + /** + * 回退金额 + */ + @XStreamAlias("return_amount") + private Integer returnAmount; + /** + * 回退描述 + */ + @XStreamAlias("description") + private String description; + /** + * 回退结果 + */ + @XStreamAlias("result") + private String result; + /** + * 失败原因 + */ + @XStreamAlias("fail_reason") + private String failReason; + /** + * 完成时间 + */ + @XStreamAlias("finish_time") + private String finishTime; +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/Receiver.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/Receiver.java index 439b60a2c..671e2951d 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/Receiver.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/profitsharing/Receiver.java @@ -1,5 +1,9 @@ package com.github.binarywang.wxpay.bean.profitsharing; +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + import java.io.Serializable; /** @@ -11,8 +15,13 @@ public class Receiver implements Serializable { private String account; private Integer amount; private String description; + private String name; + private String relationType; + private String customRelation; /** + * 此构造函数用于单次分账 + * * @param type MERCHANT_ID:商户ID * PERSONAL_WECHATID:个人微信号PERSONAL_OPENID:个人openid(由父商户APPID转换得到)PERSONAL_SUB_OPENID: 个人sub_openid(由子商户APPID转换得到) * @param account 类型是MERCHANT_ID时,是商户ID @@ -29,6 +38,66 @@ public class Receiver implements Serializable { this.description = description; } + /** + * 此构造用于添加分账方 + * + * @param type MERCHANT_ID:商户ID + * PERSONAL_WECHATID:个人微信号PERSONAL_OPENID:个人openid(由父商户APPID转换得到)PERSONAL_SUB_OPENID: 个人sub_openid(由子商户APPID转换得到) + * @param account 类型是MERCHANT_ID时,是商户ID + * 类型是PERSONAL_WECHATID时,是个人微信号 + * 类型是PERSONAL_OPENID时,是个人openid + * 类型是PERSONAL_SUB_OPENID时,是个人sub_openid + * @param name 分账接收方类型是MERCHANT_ID时,是商户全称(必传) + * 分账接收方类型是PERSONAL_NAME 时,是个人姓名(必传) + * 分账接收方类型是PERSONAL_OPENID时,是个人姓名(选传,传则校验) + * 分账接收方类型是PERSONAL_SUB_OPENID时,是个人姓名(选传,传则校验) + * @param relationType 子商户与接收方的关系。 + * 本字段值为枚举: + * SERVICE_PROVIDER:服务商 + * STORE:门店 + * STAFF:员工 + * STORE_OWNER:店主 + * PARTNER:合作伙伴 + * HEADQUARTER:总部 + * BRAND:品牌方 + * DISTRIBUTOR:分销商 + * USER:用户 + * SUPPLIER:供应商 + * CUSTOM:自定义 + * @param customRelation 子商户与接收方具体的关系,本字段最多10个字。 + * 当字段relation_type的值为CUSTOM时,本字段必填 + * 当字段relation_type的值不为CUSTOM时,本字段无需填写 + */ + public Receiver(String type, String account, String name, String relationType, String customRelation) { + this.type = type; + this.account = account; + this.name = name; + this.relationType = relationType; + this.customRelation = customRelation; + } + + /** + * 用于删除分账接受方 + * + * @param type MERCHANT_ID:商户ID + * PERSONAL_WECHATID:个人微信号PERSONAL_OPENID:个人openid(由父商户APPID转换得到)PERSONAL_SUB_OPENID: 个人sub_openid(由子商户APPID转换得到) + * @param account 类型是MERCHANT_ID时,是商户ID + * 类型是PERSONAL_WECHATID时,是个人微信号 + * 类型是PERSONAL_OPENID时,是个人openid + * 类型是PERSONAL_SUB_OPENID时,是个人sub_openid + */ + public Receiver(String type, String account) { + this.type = type; + this.account = account; + } + + public String toJSONString() { + GsonBuilder gsonBuilder = new GsonBuilder(); + gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES); + Gson gson = gsonBuilder.create(); + return gson.toJson(this); + } + public String getType() { return type; } @@ -44,4 +113,16 @@ public class Receiver implements Serializable { public String getDescription() { return description; } + + public String getName() { + return name; + } + + public String getRelationType() { + return relationType; + } + + public String getCustomRelation() { + return customRelation; + } } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderRequest.java index 5ce85e7ff..54d443881 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderRequest.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayUnifiedOrderRequest.java @@ -346,6 +346,17 @@ public class WxPayUnifiedOrderRequest extends BaseWxPayRequest { */ @XStreamAlias("fingerprint") private String fingerprint; + /** + *
+   * 字段名:是否指定服务商分账.
+   * 变量名:profit_sharing
+   * 是否必填:否
+   * 详情:Y-是,需要分账  N-否,不分账,字母要求大写,不传默认不分账
+   * 详细参考 https://pay.weixin.qq.com/wiki/doc/api/allocation_sl.php?chapter=24_3&index=3
+   * 
+ */ + @XStreamAlias("profit_sharing") + private String profitSharing; /** * 如果配置中已经设置,可以不设置值. diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/ProfitSharingService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/ProfitSharingService.java index 754c7c2fd..8371fcb0b 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/ProfitSharingService.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/ProfitSharingService.java @@ -1,26 +1,136 @@ package com.github.binarywang.wxpay.service; -import com.github.binarywang.wxpay.bean.profitsharing.ProfitSharingResult; -import com.github.binarywang.wxpay.bean.profitsharing.ProfitsharingRequest; +import com.github.binarywang.wxpay.bean.profitsharing.*; import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.bean.profitsharing.ProfitSharingResult; +import com.github.binarywang.wxpay.bean.profitsharing.ProfitSharingRequest; + /** + * 注意:微信最高分账比例为30% + * 可多次分账到同一个人,但是依然不能超过30% + * * @author Wang GuangXin 2019/10/22 10:05 * @version 1.0 */ public interface ProfitSharingService { /** + *
    * 单次分账请求按照传入的分账接收方账号和资金进行分账,同时会将订单剩余的待分账金额解冻给特约商户。故操作成功后,订单不能再进行分账,也不能进行分账完结。
-   * 

* 接口频率:30QPS * 文档详见: https://pay.weixin.qq.com/wiki/doc/api/allocation_sl.php?chapter=25_1&index=1 * 接口链接:https://api.mch.weixin.qq.com/secapi/pay/profitsharing + *

* * @param profitsharingRequest * @return * @throws WxPayException the wx pay exception */ - ProfitSharingResult profitsharing(ProfitsharingRequest profitsharingRequest) throws WxPayException; + ProfitSharingResult profitsharing(ProfitSharingRequest profitsharingRequest) throws WxPayException; + + /** + *
+   * 微信订单支付成功后,服务商代子商户发起分账请求,将结算后的钱分到分账接收方。多次分账请求仅会按照传入的分账接收方进行分账,不会对剩余的金额进行任何操作。故操作成功后,在待分账金额不等于零时,订单依旧能够再次进行分账。
+   * 多次分账,可以将本商户作为分账接收方直接传入,实现释放资金给本商户的功能
+   * 对同一笔订单最多能发起20次多次分账请求
+   * 接口频率:30QPS
+   * 
+ * 文档详见: https://pay.weixin.qq.com/wiki/doc/api/allocation_sl.php?chapter=25_6&index=2 + * 接口链接:https://api.mch.weixin.qq.com/secapi/pay/multiprofitsharing + * + * @param profitsharingRequest + * @return + * @throws WxPayException the wx pay exception + */ + ProfitSharingResult multiprofitsharing(ProfitSharingRequest profitsharingRequest) throws WxPayException; + + /** + *
+   * 1、不需要进行分账的订单,可直接调用本接口将订单的金额全部解冻给特约商户
+   * 2、调用多次分账接口后,需要解冻剩余资金时,调用本接口将剩余的分账金额全部解冻给特约商户
+   * 3、已调用请求单次分账后,剩余待分账金额为零,不需要再调用此接口。
+   * 接口频率:30QPS
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/api/allocation_sl.php?chapter=25_5&index=6
+   * 接口链接:https://api.mch.weixin.qq.com/secapi/pay/profitsharingfinish
+   * 
+ * + * @param profitSharingFinishRequest + * @return + * @throws WxPayException the wx pay exception + */ + ProfitSharingResult profitsharingfinish(ProfitSharingFinishRequest profitSharingFinishRequest) throws WxPayException; + + /** + *
+   * 服务商代子商户发起添加分账接收方请求,后续可通过发起分账请求将结算后的钱分到该分账接收方。
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/api/allocation_sl.php?chapter=25_3&index=4
+   * 接口链接:https://api.mch.weixin.qq.com/pay/profitsharingaddreceiver
+   * 
+ * + * @param profitSharingReceiverRequest + * @return + * @throws WxPayException + */ + ProfitSharingReceiverResult addReceiver(ProfitSharingReceiverRequest profitSharingReceiverRequest) throws WxPayException; + + /** + *
+   * 服务商代子商户发起删除分账接收方请求,删除后不支持将结算后的钱分到该分账接收方。
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/api/allocation_sl.php?chapter=25_4&index=5
+   * 接口链接:https://api.mch.weixin.qq.com/pay/profitsharingremovereceiver
+   * 
+ * + * @param profitSharingReceiverRequest + * @return + * @throws WxPayException + */ + ProfitSharingReceiverResult removeReceiver(ProfitSharingReceiverRequest profitSharingReceiverRequest) throws WxPayException; + + /** + * TODO:微信返回签名失败 + *
+   * 发起分账请求后,可调用此接口查询分账结果;发起分账完结请求后,可调用此接口查询分账完结的执行结果。
+   * 接口频率:80QPS
+   * 
+ * + * @param profitSharingReceiverRequest + * @return + * @throws WxPayException + */ + ProfitSharingQueryResult profitsharingQuery(ProfitSharingQueryRequest profitSharingReceiverRequest) throws WxPayException; + + /** + * TODO:这个接口用真实的数据返回【参数不正确】,我对比官方文档除了缺少sub_mch_id,和sub_appid之外其他相同,当我随便填了一个商户id的时候,提示【回退方没有开通分账回退功能】 + *
+   * 仅对订单进行退款时,如果订单已经分账,可以先调用此接口将指定的金额从分账接收方(仅限商户类型的分账接收方)回退给特约商户,然后再退款。
+   * 回退以原分账请求为依据,可以对分给分账接收方的金额进行多次回退,只要满足累计回退不超过该请求中分给接收方的金额。
+   * 此接口采用同步处理模式,即在接收到商户请求后,会实时返回处理结果。
+   * 此功能需要接收方在商户平台-交易中心-分账-分账接收设置下,开启同意分账回退后,才能使用。
+   * 接口频率:30QPS
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/api/allocation_sl.php?chapter=25_7&index=7
+   * 接口链接:https://api.mch.weixin.qq.com/secapi/pay/profitsharingreturn
+   * 
+ * + * @param profitSharingReturnRequest + * @return + * @throws WxPayException + */ + ProfitSharingReturnResult profitsharingReturn(ProfitSharingReturnRequest profitSharingReturnRequest) throws WxPayException; + + /** + * TODO:因profitsharingReturn接口无法使用,没有办法对这里进行真实的测试,模拟数据这里返回【记录不存在】 + *
+   * 商户需要核实回退结果,可调用此接口查询回退结果。
+   * 如果分账回退接口返回状态为处理中,可调用此接口查询回退结果。
+   * 接口频率:30QPS
+   * 文档详见: https://pay.weixin.qq.com/wiki/doc/api/allocation_sl.php?chapter=25_8&index=8
+   * 接口链接:https://api.mch.weixin.qq.com/pay/profitsharingreturnquery
+   * 
+ * + * @param profitSharingReturnQueryRequest + * @return + * @throws WxPayException + */ + ProfitSharingReturnResult profitsharingReturnQuery(ProfitSharingReturnQueryRequest profitSharingReturnQueryRequest) throws WxPayException; - ; } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/ProfitSharingServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/ProfitSharingServiceImpl.java index dd6c23d06..d660f67c5 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/ProfitSharingServiceImpl.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/ProfitSharingServiceImpl.java @@ -1,8 +1,8 @@ package com.github.binarywang.wxpay.service.impl; -import com.github.binarywang.wxpay.bean.entpay.EntPayResult; +import com.github.binarywang.wxpay.bean.profitsharing.*; import com.github.binarywang.wxpay.bean.profitsharing.ProfitSharingResult; -import com.github.binarywang.wxpay.bean.profitsharing.ProfitsharingRequest; +import com.github.binarywang.wxpay.bean.profitsharing.ProfitSharingRequest; import com.github.binarywang.wxpay.bean.result.BaseWxPayResult; import com.github.binarywang.wxpay.exception.WxPayException; import com.github.binarywang.wxpay.service.ProfitSharingService; @@ -14,12 +14,13 @@ import com.github.binarywang.wxpay.service.WxPayService; */ public class ProfitSharingServiceImpl implements ProfitSharingService { private WxPayService payService; + public ProfitSharingServiceImpl(WxPayService payService) { this.payService = payService; } @Override - public ProfitSharingResult profitsharing(ProfitsharingRequest request) throws WxPayException { + public ProfitSharingResult profitsharing(ProfitSharingRequest request) throws WxPayException { request.checkAndSign(this.payService.getConfig()); String url = this.payService.getPayBaseUrl() + "/secapi/pay/profitsharing"; @@ -28,4 +29,82 @@ public class ProfitSharingServiceImpl implements ProfitSharingService { result.checkResult(this.payService, request.getSignType(), true); return result; } + + @Override + public ProfitSharingResult multiprofitsharing(ProfitSharingRequest request) throws WxPayException { + request.checkAndSign(this.payService.getConfig()); + String url = this.payService.getPayBaseUrl() + "/secapi/pay/multiprofitsharing"; + + String responseContent = this.payService.post(url, request.toXML(), true); + ProfitSharingResult result = BaseWxPayResult.fromXML(responseContent, ProfitSharingResult.class); + result.checkResult(this.payService, request.getSignType(), true); + return result; + } + + @Override + public ProfitSharingResult profitsharingfinish(ProfitSharingFinishRequest request) throws WxPayException { + request.checkAndSign(this.payService.getConfig()); + String url = this.payService.getPayBaseUrl() + "/secapi/pay/profitsharingfinish"; + + String responseContent = this.payService.post(url, request.toXML(), true); + ProfitSharingResult result = BaseWxPayResult.fromXML(responseContent, ProfitSharingResult.class); + result.checkResult(this.payService, request.getSignType(), true); + return result; + } + + @Override + public ProfitSharingReceiverResult addReceiver(ProfitSharingReceiverRequest request) throws WxPayException { + request.checkAndSign(this.payService.getConfig()); + String url = this.payService.getPayBaseUrl() + "/pay/profitsharingaddreceiver"; + + String responseContent = this.payService.post(url, request.toXML(), true); + ProfitSharingReceiverResult result = BaseWxPayResult.fromXML(responseContent, ProfitSharingReceiverResult.class); + result.checkResult(this.payService, request.getSignType(), true); + return result; + } + + @Override + public ProfitSharingReceiverResult removeReceiver(ProfitSharingReceiverRequest request) throws WxPayException { + request.checkAndSign(this.payService.getConfig()); + String url = this.payService.getPayBaseUrl() + "/pay/profitsharingremovereceiver"; + + String responseContent = this.payService.post(url, request.toXML(), true); + ProfitSharingReceiverResult result = BaseWxPayResult.fromXML(responseContent, ProfitSharingReceiverResult.class); + result.checkResult(this.payService, request.getSignType(), true); + return result; + } + + @Override + public ProfitSharingQueryResult profitsharingQuery(ProfitSharingQueryRequest request) throws WxPayException { + if (true) throw new WxPayException("暂不支持,微信一直返回签名失败"); + request.checkAndSign(this.payService.getConfig()); + String url = this.payService.getPayBaseUrl() + "/pay/profitsharingquery"; + + String responseContent = this.payService.post(url, request.toXML(), true); + ProfitSharingQueryResult result = BaseWxPayResult.fromXML(responseContent, ProfitSharingQueryResult.class); + result.checkResult(this.payService, request.getSignType(), true); + return result; + } + + @Override + public ProfitSharingReturnResult profitsharingReturn(ProfitSharingReturnRequest request) throws WxPayException { + request.checkAndSign(this.payService.getConfig()); + String url = this.payService.getPayBaseUrl() + "/secapi/pay/profitsharingreturn"; + + String responseContent = this.payService.post(url, request.toXML(), true); + ProfitSharingReturnResult result = BaseWxPayResult.fromXML(responseContent, ProfitSharingReturnResult.class); + result.checkResult(this.payService, request.getSignType(), true); + return result; + } + + @Override + public ProfitSharingReturnResult profitsharingReturnQuery(ProfitSharingReturnQueryRequest request) throws WxPayException { + request.checkAndSign(this.payService.getConfig()); + String url = this.payService.getPayBaseUrl() + "/pay/profitsharingreturnquery"; + + String responseContent = this.payService.post(url, request.toXML(), true); + ProfitSharingReturnResult result = BaseWxPayResult.fromXML(responseContent, ProfitSharingReturnResult.class); + result.checkResult(this.payService, request.getSignType(), true); + return result; + } } diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/ProfitSharingServiceImplTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/ProfitSharingServiceImplTest.java index 5d955e51d..44949896e 100644 --- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/ProfitSharingServiceImplTest.java +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/ProfitSharingServiceImplTest.java @@ -1,6 +1,7 @@ package com.github.binarywang.wxpay.service.impl; -import com.github.binarywang.wxpay.bean.profitsharing.ProfitsharingRequest; +import com.github.binarywang.wxpay.bean.profitsharing.*; +import com.github.binarywang.wxpay.bean.profitsharing.ProfitSharingRequest; import com.github.binarywang.wxpay.bean.profitsharing.Receiver; import com.github.binarywang.wxpay.bean.profitsharing.ReceiverList; import com.github.binarywang.wxpay.constant.WxPayConstants; @@ -26,15 +27,105 @@ public class ProfitSharingServiceImplTest { ReceiverList instance = ReceiverList.getInstance(); instance.add(new Receiver(WxPayConstants.ReceiverType.PERSONAL_OPENID, "oyOUE5ql4TtzrBg5cVOwxq6tbjOs", - 100, - "分到用户")); - ProfitsharingRequest request = ProfitsharingRequest + 20, + "***")); + //30000002922019102310811092093 + ProfitSharingRequest request = ProfitSharingRequest .newBuilder() - .outOrderNo("P20150806125346") - .transactionId("4208450740201411110007820472") -// .receivers("[{\"type\": \"PERSONAL_OPENID\",\"account\":\"oyOUE5ql4TtzrBg5cVOwxq6tbjOs\",\"amount\":100,\"description\": \"分到用户\"}]") + .outOrderNo("20191023112023031060677") + .transactionId("4200000431201910234736634272") .receivers(instance.toJSONString()) .build(); this.logger.info(this.payService.getProfitSharingService().profitsharing(request).toString()); } + + @Test + public void testMultiprofitsharing() throws WxPayException { + ReceiverList instance = ReceiverList.getInstance(); + instance.add(new Receiver(WxPayConstants.ReceiverType.MERCHANT_ID, + "86693852", + 1, + "***")); + ProfitSharingRequest request = ProfitSharingRequest + .newBuilder() + .outOrderNo("20191023154723316420060") + .transactionId("4200000448201910238249687345")//order_id=30000102922019102310821824010 + .receivers(instance.toJSONString()) + .build(); + this.logger.info(this.payService.getProfitSharingService().multiprofitsharing(request).toString()); + } + + @Test + public void testProfitsharingFinish() throws WxPayException { + ProfitSharingFinishRequest request = ProfitSharingFinishRequest + .newBuilder() + .outOrderNo("20191023103251431856285") + .transactionId("4200000441201910238267278073") + .description("分账完成") + .build(); + this.logger.info(this.payService.getProfitSharingService().profitsharingfinish(request).toString()); + } + + @Test + public void testAddreceiver() throws WxPayException { + Receiver receiver = new Receiver(WxPayConstants.ReceiverType.PERSONAL_OPENID, + "oyOUE5ql4TtzrBg5cVOwxq6tbjOs", + "***", + "STORE_OWNER", + null); + ProfitSharingReceiverRequest request = ProfitSharingReceiverRequest + .newBuilder() + .receiver(receiver.toJSONString()) + .build(); + this.logger.info(this.payService.getProfitSharingService().addReceiver(request).toString()); + } + + @Test + public void testRemoveReceiver() throws WxPayException { + Receiver receiver = new Receiver(WxPayConstants.ReceiverType.PERSONAL_OPENID, + "oyOUE5ql4TtzrBg5cVOwxq6tbjOs"); + ProfitSharingReceiverRequest request = ProfitSharingReceiverRequest + .newBuilder() + .receiver(receiver.toJSONString()) + .build(); + this.logger.info(this.payService.getProfitSharingService().removeReceiver(request).toString()); + } + + @Test + public void testProfitsharingQuery() throws WxPayException { + ProfitSharingQueryRequest request = ProfitSharingQueryRequest + .newBuilder() + .outOrderNo("20191023112023031060677") + .transactionId("4200000431201910234736634272") + .build(); + ProfitSharingQueryResult result = this.payService.getProfitSharingService().profitsharingQuery(request); + this.logger.info(result.formatReceivers().toString()); + this.logger.info(result.toString()); + } + + @Test + public void testProfitsharingReturn() throws WxPayException { + ProfitSharingReturnRequest request = ProfitSharingReturnRequest + .newBuilder() + .outOrderNo("20191023154723316420060") + .outReturnNo("R2019102315") + .returnAccountType("MERCHANT_ID") + .returnAccount("86693852") + .returnAmount(2) + .description("用户退款") + .build(); + this.logger.info(this.payService.getProfitSharingService().profitsharingReturn(request).toString()); + } + + @Test + public void testProfitsharingReturnQuery() throws WxPayException { + ProfitSharingReturnQueryRequest request = ProfitSharingReturnQueryRequest + .newBuilder() + .outOrderNo("20191023154723316420060") + .outReturnNo("R2019102315") + .build(); + this.logger.info(this.payService.getProfitSharingService().profitsharingReturnQuery(request).toString()); + } + + }