Skip to content

Commit 4034834

Browse files
authored
🎨 #3994 【微信支付】对齐新版「商家转账-电子回单」接口规范:将直连商户/服务商的电子回单申请与查询接口从旧版 v3/transfer/bill-receipt 切换到新版 v3/fund-app/mch-transfer/elecsign/out-bill-no/
1 parent b9bf178 commit 4034834

9 files changed

Lines changed: 185 additions & 46 deletions

File tree

weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/BillReceiptResult.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,16 @@ public String toString() {
2929

3030
/**
3131
* <pre>
32-
* 字段名:商家批次单号
33-
* 变量名:out_batch_no
32+
* 字段名:商户转账单号
33+
* 变量名:out_bill_no
3434
* 是否必填:是
3535
* 类型:string[5,32]
3636
* 描述:
37-
* 商户系统内部的商家批次单号,在商户系统内部唯一。需要电子回单的批次单号
37+
* 商户系统内部的商户转账单号,在商户系统内部唯一。兼容旧字段out_batch_no
3838
* 示例值:plfk2020042013
3939
* </pre>
4040
*/
41-
@SerializedName(value = "out_batch_no")
41+
@SerializedName(value = "out_bill_no", alternate = {"out_batch_no"})
4242
private String outBatchNo;
4343

4444
/**
@@ -58,17 +58,18 @@ public String toString() {
5858
/**
5959
* <pre>
6060
* 字段名:电子回单状态
61-
* 变量名:signature_status
61+
* 变量名:state
6262
* 是否必填:否
6363
* 类型:string[1,10]
6464
* 描述:
6565
* 枚举值:
6666
* ACCEPTED:已受理,电子签章已受理成功
6767
* FINISHED:已完成。电子签章已处理完成
68+
* 兼容旧字段signature_status
6869
* 示例值:ACCEPTED
6970
* </pre>
7071
*/
71-
@SerializedName(value = "signature_status")
72+
@SerializedName(value = "state", alternate = {"signature_status"})
7273
private String signatureStatus;
7374

7475
/**

weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/marketing/transfer/ReceiptBillRequest.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
/**
1010
* 转账电子回单申请受理API
1111
* <pre>
12-
* 文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter4_1.shtml
12+
* 文档地址:https://pay.weixin.qq.com/doc/v3/merchant/4012716452
1313
* </pre>
1414
*
1515
* @author xiaoqiang
@@ -21,15 +21,15 @@ public class ReceiptBillRequest implements Serializable {
2121
private static final long serialVersionUID = 1L;
2222
/**
2323
* <pre>
24-
* 字段名:商家批次单号
25-
* 变量名:out_batch_no
24+
* 字段名:商户转账单号
25+
* 变量名:out_bill_no
2626
* 是否必填:是
2727
* 类型:string[5, 32]
2828
* 描述:
29-
* body商户系统内部的商家批次单号,在商户系统内部唯一。需要电子回单的批次单号
29+
* body商户系统内部的商户转账单号,在商户系统内部唯一。兼容旧字段out_batch_no
3030
* 示例值:plfk2020042013
3131
* </pre>
3232
*/
33-
@SerializedName(value = "out_batch_no")
33+
@SerializedName(value = "out_bill_no", alternate = {"out_batch_no"})
3434
private String outBatchNo;
3535
}

weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/merchanttransfer/ElectronicBillApplyRequest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,15 @@ public class ElectronicBillApplyRequest implements Serializable {
2424
private static final long serialVersionUID = -2121536206019844928L;
2525
/**
2626
* <pre>
27-
* 字段名:商家批次单号
28-
* 变量名:out_batch_no
27+
* 字段名:商户转账单号
28+
* 变量名:out_bill_no
2929
* 是否必填:是
3030
* 类型:string[5,32]
3131
* 描述:
32-
* body商户系统内部的商家批次单号,在商户系统内部唯一。需要电子回单的批次单号
32+
* body商户系统内部的商户转账单号,在商户系统内部唯一。兼容旧字段out_batch_no
3333
* 示例值:plfk2020042013
3434
* </pre>
3535
*/
36-
@SerializedName("out_batch_no")
36+
@SerializedName(value = "out_bill_no", alternate = {"out_batch_no"})
3737
private String outBatchNo;
3838
}

weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/merchanttransfer/ElectronicBillResult.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,16 @@ public class ElectronicBillResult implements Serializable {
2424
private static final long serialVersionUID = 7528245102572829190L;
2525
/**
2626
* <pre>
27-
* 字段名:商家批次单号
28-
* 变量名:out_batch_no
27+
* 字段名:商户转账单号
28+
* 变量名:out_bill_no
2929
* 是否必填:是
3030
* 类型:string[5,32]
3131
* 描述:
32-
* body商户系统内部的商家批次单号,在商户系统内部唯一。需要电子回单的批次单号
32+
* body商户系统内部的商户转账单号,在商户系统内部唯一。兼容旧字段out_batch_no
3333
* 示例值:plfk2020042013
3434
* </pre>
3535
*/
36-
@SerializedName("out_batch_no")
36+
@SerializedName(value = "out_bill_no", alternate = {"out_batch_no"})
3737
private String outBatchNo;
3838

3939
/**
@@ -53,17 +53,18 @@ public class ElectronicBillResult implements Serializable {
5353
/**
5454
* <pre>
5555
* 字段名:电子回单状态
56-
* 变量名:signature_status
56+
* 变量名:state
5757
* 是否必填:否
5858
* 类型:string[1,10]
5959
* 描述:
6060
* 枚举值:
6161
* ACCEPTED:已受理,电子签章已受理成功
6262
* FINISHED:已完成。电子签章已处理完成
63+
* 兼容旧字段signature_status
6364
* 示例值:ACCEPTED
6465
* </pre>
6566
*/
66-
@SerializedName("signature_status")
67+
@SerializedName(value = "state", alternate = {"signature_status"})
6768
private String signatureStatus;
6869

6970
/**

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,8 @@ public interface MerchantTransferService {
9191
* 转账电子回单申请受理API
9292
* <p>
9393
* 适用对象:直连商户
94-
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter4_3_7.shtml
95-
* 请求URL:https://api.mch.weixin.qq.com/v3/transfer/bill-receipt
94+
* 文档详见: https://pay.weixin.qq.com/doc/v3/merchant/4012716452
95+
* 请求URL:https://api.mch.weixin.qq.com/v3/fund-app/mch-transfer/elecsign/out-bill-no
9696
* 请求方式:POST
9797
* 接口限频: 单个商户 20QPS,如果超过频率限制,会报错FREQUENCY_LIMITED,请降低频率请求。
9898
*
@@ -106,15 +106,15 @@ public interface MerchantTransferService {
106106
* 查询转账电子回单API
107107
* <p>
108108
* 适用对象:直连商户
109-
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter4_3_8.shtml
110-
* 请求URL:https://api.mch.weixin.qq.com/v3/transfer/bill-receipt/{out_batch_no}
109+
* 文档详见: https://pay.weixin.qq.com/doc/v3/merchant/4012716436
110+
* 请求URL:https://api.mch.weixin.qq.com/v3/fund-app/mch-transfer/elecsign/out-bill-no/{out_bill_no}
111111
* 请求方式:GET
112112
*
113-
* @param outBatchNo the out batch no
113+
* @param outBillNo 商户转账单号
114114
* @return electronic bill result
115115
* @throws WxPayException the wx pay exception
116116
*/
117-
ElectronicBillResult queryElectronicBill(String outBatchNo) throws WxPayException;
117+
ElectronicBillResult queryElectronicBill(String outBillNo) throws WxPayException;
118118

119119
/**
120120
* 转账明细电子回单受理API

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,11 @@ public interface PartnerTransferService {
9999
* 转账电子回单申请受理API
100100
* 接口说明
101101
* 适用对象:直连商户 服务商
102-
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter4_1.shtml
103-
* 请求URL:https://api.mch.weixin.qq.com/v3/transfer/bill-receipt
102+
* 文档详见: https://pay.weixin.qq.com/doc/v3/merchant/4012716452
103+
* 请求URL:https://api.mch.weixin.qq.com/v3/fund-app/mch-transfer/elecsign/out-bill-no
104104
* 请求方式:POST
105105
*
106-
* @param request 商家批次单号
106+
* @param request 商户转账单号
107107
* @return 返回数据 fund balance result
108108
* @throws WxPayException the wx pay exception
109109
*/
@@ -114,15 +114,15 @@ public interface PartnerTransferService {
114114
* 查询转账电子回单API
115115
* 接口说明
116116
* 适用对象:直连商户 服务商
117-
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter4_2.shtml
118-
* 请求URL:https://api.mch.weixin.qq.com/v3/transfer/bill-receipt/{out_batch_no}
117+
* 文档详见: https://pay.weixin.qq.com/doc/v3/merchant/4012716436
118+
* 请求URL:https://api.mch.weixin.qq.com/v3/fund-app/mch-transfer/elecsign/out-bill-no/{out_bill_no}
119119
* 请求方式:GET
120120
*
121-
* @param outBatchNo 商家批次单号
121+
* @param outBillNo 商户转账单号
122122
* @return 返回数据 fund balance result
123123
* @throws WxPayException the wx pay exception
124124
*/
125-
BillReceiptResult queryBillReceipt(String outBatchNo) throws WxPayException;
125+
BillReceiptResult queryBillReceipt(String outBillNo) throws WxPayException;
126126

127127
/**
128128
* 转账明细电子回单受理API

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,14 +92,15 @@ public DetailsQueryResult queryMerchantDetails(MerchantDetailsQueryRequest reque
9292

9393
@Override
9494
public ElectronicBillResult applyElectronicBill(ElectronicBillApplyRequest request) throws WxPayException {
95-
String url = String.format("%s/v3/transfer/bill-receipt", this.wxPayService.getPayBaseUrl());
95+
String url = String.format("%s/v3/fund-app/mch-transfer/elecsign/out-bill-no", this.wxPayService.getPayBaseUrl());
9696
String response = wxPayService.postV3(url, GSON.toJson(request));
9797
return GSON.fromJson(response, ElectronicBillResult.class);
9898
}
9999

100100
@Override
101-
public ElectronicBillResult queryElectronicBill(String outBatchNo) throws WxPayException {
102-
String url = String.format("%s/v3/transfer/bill-receipt/%s", this.wxPayService.getPayBaseUrl(), outBatchNo);
101+
public ElectronicBillResult queryElectronicBill(String outBillNo) throws WxPayException {
102+
String url = String.format("%s/v3/fund-app/mch-transfer/elecsign/out-bill-no/%s",
103+
this.wxPayService.getPayBaseUrl(), outBillNo);
103104
String response = wxPayService.getV3(url);
104105
return GSON.fromJson(response, ElectronicBillResult.class);
105106
}

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

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -186,17 +186,17 @@ public BatchDetailsResult queryBatchDetailByMch(String outBatchNo, String outDet
186186
* 转账电子回单申请受理API
187187
* 接口说明
188188
* 适用对象:直连商户 服务商
189-
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter4_1.shtml
190-
* 请求URL:https://api.mch.weixin.qq.com/v3/transfer/bill-receipt
189+
* 文档详见: https://pay.weixin.qq.com/doc/v3/merchant/4012716452
190+
* 请求URL:https://api.mch.weixin.qq.com/v3/fund-app/mch-transfer/elecsign/out-bill-no
191191
* 请求方式:POST
192192
*
193-
* @param request 商家批次单号
193+
* @param request 商户转账单号
194194
* @return 返回数据 fund balance result
195195
* @throws WxPayException the wx pay exception
196196
*/
197197
@Override
198198
public BillReceiptResult receiptBill(ReceiptBillRequest request) throws WxPayException {
199-
String url = String.format("%s/v3/transfer/bill-receipt", this.payService.getPayBaseUrl());
199+
String url = String.format("%s/v3/fund-app/mch-transfer/elecsign/out-bill-no", this.payService.getPayBaseUrl());
200200
String response = this.payService.postV3(url, GSON.toJson(request));
201201
return GSON.fromJson(response, BillReceiptResult.class);
202202
}
@@ -206,17 +206,18 @@ public BillReceiptResult receiptBill(ReceiptBillRequest request) throws WxPayExc
206206
* 查询转账电子回单API
207207
* 接口说明
208208
* 适用对象:直连商户 服务商
209-
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter4_2.shtml
210-
* 请求URL:https://api.mch.weixin.qq.com/v3/transfer/bill-receipt/{out_batch_no}
209+
* 文档详见: https://pay.weixin.qq.com/doc/v3/merchant/4012716436
210+
* 请求URL:https://api.mch.weixin.qq.com/v3/fund-app/mch-transfer/elecsign/out-bill-no/{out_bill_no}
211211
* 请求方式:GET
212212
*
213-
* @param outBatchNo 商家批次单号
213+
* @param outBillNo 商户转账单号
214214
* @return 返回数据 fund balance result
215215
* @throws WxPayException the wx pay exception
216216
*/
217217
@Override
218-
public BillReceiptResult queryBillReceipt(String outBatchNo) throws WxPayException {
219-
String url = String.format("%s/v3/transfer/bill-receipt/%s", this.payService.getPayBaseUrl(), outBatchNo);
218+
public BillReceiptResult queryBillReceipt(String outBillNo) throws WxPayException {
219+
String url = String.format("%s/v3/fund-app/mch-transfer/elecsign/out-bill-no/%s",
220+
this.payService.getPayBaseUrl(), outBillNo);
220221
String response = this.payService.getV3(url);
221222
return GSON.fromJson(response, BillReceiptResult.class);
222223
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package com.github.binarywang.wxpay.service.impl;
2+
3+
import com.github.binarywang.wxpay.bean.marketing.transfer.BillReceiptResult;
4+
import com.github.binarywang.wxpay.bean.marketing.transfer.ReceiptBillRequest;
5+
import com.github.binarywang.wxpay.bean.merchanttransfer.ElectronicBillApplyRequest;
6+
import com.github.binarywang.wxpay.bean.merchanttransfer.ElectronicBillResult;
7+
import com.github.binarywang.wxpay.exception.WxPayException;
8+
import com.github.binarywang.wxpay.service.WxPayService;
9+
import com.google.gson.Gson;
10+
import org.testng.Assert;
11+
import org.testng.annotations.Test;
12+
13+
import java.lang.reflect.InvocationHandler;
14+
import java.lang.reflect.Method;
15+
import java.lang.reflect.Proxy;
16+
17+
@Test
18+
public class TransferReceiptApiCompatibilityTest {
19+
20+
private static final String BASE_URL = "https://api.mch.weixin.qq.com";
21+
22+
/**
23+
* 验证直连商户电子回单接口已切换到新版fund-app路径。
24+
*/
25+
public void shouldUseNewMerchantTransferElecsignApiPath() throws WxPayException {
26+
RequestCaptureHandler handler = new RequestCaptureHandler();
27+
WxPayService wxPayService = handler.createWxPayService();
28+
MerchantTransferServiceImpl merchantTransferService = new MerchantTransferServiceImpl(wxPayService);
29+
30+
merchantTransferService.applyElectronicBill(new ElectronicBillApplyRequest().setOutBatchNo("plfk2020042013"));
31+
Assert.assertEquals(handler.lastPostUrl, BASE_URL + "/v3/fund-app/mch-transfer/elecsign/out-bill-no");
32+
Assert.assertTrue(handler.lastPostBody.contains("\"out_bill_no\""));
33+
Assert.assertFalse(handler.lastPostBody.contains("\"out_batch_no\""));
34+
35+
merchantTransferService.queryElectronicBill("plfk2020042013");
36+
Assert.assertEquals(handler.lastGetUrl, BASE_URL + "/v3/fund-app/mch-transfer/elecsign/out-bill-no/plfk2020042013");
37+
}
38+
39+
/**
40+
* 验证服务商电子回单接口已切换到新版fund-app路径。
41+
*/
42+
public void shouldUseNewPartnerTransferElecsignApiPath() throws WxPayException {
43+
RequestCaptureHandler handler = new RequestCaptureHandler();
44+
WxPayService wxPayService = handler.createWxPayService();
45+
PartnerTransferServiceImpl partnerTransferService = new PartnerTransferServiceImpl(wxPayService);
46+
47+
ReceiptBillRequest request = new ReceiptBillRequest();
48+
request.setOutBatchNo("plfk2020042013");
49+
partnerTransferService.receiptBill(request);
50+
Assert.assertEquals(handler.lastPostUrl, BASE_URL + "/v3/fund-app/mch-transfer/elecsign/out-bill-no");
51+
Assert.assertTrue(handler.lastPostBody.contains("\"out_bill_no\""));
52+
Assert.assertFalse(handler.lastPostBody.contains("\"out_batch_no\""));
53+
54+
partnerTransferService.queryBillReceipt("plfk2020042013");
55+
Assert.assertEquals(handler.lastGetUrl, BASE_URL + "/v3/fund-app/mch-transfer/elecsign/out-bill-no/plfk2020042013");
56+
}
57+
58+
/**
59+
* 验证新版字段名能够正确反序列化到现有结果对象。
60+
*/
61+
public void shouldDeserializeNewResponseFieldNames() {
62+
Gson gson = new Gson();
63+
BillReceiptResult billReceiptResult =
64+
gson.fromJson("{\"out_bill_no\":\"plfk2020042013\",\"state\":\"FINISHED\"}", BillReceiptResult.class);
65+
Assert.assertEquals(billReceiptResult.getOutBatchNo(), "plfk2020042013");
66+
Assert.assertEquals(billReceiptResult.getSignatureStatus(), "FINISHED");
67+
68+
ElectronicBillResult electronicBillResult =
69+
gson.fromJson("{\"out_bill_no\":\"plfk2020042013\",\"state\":\"FINISHED\"}", ElectronicBillResult.class);
70+
Assert.assertEquals(electronicBillResult.getOutBatchNo(), "plfk2020042013");
71+
Assert.assertEquals(electronicBillResult.getSignatureStatus(), "FINISHED");
72+
}
73+
74+
/**
75+
* 通过动态代理拦截WxPayService请求并记录URL/请求体,便于断言接口路径和参数。
76+
*/
77+
private static class RequestCaptureHandler implements InvocationHandler {
78+
private String lastPostUrl;
79+
private String lastPostBody;
80+
private String lastGetUrl;
81+
82+
private WxPayService createWxPayService() {
83+
return (WxPayService) Proxy.newProxyInstance(
84+
WxPayService.class.getClassLoader(),
85+
new Class<?>[]{WxPayService.class},
86+
this
87+
);
88+
}
89+
90+
@Override
91+
public Object invoke(Object proxy, Method method, Object[] args) {
92+
if ("getPayBaseUrl".equals(method.getName())) {
93+
return BASE_URL;
94+
}
95+
if ("postV3".equals(method.getName())) {
96+
this.lastPostUrl = (String) args[0];
97+
this.lastPostBody = (String) args[1];
98+
return "{}";
99+
}
100+
if ("getV3".equals(method.getName())) {
101+
this.lastGetUrl = (String) args[0];
102+
return "{}";
103+
}
104+
if ("toString".equals(method.getName())) {
105+
return "MockWxPayService";
106+
}
107+
Class<?> returnType = method.getReturnType();
108+
if (boolean.class.equals(returnType)) {
109+
return false;
110+
}
111+
if (int.class.equals(returnType)) {
112+
return 0;
113+
}
114+
if (long.class.equals(returnType)) {
115+
return 0L;
116+
}
117+
if (double.class.equals(returnType)) {
118+
return 0D;
119+
}
120+
if (float.class.equals(returnType)) {
121+
return 0F;
122+
}
123+
if (short.class.equals(returnType)) {
124+
return (short) 0;
125+
}
126+
if (byte.class.equals(returnType)) {
127+
return (byte) 0;
128+
}
129+
if (char.class.equals(returnType)) {
130+
return (char) 0;
131+
}
132+
return null;
133+
}
134+
}
135+
}

0 commit comments

Comments
 (0)