From 115f910e3c47e6f45d7733a8ba9fbfd3622b1ea4 Mon Sep 17 00:00:00 2001 From: f00lish Date: Fri, 18 Sep 2020 14:07:03 +0800 Subject: [PATCH] =?UTF-8?q?:new:=20#1768=20=E5=BE=AE=E4=BF=A1=E6=94=AF?= =?UTF-8?q?=E4=BB=98=E5=A2=9E=E5=8A=A0=E7=94=B5=E5=95=86=E6=94=B6=E4=BB=98?= =?UTF-8?q?=E9=80=9A=E5=AE=8C=E7=BB=93=E5=88=86=E8=B4=A6=E5=92=8C=E9=80=80?= =?UTF-8?q?=E6=AC=BE=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 微信收付通增加完结分账和退款接口 --- .../bean/ecommerce/FinishOrderRequest.java | 81 ++++++ .../wxpay/bean/ecommerce/RefundsRequest.java | 206 +++++++++++++++ .../wxpay/bean/ecommerce/RefundsResult.java | 247 ++++++++++++++++++ .../wxpay/service/EcommerceService.java | 24 ++ .../service/impl/EcommerceServiceImpl.java | 14 + 5 files changed, 572 insertions(+) create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/FinishOrderRequest.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/RefundsRequest.java create mode 100644 weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/RefundsResult.java diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/FinishOrderRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/FinishOrderRequest.java new file mode 100644 index 000000000..1b09ba6ff --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/FinishOrderRequest.java @@ -0,0 +1,81 @@ +package com.github.binarywang.wxpay.bean.ecommerce; + +import com.google.gson.annotations.SerializedName; +import lombok.*; + +import java.io.Serializable; + +/** + * 完结分账 对象 + *
+ *   文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/ecommerce/profitsharing/chapter3_5.shtml
+ * 
+ * @author: f00lish + * @date: 2020/09/12 + */ +@Data +@Builder +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class FinishOrderRequest implements Serializable { + + private static final long serialVersionUID = -8662837652326828377L; + + /** + *
+   * 字段名:二级商户号
+   * 变量名:sub_mchid
+   * 是否必填:是
+   * 类型:string(32)
+   * 描述:
+   *  分账出资的电商平台二级商户,填写微信支付分配的商户号。
+   *  示例值:1900000109
+   * 
+ */ + @SerializedName(value = "sub_mchid") + private String subMchid; + + /** + *
+   * 字段名:微信订单号
+   * 变量名:transaction_id
+   * 是否必填:是
+   * 类型:string(32)
+   * 描述:
+   *  微信支付订单号。
+   *  示例值:4208450740201411110007820472
+   * 
+ */ + @SerializedName(value = "transaction_id") + private String transactionId; + + /** + *
+   * 字段名:商户分账单号
+   * 变量名:out_order_no
+   * 是否必填:是
+   * 类型:string(64)
+   * 描述:
+   *  商户系统内部的分账单号,在商户系统内部唯一(单次分账、多次分账、完结分账应使用不同的商户分账单号),同一分账单号多次请求等同一次。
+   *  示例值:P20150806125346
+   * 
+ */ + @SerializedName(value = "out_order_no") + private String outOrderNo; + + /** + *
+   * 字段名:分账描述
+   * 变量名:description
+   * 是否必填:是
+   * 类型:string(80)
+   * 描述:
+   *  分账的原因描述,分账账单中需要体现。
+   *  示例值:分给商户1900000109
+   * 
+ */ + @SerializedName(value = "description") + private String description; + + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/RefundsRequest.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/RefundsRequest.java new file mode 100644 index 000000000..9277b3557 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/RefundsRequest.java @@ -0,0 +1,206 @@ +package com.github.binarywang.wxpay.bean.ecommerce; + +/** + * @author: f00lish + * @date: 2020/09/17 + */ + +import com.google.gson.annotations.SerializedName; +import lombok.*; + +import java.io.Serializable; + +/** + * 退款申请 + * *
+ *  *   文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/ecommerce/refunds/chapter3_1.shtml
+ *  * 
+ * @author: f00lish + * @date: 2020/09/14 + */ +@Data +@Builder +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class RefundsRequest implements Serializable { + private static final long serialVersionUID = -3186851559004865784L; + + /** + *
+   * 字段名:二级商户号
+   * 变量名:sub_mchid
+   * 是否必填:是
+   * 类型:string(32)
+   * 描述:
+   *  微信支付分配二级商户的商户号。
+   *  示例值:1900000109
+   * 
+ */ + @SerializedName(value = "sub_mchid") + private String subMchid; + + /** + *
+   * 字段名:电商平台APPID
+   * 变量名:sp_appid
+   * 是否必填:是
+   * 类型:string(32)
+   * 描述:
+   *  电商平台在微信公众平台申请服务号对应的APPID,申请商户功能的时候微信支付会配置绑定关系。
+   *  示例值:wx8888888888888888
+   * 
+ */ + @SerializedName(value = "sp_appid") + private String spAppid; + + /** + *
+   * 字段名:二级商户APPID
+   * 变量名:sub_appid
+   * 是否必填:否
+   * 类型:string(32)
+   * 描述:
+   *  二级商户在微信申请公众号成功后分配的帐号ID,需要电商平台侧配置绑定关系才能传参。
+   *  示例值:wxd678efh567hg6999
+   * 
+ */ + @SerializedName(value = "sub_appid") + private String subAppid; + + /** + *
+   * 字段名:微信订单号
+   * 变量名:transaction_id
+   * 是否必填:与out_order_no二选一
+   * 类型:string(32)
+   * 描述:
+   *  微信支付订单号。
+   *  示例值:4208450740201411110007820472
+   * 
+ */ + @SerializedName(value = "transaction_id") + private String transactionId; + + /** + *
+   * 字段名:商户订单号
+   * 变量名:out_order_no
+   * 是否必填:与transaction_id二选一
+   * 类型:string(64)
+   * 描述:
+   *   原支付交易对应的商户订单号。
+   *   示例值:P20150806125346
+   * 
+ */ + @SerializedName(value = "out_order_no") + private String outOrderNo; + + /** + *
+   * 字段名:商户退款单号
+   * 变量名:out_refund_no
+   * 是否必填:是
+   * 类型:string(64)
+   * 描述:
+   *   商户系统内部的退款单号,商户系统内部唯一,只能是数字、大小写字母_-|*@,同一退款单号多次请求只退一笔。
+   *   示例值:1217752501201407033233368018
+   * 
+ */ + @SerializedName(value = "out_refund_no") + private String outRefundNo; + + /** + *
+   * 字段名:退款原因
+   * 变量名:reason
+   * 是否必填:是
+   * 类型:string(80)
+   * 描述:
+   *   若商户传入,会在下发给用户的退款消息中体现退款原因。
+   *   注意:若订单退款金额≤1元,且属于部分退款,则不会在退款消息中体现退款原因
+   *   示例值:商品已售完
+   * 
+ */ + @SerializedName(value = "reason") + private String reason; + + /** + *
+   * 字段名:订单金额
+   * 变量名:amount
+   * 是否必填:是
+   * 类型:object
+   * 描述:
+   *  订单金额信息
+   * 
+ */ + @SerializedName(value = "amount") + private Amount amount; + + /** + *
+   * 字段名:退款结果回调url
+   * 变量名:notify_url
+   * 是否必填:是
+   * 类型:string(256)
+   * 描述:
+   *   异步接收微信支付退款结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。 如果参数中传了notify_url,则商户平台上配置的回调地址将不会生效,优先回调当前传的地址。
+   *   示例值:https://weixin.qq.com
+   * 
+ */ + @SerializedName(value = "notify_url") + private String notifyUrl; + + @Data + @Builder + @NoArgsConstructor(access = AccessLevel.PRIVATE) + @AllArgsConstructor(access = AccessLevel.PRIVATE) + public static class Amount implements Serializable { + + private static final long serialVersionUID = 7383027142329410399L; + + /** + *
+     * 字段名:退款金额
+     * 变量名:refund
+     * 是否必填:是
+     * 类型:int
+     * 描述:
+     *  退款金额,币种的最小单位,只能为整数,不能超过原订单支付金额。
+     *  示例值:888
+     * 
+ */ + @SerializedName(value = "refund") + private Integer refund; + + /** + *
+     * 字段名:原订单金额
+     * 变量名:total
+     * 是否必填:是
+     * 类型:int64
+     * 描述:
+     *  订单总金额,单位为分。
+     *  示例值:888
+     * 
+ */ + @SerializedName(value = "total") + private Integer total; + + /** + *
+     * 字段名:币类型
+     * 变量名:currency
+     * 是否必填:否
+     * 类型:string(18)
+     * 描述:
+     *  符合ISO 4217标准的三位字母代码,目前只支持人民币:CNY。
+     *  示例值:CNY
+     * 
+ */ + @SerializedName(value = "currency") + private String currency; + + } + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/RefundsResult.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/RefundsResult.java new file mode 100644 index 000000000..9ba480104 --- /dev/null +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/RefundsResult.java @@ -0,0 +1,247 @@ +package com.github.binarywang.wxpay.bean.ecommerce; + +/** + * @author: f00lish + * @date: 2020/09/17 + */ + +import com.google.gson.annotations.SerializedName; +import lombok.*; + +import java.io.Serializable; +import java.util.Date; + +/** + * 退款结果 + * *
+ *  *   文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/ecommerce/refunds/chapter3_1.shtml
+ *  * 
+ * @author: f00lish + * @date: 2020/09/14 + */ +@Data +@Builder +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class RefundsResult implements Serializable { + private static final long serialVersionUID = -3186851559004865784L; + + /** + *
+   * 字段名:微信退款单号
+   * 变量名:refund_id
+   * 是否必填:是
+   * 类型:string(32)
+   * 描述:
+   *  微信支付退款订单号。
+   *  示例值:1217752501201407033233368018
+   * 
+ */ + @SerializedName(value = "refund_id") + private String refundId; + + /** + *
+   * 字段名:商户订单号
+   * 变量名:out_order_no
+   * 是否必填:与transaction_id二选一
+   * 类型:string(64)
+   * 描述:
+   *   原支付交易对应的商户订单号。
+   *   示例值:P20150806125346
+   * 
+ */ + @SerializedName(value = "out_order_no") + private String outOrderNo; + + /** + *
+   * 字段名:退款创建时间
+   * 变量名:create_time
+   * 是否必填:是
+   * 类型:string(64)
+   * 描述:
+   *   退款受理时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss表示时分秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35+08:00表示,北京时间2015年5月20日13点29分35秒。
+   *   示例值:2018-06-08T10:34:56+08:00
+   * 
+ */ + @SerializedName(value = "create_time") + private Date createTime; + + /** + *
+   * 字段名:订单金额
+   * 变量名:amount
+   * 是否必填:是
+   * 类型:object
+   * 描述:
+   *  订单金额信息
+   * 
+ */ + @SerializedName(value = "amount") + private Amount amount; + + /** + *
+   * 字段名:优惠退款详情
+   * 变量名:promotion_detail
+   * 是否必填:否
+   * 类型:array
+   * 描述:
+   *   优惠退款功能信息
+   * 
+ */ + @SerializedName(value = "promotion_detail") + private PromotionDetail[] promotionDetail; + + @Data + @Builder + @NoArgsConstructor(access = AccessLevel.PRIVATE) + @AllArgsConstructor(access = AccessLevel.PRIVATE) + public static class Amount implements Serializable { + + private static final long serialVersionUID = 7383027142329410399L; + + /** + *
+     * 字段名:退款金额
+     * 变量名:refund
+     * 是否必填:是
+     * 类型:int
+     * 描述:
+     *  退款金额,币种的最小单位,只能为整数,不能超过原订单支付金额。
+     *  示例值:888
+     * 
+ */ + @SerializedName(value = "refund") + private Integer refund; + + /** + *
+     * 字段名:用户退款金额
+     * 变量名:payer_refund
+     * 是否必填:是
+     * 类型:int64
+     * 描述:
+     *  退款给用户的金额,不包含所有优惠券金额。
+     *  示例值:888
+     * 
+ */ + @SerializedName(value = "payer_refund") + private Integer payerRefund; + + /** + *
+     * 字段名:优惠退款金额
+     * 变量名:discount_refund
+     * 是否必填:否
+     * 类型:int64
+     * 描述:
+     *  优惠券的退款金额,原支付单的优惠按比例退款。
+     *  示例值:888
+     * 
+ */ + @SerializedName(value = "discount_refund") + private Integer discountRefund; + + /** + *
+     * 字段名:币类型
+     * 变量名:currency
+     * 是否必填:否
+     * 类型:string(18)
+     * 描述:
+     *  符合ISO 4217标准的三位字母代码,目前只支持人民币:CNY。
+     *  示例值:CNY
+     * 
+ */ + @SerializedName(value = "currency") + private String currency; + + } + + @Data + @Builder + @NoArgsConstructor(access = AccessLevel.PRIVATE) + @AllArgsConstructor(access = AccessLevel.PRIVATE) + public static class PromotionDetail implements Serializable { + + private static final long serialVersionUID = 7383027142329410399L; + + /** + *
+     * 字段名:券ID
+     * 变量名:promotion_id
+     * 是否必填:是
+     * 类型:string(32)
+     * 描述:
+     *  券或者立减优惠id。
+     *  示例值:109519
+     * 
+ */ + @SerializedName(value = "promotion_id") + private String promotionId; + + /** + *
+     * 字段名:优惠范围
+     * 变量名:scope
+     * 是否必填:是
+     * 类型:string(32)
+     * 描述:
+     *  枚举值:
+     *  GLOBAL:全场代金券
+     *  SINGLE:单品优惠
+     *  示例值:SINGLE
+     * 
+ */ + @SerializedName(value = "scope") + private String scope; + + /** + *
+     * 字段名:优惠类型
+     * 变量名:type
+     * 是否必填:是
+     * 类型:string(32)
+     * 描述:
+     *  枚举值:
+     *  COUPON:充值型代金券,商户需要预先充值营销经费
+     *  DISCOUNT:免充值型优惠券,商户不需要预先充值营销经费
+     *  示例值:DISCOUNT
+     * 
+ */ + @SerializedName(value = "type") + private String type; + + /** + *
+     * 字段名:优惠券面额
+     * 变量名:amount
+     * 是否必填:是
+     * 类型:int
+     * 描述:
+     *  用户享受优惠的金额(优惠券面额=微信出资金额+商家出资金额+其他出资方金额 )。
+     *  示例值:5
+     * 
+ */ + @SerializedName(value = "amount") + private Integer amount; + + /** + *
+     * 字段名:优惠退款金额
+     * 变量名:refund_amount
+     * 是否必填:是
+     * 类型:int
+     * 描述:
+     *  代金券退款金额<=退款金额,退款金额-代金券或立减优惠退款金额为现金,说明详见《代金券或立减优惠》https://pay.weixin.qq.com/wiki/doc/api/tools/sp_coupon.php?chapter=12_1 。
+     *  示例值:CNY
+     * 
+ */ + @SerializedName(value = "refund_amount") + private Integer refundAmount; + + } + +} diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java index 90997fb37..6d8160b00 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/EcommerceService.java @@ -213,4 +213,28 @@ public interface EcommerceService { */ ReturnOrdersResult returnOrders(ReturnOrdersRequest request) throws WxPayException; + /** + *
+   * 完结分账API
+   * 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/ecommerce/profitsharing/chapter3_5.shtml
+   * 
+ * + * @param request 完结分账请求 + * @return 返回数据 return orders result + * @throws WxPayException the wx pay exception + */ + ProfitSharingResult finishOrder(FinishOrderRequest request) throws WxPayException; + + /** + *
+   * 退款申请API
+   * 文档地址: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/ecommerce/refunds/chapter3_1.shtml
+   * 
+ * + * @param request 退款请求 + * @return 返回数据 return refunds result + * @throws WxPayException the wx pay exception + */ + RefundsResult refunds(RefundsRequest request) throws WxPayException; + } diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java index b792771a1..53ecb9ceb 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/EcommerceServiceImpl.java @@ -162,6 +162,20 @@ public class EcommerceServiceImpl implements EcommerceService { return GSON.fromJson(response, ReturnOrdersResult.class); } + @Override + public ProfitSharingResult finishOrder(FinishOrderRequest request) throws WxPayException { + String url = String.format("%s/v3/ecommerce/profitsharing/finish-order", this.payService.getPayBaseUrl()); + String response = this.payService.postV3(url, GSON.toJson(request)); + return GSON.fromJson(response, ProfitSharingResult.class); + } + + @Override + public RefundsResult refunds(RefundsRequest request) throws WxPayException { + String url = String.format("%s/v3/ecommerce/refunds/apply", this.payService.getPayBaseUrl()); + String response = this.payService.postV3(url, GSON.toJson(request)); + return GSON.fromJson(response, RefundsResult.class); + } + private boolean verifyNotifySign(SignatureHeader header, String data) { String beforeSign = String.format("%s\n%s\n%s\n", header.getTimeStamp(),