🎨 #3994 【微信支付】对齐新版「商家转账-电子回单」接口规范:将直连商户/服务商的电子回单申请与查询接口从旧版 v3/transfer/bill-receipt 切换到新版 v3/fund-app/mch-transfer/elecsign/out-bill-no/
This commit is contained in:
@@ -29,16 +29,16 @@ public class BillReceiptResult implements Serializable {
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:商家批次单号
|
||||
* 变量名:out_batch_no
|
||||
* 字段名:商户转账单号
|
||||
* 变量名:out_bill_no
|
||||
* 是否必填:是
|
||||
* 类型:string[5,32]
|
||||
* 描述:
|
||||
* 商户系统内部的商家批次单号,在商户系统内部唯一。需要电子回单的批次单号
|
||||
* 商户系统内部的商户转账单号,在商户系统内部唯一。兼容旧字段out_batch_no
|
||||
* 示例值:plfk2020042013
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "out_batch_no")
|
||||
@SerializedName(value = "out_bill_no", alternate = {"out_batch_no"})
|
||||
private String outBatchNo;
|
||||
|
||||
/**
|
||||
@@ -58,17 +58,18 @@ public class BillReceiptResult implements Serializable {
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:电子回单状态
|
||||
* 变量名:signature_status
|
||||
* 变量名:state
|
||||
* 是否必填:否
|
||||
* 类型:string[1,10]
|
||||
* 描述:
|
||||
* 枚举值:
|
||||
* ACCEPTED:已受理,电子签章已受理成功
|
||||
* FINISHED:已完成。电子签章已处理完成
|
||||
* 兼容旧字段signature_status
|
||||
* 示例值:ACCEPTED
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "signature_status")
|
||||
@SerializedName(value = "state", alternate = {"signature_status"})
|
||||
private String signatureStatus;
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,7 +9,7 @@ import java.io.Serializable;
|
||||
/**
|
||||
* 转账电子回单申请受理API
|
||||
* <pre>
|
||||
* 文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter4_1.shtml
|
||||
* 文档地址:https://pay.weixin.qq.com/doc/v3/merchant/4012716452
|
||||
* </pre>
|
||||
*
|
||||
* @author xiaoqiang
|
||||
@@ -21,15 +21,15 @@ public class ReceiptBillRequest implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:商家批次单号
|
||||
* 变量名:out_batch_no
|
||||
* 字段名:商户转账单号
|
||||
* 变量名:out_bill_no
|
||||
* 是否必填:是
|
||||
* 类型:string[5, 32]
|
||||
* 描述:
|
||||
* body商户系统内部的商家批次单号,在商户系统内部唯一。需要电子回单的批次单号
|
||||
* body商户系统内部的商户转账单号,在商户系统内部唯一。兼容旧字段out_batch_no
|
||||
* 示例值:plfk2020042013
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName(value = "out_batch_no")
|
||||
@SerializedName(value = "out_bill_no", alternate = {"out_batch_no"})
|
||||
private String outBatchNo;
|
||||
}
|
||||
|
||||
@@ -24,15 +24,15 @@ public class ElectronicBillApplyRequest implements Serializable {
|
||||
private static final long serialVersionUID = -2121536206019844928L;
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:商家批次单号
|
||||
* 变量名:out_batch_no
|
||||
* 字段名:商户转账单号
|
||||
* 变量名:out_bill_no
|
||||
* 是否必填:是
|
||||
* 类型:string[5,32]
|
||||
* 描述:
|
||||
* body商户系统内部的商家批次单号,在商户系统内部唯一。需要电子回单的批次单号
|
||||
* body商户系统内部的商户转账单号,在商户系统内部唯一。兼容旧字段out_batch_no
|
||||
* 示例值:plfk2020042013
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("out_batch_no")
|
||||
@SerializedName(value = "out_bill_no", alternate = {"out_batch_no"})
|
||||
private String outBatchNo;
|
||||
}
|
||||
|
||||
@@ -24,16 +24,16 @@ public class ElectronicBillResult implements Serializable {
|
||||
private static final long serialVersionUID = 7528245102572829190L;
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:商家批次单号
|
||||
* 变量名:out_batch_no
|
||||
* 字段名:商户转账单号
|
||||
* 变量名:out_bill_no
|
||||
* 是否必填:是
|
||||
* 类型:string[5,32]
|
||||
* 描述:
|
||||
* body商户系统内部的商家批次单号,在商户系统内部唯一。需要电子回单的批次单号
|
||||
* body商户系统内部的商户转账单号,在商户系统内部唯一。兼容旧字段out_batch_no
|
||||
* 示例值:plfk2020042013
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("out_batch_no")
|
||||
@SerializedName(value = "out_bill_no", alternate = {"out_batch_no"})
|
||||
private String outBatchNo;
|
||||
|
||||
/**
|
||||
@@ -53,17 +53,18 @@ public class ElectronicBillResult implements Serializable {
|
||||
/**
|
||||
* <pre>
|
||||
* 字段名:电子回单状态
|
||||
* 变量名:signature_status
|
||||
* 变量名:state
|
||||
* 是否必填:否
|
||||
* 类型:string[1,10]
|
||||
* 描述:
|
||||
* 枚举值:
|
||||
* ACCEPTED:已受理,电子签章已受理成功
|
||||
* FINISHED:已完成。电子签章已处理完成
|
||||
* 兼容旧字段signature_status
|
||||
* 示例值:ACCEPTED
|
||||
* </pre>
|
||||
*/
|
||||
@SerializedName("signature_status")
|
||||
@SerializedName(value = "state", alternate = {"signature_status"})
|
||||
private String signatureStatus;
|
||||
|
||||
/**
|
||||
|
||||
@@ -91,8 +91,8 @@ public interface MerchantTransferService {
|
||||
* 转账电子回单申请受理API
|
||||
* <p>
|
||||
* 适用对象:直连商户
|
||||
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter4_3_7.shtml
|
||||
* 请求URL:https://api.mch.weixin.qq.com/v3/transfer/bill-receipt
|
||||
* 文档详见: https://pay.weixin.qq.com/doc/v3/merchant/4012716452
|
||||
* 请求URL:https://api.mch.weixin.qq.com/v3/fund-app/mch-transfer/elecsign/out-bill-no
|
||||
* 请求方式:POST
|
||||
* 接口限频: 单个商户 20QPS,如果超过频率限制,会报错FREQUENCY_LIMITED,请降低频率请求。
|
||||
*
|
||||
@@ -106,15 +106,15 @@ public interface MerchantTransferService {
|
||||
* 查询转账电子回单API
|
||||
* <p>
|
||||
* 适用对象:直连商户
|
||||
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter4_3_8.shtml
|
||||
* 请求URL:https://api.mch.weixin.qq.com/v3/transfer/bill-receipt/{out_batch_no}
|
||||
* 文档详见: https://pay.weixin.qq.com/doc/v3/merchant/4012716436
|
||||
* 请求URL:https://api.mch.weixin.qq.com/v3/fund-app/mch-transfer/elecsign/out-bill-no/{out_bill_no}
|
||||
* 请求方式:GET
|
||||
*
|
||||
* @param outBatchNo the out batch no
|
||||
* @param outBillNo 商户转账单号
|
||||
* @return electronic bill result
|
||||
* @throws WxPayException the wx pay exception
|
||||
*/
|
||||
ElectronicBillResult queryElectronicBill(String outBatchNo) throws WxPayException;
|
||||
ElectronicBillResult queryElectronicBill(String outBillNo) throws WxPayException;
|
||||
|
||||
/**
|
||||
* 转账明细电子回单受理API
|
||||
|
||||
@@ -99,11 +99,11 @@ public interface PartnerTransferService {
|
||||
* 转账电子回单申请受理API
|
||||
* 接口说明
|
||||
* 适用对象:直连商户 服务商
|
||||
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter4_1.shtml
|
||||
* 请求URL:https://api.mch.weixin.qq.com/v3/transfer/bill-receipt
|
||||
* 文档详见: https://pay.weixin.qq.com/doc/v3/merchant/4012716452
|
||||
* 请求URL:https://api.mch.weixin.qq.com/v3/fund-app/mch-transfer/elecsign/out-bill-no
|
||||
* 请求方式:POST
|
||||
*
|
||||
* @param request 商家批次单号
|
||||
* @param request 商户转账单号
|
||||
* @return 返回数据 fund balance result
|
||||
* @throws WxPayException the wx pay exception
|
||||
*/
|
||||
@@ -114,15 +114,15 @@ public interface PartnerTransferService {
|
||||
* 查询转账电子回单API
|
||||
* 接口说明
|
||||
* 适用对象:直连商户 服务商
|
||||
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter4_2.shtml
|
||||
* 请求URL:https://api.mch.weixin.qq.com/v3/transfer/bill-receipt/{out_batch_no}
|
||||
* 文档详见: https://pay.weixin.qq.com/doc/v3/merchant/4012716436
|
||||
* 请求URL:https://api.mch.weixin.qq.com/v3/fund-app/mch-transfer/elecsign/out-bill-no/{out_bill_no}
|
||||
* 请求方式:GET
|
||||
*
|
||||
* @param outBatchNo 商家批次单号
|
||||
* @param outBillNo 商户转账单号
|
||||
* @return 返回数据 fund balance result
|
||||
* @throws WxPayException the wx pay exception
|
||||
*/
|
||||
BillReceiptResult queryBillReceipt(String outBatchNo) throws WxPayException;
|
||||
BillReceiptResult queryBillReceipt(String outBillNo) throws WxPayException;
|
||||
|
||||
/**
|
||||
* 转账明细电子回单受理API
|
||||
|
||||
@@ -92,14 +92,15 @@ public class MerchantTransferServiceImpl implements MerchantTransferService {
|
||||
|
||||
@Override
|
||||
public ElectronicBillResult applyElectronicBill(ElectronicBillApplyRequest request) throws WxPayException {
|
||||
String url = String.format("%s/v3/transfer/bill-receipt", this.wxPayService.getPayBaseUrl());
|
||||
String url = String.format("%s/v3/fund-app/mch-transfer/elecsign/out-bill-no", this.wxPayService.getPayBaseUrl());
|
||||
String response = wxPayService.postV3(url, GSON.toJson(request));
|
||||
return GSON.fromJson(response, ElectronicBillResult.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElectronicBillResult queryElectronicBill(String outBatchNo) throws WxPayException {
|
||||
String url = String.format("%s/v3/transfer/bill-receipt/%s", this.wxPayService.getPayBaseUrl(), outBatchNo);
|
||||
public ElectronicBillResult queryElectronicBill(String outBillNo) throws WxPayException {
|
||||
String url = String.format("%s/v3/fund-app/mch-transfer/elecsign/out-bill-no/%s",
|
||||
this.wxPayService.getPayBaseUrl(), outBillNo);
|
||||
String response = wxPayService.getV3(url);
|
||||
return GSON.fromJson(response, ElectronicBillResult.class);
|
||||
}
|
||||
|
||||
@@ -186,17 +186,17 @@ public class PartnerTransferServiceImpl implements PartnerTransferService {
|
||||
* 转账电子回单申请受理API
|
||||
* 接口说明
|
||||
* 适用对象:直连商户 服务商
|
||||
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter4_1.shtml
|
||||
* 请求URL:https://api.mch.weixin.qq.com/v3/transfer/bill-receipt
|
||||
* 文档详见: https://pay.weixin.qq.com/doc/v3/merchant/4012716452
|
||||
* 请求URL:https://api.mch.weixin.qq.com/v3/fund-app/mch-transfer/elecsign/out-bill-no
|
||||
* 请求方式:POST
|
||||
*
|
||||
* @param request 商家批次单号
|
||||
* @param request 商户转账单号
|
||||
* @return 返回数据 fund balance result
|
||||
* @throws WxPayException the wx pay exception
|
||||
*/
|
||||
@Override
|
||||
public BillReceiptResult receiptBill(ReceiptBillRequest request) throws WxPayException {
|
||||
String url = String.format("%s/v3/transfer/bill-receipt", this.payService.getPayBaseUrl());
|
||||
String url = String.format("%s/v3/fund-app/mch-transfer/elecsign/out-bill-no", this.payService.getPayBaseUrl());
|
||||
String response = this.payService.postV3(url, GSON.toJson(request));
|
||||
return GSON.fromJson(response, BillReceiptResult.class);
|
||||
}
|
||||
@@ -206,17 +206,18 @@ public class PartnerTransferServiceImpl implements PartnerTransferService {
|
||||
* 查询转账电子回单API
|
||||
* 接口说明
|
||||
* 适用对象:直连商户 服务商
|
||||
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter4_2.shtml
|
||||
* 请求URL:https://api.mch.weixin.qq.com/v3/transfer/bill-receipt/{out_batch_no}
|
||||
* 文档详见: https://pay.weixin.qq.com/doc/v3/merchant/4012716436
|
||||
* 请求URL:https://api.mch.weixin.qq.com/v3/fund-app/mch-transfer/elecsign/out-bill-no/{out_bill_no}
|
||||
* 请求方式:GET
|
||||
*
|
||||
* @param outBatchNo 商家批次单号
|
||||
* @param outBillNo 商户转账单号
|
||||
* @return 返回数据 fund balance result
|
||||
* @throws WxPayException the wx pay exception
|
||||
*/
|
||||
@Override
|
||||
public BillReceiptResult queryBillReceipt(String outBatchNo) throws WxPayException {
|
||||
String url = String.format("%s/v3/transfer/bill-receipt/%s", this.payService.getPayBaseUrl(), outBatchNo);
|
||||
public BillReceiptResult queryBillReceipt(String outBillNo) throws WxPayException {
|
||||
String url = String.format("%s/v3/fund-app/mch-transfer/elecsign/out-bill-no/%s",
|
||||
this.payService.getPayBaseUrl(), outBillNo);
|
||||
String response = this.payService.getV3(url);
|
||||
return GSON.fromJson(response, BillReceiptResult.class);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
package com.github.binarywang.wxpay.service.impl;
|
||||
|
||||
import com.github.binarywang.wxpay.bean.marketing.transfer.BillReceiptResult;
|
||||
import com.github.binarywang.wxpay.bean.marketing.transfer.ReceiptBillRequest;
|
||||
import com.github.binarywang.wxpay.bean.merchanttransfer.ElectronicBillApplyRequest;
|
||||
import com.github.binarywang.wxpay.bean.merchanttransfer.ElectronicBillResult;
|
||||
import com.github.binarywang.wxpay.exception.WxPayException;
|
||||
import com.github.binarywang.wxpay.service.WxPayService;
|
||||
import com.google.gson.Gson;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
|
||||
@Test
|
||||
public class TransferReceiptApiCompatibilityTest {
|
||||
|
||||
private static final String BASE_URL = "https://api.mch.weixin.qq.com";
|
||||
|
||||
/**
|
||||
* 验证直连商户电子回单接口已切换到新版fund-app路径。
|
||||
*/
|
||||
public void shouldUseNewMerchantTransferElecsignApiPath() throws WxPayException {
|
||||
RequestCaptureHandler handler = new RequestCaptureHandler();
|
||||
WxPayService wxPayService = handler.createWxPayService();
|
||||
MerchantTransferServiceImpl merchantTransferService = new MerchantTransferServiceImpl(wxPayService);
|
||||
|
||||
merchantTransferService.applyElectronicBill(new ElectronicBillApplyRequest().setOutBatchNo("plfk2020042013"));
|
||||
Assert.assertEquals(handler.lastPostUrl, BASE_URL + "/v3/fund-app/mch-transfer/elecsign/out-bill-no");
|
||||
Assert.assertTrue(handler.lastPostBody.contains("\"out_bill_no\""));
|
||||
Assert.assertFalse(handler.lastPostBody.contains("\"out_batch_no\""));
|
||||
|
||||
merchantTransferService.queryElectronicBill("plfk2020042013");
|
||||
Assert.assertEquals(handler.lastGetUrl, BASE_URL + "/v3/fund-app/mch-transfer/elecsign/out-bill-no/plfk2020042013");
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证服务商电子回单接口已切换到新版fund-app路径。
|
||||
*/
|
||||
public void shouldUseNewPartnerTransferElecsignApiPath() throws WxPayException {
|
||||
RequestCaptureHandler handler = new RequestCaptureHandler();
|
||||
WxPayService wxPayService = handler.createWxPayService();
|
||||
PartnerTransferServiceImpl partnerTransferService = new PartnerTransferServiceImpl(wxPayService);
|
||||
|
||||
ReceiptBillRequest request = new ReceiptBillRequest();
|
||||
request.setOutBatchNo("plfk2020042013");
|
||||
partnerTransferService.receiptBill(request);
|
||||
Assert.assertEquals(handler.lastPostUrl, BASE_URL + "/v3/fund-app/mch-transfer/elecsign/out-bill-no");
|
||||
Assert.assertTrue(handler.lastPostBody.contains("\"out_bill_no\""));
|
||||
Assert.assertFalse(handler.lastPostBody.contains("\"out_batch_no\""));
|
||||
|
||||
partnerTransferService.queryBillReceipt("plfk2020042013");
|
||||
Assert.assertEquals(handler.lastGetUrl, BASE_URL + "/v3/fund-app/mch-transfer/elecsign/out-bill-no/plfk2020042013");
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证新版字段名能够正确反序列化到现有结果对象。
|
||||
*/
|
||||
public void shouldDeserializeNewResponseFieldNames() {
|
||||
Gson gson = new Gson();
|
||||
BillReceiptResult billReceiptResult =
|
||||
gson.fromJson("{\"out_bill_no\":\"plfk2020042013\",\"state\":\"FINISHED\"}", BillReceiptResult.class);
|
||||
Assert.assertEquals(billReceiptResult.getOutBatchNo(), "plfk2020042013");
|
||||
Assert.assertEquals(billReceiptResult.getSignatureStatus(), "FINISHED");
|
||||
|
||||
ElectronicBillResult electronicBillResult =
|
||||
gson.fromJson("{\"out_bill_no\":\"plfk2020042013\",\"state\":\"FINISHED\"}", ElectronicBillResult.class);
|
||||
Assert.assertEquals(electronicBillResult.getOutBatchNo(), "plfk2020042013");
|
||||
Assert.assertEquals(electronicBillResult.getSignatureStatus(), "FINISHED");
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过动态代理拦截WxPayService请求并记录URL/请求体,便于断言接口路径和参数。
|
||||
*/
|
||||
private static class RequestCaptureHandler implements InvocationHandler {
|
||||
private String lastPostUrl;
|
||||
private String lastPostBody;
|
||||
private String lastGetUrl;
|
||||
|
||||
private WxPayService createWxPayService() {
|
||||
return (WxPayService) Proxy.newProxyInstance(
|
||||
WxPayService.class.getClassLoader(),
|
||||
new Class<?>[]{WxPayService.class},
|
||||
this
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) {
|
||||
if ("getPayBaseUrl".equals(method.getName())) {
|
||||
return BASE_URL;
|
||||
}
|
||||
if ("postV3".equals(method.getName())) {
|
||||
this.lastPostUrl = (String) args[0];
|
||||
this.lastPostBody = (String) args[1];
|
||||
return "{}";
|
||||
}
|
||||
if ("getV3".equals(method.getName())) {
|
||||
this.lastGetUrl = (String) args[0];
|
||||
return "{}";
|
||||
}
|
||||
if ("toString".equals(method.getName())) {
|
||||
return "MockWxPayService";
|
||||
}
|
||||
Class<?> returnType = method.getReturnType();
|
||||
if (boolean.class.equals(returnType)) {
|
||||
return false;
|
||||
}
|
||||
if (int.class.equals(returnType)) {
|
||||
return 0;
|
||||
}
|
||||
if (long.class.equals(returnType)) {
|
||||
return 0L;
|
||||
}
|
||||
if (double.class.equals(returnType)) {
|
||||
return 0D;
|
||||
}
|
||||
if (float.class.equals(returnType)) {
|
||||
return 0F;
|
||||
}
|
||||
if (short.class.equals(returnType)) {
|
||||
return (short) 0;
|
||||
}
|
||||
if (byte.class.equals(returnType)) {
|
||||
return (byte) 0;
|
||||
}
|
||||
if (char.class.equals(returnType)) {
|
||||
return (char) 0;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user