查询单笔退款(通过商户退款单号)

更新时间:2025.01.09

提交退款申请后,推荐每间隔1分钟调用该接口查询一次退款状态,若超过5分钟仍是退款处理中状态,建议开始逐步衰减查询频率(比如之后间隔5分钟、10分钟、20分钟、30分钟……查询一次)。

  1. 退款有一定延时,零钱支付的订单退款一般5分钟内到账,银行卡支付的订单退款一般1-3个工作日到账。

  2. 同一商户号查询退款频率限制为300qps,如返回FREQUENCY_LIMITED频率限制报错可间隔1分钟再重试查询。

接口说明

支持商户:【普通商户】

请求方式:【GET】/v3/refund/domestic/refunds/{out_refund_no}

请求域名:【主域名】https://api.mch.weixin.qq.com 使用该域名将访问就近的接入点

     【备域名】https://api2.mch.weixin.qq.com 使用该域名将访问异地的接入点 ,指引点击查看

请求参数

Header HTTP头参数

Authorization  必填 string

请参考签名认证生成认证信息


Accept  必填 string

请设置为application/json


path 路径参数

out_refund_no  必填 string(64)

【商户退款单号】 商户申请退款时传入的商户系统内部退款单号

请求示例

Java
Go
curl

需配合微信支付工具库 WXPayUtility 使用,请参考 Java 

1package com.java.demo;
2
3import com.google.gson.annotations.Expose;
4import com.google.gson.annotations.SerializedName;
5import com.java.utils.WXPayUtility; // 引用微信支付工具库 参考:https://pay.weixin.qq.com/doc/v3/merchant/4014931831
6import java.io.IOException;
7import java.io.UncheckedIOException;
8import java.security.PrivateKey;
9import java.security.PublicKey;
10import java.util.ArrayList;
11import java.util.HashMap;
12import java.util.List;
13import java.util.Map;
14import okhttp3.MediaType;
15import okhttp3.OkHttpClient;
16import okhttp3.Request;
17import okhttp3.RequestBody;
18import okhttp3.Response;
19
20/**
21 * 查询单笔退款(通过商户退款单号)
22 */
23public class QueryByOutRefundNo {
24  private static String HOST = "https://api.mch.weixin.qq.com";
25  private static String METHOD = "GET";
26  private static String PATH = "/v3/refund/domestic/refunds/{out_refund_no}";
27
28  public static void main(String[] args) {
29    // TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/merchant/4013070756
30    QueryByOutRefundNo client = new QueryByOutRefundNo(
31        "填入 商户号",                  // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考https://pay.weixin.qq.com/doc/v3/merchant/4013070756
32        "填入 商户API证书序列号",       // 商户API证书序列号,如何获取请参考https://pay.weixin.qq.com/doc/v3/merchant/4013053053
33        "填入 商户API证书私钥文件路径", // 商户API证书私钥文件路径,本地文件路径
34        "填入 微信支付公钥ID",          // 微信支付公钥ID,如何获取请参考https://pay.weixin.qq.com/doc/v3/merchant/4013038816
35        "填入 微信支付公钥文件路径"     // 微信支付公钥文件路径,本地文件路径
36    );
37
38    QueryByOutRefundNoRequest request = new QueryByOutRefundNoRequest();
39    request.outRefundNo = "1217752501201407033233368018";
40    try {
41      Refund response = client.run(request);
42
43      // TODO: 请求成功,继续业务逻辑
44      System.out.println(response);
45    } catch (WXPayUtility.ApiException e) {
46      // TODO: 请求失败,根据状态码执行不同的逻辑
47      e.printStackTrace();
48    }
49  }
50
51  public Refund run(QueryByOutRefundNoRequest request) {
52    String uri = PATH.replace("{out_refund_no}", WXPayUtility.urlEncode(request.outRefundNo));
53
54    Request.Builder reqBuilder = new Request.Builder().url(HOST + uri);
55    reqBuilder.addHeader("Accept", "application/json");
56    reqBuilder.addHeader("Wechatpay-Serial", wechatPayPublicKeyId);
57    reqBuilder.addHeader("Authorization", WXPayUtility.buildAuthorization(mchid, certificateSerialNo, privateKey, METHOD, uri, null));
58    reqBuilder.method(METHOD, null);
59    Request httpRequest = reqBuilder.build();
60
61    // 发送HTTP请求
62    OkHttpClient client = new OkHttpClient.Builder().build();
63    try (Response httpResponse = client.newCall(httpRequest).execute()) {
64      String respBody = WXPayUtility.extractBody(httpResponse);
65      if (httpResponse.code() >= 200 && httpResponse.code() < 300) {
66        // 2XX 成功,验证应答签名
67        WXPayUtility.validateResponse(this.wechatPayPublicKeyId, this.wechatPayPublicKey,
68                                      httpResponse.headers(), respBody);
69
70        // 从HTTP应答报文构建返回数据
71        return WXPayUtility.fromJson(respBody, Refund.class);
72      } else {
73        throw new WXPayUtility.ApiException(httpResponse.code(), respBody, httpResponse.headers());
74      }
75    } catch (IOException e) {
76      throw new UncheckedIOException("Sending request to " + uri + " failed.", e);
77    }
78  }
79
80  private final String mchid;
81  private final String certificateSerialNo;
82  private final PrivateKey privateKey;
83  private final String wechatPayPublicKeyId;
84  private final PublicKey wechatPayPublicKey;
85
86  public QueryByOutRefundNo(String mchid, String certificateSerialNo, String privateKeyFilePath, String wechatPayPublicKeyId, String wechatPayPublicKeyFilePath) {
87    this.mchid = mchid;
88    this.certificateSerialNo = certificateSerialNo;
89    this.privateKey = WXPayUtility.loadPrivateKeyFromPath(privateKeyFilePath);
90    this.wechatPayPublicKeyId = wechatPayPublicKeyId;
91    this.wechatPayPublicKey = WXPayUtility.loadPublicKeyFromPath(wechatPayPublicKeyFilePath);
92  }
93
94  public enum Status {
95    @SerializedName("SUCCESS")
96    SUCCESS,
97    @SerializedName("CLOSED")
98    CLOSED,
99    @SerializedName("PROCESSING")
100    PROCESSING,
101    @SerializedName("ABNORMAL")
102    ABNORMAL
103  }
104
105  public enum Account {
106    @SerializedName("AVAILABLE")
107    AVAILABLE,
108    @SerializedName("UNAVAILABLE")
109    UNAVAILABLE
110  }
111
112  public enum PromotionType {
113    @SerializedName("COUPON")
114    COUPON,
115    @SerializedName("DISCOUNT")
116    DISCOUNT
117  }
118
119  public static class Refund {
120    @SerializedName("refund_id")
121    public String refundId;
122
123    @SerializedName("out_refund_no")
124    public String outRefundNo;
125
126    @SerializedName("transaction_id")
127    public String transactionId;
128
129    @SerializedName("out_trade_no")
130    public String outTradeNo;
131
132    @SerializedName("channel")
133    public Channel channel;
134
135    @SerializedName("user_received_account")
136    public String userReceivedAccount;
137
138    @SerializedName("success_time")
139    public String successTime;
140
141    @SerializedName("create_time")
142    public String createTime;
143
144    @SerializedName("status")
145    public Status status;
146
147    @SerializedName("funds_account")
148    public FundsAccount fundsAccount;
149
150    @SerializedName("amount")
151    public Amount amount;
152
153    @SerializedName("promotion_detail")
154    public List<Promotion> promotionDetail;
155  }
156
157  public static class GoodsDetail {
158    @SerializedName("merchant_goods_id")
159    public String merchantGoodsId;
160
161    @SerializedName("wechatpay_goods_id")
162    public String wechatpayGoodsId;
163
164    @SerializedName("goods_name")
165    public String goodsName;
166
167    @SerializedName("unit_price")
168    public Long unitPrice;
169
170    @SerializedName("refund_amount")
171    public Long refundAmount;
172
173    @SerializedName("refund_quantity")
174    public Integer refundQuantity;
175  }
176
177  public static class QueryByOutRefundNoRequest {
178    @SerializedName("out_refund_no")
179    @Expose(serialize = false)
180    public String outRefundNo;
181  }
182
183  public enum Channel {
184    @SerializedName("ORIGINAL")
185    ORIGINAL,
186    @SerializedName("BALANCE")
187    BALANCE,
188    @SerializedName("OTHER_BALANCE")
189    OTHER_BALANCE,
190    @SerializedName("OTHER_BANKCARD")
191    OTHER_BANKCARD
192  }
193
194  public static class Amount {
195    @SerializedName("total")
196    public Long total;
197
198    @SerializedName("refund")
199    public Long refund;
200
201    @SerializedName("from")
202    public List<FundsFromItem> from;
203
204    @SerializedName("payer_total")
205    public Long payerTotal;
206
207    @SerializedName("payer_refund")
208    public Long payerRefund;
209
210    @SerializedName("settlement_refund")
211    public Long settlementRefund;
212
213    @SerializedName("settlement_total")
214    public Long settlementTotal;
215
216    @SerializedName("discount_refund")
217    public Long discountRefund;
218
219    @SerializedName("currency")
220    public String currency;
221
222    @SerializedName("refund_fee")
223    public Long refundFee;
224  }
225
226  public static class FundsFromItem {
227    @SerializedName("account")
228    public Account account;
229
230    @SerializedName("amount")
231    public Long amount;
232  }
233
234  public enum FundsAccount {
235    @SerializedName("UNSETTLED")
236    UNSETTLED,
237    @SerializedName("AVAILABLE")
238    AVAILABLE,
239    @SerializedName("UNAVAILABLE")
240    UNAVAILABLE,
241    @SerializedName("OPERATION")
242    OPERATION,
243    @SerializedName("BASIC")
244    BASIC,
245    @SerializedName("ECNY_BASIC")
246    ECNY_BASIC
247  }
248
249  public static class Promotion {
250    @SerializedName("promotion_id")
251    public String promotionId;
252
253    @SerializedName("scope")
254    public PromotionScope scope;
255
256    @SerializedName("type")
257    public PromotionType type;
258
259    @SerializedName("amount")
260    public Long amount;
261
262    @SerializedName("refund_amount")
263    public Long refundAmount;
264
265    @SerializedName("goods_detail")
266    public List<GoodsDetail> goodsDetail;
267  }
268
269  public enum PromotionScope {
270    @SerializedName("GLOBAL")
271    GLOBAL,
272    @SerializedName("SINGLE")
273    SINGLE
274  }
275}

应答参数

200 OK

refund_id   必填 string(32)

【微信支付退款单号】申请退款受理成功时,该笔退款单在微信支付侧生成的唯一标识。


out_refund_no   必填 string(64)

【商户退款单号】商户申请退款时传入的商户系统内部退款单号


transaction_id   必填 string(32)

【微信支付订单号】微信支付侧订单的唯一标识。


out_trade_no   必填 string(32)

【商户订单号】 商户下单时传入的商户系统内部订单号。


channel   必填 string

【退款渠道】 订单退款渠道
以下枚举:

  • ORIGINAL: 原路退款

  • BALANCE: 退回到余额

  • OTHER_BALANCE: 原账户异常退到其他余额账户

  • OTHER_BANKCARD: 原银行卡异常退到其他银行卡(发起异常退款成功后返回)


user_received_account   必填 string(64)

【退款入账账户】 当前退款单的退款入账方,取值有以下几种情况:
1)退回银行卡:{银行名称}{卡类型}{卡尾号}
2)退回支付用户零钱:支付用户零钱
3)退还商户:商户基本账户商户结算银行账户
4)退回支付用户零钱通:支付用户零钱通
5)退回支付用户银行电子账户:支付用户银行电子账户
6)退回支付用户零花钱:支付用户零花钱
7)退回用户经营账户:用户经营账户
8)退回支付用户来华零钱包:支付用户来华零钱包
9)退回企业支付商户:企业支付商户


success_time   选填 string(64)

【退款成功时间】

1、定义:退款成功的时间,该字段在退款状态status为SUCCESS(退款成功)时返回。

2、格式:遵循rfc3339标准格式:yyyy-MM-DDTHH:mm:ss+TIMEZONE。yyyy-MM-DD 表示年月日;T 字符用于分隔日期和时间部分;HH:mm:ss 表示具体的时分秒;TIMEZONE 表示时区(例如,+08:00 对应东八区时间,即北京时间)。

示例:2015-05-20T13:29:35+08:00 表示北京时间2015年5月20日13点29分35秒。


create_time   必填 string(64)

【退款创建时间】

1、定义:提交退款申请成功,微信受理退款申请单的时间。

2、格式遵循rfc3339标准格式:yyyy-MM-DDTHH:mm:ss+TIMEZONEyyyy-MM-DD 表示年月日;T 字符用于分隔日期和时间部分;HH:mm:ss 表示具体的时分秒;TIMEZONE 表示时区(例如,+08:00 对应东八区时间,即北京时间)。

示例:2015-05-20T13:29:35+08:00 表示北京时间2015年5月20日13点29分35秒。


status   必填 string

【退款状态】退款单的退款处理状态。

  • SUCCESS: 退款成功

  • CLOSED: 退款关闭

  • PROCESSING: 退款处理中

  • ABNORMAL: 退款异常,退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,可前往商户平台-交易中心,手动处理此笔退款,可参考: 退款异常的处理或者通过发起异常退款接口进行处理。
    注:状态流转说明请参考状态流转图


funds_account   必填 string

【资金账户】 退款所使用资金对应的资金账户类型

  • UNSETTLED: 未结算资金

  • AVAILABLE: 可用余额

  • UNAVAILABLE: 不可用余额

  • OPERATION: 运营账户

  • BASIC: 基本账户(含可用余额和不可用余额)

  • ECNY_BASIC: 数字人民币基本账户


amount   必填 object

【金额信息】订单退款金额信息

属性

promotion_detail   选填 array[object]

【优惠退款详情】 订单各个代金券的退款详情,订单使用了代金券且代金券发生退款时返回。

属性

应答示例

200 OK

1{
2  "refund_id" : "50000000382019052709732678859",
3  "out_refund_no" : "1217752501201407033233368018",
4  "transaction_id" : "1217752501201407033233368018",
5  "out_trade_no" : "1217752501201407033233368018",
6  "channel" : "ORIGINAL",
7  "user_received_account" : "招商银行信用卡0403",
8  "success_time" : "2020-12-01T16:18:12+08:00",
9  "create_time" : "2020-12-01T16:18:12+08:00",
10  "status" : "SUCCESS",
11  "funds_account" : "UNSETTLED",
12  "amount" : {
13    "total" : 100,
14    "refund" : 100,
15    "from" : [
16      {
17        "account" : "AVAILABLE",
18        "amount" : 444
19      }
20    ],
21    "payer_total" : 90,
22    "payer_refund" : 90,
23    "settlement_refund" : 100,
24    "settlement_total" : 100,
25    "discount_refund" : 10,
26    "currency" : "CNY",
27    "refund_fee" : 100
28  },
29  "promotion_detail" : [
30    {
31      "promotion_id" : "109519",
32      "scope" : "GLOBAL",
33      "type" : "COUPON",
34      "amount" : 5,
35      "refund_amount" : 100,
36      "goods_detail" : [
37        {
38          "merchant_goods_id" : "1217752501201407033233368018",
39          "wechatpay_goods_id" : "1001",
40          "goods_name" : "iPhone6s 16G",
41          "unit_price" : 528800,
42          "refund_amount" : 528800,
43          "refund_quantity" : 1
44        }
45      ]
46    }
47  ]
48}

错误码

公共错误码

状态码

错误码

描述

解决方案

400

PARAM_ERROR

参数错误

请根据错误提示正确传入参数

400

INVALID_REQUEST

HTTP 请求不符合微信支付 APIv3 接口规则

请参阅 接口规则

401

SIGN_ERROR

验证不通过

请参阅 签名常见问题

500

SYSTEM_ERROR

系统异常,请稍后重试

请稍后重试

业务错误码

状态码

错误码

描述

解决方案

401

SIGN_ERROR

签名错误

请检查签名参数和方法是否都符合签名算法要求,参考:如何生成签名

404

MCH_NOT_EXISTS

MCHID不存在

请检查商户号是否正确,商户号获取方式请参考普通商户模式开发必要参数说明

404

RESOURCE_NOT_EXISTS

退款单不存在

请检查商户退款单号是否有误以及订单状态是否正确,如:未支付

500

SYSTEM_ERROR

系统超时

请不要更换商户退款单号,请使用相同参数再次调用API。

 

 

反馈
咨询
目录
置顶