Skip to content

Commit a24a442

Browse files
authored
添加用户授权免确认相关接口的单元测试与集成测试桩
1 parent 6872328 commit a24a442

2 files changed

Lines changed: 340 additions & 0 deletions

File tree

Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
package com.github.binarywang.wxpay.service.impl;
2+
3+
import com.github.binarywang.wxpay.bean.transfer.ReservationTransferBatchRequest;
4+
import com.github.binarywang.wxpay.bean.transfer.ReservationTransferBatchGetResult;
5+
import com.github.binarywang.wxpay.bean.transfer.ReservationTransferBatchResult;
6+
import com.github.binarywang.wxpay.bean.transfer.UserAuthorizationStatusResult;
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+
import java.util.Collections;
17+
18+
/**
19+
* 用户授权免确认相关接口 API 路径兼容性测试
20+
* <p>
21+
* 通过动态代理拦截 WxPayService 调用,验证各接口的请求路径和参数是否符合官方文档要求,
22+
* 无需真实微信 API 凭据即可运行。
23+
* </p>
24+
*
25+
* @author GitHub Copilot
26+
*/
27+
@Test
28+
public class TransferAuthorizationApiCompatibilityTest {
29+
30+
private static final String BASE_URL = "https://api.mch.weixin.qq.com";
31+
32+
/**
33+
* 验证查询用户授权状态接口使用正确的 API 路径和查询参数。
34+
*/
35+
public void shouldGetUserAuthorizationStatusWithCorrectPath() throws WxPayException {
36+
RequestCaptureHandler handler = new RequestCaptureHandler();
37+
WxPayService wxPayService = handler.createWxPayService();
38+
TransferServiceImpl transferService = new TransferServiceImpl(wxPayService);
39+
40+
transferService.getUserAuthorizationStatus("oX_7Jzr9gSZz4X_Xc9-_7HGf8XzI", "1005");
41+
42+
Assert.assertEquals(handler.lastGetUrl,
43+
BASE_URL + "/v3/fund-app/mch-transfer/authorization/openid/oX_7Jzr9gSZz4X_Xc9-_7HGf8XzI?transfer_scene_id=1005");
44+
}
45+
46+
/**
47+
* 验证查询用户授权状态接口返回结果可正确反序列化。
48+
*/
49+
public void shouldDeserializeUserAuthorizationStatusResult() {
50+
Gson gson = new Gson();
51+
String json = "{\"appid\":\"wxf636efh5xxxxx\",\"mch_id\":\"1900000109\","
52+
+ "\"openid\":\"oX_7Jzr9gSZz4X_Xc9-_7HGf8XzI\","
53+
+ "\"authorization_state\":\"AUTHORIZED\","
54+
+ "\"authorize_time\":\"2024-01-01T10:00:00+08:00\","
55+
+ "\"deauthorize_time\":null}";
56+
UserAuthorizationStatusResult result = gson.fromJson(json, UserAuthorizationStatusResult.class);
57+
58+
Assert.assertEquals(result.getAppid(), "wxf636efh5xxxxx");
59+
Assert.assertEquals(result.getMchId(), "1900000109");
60+
Assert.assertEquals(result.getOpenid(), "oX_7Jzr9gSZz4X_Xc9-_7HGf8XzI");
61+
Assert.assertEquals(result.getAuthorizationState(), "AUTHORIZED");
62+
Assert.assertEquals(result.getAuthorizeTime(), "2024-01-01T10:00:00+08:00");
63+
}
64+
65+
/**
66+
* 验证批量预约商家转账接口使用正确的 API 路径。
67+
*/
68+
public void shouldReservationTransferBatchUseCorrectPath() throws WxPayException {
69+
RequestCaptureHandler handler = new RequestCaptureHandler();
70+
WxPayService wxPayService = handler.createWxPayService();
71+
TransferServiceImpl transferService = new TransferServiceImpl(wxPayService);
72+
73+
ReservationTransferBatchRequest request = ReservationTransferBatchRequest.newBuilder()
74+
.appid("wxf636efh5xxxxx")
75+
.outBatchNo("BATCH20240101001")
76+
.transferSceneId("1005")
77+
.batchRemark("测试批量预约转账")
78+
.totalAmount(1000)
79+
.totalNum(1)
80+
.transferDetailList(Collections.singletonList(
81+
ReservationTransferBatchRequest.TransferDetail.newBuilder()
82+
.outDetailNo("detail001")
83+
.transferAmount(1000)
84+
.transferRemark("测试转账")
85+
.openid("oX_7Jzr9gSZz4X_Xc9-_7HGf8XzI")
86+
// 不设置 userName,避免触发证书加密流程
87+
.build()
88+
))
89+
.notifyUrl("https://example.com/notify")
90+
.build();
91+
92+
transferService.reservationTransferBatch(request);
93+
94+
Assert.assertEquals(handler.lastPostUrl,
95+
BASE_URL + "/v3/fund-app/mch-transfer/reservation/transfer-batches");
96+
Assert.assertTrue(handler.lastPostBody.contains("\"out_batch_no\""));
97+
Assert.assertTrue(handler.lastPostBody.contains("BATCH20240101001"));
98+
Assert.assertTrue(handler.lastPostBody.contains("\"transfer_detail_list\""));
99+
}
100+
101+
/**
102+
* 验证批量预约商家转账响应结果可正确反序列化。
103+
*/
104+
public void shouldDeserializeReservationTransferBatchResult() {
105+
Gson gson = new Gson();
106+
String json = "{\"out_batch_no\":\"BATCH20240101001\","
107+
+ "\"reservation_batch_no\":\"1030000071100999991182020050700019480001\","
108+
+ "\"create_time\":\"2024-01-01T10:00:00+08:00\","
109+
+ "\"batch_state\":\"ACCEPTED\"}";
110+
ReservationTransferBatchResult result = gson.fromJson(json, ReservationTransferBatchResult.class);
111+
112+
Assert.assertEquals(result.getOutBatchNo(), "BATCH20240101001");
113+
Assert.assertEquals(result.getReservationBatchNo(), "1030000071100999991182020050700019480001");
114+
Assert.assertEquals(result.getBatchState(), "ACCEPTED");
115+
Assert.assertEquals(result.getCreateTime(), "2024-01-01T10:00:00+08:00");
116+
}
117+
118+
/**
119+
* 验证商户预约批次单号查询接口使用正确的 API 路径和查询参数。
120+
*/
121+
public void shouldGetReservationBatchByOutBatchNoWithCorrectPath() throws WxPayException {
122+
RequestCaptureHandler handler = new RequestCaptureHandler();
123+
WxPayService wxPayService = handler.createWxPayService();
124+
TransferServiceImpl transferService = new TransferServiceImpl(wxPayService);
125+
126+
transferService.getReservationTransferBatchByOutBatchNo(
127+
"BATCH20240101001", true, 0, 20, "SUCCESS");
128+
129+
Assert.assertTrue(handler.lastGetUrl.contains(
130+
"/v3/fund-app/mch-transfer/reservation/transfer-batches/out-batch-no/BATCH20240101001"));
131+
Assert.assertTrue(handler.lastGetUrl.contains("need_query_detail=true"));
132+
Assert.assertTrue(handler.lastGetUrl.contains("offset=0"));
133+
Assert.assertTrue(handler.lastGetUrl.contains("limit=20"));
134+
Assert.assertTrue(handler.lastGetUrl.contains("detail_state=SUCCESS"));
135+
}
136+
137+
/**
138+
* 验证不携带可选参数时商户预约批次单号查询接口仍能正确构造 URL。
139+
*/
140+
public void shouldGetReservationBatchByOutBatchNoWithoutOptionalParams() throws WxPayException {
141+
RequestCaptureHandler handler = new RequestCaptureHandler();
142+
WxPayService wxPayService = handler.createWxPayService();
143+
TransferServiceImpl transferService = new TransferServiceImpl(wxPayService);
144+
145+
transferService.getReservationTransferBatchByOutBatchNo(
146+
"BATCH20240101001", false, null, null, null);
147+
148+
Assert.assertTrue(handler.lastGetUrl.contains(
149+
"/v3/fund-app/mch-transfer/reservation/transfer-batches/out-batch-no/BATCH20240101001"));
150+
Assert.assertTrue(handler.lastGetUrl.contains("need_query_detail=false"));
151+
Assert.assertFalse(handler.lastGetUrl.contains("offset="));
152+
Assert.assertFalse(handler.lastGetUrl.contains("limit="));
153+
Assert.assertFalse(handler.lastGetUrl.contains("detail_state="));
154+
}
155+
156+
/**
157+
* 验证微信预约批次单号查询接口使用正确的 API 路径和查询参数。
158+
*/
159+
public void shouldGetReservationBatchByReservationBatchNoWithCorrectPath() throws WxPayException {
160+
RequestCaptureHandler handler = new RequestCaptureHandler();
161+
WxPayService wxPayService = handler.createWxPayService();
162+
TransferServiceImpl transferService = new TransferServiceImpl(wxPayService);
163+
164+
transferService.getReservationTransferBatchByReservationBatchNo(
165+
"1030000071100999991182020050700019480001", true, 0, 20, "PROCESSING");
166+
167+
Assert.assertTrue(handler.lastGetUrl.contains(
168+
"/v3/fund-app/mch-transfer/reservation/transfer-batches/reservation-batch-no/"
169+
+ "1030000071100999991182020050700019480001"));
170+
Assert.assertTrue(handler.lastGetUrl.contains("need_query_detail=true"));
171+
Assert.assertTrue(handler.lastGetUrl.contains("offset=0"));
172+
Assert.assertTrue(handler.lastGetUrl.contains("limit=20"));
173+
Assert.assertTrue(handler.lastGetUrl.contains("detail_state=PROCESSING"));
174+
}
175+
176+
/**
177+
* 验证批次查询结果可正确反序列化(包括明细列表)。
178+
*/
179+
public void shouldDeserializeReservationBatchGetResult() {
180+
Gson gson = new Gson();
181+
String json = "{\"mch_id\":\"1900000109\","
182+
+ "\"out_batch_no\":\"BATCH20240101001\","
183+
+ "\"reservation_batch_no\":\"1030000071100999991182020050700019480001\","
184+
+ "\"appid\":\"wxf636efh5xxxxx\","
185+
+ "\"batch_state\":\"FINISHED\","
186+
+ "\"total_amount\":1000,"
187+
+ "\"total_num\":1,"
188+
+ "\"success_amount\":1000,"
189+
+ "\"success_num\":1,"
190+
+ "\"fail_amount\":0,"
191+
+ "\"fail_num\":0,"
192+
+ "\"transfer_detail_list\":["
193+
+ "{\"out_detail_no\":\"detail001\","
194+
+ "\"transfer_bill_no\":\"bill001\","
195+
+ "\"detail_state\":\"SUCCESS\"}"
196+
+ "]}";
197+
ReservationTransferBatchGetResult result = gson.fromJson(json, ReservationTransferBatchGetResult.class);
198+
199+
Assert.assertEquals(result.getMchId(), "1900000109");
200+
Assert.assertEquals(result.getBatchState(), "FINISHED");
201+
Assert.assertEquals(result.getTotalAmount(), Integer.valueOf(1000));
202+
Assert.assertEquals(result.getSuccessNum(), Integer.valueOf(1));
203+
Assert.assertNotNull(result.getTransferDetailList());
204+
Assert.assertEquals(result.getTransferDetailList().size(), 1);
205+
Assert.assertEquals(result.getTransferDetailList().get(0).getDetailState(), "SUCCESS");
206+
}
207+
208+
/**
209+
* 验证关闭预约商家转账批次接口使用正确的 API 路径。
210+
*/
211+
public void shouldCloseReservationTransferBatchWithCorrectPath() throws WxPayException {
212+
RequestCaptureHandler handler = new RequestCaptureHandler();
213+
WxPayService wxPayService = handler.createWxPayService();
214+
TransferServiceImpl transferService = new TransferServiceImpl(wxPayService);
215+
216+
transferService.closeReservationTransferBatch("BATCH20240101001");
217+
218+
Assert.assertEquals(handler.lastPostUrl,
219+
BASE_URL + "/v3/fund-app/mch-transfer/reservation/transfer-batches/out-batch-no/BATCH20240101001/close");
220+
Assert.assertEquals(handler.lastPostBody, "");
221+
}
222+
223+
/**
224+
* 通过动态代理拦截 WxPayService 请求并记录 URL/请求体,便于断言接口路径和参数。
225+
*/
226+
private static class RequestCaptureHandler implements InvocationHandler {
227+
private String lastPostUrl;
228+
private String lastPostBody;
229+
private String lastGetUrl;
230+
231+
private WxPayService createWxPayService() {
232+
return (WxPayService) Proxy.newProxyInstance(
233+
WxPayService.class.getClassLoader(),
234+
new Class<?>[]{WxPayService.class},
235+
this
236+
);
237+
}
238+
239+
@Override
240+
public Object invoke(Object proxy, Method method, Object[] args) {
241+
if ("getPayBaseUrl".equals(method.getName())) {
242+
return BASE_URL;
243+
}
244+
if ("postV3".equals(method.getName())) {
245+
this.lastPostUrl = (String) args[0];
246+
this.lastPostBody = (String) args[1];
247+
return "{}";
248+
}
249+
if ("postV3WithWechatpaySerial".equals(method.getName())) {
250+
this.lastPostUrl = (String) args[0];
251+
this.lastPostBody = (String) args[1];
252+
return "{}";
253+
}
254+
if ("getV3".equals(method.getName())) {
255+
this.lastGetUrl = (String) args[0];
256+
return "{}";
257+
}
258+
if ("toString".equals(method.getName())) {
259+
return "MockWxPayService";
260+
}
261+
Class<?> returnType = method.getReturnType();
262+
if (boolean.class.equals(returnType)) {
263+
return false;
264+
}
265+
if (int.class.equals(returnType)) {
266+
return 0;
267+
}
268+
if (long.class.equals(returnType)) {
269+
return 0L;
270+
}
271+
if (double.class.equals(returnType)) {
272+
return 0D;
273+
}
274+
if (float.class.equals(returnType)) {
275+
return 0F;
276+
}
277+
if (short.class.equals(returnType)) {
278+
return (short) 0;
279+
}
280+
if (byte.class.equals(returnType)) {
281+
return (byte) 0;
282+
}
283+
if (char.class.equals(returnType)) {
284+
return (char) 0;
285+
}
286+
return null;
287+
}
288+
}
289+
}

weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/impl/TransferServiceImplTest.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.github.binarywang.wxpay.service.impl;
22

33
import com.github.binarywang.wxpay.bean.transfer.QueryTransferBatchesRequest;
4+
import com.github.binarywang.wxpay.bean.transfer.ReservationTransferBatchRequest;
45
import com.github.binarywang.wxpay.bean.transfer.TransferBatchesRequest;
56
import com.github.binarywang.wxpay.bean.transfer.TransferBillsRequest;
67
import com.github.binarywang.wxpay.exception.WxPayException;
@@ -12,6 +13,7 @@
1213
import org.testng.annotations.Test;
1314

1415
import java.util.ArrayList;
16+
import java.util.Collections;
1517
import java.util.List;
1618

1719
/**
@@ -102,4 +104,53 @@ public void testGetBillsByOutBillNo() throws WxPayException {
102104
public void testGetBillsByTransferBillNo() throws WxPayException {
103105
log.info("微信单号查询转账单:{}", this.payService.getTransferService().getBillsByTransferBillNo("123456"));
104106
}
107+
108+
@Test
109+
public void testGetUserAuthorizationStatus() throws WxPayException {
110+
log.info("查询用户授权状态:{}",
111+
this.payService.getTransferService().getUserAuthorizationStatus("oX_7Jzr9gSZz4X_Xc9-_7HGf8XzI", "1005"));
112+
}
113+
114+
@Test
115+
public void testReservationTransferBatch() throws WxPayException {
116+
ReservationTransferBatchRequest request = ReservationTransferBatchRequest.newBuilder()
117+
.appid("wxf636efh5xxxxx")
118+
.outBatchNo("BATCH20240101001")
119+
.transferSceneId("1005")
120+
.batchRemark("测试批量预约转账")
121+
.totalAmount(1000)
122+
.totalNum(1)
123+
.transferDetailList(Collections.singletonList(
124+
ReservationTransferBatchRequest.TransferDetail.newBuilder()
125+
.outDetailNo("detail001")
126+
.transferAmount(1000)
127+
.transferRemark("测试转账备注")
128+
.openid("oX_7Jzr9gSZz4X_Xc9-_7HGf8XzI")
129+
.build()
130+
))
131+
.notifyUrl("https://example.com/notify")
132+
.build();
133+
log.info("批量预约商家转账:{}", this.payService.getTransferService().reservationTransferBatch(request));
134+
}
135+
136+
@Test
137+
public void testGetReservationTransferBatchByOutBatchNo() throws WxPayException {
138+
log.info("商户预约批次单号查询批次单:{}",
139+
this.payService.getTransferService()
140+
.getReservationTransferBatchByOutBatchNo("BATCH20240101001", true, 0, 20, "SUCCESS"));
141+
}
142+
143+
@Test
144+
public void testGetReservationTransferBatchByReservationBatchNo() throws WxPayException {
145+
log.info("微信预约批次单号查询批次单:{}",
146+
this.payService.getTransferService()
147+
.getReservationTransferBatchByReservationBatchNo(
148+
"1030000071100999991182020050700019480001", false, null, null, null));
149+
}
150+
151+
@Test
152+
public void testCloseReservationTransferBatch() throws WxPayException {
153+
this.payService.getTransferService().closeReservationTransferBatch("BATCH20240101001");
154+
log.info("关闭预约商家转账批次成功");
155+
}
105156
}

0 commit comments

Comments
 (0)