Skip to content

Commit 92c0fd6

Browse files
committed
🆕 #1529 微信支付退款增加支持单品退款和对应查询的接口
1 parent 58b2617 commit 92c0fd6

File tree

10 files changed

+426
-7
lines changed

10 files changed

+426
-7
lines changed

weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundQueryRequest.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,8 @@ protected void checkConstraints() throws WxPayException {
8989
&& StringUtils.isBlank(outRefundNo) && StringUtils.isBlank(refundId)) ||
9090
(StringUtils.isNotBlank(transactionId) && StringUtils.isNotBlank(outTradeNo)
9191
&& StringUtils.isNotBlank(outRefundNo) && StringUtils.isNotBlank(refundId))) {
92-
throw new WxPayException("transaction_id,out_trade_no,out_refund_no,refund_id 必须四选一");
92+
throw new WxPayException("transactionId,outRefundNo,transactionId,refundId 必须四选一");
9393
}
94-
9594
}
9695

9796
@Override

weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/request/WxPayRefundRequest.java

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44
import com.github.binarywang.wxpay.constant.WxPayConstants.RefundAccountSource;
55
import com.github.binarywang.wxpay.exception.WxPayException;
66
import com.thoughtworks.xstream.annotations.XStreamAlias;
7+
import com.thoughtworks.xstream.annotations.XStreamConverter;
78
import lombok.*;
9+
import lombok.experimental.Accessors;
810
import me.chanjar.weixin.common.annotation.Required;
11+
import me.chanjar.weixin.common.util.xml.XStreamCDataConverter;
912
import org.apache.commons.lang3.ArrayUtils;
1013
import org.apache.commons.lang3.StringUtils;
1114

@@ -26,7 +29,10 @@
2629
@NoArgsConstructor
2730
@AllArgsConstructor
2831
@XStreamAlias("xml")
32+
@Accessors(chain = true)
2933
public class WxPayRefundRequest extends BaseWxPayRequest {
34+
private static final long serialVersionUID = 522565152886671848L;
35+
3036
private static final String[] REFUND_ACCOUNT = new String[]{
3137
RefundAccountSource.RECHARGE_FUNDS, RefundAccountSource.UNSETTLED_FUNDS};
3238

@@ -127,7 +133,6 @@ public class WxPayRefundRequest extends BaseWxPayRequest {
127133
* 描述:操作员帐号, 默认为商户号
128134
* </pre>
129135
*/
130-
//@Required
131136
@XStreamAlias("op_user_id")
132137
private String opUserId;
133138
/**
@@ -172,6 +177,54 @@ public class WxPayRefundRequest extends BaseWxPayRequest {
172177
@XStreamAlias("notify_url")
173178
private String notifyUrl;
174179

180+
/**
181+
* <pre>
182+
* 字段名:商品详情
183+
* 变量名:detail
184+
* 类型:否
185+
* 示例值:String(6000)
186+
* 退款包含的商品列表信息detail字段列表说明:
187+
*
188+
* 字段名 变量名 必填 类型 示例值 描述
189+
* 商品列表 goods_detail 是 String 示例见下文 商品信息,使用Json数组格式提交
190+
* 商品列表goods_detail字段列表说明:
191+
*
192+
* 字段名 变量名 必填 类型 示例值 描述
193+
* 商品编码 goods_id 是 String(32) 商品编码 由半角的大小写字母、数字、中划线、下划线中的一种或几种组成
194+
* 微信侧商品编码 wxpay_goods_id 否 String(32) 1001 微信支付定义的统一商品编号(没有可不传)
195+
* 商品名称 goods_name 否 String(256) iPhone6s 16G 商品的实际名称
196+
* 商品退款金额 refund_amount 是 int 528800 商品退款金额
197+
* 商品退货数量 refund_quantity 是 int 1 单品的退款数量
198+
* 商品单价 price 是 int 528800 单位为:分。如果商户有优惠,需传输商户优惠后的单价(例如:用户对一笔100元的订单使用了商场发的优惠券100-50,则活动商品的单价应为原单价-50)
199+
* detail字段值举例如下:
200+
*
201+
* {
202+
* "goods_detail": [
203+
* {
204+
* "goods_id": "商品编码",
205+
* "wxpay_goods_id": "1001",
206+
* "goods_name": "iPhone6s 16G",
207+
* "refund_amount": 528800,
208+
* "refund_quantity": 1,
209+
* "price": 528800
210+
* },
211+
* {
212+
* "goods_id": "商品编码",
213+
* "wxpay_goods_id": "1001",
214+
* "goods_name": "iPhone6s 16G",
215+
* "refund_amount": 528800,
216+
* "refund_quantity": 1,
217+
* "price": 608800
218+
* }
219+
* ]
220+
* }
221+
* 描述:退款包含的商品列表信息,全额退款可不传,必须按照规范上传,JSON格式
222+
* </pre>
223+
*/
224+
@XStreamAlias("detail")
225+
@XStreamConverter(value = XStreamCDataConverter.class)
226+
private String detail;
227+
175228
@Override
176229
public void checkAndSign(WxPayConfig config) throws WxPayException {
177230
if (StringUtils.isBlank(this.getOpUserId())) {
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package com.github.binarywang.wxpay.bean.result;
2+
3+
import com.google.gson.annotations.SerializedName;
4+
import lombok.Data;
5+
6+
import java.io.Serializable;
7+
import java.util.List;
8+
9+
/**
10+
* 营销详情 .
11+
*
12+
* @author <a href="https://github.com/binarywang">Binary Wang</a>
13+
* @date 2020-06-07
14+
*/
15+
@Data
16+
public class WxPayRefundPromotionDetail implements Serializable {
17+
private static final long serialVersionUID = 2197712244944584263L;
18+
19+
/**
20+
* 字段名:券ID
21+
* 变量名:promotion_id
22+
* 是否必填:是
23+
* 类型:String(32)
24+
* 示例例:109519
25+
* 描述:券或者立减优惠id
26+
*/
27+
@SerializedName("promotion_id")
28+
private String promotionId;
29+
/**
30+
* 字段名:优惠范围
31+
* 变量名:scope
32+
* 是否必填:是
33+
* 类型:String(32)
34+
* 示例例:SINGLE
35+
* 描述:GLOBAL- 全场代金券,SINGLE- 单品优惠
36+
*/
37+
@SerializedName("scope")
38+
private String scope;
39+
/**
40+
* 字段名:优惠类型
41+
* 变量名:type
42+
* 是否必填:是
43+
* 类型:String(32)
44+
* 示例例:DISCOUNT
45+
* 描述:COUPON- 代金券,需要走结算资金的充值型代金券,(境外商户券币种与支付币种一致),DISCOUNT- 优惠券,不走结算资金的免充值型优惠券,(境外商户券币种与标价币种一致
46+
*/
47+
@SerializedName("type")
48+
private String type;
49+
/**
50+
* 字段名:代金券退款金额
51+
* 变量名:refund_amount
52+
* 是否必填:是
53+
* 类型:Int
54+
* 示例例:100
55+
* 描述:代金券退款金额<=退款金额,退款金额-代金券或立减优惠退款金额为现金,说明详见代金券或立减优惠
56+
*/
57+
@SerializedName("refund_amount")
58+
private Integer refundAmount;
59+
/**
60+
* 字段名:商品列表
61+
* 变量名:goods_detail
62+
* 是否必填:否
63+
* 类型:String
64+
* 示例例:见下文
65+
* 描述:商品信息,使用Json格式
66+
*/
67+
@SerializedName("goods_detail")
68+
private List<GoodDetail> goodsDetails;
69+
70+
@Data
71+
public static class GoodDetail {
72+
/**
73+
* 字段名:商品编码
74+
* 变量名:goods_id
75+
* 是否必填:是
76+
* 类型:String(32)
77+
* 示例值:商品编码
78+
* 描述:由半角的大小写字母、数字、中划线、下划线中的一种或几种组成
79+
*/
80+
@SerializedName("goods_id")
81+
private String goodsId;
82+
83+
/**
84+
* 字段名:优惠退款金额
85+
* 变量名:refund_amount
86+
* 是否必填:是
87+
* 类型:int
88+
* 示例值:528800
89+
* 描述:优惠退款金额
90+
*/
91+
@SerializedName("refund_amount")
92+
private Integer refundAmount;
93+
94+
/**
95+
* 字段名:商品退货数量
96+
* 变量名:refund_quantity
97+
* 是否必填:是
98+
* 类型:int
99+
* 示例值:1
100+
* 描述:单品的退货数量
101+
*/
102+
@SerializedName("refund_quantity")
103+
private Integer refundQuantity;
104+
105+
/**
106+
* 字段名:商品单价
107+
* 变量名:price
108+
* 是否必填:是
109+
* 类型:int
110+
* 示例值:528800
111+
* 描述:单位为:分。如果商户有优惠,需传输商户优惠后的单价(例如:用户对一笔100元的订单使用了商场发的优惠券100-50,则活动商品的单价应为原单价-50)
112+
*/
113+
@SerializedName("price")
114+
private Integer price;
115+
116+
}
117+
}

weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundQueryResult.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,17 @@
33
import java.util.List;
44

55
import com.google.common.collect.Lists;
6+
import com.google.gson.JsonElement;
7+
import com.google.gson.JsonParser;
8+
import com.google.gson.reflect.TypeToken;
69
import com.thoughtworks.xstream.annotations.XStreamAlias;
710
import lombok.AllArgsConstructor;
811
import lombok.Builder;
912
import lombok.Data;
1013
import lombok.EqualsAndHashCode;
1114
import lombok.NoArgsConstructor;
15+
import me.chanjar.weixin.common.util.json.WxGsonBuilder;
16+
import org.apache.commons.lang3.StringUtils;
1217
import org.w3c.dom.Document;
1318

1419
/**
@@ -130,6 +135,34 @@ public class WxPayRefundQueryResult extends BaseWxPayResult {
130135

131136
private List<RefundRecord> refundRecords;
132137

138+
/**
139+
* 营销详情.
140+
*/
141+
@XStreamAlias("promotion_detail")
142+
private String promotionDetailString;
143+
144+
private List<WxPayRefundPromotionDetail> promotionDetails;
145+
146+
/**
147+
* 组装生成营销详情信息.
148+
*/
149+
public void composePromotionDetails() {
150+
if (StringUtils.isEmpty(this.promotionDetailString)) {
151+
return;
152+
}
153+
154+
JsonElement tmpJsonElement = new JsonParser().parse(this.promotionDetailString);
155+
156+
final List<WxPayRefundPromotionDetail> promotionDetail = WxGsonBuilder.create()
157+
.fromJson(tmpJsonElement.getAsJsonObject().get("promotion_detail"),
158+
new TypeToken<List<WxPayRefundPromotionDetail>>() {
159+
}.getType()
160+
);
161+
162+
this.setPromotionDetails(promotionDetail);
163+
}
164+
165+
133166
/**
134167
* 组装生成退款记录属性的内容.
135168
*/

weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/result/WxPayRefundResult.java

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
package com.github.binarywang.wxpay.bean.result;
22

3-
import java.io.Serializable;
4-
import java.util.List;
5-
63
import com.google.common.collect.Lists;
4+
import com.google.gson.JsonElement;
5+
import com.google.gson.JsonParser;
6+
import com.google.gson.reflect.TypeToken;
77
import com.thoughtworks.xstream.annotations.XStreamAlias;
88
import lombok.Data;
99
import lombok.EqualsAndHashCode;
1010
import lombok.NoArgsConstructor;
11+
import me.chanjar.weixin.common.util.json.WxGsonBuilder;
12+
import org.apache.commons.lang3.StringUtils;
1113
import org.w3c.dom.Document;
1214

15+
import java.io.Serializable;
16+
import java.util.List;
17+
1318
/**
1419
* <pre>
1520
* 微信支付-申请退款返回结果.
@@ -115,8 +120,35 @@ public class WxPayRefundResult extends BaseWxPayResult implements Serializable {
115120
@XStreamAlias("coupon_refund_fee")
116121
private Integer couponRefundFee;
117122

123+
/**
124+
* 营销详情.
125+
*/
126+
@XStreamAlias("promotion_detail")
127+
private String promotionDetailString;
128+
129+
private List<WxPayRefundPromotionDetail> promotionDetails;
130+
118131
private List<WxPayRefundCouponInfo> refundCoupons;
119132

133+
/**
134+
* 组装生成营销详情信息.
135+
*/
136+
public void composePromotionDetails() {
137+
if (StringUtils.isEmpty(this.promotionDetailString)) {
138+
return;
139+
}
140+
141+
JsonElement tmpJsonElement = new JsonParser().parse(this.promotionDetailString);
142+
143+
final List<WxPayRefundPromotionDetail> promotionDetail = WxGsonBuilder.create()
144+
.fromJson(tmpJsonElement.getAsJsonObject().get("promotion_detail"),
145+
new TypeToken<List<WxPayRefundPromotionDetail>>() {
146+
}.getType()
147+
);
148+
149+
this.setPromotionDetails(promotionDetail);
150+
}
151+
120152
/**
121153
* 组装生成退款代金券信息.
122154
*/

weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/WxPayService.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,33 @@ public interface WxPayService {
256256
*/
257257
WxPayRefundResult refund(WxPayRefundRequest request) throws WxPayException;
258258

259+
/**
260+
* <pre>
261+
* 申请退款API(支持单品).
262+
* 详见 https://pay.weixin.qq.com/wiki/doc/api/danpin.php?chapter=9_103&index=3
263+
*
264+
* 应用场景
265+
* 当交易发生之后一段时间内,由于买家或者卖家的原因需要退款时,卖家可以通过退款接口将支付款退还给买家,微信支付将在收到退款请求并且验证成功之后,按照退款规则将支付款按原路退到买家帐号上。
266+
*
267+
* 注意:
268+
* 1、交易时间超过一年的订单无法提交退款;
269+
* 2、微信支付退款支持单笔交易分多次退款,多次退款需要提交原支付订单的商户订单号和设置不同的退款单号。申请退款总金额不能超过订单金额。 一笔退款失败后重新提交,请不要更换退款单号,请使用原商户退款单号。
270+
* 3、请求频率限制:150qps,即每秒钟正常的申请退款请求次数不超过150次
271+
* 错误或无效请求频率限制:6qps,即每秒钟异常或错误的退款申请请求不超过6次
272+
* 4、每个支付订单的部分退款次数不能超过50次
273+
* 5、本接口支持单品优惠订单全额退款和单品优惠订单部分退款,推荐使用本接口,如果使用不支持单品优惠部分退款的历史接口,请看https://pay.weixin.qq.com/wiki/doc/api/jsapi_sl.php?chapter=9_4
274+
*
275+
* 接口地址
276+
* https://api.mch.weixin.qq.com/secapi/pay/refundv2
277+
* https://api2.mch.weixin.qq.com/secapi/pay/refundv2(备用域名)见跨城冗灾方案
278+
* </pre>
279+
*
280+
* @param request 请求对象
281+
* @return 退款操作结果 wx pay refund result
282+
* @throws WxPayException the wx pay exception
283+
*/
284+
WxPayRefundResult refundV2(WxPayRefundRequest request) throws WxPayException;
285+
259286
/**
260287
* <pre>
261288
* 微信支付-查询退款.
@@ -293,6 +320,29 @@ WxPayRefundQueryResult refundQuery(String transactionId, String outTradeNo, Stri
293320
*/
294321
WxPayRefundQueryResult refundQuery(WxPayRefundQueryRequest request) throws WxPayException;
295322

323+
/**
324+
* <pre>
325+
* 微信支付-查询退款API(支持单品).
326+
* 应用场景
327+
* 提交退款申请后,通过调用该接口查询退款状态。退款有一定延时,用零钱支付的退款20分钟内到账,银行卡支付的退款3个工作日后重新查询退款状态。
328+
* 注意:
329+
* 1、本接口支持查询单品优惠相关退款信息,且仅支持按微信退款单号或商户退款单号查询,若继续调用老查询退款接口,
330+
* 请见https://pay.weixin.qq.com/wiki/doc/api/jsapi_sl.php?chapter=9_5
331+
* 2、请求频率限制:300qps,即每秒钟正常的退款查询请求次数不超过300次
332+
* 3、错误或无效请求频率限制:6qps,即每秒钟异常或错误的退款查询请求不超过6次
333+
*
334+
* 接口地址
335+
* https://api.mch.weixin.qq.com/pay/refundqueryv2
336+
* https://api2.mch.weixin.qq.com/pay/refundqueryv2(备用域名)见跨城冗灾方案
337+
* 详见 https://pay.weixin.qq.com/wiki/doc/api/danpin.php?chapter=9_104&index=4
338+
* </pre>
339+
*
340+
* @param request 微信退款单号
341+
* @return 退款信息 wx pay refund query result
342+
* @throws WxPayException the wx pay exception
343+
*/
344+
WxPayRefundQueryResult refundQueryV2(WxPayRefundQueryRequest request) throws WxPayException;
345+
296346
/**
297347
* 解析支付结果通知.
298348
* 详见https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7

0 commit comments

Comments
 (0)