reactor:移除 pay 的 api 包
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
package cn.iocoder.yudao.module.pay.framework.pay.core.client.impl;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.iocoder.yudao.module.pay.enums.PayChannelEnum;
|
||||
import cn.iocoder.yudao.module.pay.framework.pay.core.client.PayClient;
|
||||
import cn.iocoder.yudao.module.pay.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.module.pay.framework.pay.core.client.impl.PayClientFactoryImpl;
|
||||
import cn.iocoder.yudao.module.pay.framework.pay.core.client.impl.alipay.AlipayPayClientConfig;
|
||||
import cn.iocoder.yudao.module.pay.framework.pay.core.client.impl.alipay.AlipayQrPayClient;
|
||||
import cn.iocoder.yudao.module.pay.framework.pay.core.client.impl.alipay.AlipayWapPayClient;
|
||||
import cn.iocoder.yudao.module.pay.framework.pay.core.client.impl.weixin.WxPayClientConfig;
|
||||
import cn.iocoder.yudao.module.pay.framework.pay.core.client.impl.weixin.WxPubPayClient;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
|
||||
/**
|
||||
* {@link PayClientFactoryImpl} 的集成测试
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Disabled
|
||||
public class PayClientFactoryImplIntegrationTest {
|
||||
|
||||
private static final String SERVER_URL_SANDBOX = "https://openapi.alipaydev.com/gateway.do";
|
||||
|
||||
private final PayClientFactoryImpl payClientFactory = new PayClientFactoryImpl();
|
||||
|
||||
/**
|
||||
* {@link WxPubPayClient} 的 V2 版本
|
||||
*/
|
||||
@Test
|
||||
public void testCreatePayClient_WX_PUB_V2() {
|
||||
// 创建配置
|
||||
WxPayClientConfig config = new WxPayClientConfig();
|
||||
config.setAppId("wx041349c6f39b268b");
|
||||
config.setMchId("1545083881");
|
||||
config.setApiVersion(WxPayClientConfig.API_VERSION_V2);
|
||||
config.setMchKey("0alL64UDQdlCwiKZ73ib7ypaIjMns06p");
|
||||
// 创建客户端
|
||||
Long channelId = RandomUtil.randomLong();
|
||||
payClientFactory.createOrUpdatePayClient(channelId, PayChannelEnum.WX_PUB.getCode(), config);
|
||||
PayClient<?> client = payClientFactory.getPayClient(channelId);
|
||||
// 发起支付
|
||||
PayOrderUnifiedReqDTO reqDTO = buildPayOrderUnifiedReqDTO();
|
||||
// CommonResult<?> result = client.unifiedOrder(reqDTO);
|
||||
// System.out.println(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link WxPubPayClient} 的 V3 版本
|
||||
*/
|
||||
@Test
|
||||
public void testCreatePayClient_WX_PUB_V3() throws FileNotFoundException {
|
||||
// 创建配置
|
||||
WxPayClientConfig config = new WxPayClientConfig();
|
||||
config.setAppId("wx041349c6f39b268b");
|
||||
config.setMchId("1545083881");
|
||||
config.setApiVersion(WxPayClientConfig.API_VERSION_V3);
|
||||
config.setPrivateKeyContent(IoUtil.readUtf8(new FileInputStream("/Users/yunai/Downloads/wx_pay/apiclient_key.pem")));
|
||||
// config.setPrivateCertContent(IoUtil.readUtf8(new FileInputStream("/Users/yunai/Downloads/wx_pay/apiclient_cert.pem")));
|
||||
config.setApiV3Key("joerVi8y5DJ3o4ttA0o1uH47Xz1u2Ase");
|
||||
// 创建客户端
|
||||
Long channelId = RandomUtil.randomLong();
|
||||
payClientFactory.createOrUpdatePayClient(channelId, PayChannelEnum.WX_PUB.getCode(), config);
|
||||
PayClient<?> client = payClientFactory.getPayClient(channelId);
|
||||
// 发起支付
|
||||
PayOrderUnifiedReqDTO reqDTO = buildPayOrderUnifiedReqDTO();
|
||||
// CommonResult<?> result = client.unifiedOrder(reqDTO);
|
||||
// System.out.println(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AlipayQrPayClient}
|
||||
*/
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testCreatePayClient_ALIPAY_QR() {
|
||||
// 创建配置
|
||||
AlipayPayClientConfig config = new AlipayPayClientConfig();
|
||||
config.setAppId("2021000118634035");
|
||||
config.setServerUrl(SERVER_URL_SANDBOX);
|
||||
config.setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT);
|
||||
config.setPrivateKey("MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCHsEV1cDupwJv890x84qbppUtRIfhaKSwSVN0thCcsDCaAsGR5MZslDkO8NCT9V4r2SVXjyY7eJUZlZd1M0C8T01Tg4UOx5LUbic0O3A1uJMy6V1n9IyYwbAW3AEZhBd5bSbPgrqvmv3NeWSTQT6Anxnllf+2iDH6zyA2fPl7cYyQtbZoDJQFGqr4F+cGh2R6akzRKNoBkAeMYwoY6es2lX8sJxCVPWUmxNUoL3tScwlSpd7Bxw0q9c/X01jMwuQ0+Va358zgFiGERTE6yD01eu40OBDXOYO3z++y+TAYHlQQ2toMO63trepo88X3xV3R44/1DH+k2pAm2IF5ixiLrAgMBAAECggEAPx3SoXcseaD7rmcGcE0p4SMfbsUDdkUSmBBbtfF0GzwnqNLkWa+mgE0rWt9SmXngTQH97vByAYmLPl1s3G82ht1V7Sk7yQMe74lhFllr8eEyTjeVx3dTK1EEM4TwN+936DTXdFsr4TELJEcJJdD0KaxcCcfBLRDs2wnitEFZ9N+GoZybVmY8w0e0MI7PLObUZ2l0X4RurQnfG9ZxjXjC7PkeMVv7cGGylpNFi3BbvkRhdhLPDC2E6wqnr9e7zk+hiENivAezXrtxtwKovzCtnWJ1r0IO14Rh47H509Ic0wFnj+o5YyUL4LdmpL7yaaH6fM7zcSLFjNZPHvZCKPwYcQKBgQDQFho98QvnL8ex4v6cry4VitGpjSXm1qP3vmMQk4rTsn8iPWtcxPjqGEqOQJjdi4Mi0VZKQOLFwlH0kl95wNrD/isJ4O1yeYfX7YAXApzHqYNINzM79HemO3Yx1qLMW3okRFJ9pPRzbQ9qkTpsaegsmyX316zOBhzGRYjKbutTYwKBgQCm7phr9XdFW5Vh+XR90mVs483nrLmMiDKg7YKxSLJ8amiDjzPejCn7i95Hah08P+2MIZLIPbh2VLacczR6ltRRzN5bg5etFuqSgfkuHyxpoDmpjbe08+Q2h8JBYqcC5Nhv1AKU4iOUhVLHo/FBAQliMcGc/J3eiYTFC7EsNx382QKBgClb20doe7cttgFTXswBvaUmfFm45kmla924B7SpvrQpDD/f+VDtDZRp05fGmxuduSjYdtA3aVtpLiTwWu22OUUvZZqHDGruYOO4Hvdz23mL5b4ayqImCwoNU4bAZIc9v18p/UNf3/55NNE3oGcf/bev9rH2OjCQ4nM+Ktwhg8CFAoGACSgvbkShzUkv0ZcIf9ppu+ZnJh1AdGgINvGwaJ8vQ0nm/8h8NOoFZ4oNoGc+wU5Ubops7dUM6FjPR5e+OjdJ4E7Xp7d5O4J1TaIZlCEbo5OpdhaTDDcQvrkFu+Z4eN0qzj+YAKjDAOOrXc4tbr5q0FsgXscwtcNfaBuzFVTUrUkCgYEAwzPnMNhWG3zOWLUs2QFA2GP4Y+J8cpUYfj6pbKKzeLwyG9qBwF1NJpN8m+q9q7V9P2LY+9Lp9e1mGsGeqt5HMEA3P6vIpcqLJLqE/4PBLLRzfccTcmqb1m71+erxTRhHBRkGS+I7dZEb3olQfnS1Y1tpMBxiwYwR3LW4oXuJwj8=");
|
||||
config.setAlipayPublicKey("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnq90KnF4dTnlzzmxpujbI05OYqi5WxAS6cL0gnZFv2gK51HExF8v/BaP7P979PhFMgWTqmOOI+Dtno5s+yD09XTY1WkshbLk6i4g2Xlr8fyW9ODnkU88RI2w9UdPhQU4cPPwBNlrsYhKkVK2OxwM3kFqjoBBY0CZoZCsSQ3LDH5WeZqPArlsS6xa2zqJBuuoKjMrdpELl3eXSjP8K54eDJCbeetCZNKWLL3DPahTPB7LZikfYmslb0QUvCgGapD0xkS7eVq70NaL1G57MWABs4tbfWgxike4Daj3EfUrzIVspQxj7w8HEj9WozJPgL88kSJSits0pqD3n5r8HSuseQIDAQAB");
|
||||
// 创建客户端
|
||||
Long channelId = RandomUtil.randomLong();
|
||||
payClientFactory.createOrUpdatePayClient(channelId, PayChannelEnum.ALIPAY_QR.getCode(), config);
|
||||
PayClient<?> client = payClientFactory.getPayClient(channelId);
|
||||
// 发起支付
|
||||
PayOrderUnifiedReqDTO reqDTO = buildPayOrderUnifiedReqDTO();
|
||||
reqDTO.setNotifyUrl("http://yunai.natapp1.cc/admin-api/pay/notify/callback/18"); // TODO @tina: 这里改成你的 natapp 回调地址
|
||||
// CommonResult<AlipayTradePrecreateResponse> result = (CommonResult<AlipayTradePrecreateResponse>) client.unifiedOrder(reqDTO);
|
||||
// System.out.println(JsonUtils.toJsonString(result));
|
||||
// System.out.println(result.getData().getQrCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AlipayWapPayClient}
|
||||
*/
|
||||
@Test
|
||||
public void testCreatePayClient_ALIPAY_WAP() {
|
||||
// 创建配置
|
||||
AlipayPayClientConfig config = new AlipayPayClientConfig();
|
||||
config.setAppId("2021000118634035");
|
||||
config.setServerUrl(SERVER_URL_SANDBOX);
|
||||
config.setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT);
|
||||
config.setPrivateKey("MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCHsEV1cDupwJv890x84qbppUtRIfhaKSwSVN0thCcsDCaAsGR5MZslDkO8NCT9V4r2SVXjyY7eJUZlZd1M0C8T01Tg4UOx5LUbic0O3A1uJMy6V1n9IyYwbAW3AEZhBd5bSbPgrqvmv3NeWSTQT6Anxnllf+2iDH6zyA2fPl7cYyQtbZoDJQFGqr4F+cGh2R6akzRKNoBkAeMYwoY6es2lX8sJxCVPWUmxNUoL3tScwlSpd7Bxw0q9c/X01jMwuQ0+Va358zgFiGERTE6yD01eu40OBDXOYO3z++y+TAYHlQQ2toMO63trepo88X3xV3R44/1DH+k2pAm2IF5ixiLrAgMBAAECggEAPx3SoXcseaD7rmcGcE0p4SMfbsUDdkUSmBBbtfF0GzwnqNLkWa+mgE0rWt9SmXngTQH97vByAYmLPl1s3G82ht1V7Sk7yQMe74lhFllr8eEyTjeVx3dTK1EEM4TwN+936DTXdFsr4TELJEcJJdD0KaxcCcfBLRDs2wnitEFZ9N+GoZybVmY8w0e0MI7PLObUZ2l0X4RurQnfG9ZxjXjC7PkeMVv7cGGylpNFi3BbvkRhdhLPDC2E6wqnr9e7zk+hiENivAezXrtxtwKovzCtnWJ1r0IO14Rh47H509Ic0wFnj+o5YyUL4LdmpL7yaaH6fM7zcSLFjNZPHvZCKPwYcQKBgQDQFho98QvnL8ex4v6cry4VitGpjSXm1qP3vmMQk4rTsn8iPWtcxPjqGEqOQJjdi4Mi0VZKQOLFwlH0kl95wNrD/isJ4O1yeYfX7YAXApzHqYNINzM79HemO3Yx1qLMW3okRFJ9pPRzbQ9qkTpsaegsmyX316zOBhzGRYjKbutTYwKBgQCm7phr9XdFW5Vh+XR90mVs483nrLmMiDKg7YKxSLJ8amiDjzPejCn7i95Hah08P+2MIZLIPbh2VLacczR6ltRRzN5bg5etFuqSgfkuHyxpoDmpjbe08+Q2h8JBYqcC5Nhv1AKU4iOUhVLHo/FBAQliMcGc/J3eiYTFC7EsNx382QKBgClb20doe7cttgFTXswBvaUmfFm45kmla924B7SpvrQpDD/f+VDtDZRp05fGmxuduSjYdtA3aVtpLiTwWu22OUUvZZqHDGruYOO4Hvdz23mL5b4ayqImCwoNU4bAZIc9v18p/UNf3/55NNE3oGcf/bev9rH2OjCQ4nM+Ktwhg8CFAoGACSgvbkShzUkv0ZcIf9ppu+ZnJh1AdGgINvGwaJ8vQ0nm/8h8NOoFZ4oNoGc+wU5Ubops7dUM6FjPR5e+OjdJ4E7Xp7d5O4J1TaIZlCEbo5OpdhaTDDcQvrkFu+Z4eN0qzj+YAKjDAOOrXc4tbr5q0FsgXscwtcNfaBuzFVTUrUkCgYEAwzPnMNhWG3zOWLUs2QFA2GP4Y+J8cpUYfj6pbKKzeLwyG9qBwF1NJpN8m+q9q7V9P2LY+9Lp9e1mGsGeqt5HMEA3P6vIpcqLJLqE/4PBLLRzfccTcmqb1m71+erxTRhHBRkGS+I7dZEb3olQfnS1Y1tpMBxiwYwR3LW4oXuJwj8=");
|
||||
config.setAlipayPublicKey("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnq90KnF4dTnlzzmxpujbI05OYqi5WxAS6cL0gnZFv2gK51HExF8v/BaP7P979PhFMgWTqmOOI+Dtno5s+yD09XTY1WkshbLk6i4g2Xlr8fyW9ODnkU88RI2w9UdPhQU4cPPwBNlrsYhKkVK2OxwM3kFqjoBBY0CZoZCsSQ3LDH5WeZqPArlsS6xa2zqJBuuoKjMrdpELl3eXSjP8K54eDJCbeetCZNKWLL3DPahTPB7LZikfYmslb0QUvCgGapD0xkS7eVq70NaL1G57MWABs4tbfWgxike4Daj3EfUrzIVspQxj7w8HEj9WozJPgL88kSJSits0pqD3n5r8HSuseQIDAQAB");
|
||||
// 创建客户端
|
||||
Long channelId = RandomUtil.randomLong();
|
||||
payClientFactory.createOrUpdatePayClient(channelId, PayChannelEnum.ALIPAY_WAP.getCode(), config);
|
||||
PayClient<?> client = payClientFactory.getPayClient(channelId);
|
||||
// 发起支付
|
||||
PayOrderUnifiedReqDTO reqDTO = buildPayOrderUnifiedReqDTO();
|
||||
// CommonResult<?> result = client.unifiedOrder(reqDTO);
|
||||
// System.out.println(JsonUtils.toJsonString(result));
|
||||
}
|
||||
|
||||
private static PayOrderUnifiedReqDTO buildPayOrderUnifiedReqDTO() {
|
||||
PayOrderUnifiedReqDTO reqDTO = new PayOrderUnifiedReqDTO();
|
||||
reqDTO.setPrice(123);
|
||||
reqDTO.setSubject("IPhone 13");
|
||||
reqDTO.setBody("biubiubiu");
|
||||
reqDTO.setOutTradeNo(String.valueOf(System.currentTimeMillis()));
|
||||
reqDTO.setUserIp("127.0.0.1");
|
||||
reqDTO.setNotifyUrl("http://127.0.0.1:8080");
|
||||
return reqDTO;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,221 @@
|
||||
package cn.iocoder.yudao.module.pay.framework.pay.core.client.impl.alipay;
|
||||
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
|
||||
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum;
|
||||
import cn.iocoder.yudao.module.pay.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.module.pay.framework.pay.core.client.dto.refund.PayRefundRespDTO;
|
||||
import cn.iocoder.yudao.module.pay.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.module.pay.framework.pay.core.client.exception.PayClientException;
|
||||
import com.alipay.api.AlipayApiException;
|
||||
import com.alipay.api.DefaultAlipayClient;
|
||||
import com.alipay.api.DefaultSigner;
|
||||
import com.alipay.api.domain.AlipayTradeRefundModel;
|
||||
import com.alipay.api.request.AlipayTradeRefundRequest;
|
||||
import com.alipay.api.response.AlipayTradeRefundResponse;
|
||||
import lombok.Setter;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentMatcher;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import jakarta.validation.ConstraintViolationException;
|
||||
import java.util.Date;
|
||||
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
|
||||
import static cn.iocoder.yudao.module.pay.framework.pay.core.client.impl.alipay.AlipayPayClientConfig.MODE_PUBLIC_KEY;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* 支付宝 Client 的测试基类
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
public abstract class AbstractAlipayClientTest extends BaseMockitoUnitTest {
|
||||
|
||||
protected AlipayPayClientConfig config = randomPojo(AlipayPayClientConfig.class, o -> {
|
||||
o.setServerUrl(randomURL());
|
||||
o.setPrivateKey(randomString());
|
||||
o.setMode(MODE_PUBLIC_KEY);
|
||||
o.setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT);
|
||||
o.setAppCertContent("");
|
||||
o.setAlipayPublicCertContent("");
|
||||
o.setRootCertContent("");
|
||||
});
|
||||
|
||||
@Mock
|
||||
protected DefaultAlipayClient defaultAlipayClient;
|
||||
|
||||
@Setter
|
||||
private AbstractAlipayPayClient client;
|
||||
|
||||
/**
|
||||
* 子类需要实现该方法. 设置 client 的具体实现
|
||||
*/
|
||||
@BeforeEach
|
||||
public abstract void setUp();
|
||||
|
||||
@Test
|
||||
@DisplayName("支付宝 Client 初始化")
|
||||
public void testDoInit() {
|
||||
// 调用
|
||||
client.doInit();
|
||||
// 断言
|
||||
DefaultAlipayClient realClient = client.getClient();
|
||||
assertNotSame(defaultAlipayClient, realClient);
|
||||
assertInstanceOf(DefaultSigner.class, realClient.getSigner());
|
||||
assertEquals(config.getPrivateKey(), ((DefaultSigner) realClient.getSigner()).getPrivateKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("支付宝 Client 统一退款:成功")
|
||||
public void testUnifiedRefund_success() throws AlipayApiException {
|
||||
// mock 方法
|
||||
String notifyUrl = randomURL();
|
||||
Date refundTime = randomDate();
|
||||
String outRefundNo = randomString();
|
||||
String outTradeNo = randomString();
|
||||
Integer refundAmount = randomInteger();
|
||||
AlipayTradeRefundResponse response = randomPojo(AlipayTradeRefundResponse.class, o -> {
|
||||
o.setSubCode("");
|
||||
o.setGmtRefundPay(refundTime);
|
||||
});
|
||||
when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeRefundRequest>) request -> {
|
||||
assertInstanceOf(AlipayTradeRefundModel.class, request.getBizModel());
|
||||
AlipayTradeRefundModel bizModel = (AlipayTradeRefundModel) request.getBizModel();
|
||||
assertEquals(outRefundNo, bizModel.getOutRequestNo());
|
||||
assertEquals(outTradeNo, bizModel.getOutTradeNo());
|
||||
assertEquals(String.valueOf(refundAmount / 100.0), bizModel.getRefundAmount());
|
||||
return true;
|
||||
}))).thenReturn(response);
|
||||
// 准备请求参数
|
||||
PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> {
|
||||
o.setOutRefundNo(outRefundNo);
|
||||
o.setOutTradeNo(outTradeNo);
|
||||
o.setNotifyUrl(notifyUrl);
|
||||
o.setRefundPrice(refundAmount);
|
||||
});
|
||||
|
||||
// 调用
|
||||
PayRefundRespDTO resp = client.unifiedRefund(refundReqDTO);
|
||||
// 断言
|
||||
assertEquals(PayRefundStatusEnum.SUCCESS.getStatus(), resp.getStatus());
|
||||
assertEquals(outRefundNo, resp.getOutRefundNo());
|
||||
assertNull(resp.getChannelRefundNo());
|
||||
assertEquals(LocalDateTimeUtil.of(refundTime), resp.getSuccessTime());
|
||||
assertSame(response, resp.getRawData());
|
||||
assertNull(resp.getChannelErrorCode());
|
||||
assertNull(resp.getChannelErrorMsg());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("支付宝 Client 统一退款:渠道返回失败")
|
||||
public void test_unified_refund_channel_failed() throws AlipayApiException {
|
||||
// mock 方法
|
||||
String notifyUrl = randomURL();
|
||||
String subCode = randomString();
|
||||
String subMsg = randomString();
|
||||
AlipayTradeRefundResponse response = randomPojo(AlipayTradeRefundResponse.class, o -> {
|
||||
o.setSubCode(subCode);
|
||||
o.setSubMsg(subMsg);
|
||||
});
|
||||
when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeRefundRequest>) request -> {
|
||||
assertInstanceOf(AlipayTradeRefundModel.class, request.getBizModel());
|
||||
return true;
|
||||
}))).thenReturn(response);
|
||||
// 准备请求参数
|
||||
String outRefundNo = randomString();
|
||||
String outTradeNo = randomString();
|
||||
PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> {
|
||||
o.setOutRefundNo(outRefundNo);
|
||||
o.setOutTradeNo(outTradeNo);
|
||||
o.setNotifyUrl(notifyUrl);
|
||||
});
|
||||
|
||||
// 调用
|
||||
PayRefundRespDTO resp = client.unifiedRefund(refundReqDTO);
|
||||
// 断言
|
||||
assertEquals(PayRefundStatusEnum.FAILURE.getStatus(), resp.getStatus());
|
||||
assertEquals(outRefundNo, resp.getOutRefundNo());
|
||||
assertNull(resp.getChannelRefundNo());
|
||||
assertNull(resp.getSuccessTime());
|
||||
assertSame(response, resp.getRawData());
|
||||
assertEquals(subCode, resp.getChannelErrorCode());
|
||||
assertEquals(subMsg, resp.getChannelErrorMsg());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("支付宝 Client 统一退款:参数校验不通过")
|
||||
public void testUnifiedRefund_paramInvalidate() {
|
||||
// 准备请求参数
|
||||
String notifyUrl = randomURL();
|
||||
PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> {
|
||||
o.setOutTradeNo("");
|
||||
o.setNotifyUrl(notifyUrl);
|
||||
});
|
||||
|
||||
// 调用,并断言
|
||||
assertThrows(ConstraintViolationException.class, () -> client.unifiedRefund(refundReqDTO));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("支付宝 Client 统一退款:抛出业务异常")
|
||||
public void testUnifiedRefund_throwServiceException() throws AlipayApiException {
|
||||
// mock 方法
|
||||
when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeRefundRequest>) request -> true)))
|
||||
.thenThrow(ServiceExceptionUtil.exception(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR));
|
||||
// 准备请求参数
|
||||
String notifyUrl = randomURL();
|
||||
PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> o.setNotifyUrl(notifyUrl));
|
||||
|
||||
// 调用,并断言
|
||||
assertThrows(ServiceException.class, () -> client.unifiedRefund(refundReqDTO));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("支付宝 Client 统一退款:抛出系统异常")
|
||||
public void testUnifiedRefund_throwPayException() throws AlipayApiException {
|
||||
// mock 方法
|
||||
when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradeRefundRequest>) request -> true)))
|
||||
.thenThrow(new RuntimeException("系统异常"));
|
||||
// 准备请求参数
|
||||
String notifyUrl = randomURL();
|
||||
PayRefundUnifiedReqDTO refundReqDTO = randomPojo(PayRefundUnifiedReqDTO.class, o -> o.setNotifyUrl(notifyUrl));
|
||||
|
||||
// 调用,并断言
|
||||
assertThrows(PayClientException.class, () -> client.unifiedRefund(refundReqDTO));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("支付宝 Client 统一下单:参数校验不通过")
|
||||
public void testUnifiedOrder_paramInvalidate() {
|
||||
// 准备请求参数
|
||||
String outTradeNo = randomString();
|
||||
String notifyUrl = randomURL();
|
||||
PayOrderUnifiedReqDTO reqDTO = randomPojo(PayOrderUnifiedReqDTO.class, o -> {
|
||||
o.setOutTradeNo(outTradeNo);
|
||||
o.setNotifyUrl(notifyUrl);
|
||||
});
|
||||
|
||||
// 调用,并断言
|
||||
assertThrows(ConstraintViolationException.class, () -> client.unifiedOrder(reqDTO));
|
||||
}
|
||||
|
||||
protected PayOrderUnifiedReqDTO buildOrderUnifiedReqDTO(String notifyUrl, String outTradeNo, Integer price) {
|
||||
return randomPojo(PayOrderUnifiedReqDTO.class, o -> {
|
||||
o.setOutTradeNo(outTradeNo);
|
||||
o.setNotifyUrl(notifyUrl);
|
||||
o.setPrice(price);
|
||||
o.setSubject(RandomUtil.randomString(32));
|
||||
o.setBody(RandomUtil.randomString(32));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,170 @@
|
||||
package cn.iocoder.yudao.module.pay.framework.pay.core.client.impl.alipay;
|
||||
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||
import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
|
||||
import cn.iocoder.yudao.module.pay.framework.pay.core.client.dto.order.PayOrderRespDTO;
|
||||
import cn.iocoder.yudao.module.pay.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.module.pay.framework.pay.core.enums.PayOrderDisplayModeEnum;
|
||||
import com.alipay.api.AlipayApiException;
|
||||
import com.alipay.api.domain.AlipayTradePayModel;
|
||||
import com.alipay.api.request.AlipayTradePayRequest;
|
||||
import com.alipay.api.response.AlipayTradePayResponse;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentMatcher;
|
||||
import org.mockito.InjectMocks;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum.CLOSED;
|
||||
import static cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum.WAITING;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* {@link AlipayBarPayClient} 单元测试
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
public class AlipayBarPayClientTest extends AbstractAlipayClientTest {
|
||||
|
||||
@InjectMocks
|
||||
private AlipayBarPayClient client = new AlipayBarPayClient(randomLongId(), config);
|
||||
|
||||
@Override
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
setClient(client);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("支付宝条码支付:非免密码支付下单成功")
|
||||
public void testUnifiedOrder_success() throws AlipayApiException {
|
||||
// mock 方法
|
||||
String outTradeNo = randomString();
|
||||
String notifyUrl = randomURL();
|
||||
Integer price = randomInteger();
|
||||
String authCode = randomString();
|
||||
AlipayTradePayResponse response = randomPojo(AlipayTradePayResponse.class, o -> o.setSubCode(""));
|
||||
when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradePayRequest>) request -> {
|
||||
assertInstanceOf(AlipayTradePayModel.class, request.getBizModel());
|
||||
assertEquals(notifyUrl, request.getNotifyUrl());
|
||||
AlipayTradePayModel model = (AlipayTradePayModel) request.getBizModel();
|
||||
assertEquals(outTradeNo, model.getOutTradeNo());
|
||||
assertEquals(String.valueOf(price / 100.0), model.getTotalAmount());
|
||||
assertEquals(authCode, model.getAuthCode());
|
||||
return true;
|
||||
}))).thenReturn(response);
|
||||
// 准备请求参数
|
||||
PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(notifyUrl, outTradeNo, price);
|
||||
Map<String, String> extraParam = new HashMap<>();
|
||||
extraParam.put("auth_code", authCode);
|
||||
reqDTO.setChannelExtras(extraParam);
|
||||
|
||||
// 调用方法
|
||||
PayOrderRespDTO resp = client.unifiedOrder(reqDTO);
|
||||
// 断言
|
||||
assertEquals(WAITING.getStatus(), resp.getStatus());
|
||||
assertEquals(outTradeNo, resp.getOutTradeNo());
|
||||
assertNull(resp.getChannelOrderNo());
|
||||
assertNull(resp.getChannelUserId());
|
||||
assertNull(resp.getSuccessTime());
|
||||
assertEquals(PayOrderDisplayModeEnum.BAR_CODE.getMode(), resp.getDisplayMode());
|
||||
assertEquals("", resp.getDisplayContent());
|
||||
assertSame(response, resp.getRawData());
|
||||
assertNull(resp.getChannelErrorCode());
|
||||
assertNull(resp.getChannelErrorMsg());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("支付宝条码支付:免密码支付下单成功")
|
||||
public void testUnifiedOrder_code10000Success() throws AlipayApiException {
|
||||
// mock 方法
|
||||
String outTradeNo = randomString();
|
||||
String channelNo = randomString();
|
||||
String channelUserId = randomString();
|
||||
Date payTime = randomDate();
|
||||
AlipayTradePayResponse response = randomPojo(AlipayTradePayResponse.class, o -> {
|
||||
o.setSubCode("");
|
||||
o.setCode("10000");
|
||||
o.setOutTradeNo(outTradeNo);
|
||||
o.setTradeNo(channelNo);
|
||||
o.setBuyerUserId(channelUserId);
|
||||
o.setGmtPayment(payTime);
|
||||
});
|
||||
when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradePayRequest>) request -> true)))
|
||||
.thenReturn(response);
|
||||
// 准备请求参数
|
||||
String authCode = randomString();
|
||||
PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(randomURL(), outTradeNo, randomInteger());
|
||||
Map<String, String> extraParam = new HashMap<>();
|
||||
extraParam.put("auth_code", authCode);
|
||||
reqDTO.setChannelExtras(extraParam);
|
||||
|
||||
// 下单请求
|
||||
PayOrderRespDTO resp = client.unifiedOrder(reqDTO);
|
||||
// 断言
|
||||
assertEquals(PayOrderStatusEnum.SUCCESS.getStatus(), resp.getStatus());
|
||||
assertEquals(outTradeNo, resp.getOutTradeNo());
|
||||
assertEquals(channelNo, resp.getChannelOrderNo());
|
||||
assertEquals(channelUserId, resp.getChannelUserId());
|
||||
assertEquals(LocalDateTimeUtil.of(payTime), resp.getSuccessTime());
|
||||
assertEquals(PayOrderDisplayModeEnum.BAR_CODE.getMode(), resp.getDisplayMode());
|
||||
assertEquals("", resp.getDisplayContent());
|
||||
assertSame(response, resp.getRawData());
|
||||
assertNull(resp.getChannelErrorCode());
|
||||
assertNull(resp.getChannelErrorMsg());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("支付宝条码支付:没有传条码")
|
||||
public void testUnifiedOrder_emptyAuthCode() {
|
||||
// 准备参数
|
||||
PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(randomURL(), randomString(), randomInteger());
|
||||
|
||||
// 调用,并断言
|
||||
assertThrows(ServiceException.class, () -> client.unifiedOrder(reqDTO));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("支付宝条码支付:渠道返回失败")
|
||||
public void test_unified_order_channel_failed() throws AlipayApiException {
|
||||
// mock 方法
|
||||
String subCode = randomString();
|
||||
String subMsg = randomString();
|
||||
AlipayTradePayResponse response = randomPojo(AlipayTradePayResponse.class, o -> {
|
||||
o.setSubCode(subCode);
|
||||
o.setSubMsg(subMsg);
|
||||
});
|
||||
when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradePayRequest>) request -> true)))
|
||||
.thenReturn(response);
|
||||
// 准备请求参数
|
||||
String authCode = randomString();
|
||||
String outTradeNo = randomString();
|
||||
PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(randomURL(), outTradeNo, randomInteger());
|
||||
Map<String, String> extraParam = new HashMap<>();
|
||||
extraParam.put("auth_code", authCode);
|
||||
reqDTO.setChannelExtras(extraParam);
|
||||
|
||||
// 调用方法
|
||||
PayOrderRespDTO resp = client.unifiedOrder(reqDTO);
|
||||
// 断言
|
||||
assertEquals(CLOSED.getStatus(), resp.getStatus());
|
||||
assertEquals(outTradeNo, resp.getOutTradeNo());
|
||||
assertNull(resp.getChannelOrderNo());
|
||||
assertNull(resp.getChannelUserId());
|
||||
assertNull(resp.getSuccessTime());
|
||||
assertNull(resp.getDisplayMode());
|
||||
assertNull(resp.getDisplayContent());
|
||||
assertSame(response, resp.getRawData());
|
||||
assertEquals(subCode, resp.getChannelErrorCode());
|
||||
assertEquals(subMsg, resp.getChannelErrorMsg());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,131 @@
|
||||
package cn.iocoder.yudao.module.pay.framework.pay.core.client.impl.alipay;
|
||||
|
||||
import cn.hutool.http.Method;
|
||||
import cn.iocoder.yudao.module.pay.framework.pay.core.client.dto.order.PayOrderRespDTO;
|
||||
import cn.iocoder.yudao.module.pay.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.module.pay.framework.pay.core.enums.PayOrderDisplayModeEnum;
|
||||
import com.alipay.api.AlipayApiException;
|
||||
import com.alipay.api.request.AlipayTradePagePayRequest;
|
||||
import com.alipay.api.response.AlipayTradePagePayResponse;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentMatcher;
|
||||
import org.mockito.InjectMocks;
|
||||
|
||||
import static cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum.CLOSED;
|
||||
import static cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum.WAITING;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* {@link AlipayPcPayClient} 单元测试
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
public class AlipayPcPayClientTest extends AbstractAlipayClientTest {
|
||||
|
||||
@InjectMocks
|
||||
private AlipayPcPayClient client = new AlipayPcPayClient(randomLongId(), config);
|
||||
|
||||
@Override
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
setClient(client);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("支付宝 PC 网站支付:URL Display Mode 下单成功")
|
||||
public void testUnifiedOrder_urlSuccess() throws AlipayApiException {
|
||||
// mock 方法
|
||||
String notifyUrl = randomURL();
|
||||
AlipayTradePagePayResponse response = randomPojo(AlipayTradePagePayResponse.class, o -> o.setSubCode(""));
|
||||
when(defaultAlipayClient.pageExecute(argThat((ArgumentMatcher<AlipayTradePagePayRequest>) request -> true),
|
||||
eq(Method.GET.name()))).thenReturn(response);
|
||||
// 准备请求参数
|
||||
String outTradeNo = randomString();
|
||||
Integer price = randomInteger();
|
||||
PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(notifyUrl, outTradeNo, price);
|
||||
reqDTO.setDisplayMode(null);
|
||||
|
||||
// 调用
|
||||
PayOrderRespDTO resp = client.unifiedOrder(reqDTO);
|
||||
// 断言
|
||||
assertEquals(WAITING.getStatus(), resp.getStatus());
|
||||
assertEquals(outTradeNo, resp.getOutTradeNo());
|
||||
assertNull(resp.getChannelOrderNo());
|
||||
assertNull(resp.getChannelUserId());
|
||||
assertNull(resp.getSuccessTime());
|
||||
assertEquals(PayOrderDisplayModeEnum.URL.getMode(), resp.getDisplayMode());
|
||||
assertEquals(response.getBody(), resp.getDisplayContent());
|
||||
assertSame(response, resp.getRawData());
|
||||
assertNull(resp.getChannelErrorCode());
|
||||
assertNull(resp.getChannelErrorMsg());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("支付宝 PC 网站支付:Form Display Mode 下单成功")
|
||||
public void testUnifiedOrder_formSuccess() throws AlipayApiException {
|
||||
// mock 方法
|
||||
String notifyUrl = randomURL();
|
||||
AlipayTradePagePayResponse response = randomPojo(AlipayTradePagePayResponse.class, o -> o.setSubCode(""));
|
||||
when(defaultAlipayClient.pageExecute(argThat((ArgumentMatcher<AlipayTradePagePayRequest>) request -> true),
|
||||
eq(Method.POST.name()))).thenReturn(response);
|
||||
// 准备请求参数
|
||||
String outTradeNo = randomString();
|
||||
Integer price = randomInteger();
|
||||
PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(notifyUrl, outTradeNo, price);
|
||||
reqDTO.setDisplayMode(PayOrderDisplayModeEnum.FORM.getMode());
|
||||
|
||||
// 调用
|
||||
PayOrderRespDTO resp = client.unifiedOrder(reqDTO);
|
||||
// 断言
|
||||
assertEquals(WAITING.getStatus(), resp.getStatus());
|
||||
assertEquals(outTradeNo, resp.getOutTradeNo());
|
||||
assertNull(resp.getChannelOrderNo());
|
||||
assertNull(resp.getChannelUserId());
|
||||
assertNull(resp.getSuccessTime());
|
||||
assertEquals(PayOrderDisplayModeEnum.FORM.getMode(), resp.getDisplayMode());
|
||||
assertEquals(response.getBody(), resp.getDisplayContent());
|
||||
assertSame(response, resp.getRawData());
|
||||
assertNull(resp.getChannelErrorCode());
|
||||
assertNull(resp.getChannelErrorMsg());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("支付宝 PC 网站支付:渠道返回失败")
|
||||
public void testUnifiedOrder_channelFailed() throws AlipayApiException {
|
||||
// mock 方法
|
||||
String subCode = randomString();
|
||||
String subMsg = randomString();
|
||||
AlipayTradePagePayResponse response = randomPojo(AlipayTradePagePayResponse.class, o -> {
|
||||
o.setSubCode(subCode);
|
||||
o.setSubMsg(subMsg);
|
||||
});
|
||||
when(defaultAlipayClient.pageExecute(argThat((ArgumentMatcher<AlipayTradePagePayRequest>) request -> true),
|
||||
eq(Method.GET.name()))).thenReturn(response);
|
||||
// 准备请求参数
|
||||
String outTradeNo = randomString();
|
||||
PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(randomURL(), outTradeNo, randomInteger());
|
||||
reqDTO.setDisplayMode(PayOrderDisplayModeEnum.URL.getMode());
|
||||
|
||||
// 调用
|
||||
PayOrderRespDTO resp = client.unifiedOrder(reqDTO);
|
||||
// 断言
|
||||
assertEquals(CLOSED.getStatus(), resp.getStatus());
|
||||
assertEquals(outTradeNo, resp.getOutTradeNo());
|
||||
assertNull(resp.getChannelOrderNo());
|
||||
assertNull(resp.getChannelUserId());
|
||||
assertNull(resp.getSuccessTime());
|
||||
assertNull(resp.getDisplayMode());
|
||||
assertNull(resp.getDisplayContent());
|
||||
assertSame(response, resp.getRawData());
|
||||
assertEquals(subCode, resp.getChannelErrorCode());
|
||||
assertEquals(subMsg, resp.getChannelErrorMsg());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,147 @@
|
||||
package cn.iocoder.yudao.module.pay.framework.pay.core.client.impl.alipay;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
|
||||
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
|
||||
import cn.iocoder.yudao.module.pay.framework.pay.core.client.dto.order.PayOrderRespDTO;
|
||||
import cn.iocoder.yudao.module.pay.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.module.pay.framework.pay.core.client.exception.PayClientException;
|
||||
import cn.iocoder.yudao.module.pay.framework.pay.core.enums.PayOrderDisplayModeEnum;
|
||||
import com.alipay.api.AlipayApiException;
|
||||
import com.alipay.api.request.AlipayTradePrecreateRequest;
|
||||
import com.alipay.api.response.AlipayTradePrecreateResponse;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentMatcher;
|
||||
import org.mockito.InjectMocks;
|
||||
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
|
||||
import static cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum.CLOSED;
|
||||
import static cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum.WAITING;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* {@link AlipayQrPayClient} 单元测试
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
public class AlipayQrPayClientTest extends AbstractAlipayClientTest {
|
||||
|
||||
@InjectMocks
|
||||
private AlipayQrPayClient client = new AlipayQrPayClient(randomLongId(), config);
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
setClient(client);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("支付宝扫描支付:下单成功")
|
||||
public void testUnifiedOrder_success() throws AlipayApiException {
|
||||
// mock 方法
|
||||
String notifyUrl = randomURL();
|
||||
String qrCode = randomString();
|
||||
Integer price = randomInteger();
|
||||
AlipayTradePrecreateResponse response = randomPojo(AlipayTradePrecreateResponse.class, o -> {
|
||||
o.setQrCode(qrCode);
|
||||
o.setSubCode("");
|
||||
});
|
||||
when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradePrecreateRequest>) request -> {
|
||||
assertEquals(notifyUrl, request.getNotifyUrl());
|
||||
return true;
|
||||
}))).thenReturn(response);
|
||||
// 准备请求参数
|
||||
String outTradeNo = randomString();
|
||||
PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(notifyUrl, outTradeNo, price);
|
||||
|
||||
// 调用
|
||||
PayOrderRespDTO resp = client.unifiedOrder(reqDTO);
|
||||
// 断言
|
||||
assertEquals(WAITING.getStatus(), resp.getStatus());
|
||||
assertEquals(outTradeNo, resp.getOutTradeNo());
|
||||
assertNull(resp.getChannelOrderNo());
|
||||
assertNull(resp.getChannelUserId());
|
||||
assertNull(resp.getSuccessTime());
|
||||
assertEquals(PayOrderDisplayModeEnum.QR_CODE.getMode(), resp.getDisplayMode());
|
||||
assertEquals(response.getQrCode(), resp.getDisplayContent());
|
||||
assertSame(response, resp.getRawData());
|
||||
assertNull(resp.getChannelErrorCode());
|
||||
assertNull(resp.getChannelErrorMsg());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("支付宝扫描支付:渠道返回失败")
|
||||
public void testUnifiedOrder_channelFailed() throws AlipayApiException {
|
||||
// mock 方法
|
||||
String notifyUrl = randomURL();
|
||||
String subCode = randomString();
|
||||
String subMsg = randomString();
|
||||
Integer price = randomInteger();
|
||||
AlipayTradePrecreateResponse response = randomPojo(AlipayTradePrecreateResponse.class, o -> {
|
||||
o.setSubCode(subCode);
|
||||
o.setSubMsg(subMsg);
|
||||
});
|
||||
// mock
|
||||
when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradePrecreateRequest>) request -> {
|
||||
assertEquals(notifyUrl, request.getNotifyUrl());
|
||||
return true;
|
||||
}))).thenReturn(response);
|
||||
// 准备请求参数
|
||||
String outTradeNo = randomString();
|
||||
PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(notifyUrl, outTradeNo, price);
|
||||
|
||||
// 调用
|
||||
PayOrderRespDTO resp = client.unifiedOrder(reqDTO);
|
||||
// 断言
|
||||
assertEquals(CLOSED.getStatus(), resp.getStatus());
|
||||
assertEquals(outTradeNo, resp.getOutTradeNo());
|
||||
assertNull(resp.getChannelOrderNo());
|
||||
assertNull(resp.getChannelUserId());
|
||||
assertNull(resp.getSuccessTime());
|
||||
assertNull(resp.getDisplayMode());
|
||||
assertNull(resp.getDisplayContent());
|
||||
assertSame(response, resp.getRawData());
|
||||
assertEquals(subCode, resp.getChannelErrorCode());
|
||||
assertEquals(subMsg, resp.getChannelErrorMsg());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("支付宝扫描支付, 抛出系统异常")
|
||||
public void testUnifiedOrder_throwPayException() throws AlipayApiException {
|
||||
// mock 方法
|
||||
String outTradeNo = randomString();
|
||||
String notifyUrl = randomURL();
|
||||
Integer price = randomInteger();
|
||||
when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradePrecreateRequest>) request -> {
|
||||
assertEquals(notifyUrl, request.getNotifyUrl());
|
||||
return true;
|
||||
}))).thenThrow(new RuntimeException("系统异常"));
|
||||
// 准备请求参数
|
||||
PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(notifyUrl, outTradeNo,price);
|
||||
|
||||
// 调用,并断言
|
||||
assertThrows(PayClientException.class, () -> client.unifiedOrder(reqDTO));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("支付宝 Client 统一下单:抛出业务异常")
|
||||
public void testUnifiedOrder_throwServiceException() throws AlipayApiException {
|
||||
// mock 方法
|
||||
String outTradeNo = randomString();
|
||||
String notifyUrl = randomURL();
|
||||
Integer price = randomInteger();
|
||||
when(defaultAlipayClient.execute(argThat((ArgumentMatcher<AlipayTradePrecreateRequest>) request -> {
|
||||
assertEquals(notifyUrl, request.getNotifyUrl());
|
||||
return true;
|
||||
}))).thenThrow(ServiceExceptionUtil.exception(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR));
|
||||
// 准备请求参数
|
||||
PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(notifyUrl, outTradeNo, price);
|
||||
|
||||
// 调用,并断言
|
||||
assertThrows(ServiceException.class, () -> client.unifiedOrder(reqDTO));
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,111 @@
|
||||
package cn.iocoder.yudao.module.pay.framework.pay.core.client.impl.alipay;
|
||||
|
||||
import cn.hutool.http.Method;
|
||||
import cn.iocoder.yudao.module.pay.framework.pay.core.client.dto.order.PayOrderRespDTO;
|
||||
import cn.iocoder.yudao.module.pay.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.module.pay.framework.pay.core.enums.PayOrderDisplayModeEnum;
|
||||
import com.alipay.api.AlipayApiException;
|
||||
import com.alipay.api.domain.AlipayTradeWapPayModel;
|
||||
import com.alipay.api.request.AlipayTradeWapPayRequest;
|
||||
import com.alipay.api.response.AlipayTradeWapPayResponse;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentMatcher;
|
||||
import org.mockito.InjectMocks;
|
||||
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
|
||||
import static cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum.CLOSED;
|
||||
import static cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum.WAITING;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* {@link AlipayWapPayClient} 单元测试
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
public class AlipayWapPayClientTest extends AbstractAlipayClientTest {
|
||||
|
||||
/**
|
||||
* 支付宝 H5 支付 Client
|
||||
*/
|
||||
@InjectMocks
|
||||
private AlipayWapPayClient client = new AlipayWapPayClient(randomLongId(), config);
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
setClient(client);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("支付宝 H5 支付:下单成功")
|
||||
public void testUnifiedOrder_success() throws AlipayApiException {
|
||||
// mock 方法
|
||||
String h5Body = randomString();
|
||||
Integer price = randomInteger();
|
||||
AlipayTradeWapPayResponse response = randomPojo(AlipayTradeWapPayResponse.class, o -> {
|
||||
o.setSubCode("");
|
||||
o.setBody(h5Body);
|
||||
});
|
||||
String notifyUrl = randomURL();
|
||||
when(defaultAlipayClient.pageExecute(argThat((ArgumentMatcher<AlipayTradeWapPayRequest>) request -> {
|
||||
assertInstanceOf(AlipayTradeWapPayModel.class, request.getBizModel());
|
||||
AlipayTradeWapPayModel bizModel = (AlipayTradeWapPayModel) request.getBizModel();
|
||||
assertEquals(String.valueOf(price / 100.0), bizModel.getTotalAmount());
|
||||
assertEquals(notifyUrl, request.getNotifyUrl());
|
||||
return true;
|
||||
}), eq(Method.GET.name()))).thenReturn(response);
|
||||
// 准备请求参数
|
||||
String outTradeNo = randomString();
|
||||
PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(notifyUrl, outTradeNo, price);
|
||||
|
||||
// 调用
|
||||
PayOrderRespDTO resp = client.unifiedOrder(reqDTO);
|
||||
// 断言
|
||||
assertEquals(WAITING.getStatus(), resp.getStatus());
|
||||
assertEquals(outTradeNo, resp.getOutTradeNo());
|
||||
assertNull(resp.getChannelOrderNo());
|
||||
assertNull(resp.getChannelUserId());
|
||||
assertNull(resp.getSuccessTime());
|
||||
assertEquals(PayOrderDisplayModeEnum.URL.getMode(), resp.getDisplayMode());
|
||||
assertEquals(response.getBody(), resp.getDisplayContent());
|
||||
assertSame(response, resp.getRawData());
|
||||
assertNull(resp.getChannelErrorCode());
|
||||
assertNull(resp.getChannelErrorMsg());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("支付宝 H5 支付:渠道返回失败")
|
||||
public void test_unified_order_channel_failed() throws AlipayApiException {
|
||||
// mock 方法
|
||||
String subCode = randomString();
|
||||
String subMsg = randomString();
|
||||
AlipayTradeWapPayResponse response = randomPojo(AlipayTradeWapPayResponse.class, o -> {
|
||||
o.setSubCode(subCode);
|
||||
o.setSubMsg(subMsg);
|
||||
});
|
||||
when(defaultAlipayClient.pageExecute(argThat((ArgumentMatcher<AlipayTradeWapPayRequest>) request -> true),
|
||||
eq(Method.GET.name()))).thenReturn(response);
|
||||
String outTradeNo = randomString();
|
||||
PayOrderUnifiedReqDTO reqDTO = buildOrderUnifiedReqDTO(randomURL(), outTradeNo, randomInteger());
|
||||
|
||||
// 调用
|
||||
PayOrderRespDTO resp = client.unifiedOrder(reqDTO);
|
||||
// 断言
|
||||
assertEquals(CLOSED.getStatus(), resp.getStatus());
|
||||
assertEquals(outTradeNo, resp.getOutTradeNo());
|
||||
assertNull(resp.getChannelOrderNo());
|
||||
assertNull(resp.getChannelUserId());
|
||||
assertNull(resp.getSuccessTime());
|
||||
assertNull(resp.getDisplayMode());
|
||||
assertNull(resp.getDisplayContent());
|
||||
assertSame(response, resp.getRawData());
|
||||
assertEquals(subCode, resp.getChannelErrorCode());
|
||||
assertEquals(subMsg, resp.getChannelErrorMsg());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,123 @@
|
||||
package cn.iocoder.yudao.module.pay.framework.pay.core.client.impl.weixin;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
|
||||
import com.github.binarywang.wxpay.bean.request.WxPayMicropayRequest;
|
||||
import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest;
|
||||
import com.github.binarywang.wxpay.bean.request.WxPayRefundV3Request;
|
||||
import com.github.binarywang.wxpay.bean.result.WxPayMicropayResult;
|
||||
import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
|
||||
import com.github.binarywang.wxpay.bean.result.WxPayRefundV3Result;
|
||||
import com.github.binarywang.wxpay.config.WxPayConfig;
|
||||
import com.github.binarywang.wxpay.exception.WxPayException;
|
||||
import com.github.binarywang.wxpay.service.WxPayService;
|
||||
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import static cn.iocoder.yudao.module.pay.framework.pay.core.client.impl.weixin.AbstractWxPayClient.formatDateV2;
|
||||
|
||||
/**
|
||||
* {@link WxBarPayClient} 的集成测试,用于快速调试微信条码支付
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Disabled
|
||||
public class WxBarPayClientIntegrationTest {
|
||||
|
||||
@Test
|
||||
public void testPayV2() throws WxPayException {
|
||||
// 创建 config 配置
|
||||
WxPayConfig config = buildWxPayConfigV2();
|
||||
// 创建 WxPayService 客户端
|
||||
WxPayService client = new WxPayServiceImpl();
|
||||
client.setConfig(config);
|
||||
|
||||
// 执行发起支付
|
||||
WxPayMicropayRequest request = WxPayMicropayRequest.newBuilder()
|
||||
.outTradeNo(String.valueOf(System.currentTimeMillis()))
|
||||
.body("测试支付-body")
|
||||
.detail("测试支付-detail")
|
||||
.totalFee(1) // 单位分
|
||||
.timeExpire(formatDateV2(LocalDateTimeUtils.addTime(Duration.ofMinutes(2))))
|
||||
.spbillCreateIp("127.0.0.1")
|
||||
.authCode("134298744426278497")
|
||||
.build();
|
||||
System.out.println("========= request ==========");
|
||||
System.out.println(JsonUtils.toJsonPrettyString(request));
|
||||
WxPayMicropayResult response = client.micropay(request);
|
||||
System.out.println("========= response ==========");
|
||||
System.out.println(JsonUtils.toJsonPrettyString(response));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseRefundNotifyV2() throws WxPayException {
|
||||
// 创建 config 配置
|
||||
WxPayConfig config = buildWxPayConfigV2();
|
||||
// 创建 WxPayService 客户端
|
||||
WxPayService client = new WxPayServiceImpl();
|
||||
client.setConfig(config);
|
||||
|
||||
// 执行解析
|
||||
String xml = "<xml><return_code>SUCCESS</return_code><appid><![CDATA[wx62056c0d5e8db250]]></appid><mch_id><![CDATA[1545083881]]></mch_id><nonce_str><![CDATA[ed8f02c21d15635cede114a42d0525a0]]></nonce_str><req_info><![CDATA[bGp+wB9DAHjoOO9Nw1iSmmIFdN2zZDhsoRWZBYdf/8bcpjowr4T8i2qjLsbMtvKQeVC5kBZOL/Agal3be6UPwnoantil+L+ojZgvLch7dXFKs/AcoxIYcVYyGka+wmnRJfUmuFRBgzt++8HOFsmJz6e2brYv1EAz+93fP2AsJtRuw1FEzodcg8eXm52hbE0KhLNqC2OyNVkn8AbOOrwIxSYobg2jVbuJ4JllYbEGIQ/6kWzNbVmMKhGJGYBy/NbUGKoQsoe4QeTQqcqQqVp08muxaOfJGThaN3B9EEMFSrog/3yT7ykVV6WQ5+Ygt89LplOf5ucWa4Ird7VJhHWtzI92ZePj4Omy1XkT1TRlwtDegA0S5MeQpM4WZ1taMrhxgmNkTUJ0JXFncx5e2KLQvbvD/HOcccx48Xv1c16JBz6G3501k8E++LWXgZ2TeNXwGsk6FyRZb0ApLyQHIx5ZtPo/UET9z3AmJCPXkrUsZ4WK46fDtbzxVPU2r8nTOcGCPbO0LUsGT6wpsuQVC4CisXDJwoZmL6kKwHfKs6mmUL2YZYzNfgoB/KgpJYSpC96kcpQyFvw+xuwqK2SXGZbAl9lADT+a83z04feQHSSIG3PCrX4QEWzpCZZ4+ySEz1Y34aoU20X9GtX+1LSwUjmQgwHrMBSvFm3/B7+IFM8OUqDB+Uvkr9Uvy7P2/KDvfy3Ih7GFcGd0C5NXpSvVTTfu1IlK/T3/t6MR/8iq78pp/2ZTYvO6eNDRJWaXYU+x6sl2dTs9n+2Z4W4AfYTvEyuxlx+aI19SqCJh7WmaFcAxidFl/9iqDjWiplb9+C6ijZv2hJtVjSCuoptIWpGDYItH7RAqlKHrx6flJD+M/5BceMHBv2w4OWCD9vPRLo8gl9o06ip0iflzO1dixhOAgLFjsQmQHNGFtR3EvCID+iS4FUlilwK+hcKNxrr0wp9Btkl9W1R9aTo289CUiIxx45skfCYzHwb+7Hqj3uTiXnep6zhCKZBAnPsDOvISXfBgXKufcFsTNtts09jX8H5/uMc9wyJ179H1cp+At1mIK2duwfo4Q9asfEoffl6Zn1olGdtEruxHGeVU0NwJ8V7RflC/Cx5RXtJ3sPJ/sHmVnBlVyR0=]]></req_info></xml>";
|
||||
WxPayRefundNotifyResult response = client.parseRefundNotifyResult(xml);
|
||||
System.out.println(response.getReqInfo());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefundV2() throws WxPayException {
|
||||
// 创建 config 配置
|
||||
WxPayConfig config = buildWxPayConfigV2();
|
||||
// 创建 WxPayService 客户端
|
||||
WxPayService client = new WxPayServiceImpl();
|
||||
client.setConfig(config);
|
||||
|
||||
// 执行发起退款
|
||||
WxPayRefundRequest request = new WxPayRefundRequest()
|
||||
.setOutTradeNo("1689545667276")
|
||||
.setOutRefundNo(String.valueOf(System.currentTimeMillis()))
|
||||
.setRefundFee(1)
|
||||
.setRefundDesc("就是想退了")
|
||||
.setTotalFee(1);
|
||||
System.out.println("========= request ==========");
|
||||
System.out.println(JsonUtils.toJsonPrettyString(request));
|
||||
WxPayRefundResult response = client.refund(request);
|
||||
System.out.println("========= response ==========");
|
||||
System.out.println(JsonUtils.toJsonPrettyString(response));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefundV3() throws WxPayException {
|
||||
// 创建 config 配置
|
||||
WxPayConfig config = buildWxPayConfigV2();
|
||||
// 创建 WxPayService 客户端
|
||||
WxPayService client = new WxPayServiceImpl();
|
||||
client.setConfig(config);
|
||||
|
||||
// 执行发起退款
|
||||
WxPayRefundV3Request request = new WxPayRefundV3Request()
|
||||
.setOutTradeNo("1689506325635")
|
||||
.setOutRefundNo(String.valueOf(System.currentTimeMillis()))
|
||||
.setAmount(new WxPayRefundV3Request.Amount().setTotal(1).setRefund(1).setCurrency("CNY"))
|
||||
.setReason("就是想退了");
|
||||
System.out.println("========= request ==========");
|
||||
System.out.println(JsonUtils.toJsonPrettyString(request));
|
||||
WxPayRefundV3Result response = client.refundV3(request);
|
||||
System.out.println("========= response ==========");
|
||||
System.out.println(JsonUtils.toJsonPrettyString(response));
|
||||
}
|
||||
|
||||
private WxPayConfig buildWxPayConfigV2() {
|
||||
WxPayConfig config = new WxPayConfig();
|
||||
config.setAppId("wx62056c0d5e8db250");
|
||||
config.setMchId("1545083881");
|
||||
config.setMchKey("dS1ngeN63JLr3NRbvPH9AJy3MyUxZdim");
|
||||
// config.setSignType(WxPayConstants.SignType.MD5);
|
||||
config.setKeyPath("/Users/yunai/Downloads/wx_pay/apiclient_cert.p12");
|
||||
return config;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,84 @@
|
||||
package cn.iocoder.yudao.module.pay.framework.pay.core.client.impl.weixin;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import com.github.binarywang.wxpay.bean.request.WxPayRefundV3Request;
|
||||
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
|
||||
import com.github.binarywang.wxpay.bean.result.WxPayRefundV3Result;
|
||||
import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
|
||||
import com.github.binarywang.wxpay.config.WxPayConfig;
|
||||
import com.github.binarywang.wxpay.exception.WxPayException;
|
||||
import com.github.binarywang.wxpay.service.WxPayService;
|
||||
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import static cn.iocoder.yudao.module.pay.framework.pay.core.client.impl.weixin.AbstractWxPayClient.formatDateV3;
|
||||
|
||||
|
||||
/**
|
||||
* {@link WxNativePayClient} 的集成测试,用于快速调试微信扫码支付
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Disabled
|
||||
public class WxNativePayClientIntegrationTest {
|
||||
|
||||
@Test
|
||||
public void testPayV3() throws WxPayException {
|
||||
// 创建 config 配置
|
||||
WxPayConfig config = buildWxPayConfigV3();
|
||||
// 创建 WxPayService 客户端
|
||||
WxPayService client = new WxPayServiceImpl();
|
||||
client.setConfig(config);
|
||||
|
||||
// 执行发起支付
|
||||
WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request()
|
||||
.setOutTradeNo(String.valueOf(System.currentTimeMillis()))
|
||||
.setDescription("测试支付-body")
|
||||
.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(1)) // 单位分
|
||||
.setTimeExpire(formatDateV3(LocalDateTimeUtils.addTime(Duration.ofMinutes(2))))
|
||||
.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp("127.0.0.1"))
|
||||
.setNotifyUrl("http://127.0.0.1:48080");
|
||||
System.out.println("========= request ==========");
|
||||
System.out.println(JsonUtils.toJsonPrettyString(request));
|
||||
String response = client.createOrderV3(TradeTypeEnum.NATIVE, request);
|
||||
System.out.println("========= response ==========");
|
||||
System.out.println(JsonUtils.toJsonPrettyString(response));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefundV3() throws WxPayException {
|
||||
// 创建 config 配置
|
||||
WxPayConfig config = buildWxPayConfigV3();
|
||||
// 创建 WxPayService 客户端
|
||||
WxPayService client = new WxPayServiceImpl();
|
||||
client.setConfig(config);
|
||||
|
||||
// 执行发起退款
|
||||
WxPayRefundV3Request request = new WxPayRefundV3Request()
|
||||
.setOutTradeNo("1689545729695")
|
||||
.setOutRefundNo(String.valueOf(System.currentTimeMillis()))
|
||||
.setAmount(new WxPayRefundV3Request.Amount().setTotal(1).setRefund(1).setCurrency("CNY"))
|
||||
.setReason("就是想退了");
|
||||
System.out.println("========= request ==========");
|
||||
System.out.println(JsonUtils.toJsonPrettyString(request));
|
||||
WxPayRefundV3Result response = client.refundV3(request);
|
||||
System.out.println("========= response ==========");
|
||||
System.out.println(JsonUtils.toJsonPrettyString(response));
|
||||
}
|
||||
|
||||
private WxPayConfig buildWxPayConfigV3() {
|
||||
WxPayConfig config = new WxPayConfig();
|
||||
config.setAppId("wx62056c0d5e8db250");
|
||||
config.setMchId("1545083881");
|
||||
config.setApiV3Key("459arNsYHl1mgkiO6H9ZH5KkhFXSxaA4");
|
||||
// config.setCertSerialNo(serialNo);
|
||||
config.setPrivateCertPath("/Users/yunai/Downloads/wx_pay/apiclient_cert.pem");
|
||||
config.setPrivateKeyPath("/Users/yunai/Downloads/wx_pay/apiclient_key.pem");
|
||||
return config;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,260 @@
|
||||
package cn.iocoder.yudao.module.pay.service.app;
|
||||
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
|
||||
import cn.iocoder.yudao.module.pay.controller.admin.app.vo.PayAppCreateReqVO;
|
||||
import cn.iocoder.yudao.module.pay.controller.admin.app.vo.PayAppPageReqVO;
|
||||
import cn.iocoder.yudao.module.pay.controller.admin.app.vo.PayAppUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO;
|
||||
import cn.iocoder.yudao.module.pay.dal.mysql.app.PayAppMapper;
|
||||
import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
|
||||
import cn.iocoder.yudao.module.pay.service.refund.PayRefundService;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
|
||||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;
|
||||
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
|
||||
import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
|
||||
import static java.util.Collections.singleton;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* {@link PayAppServiceImpl} 的单元测试
|
||||
*
|
||||
* @author aquan
|
||||
*/
|
||||
@Disabled // TODO 芋艿:后续 fix 补充的单测
|
||||
@Import(PayAppServiceImpl.class)
|
||||
public class PayAppServiceTest extends BaseDbUnitTest {
|
||||
|
||||
@Resource
|
||||
private PayAppServiceImpl appService;
|
||||
|
||||
@Resource
|
||||
private PayAppMapper appMapper;
|
||||
|
||||
@MockBean
|
||||
private PayOrderService orderService;
|
||||
@MockBean
|
||||
private PayRefundService refundService;
|
||||
|
||||
@Test
|
||||
public void testCreateApp_success() {
|
||||
// 准备参数
|
||||
PayAppCreateReqVO reqVO = randomPojo(PayAppCreateReqVO.class, o ->
|
||||
o.setStatus((RandomUtil.randomEle(CommonStatusEnum.values()).getStatus()))
|
||||
.setOrderNotifyUrl(randomURL())
|
||||
.setRefundNotifyUrl(randomURL()));
|
||||
|
||||
// 调用
|
||||
Long appId = appService.createApp(reqVO);
|
||||
// 断言
|
||||
assertNotNull(appId);
|
||||
PayAppDO app = appMapper.selectById(appId);
|
||||
assertPojoEquals(reqVO, app);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateApp_success() {
|
||||
// mock 数据
|
||||
PayAppDO dbApp = randomPojo(PayAppDO.class);
|
||||
appMapper.insert(dbApp);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
PayAppUpdateReqVO reqVO = randomPojo(PayAppUpdateReqVO.class, o -> {
|
||||
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
o.setOrderNotifyUrl(randomURL()).setRefundNotifyUrl(randomURL());
|
||||
o.setId(dbApp.getId()); // 设置更新的 ID
|
||||
});
|
||||
|
||||
// 调用
|
||||
appService.updateApp(reqVO);
|
||||
// 校验是否更新正确
|
||||
PayAppDO app = appMapper.selectById(reqVO.getId()); // 获取最新的
|
||||
assertPojoEquals(reqVO, app);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateApp_notExists() {
|
||||
// 准备参数
|
||||
PayAppUpdateReqVO reqVO = randomPojo(PayAppUpdateReqVO.class, o ->
|
||||
o.setStatus((RandomUtil.randomEle(CommonStatusEnum.values()).getStatus())));
|
||||
// 调用, 并断言异常
|
||||
assertServiceException(() -> appService.updateApp(reqVO), APP_NOT_FOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateAppStatus() {
|
||||
// mock 数据
|
||||
PayAppDO dbApp = randomPojo(PayAppDO.class, o ->
|
||||
o.setStatus(CommonStatusEnum.DISABLE.getStatus()));
|
||||
appMapper.insert(dbApp);// @Sql: 先插入出一条存在的数据
|
||||
|
||||
// 准备参数
|
||||
Long id = dbApp.getId();
|
||||
Integer status = CommonStatusEnum.ENABLE.getStatus();
|
||||
// 调用
|
||||
appService.updateAppStatus(id, status);
|
||||
// 断言
|
||||
PayAppDO app = appMapper.selectById(id); // 获取最新的
|
||||
assertEquals(status, app.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteApp_success() {
|
||||
// mock 数据
|
||||
PayAppDO dbApp = randomPojo(PayAppDO.class);
|
||||
appMapper.insert(dbApp);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
Long id = dbApp.getId();
|
||||
|
||||
// 调用
|
||||
appService.deleteApp(id);
|
||||
// 校验数据不存在了
|
||||
assertNull(appMapper.selectById(id));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteApp_notExists() {
|
||||
// 准备参数
|
||||
Long id = randomLongId();
|
||||
|
||||
// 调用, 并断言异常
|
||||
assertServiceException(() -> appService.deleteApp(id), APP_NOT_FOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteApp_existOrder() {
|
||||
// mock 数据
|
||||
PayAppDO dbApp = randomPojo(PayAppDO.class);
|
||||
appMapper.insert(dbApp);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
Long id = dbApp.getId();
|
||||
// mock 订单有订单
|
||||
when(orderService.getOrderCountByAppId(eq(id))).thenReturn(10L);
|
||||
|
||||
// 调用, 并断言异常
|
||||
assertServiceException(() -> appService.deleteApp(id), APP_EXIST_ORDER_CANT_DELETE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteApp_existRefund() {
|
||||
// mock 数据
|
||||
PayAppDO dbApp = randomPojo(PayAppDO.class);
|
||||
appMapper.insert(dbApp);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
Long id = dbApp.getId();
|
||||
// mock 订单有订单
|
||||
when(refundService.getRefundCountByAppId(eq(id))).thenReturn(10L);
|
||||
|
||||
// 调用, 并断言异常
|
||||
assertServiceException(() -> appService.deleteApp(id), APP_EXIST_REFUND_CANT_DELETE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testApp() {
|
||||
// mock 数据
|
||||
PayAppDO dbApp = randomPojo(PayAppDO.class);
|
||||
appMapper.insert(dbApp);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
Long id = dbApp.getId();
|
||||
|
||||
// 调用
|
||||
PayAppDO app = appService.getApp(id);
|
||||
// 校验数据一致
|
||||
assertPojoEquals(app, dbApp);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAppMap() {
|
||||
// mock 数据
|
||||
PayAppDO dbApp01 = randomPojo(PayAppDO.class);
|
||||
appMapper.insert(dbApp01);// @Sql: 先插入出一条存在的数据
|
||||
PayAppDO dbApp02 = randomPojo(PayAppDO.class);
|
||||
appMapper.insert(dbApp02);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
Long id = dbApp01.getId();
|
||||
|
||||
// 调用
|
||||
Map<Long, PayAppDO> appMap = appService.getAppMap(singleton(id));
|
||||
// 校验数据一致
|
||||
assertEquals(1, appMap.size());
|
||||
assertPojoEquals(dbApp01, appMap.get(id));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAppPage() {
|
||||
// mock 数据
|
||||
PayAppDO dbApp = randomPojo(PayAppDO.class, o -> { // 等会查询到
|
||||
o.setName("灿灿姐的杂货铺");
|
||||
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
o.setCreateTime(buildTime(2021,11,20));
|
||||
});
|
||||
|
||||
appMapper.insert(dbApp);
|
||||
// 测试 name 不匹配
|
||||
appMapper.insert(cloneIgnoreId(dbApp, o -> o.setName("敏敏姐的杂货铺")));
|
||||
// 测试 status 不匹配
|
||||
appMapper.insert(cloneIgnoreId(dbApp, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
|
||||
// 测试 createTime 不匹配
|
||||
appMapper.insert(cloneIgnoreId(dbApp, o -> o.setCreateTime(buildTime(2021,12,21))));
|
||||
// 准备参数
|
||||
PayAppPageReqVO reqVO = new PayAppPageReqVO();
|
||||
reqVO.setName("灿灿姐的杂货铺");
|
||||
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
reqVO.setCreateTime(buildBetweenTime(2021, 11, 19, 2021, 11, 21));
|
||||
|
||||
// 调用
|
||||
PageResult<PayAppDO> pageResult = appService.getAppPage(reqVO);
|
||||
// 断言
|
||||
assertEquals(1, pageResult.getTotal());
|
||||
assertEquals(1, pageResult.getList().size());
|
||||
assertPojoEquals(dbApp, pageResult.getList().get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidPayApp_success() {
|
||||
// mock 数据
|
||||
PayAppDO dbApp = randomPojo(PayAppDO.class,
|
||||
o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()));
|
||||
appMapper.insert(dbApp);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
Long id = dbApp.getId();
|
||||
|
||||
// 调用
|
||||
PayAppDO app = appService.validPayApp(id);
|
||||
// 校验数据一致
|
||||
assertPojoEquals(app, dbApp);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidPayApp_notFound() {
|
||||
assertServiceException(() -> appService.validPayApp(randomLongId()), APP_NOT_FOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidPayApp_disable() {
|
||||
// mock 数据
|
||||
PayAppDO dbApp = randomPojo(PayAppDO.class,
|
||||
o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()));
|
||||
appMapper.insert(dbApp);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
Long id = dbApp.getId();
|
||||
|
||||
// 调用,并断言异常
|
||||
assertServiceException(() -> appService.validPayApp(id), APP_IS_DISABLE);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,337 @@
|
||||
package cn.iocoder.yudao.module.pay.service.channel;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import cn.iocoder.yudao.module.pay.enums.PayChannelEnum;
|
||||
import cn.iocoder.yudao.module.pay.framework.pay.core.client.PayClient;
|
||||
import cn.iocoder.yudao.module.pay.framework.pay.core.client.PayClientFactory;
|
||||
import cn.iocoder.yudao.module.pay.framework.pay.core.client.impl.alipay.AlipayPayClientConfig;
|
||||
import cn.iocoder.yudao.module.pay.framework.pay.core.client.impl.weixin.WxPayClientConfig;
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
|
||||
import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelCreateReqVO;
|
||||
import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO;
|
||||
import cn.iocoder.yudao.module.pay.dal.mysql.channel.PayChannelMapper;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Validator;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
|
||||
import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@Import({PayChannelServiceImpl.class})
|
||||
public class PayChannelServiceTest extends BaseDbUnitTest {
|
||||
|
||||
@Resource
|
||||
private PayChannelServiceImpl channelService;
|
||||
|
||||
@Resource
|
||||
private PayChannelMapper channelMapper;
|
||||
|
||||
@MockBean
|
||||
private PayClientFactory payClientFactory;
|
||||
@MockBean
|
||||
private Validator validator;
|
||||
|
||||
@Test
|
||||
public void testCreateChannel_success() {
|
||||
// 准备参数
|
||||
WxPayClientConfig config = randomWxPayClientConfig();
|
||||
PayChannelCreateReqVO reqVO = randomPojo(PayChannelCreateReqVO.class, o -> {
|
||||
o.setStatus(randomCommonStatus());
|
||||
o.setCode(PayChannelEnum.WX_PUB.getCode());
|
||||
o.setConfig(JsonUtils.toJsonString(config));
|
||||
});
|
||||
|
||||
// 调用
|
||||
Long channelId = channelService.createChannel(reqVO);
|
||||
// 校验记录的属性是否正确
|
||||
PayChannelDO channel = channelMapper.selectById(channelId);
|
||||
assertPojoEquals(reqVO, channel, "config");
|
||||
assertPojoEquals(config, channel.getConfig());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateChannel_exists() {
|
||||
// mock 数据
|
||||
PayChannelDO dbChannel = randomPojo(PayChannelDO.class,
|
||||
o -> o.setConfig(randomWxPayClientConfig()));
|
||||
channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
PayChannelCreateReqVO reqVO = randomPojo(PayChannelCreateReqVO.class, o -> {
|
||||
o.setAppId(dbChannel.getAppId());
|
||||
o.setCode(dbChannel.getCode());
|
||||
});
|
||||
|
||||
// 调用, 并断言异常
|
||||
assertServiceException(() -> channelService.createChannel(reqVO), CHANNEL_EXIST_SAME_CHANNEL_ERROR);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateChannel_success() {
|
||||
// mock 数据
|
||||
PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> {
|
||||
o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
|
||||
o.setConfig(randomAlipayPayClientConfig());
|
||||
});
|
||||
channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
AlipayPayClientConfig config = randomAlipayPayClientConfig();
|
||||
PayChannelUpdateReqVO reqVO = randomPojo(PayChannelUpdateReqVO.class, o -> {
|
||||
o.setId(dbChannel.getId()); // 设置更新的 ID
|
||||
o.setStatus(randomCommonStatus());
|
||||
o.setConfig(JsonUtils.toJsonString(config));
|
||||
});
|
||||
|
||||
// 调用
|
||||
channelService.updateChannel(reqVO);
|
||||
// 校验是否更新正确
|
||||
PayChannelDO channel = channelMapper.selectById(reqVO.getId()); // 获取最新的
|
||||
assertPojoEquals(reqVO, channel, "config");
|
||||
assertPojoEquals(config, channel.getConfig());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateChannel_notExists() {
|
||||
// 准备参数
|
||||
AlipayPayClientConfig payClientPublicKeyConfig = randomAlipayPayClientConfig();
|
||||
PayChannelUpdateReqVO reqVO = randomPojo(PayChannelUpdateReqVO.class, o -> {
|
||||
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
o.setConfig(JSON.toJSONString(payClientPublicKeyConfig));
|
||||
});
|
||||
|
||||
// 调用, 并断言异常
|
||||
assertServiceException(() -> channelService.updateChannel(reqVO), CHANNEL_NOT_FOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteChannel_success() {
|
||||
// mock 数据
|
||||
PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> {
|
||||
o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
|
||||
o.setConfig(randomAlipayPayClientConfig());
|
||||
});
|
||||
channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
Long id = dbChannel.getId();
|
||||
|
||||
// 调用
|
||||
channelService.deleteChannel(id);
|
||||
// 校验数据不存在了
|
||||
assertNull(channelMapper.selectById(id));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteChannel_notExists() {
|
||||
// 准备参数
|
||||
Long id = randomLongId();
|
||||
|
||||
// 调用, 并断言异常
|
||||
assertServiceException(() -> channelService.deleteChannel(id), CHANNEL_NOT_FOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetChannel() {
|
||||
// mock 数据
|
||||
PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> {
|
||||
o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
|
||||
o.setConfig(randomAlipayPayClientConfig());
|
||||
});
|
||||
channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
Long id = dbChannel.getId();
|
||||
|
||||
// 调用
|
||||
PayChannelDO channel = channelService.getChannel(id);
|
||||
// 校验是否更新正确
|
||||
assertPojoEquals(dbChannel, channel);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetChannelListByAppIds() {
|
||||
// mock 数据
|
||||
PayChannelDO dbChannel01 = randomPojo(PayChannelDO.class, o -> {
|
||||
o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
|
||||
o.setConfig(randomAlipayPayClientConfig());
|
||||
});
|
||||
channelMapper.insert(dbChannel01);// @Sql: 先插入出一条存在的数据
|
||||
PayChannelDO dbChannel02 = randomPojo(PayChannelDO.class, o -> {
|
||||
o.setCode(PayChannelEnum.WX_PUB.getCode());
|
||||
o.setConfig(randomWxPayClientConfig());
|
||||
});
|
||||
channelMapper.insert(dbChannel02);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
Long appId = dbChannel01.getAppId();
|
||||
|
||||
// 调用
|
||||
List<PayChannelDO> channels = channelService.getChannelListByAppIds(Collections.singleton(appId));
|
||||
// 校验是否更新正确
|
||||
assertEquals(1, channels.size());
|
||||
assertPojoEquals(dbChannel01, channels.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetChannelByAppIdAndCode() {
|
||||
// mock 数据
|
||||
PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> {
|
||||
o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
|
||||
o.setConfig(randomAlipayPayClientConfig());
|
||||
});
|
||||
channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
Long appId = dbChannel.getAppId();
|
||||
String code = dbChannel.getCode();
|
||||
|
||||
// 调用
|
||||
PayChannelDO channel = channelService.getChannelByAppIdAndCode(appId, code);
|
||||
// 断言
|
||||
assertPojoEquals(channel, dbChannel);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidPayChannel_notExists() {
|
||||
// 准备参数
|
||||
Long id = randomLongId();
|
||||
|
||||
// 调用, 并断言异常
|
||||
assertServiceException(() -> channelService.validPayChannel(id), CHANNEL_NOT_FOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidPayChannel_isDisable() {
|
||||
// mock 数据
|
||||
PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> {
|
||||
o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
|
||||
o.setConfig(randomAlipayPayClientConfig());
|
||||
o.setStatus(CommonStatusEnum.DISABLE.getStatus());
|
||||
});
|
||||
channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
Long id = dbChannel.getId();
|
||||
|
||||
// 调用, 并断言异常
|
||||
assertServiceException(() -> channelService.validPayChannel(id), CHANNEL_IS_DISABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidPayChannel_success() {
|
||||
// mock 数据
|
||||
PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> {
|
||||
o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
|
||||
o.setConfig(randomAlipayPayClientConfig());
|
||||
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
});
|
||||
channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
Long id = dbChannel.getId();
|
||||
|
||||
// 调用
|
||||
PayChannelDO channel = channelService.validPayChannel(id);
|
||||
// 断言异常
|
||||
assertPojoEquals(channel, dbChannel);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidPayChannel_appIdAndCode() {
|
||||
// mock 数据
|
||||
PayChannelDO dbChannel = randomPojo(PayChannelDO.class, o -> {
|
||||
o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
|
||||
o.setConfig(randomAlipayPayClientConfig());
|
||||
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
});
|
||||
channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据
|
||||
// 准备参数
|
||||
Long appId = dbChannel.getAppId();
|
||||
String code = dbChannel.getCode();
|
||||
|
||||
// 调用
|
||||
PayChannelDO channel = channelService.validPayChannel(appId, code);
|
||||
// 断言异常
|
||||
assertPojoEquals(channel, dbChannel);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetEnableChannelList() {
|
||||
// 准备参数
|
||||
Long appId = randomLongId();
|
||||
// mock 数据 01(enable 不匹配)
|
||||
PayChannelDO dbChannel01 = randomPojo(PayChannelDO.class, o -> {
|
||||
o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
|
||||
o.setConfig(randomAlipayPayClientConfig());
|
||||
o.setStatus(CommonStatusEnum.DISABLE.getStatus());
|
||||
});
|
||||
channelMapper.insert(dbChannel01);// @Sql: 先插入出一条存在的数据
|
||||
// mock 数据 02(appId 不匹配)
|
||||
PayChannelDO dbChannel02 = randomPojo(PayChannelDO.class, o -> {
|
||||
o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
|
||||
o.setConfig(randomAlipayPayClientConfig());
|
||||
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
});
|
||||
channelMapper.insert(dbChannel02);// @Sql: 先插入出一条存在的数据
|
||||
// mock 数据 03
|
||||
PayChannelDO dbChannel03 = randomPojo(PayChannelDO.class, o -> {
|
||||
o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
|
||||
o.setConfig(randomAlipayPayClientConfig());
|
||||
o.setAppId(appId);
|
||||
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
});
|
||||
channelMapper.insert(dbChannel03);// @Sql: 先插入出一条存在的数据
|
||||
|
||||
// 调用
|
||||
List<PayChannelDO> channel = channelService.getEnableChannelList(appId);
|
||||
// 断言异常
|
||||
assertPojoEquals(channel, dbChannel03);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPayClient() {
|
||||
// mock 数据
|
||||
PayChannelDO channel = randomPojo(PayChannelDO.class, o -> {
|
||||
o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
|
||||
o.setConfig(randomAlipayPayClientConfig());
|
||||
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
});
|
||||
channelMapper.insert(channel);
|
||||
// mock 参数
|
||||
Long id = channel.getId();
|
||||
// mock 方法
|
||||
PayClient mockClient = mock(PayClient.class);
|
||||
when(payClientFactory.createOrUpdatePayClient(eq(id), eq(channel.getCode()), eq(channel.getConfig())))
|
||||
.thenReturn(mockClient);
|
||||
|
||||
// 调用
|
||||
PayClient<?> client = channelService.getPayClient(id);
|
||||
// 断言
|
||||
assertSame(client, mockClient);
|
||||
}
|
||||
|
||||
public WxPayClientConfig randomWxPayClientConfig() {
|
||||
return new WxPayClientConfig()
|
||||
.setAppId(randomString())
|
||||
.setMchId(randomString())
|
||||
.setApiVersion(WxPayClientConfig.API_VERSION_V2)
|
||||
.setMchKey(randomString());
|
||||
}
|
||||
|
||||
public AlipayPayClientConfig randomAlipayPayClientConfig() {
|
||||
return new AlipayPayClientConfig()
|
||||
.setServerUrl(randomURL())
|
||||
.setAppId(randomString())
|
||||
.setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT)
|
||||
.setMode(AlipayPayClientConfig.MODE_PUBLIC_KEY)
|
||||
.setPrivateKey(randomString())
|
||||
.setAlipayPublicKey(randomString());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,353 @@
|
||||
package cn.iocoder.yudao.module.pay.service.notify;
|
||||
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
|
||||
import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskPageReqVO;
|
||||
import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyLogDO;
|
||||
import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyTaskDO;
|
||||
import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
|
||||
import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO;
|
||||
import cn.iocoder.yudao.module.pay.dal.mysql.notify.PayNotifyLogMapper;
|
||||
import cn.iocoder.yudao.module.pay.dal.mysql.notify.PayNotifyTaskMapper;
|
||||
import cn.iocoder.yudao.module.pay.dal.redis.notify.PayNotifyLockRedisDAO;
|
||||
import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyStatusEnum;
|
||||
import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum;
|
||||
import cn.iocoder.yudao.module.pay.framework.job.config.PayJobConfiguration;
|
||||
import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
|
||||
import cn.iocoder.yudao.module.pay.service.refund.PayRefundService;
|
||||
import cn.iocoder.yudao.module.pay.service.refund.PayRefundServiceImpl;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.redisson.api.RLock;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;
|
||||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;
|
||||
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* {@link PayRefundServiceImpl} 的单元测试类
|
||||
*
|
||||
* @author 芋艿
|
||||
*/
|
||||
@Disabled // TODO 芋艿:后续 fix 补充的单测
|
||||
@Import({PayJobConfiguration.class, PayNotifyServiceImpl.class, PayNotifyLockRedisDAO.class})
|
||||
public class PayNotifyServiceTest extends BaseDbUnitTest {
|
||||
|
||||
@Resource
|
||||
private PayNotifyServiceImpl notifyService;
|
||||
|
||||
@MockBean
|
||||
private PayOrderService orderService;
|
||||
@MockBean
|
||||
private PayRefundService refundService;
|
||||
|
||||
@Resource
|
||||
private PayNotifyTaskMapper notifyTaskMapper;
|
||||
@Resource
|
||||
private PayNotifyLogMapper notifyLogMapper;
|
||||
|
||||
@MockBean
|
||||
private RedissonClient redissonClient;
|
||||
|
||||
@Test
|
||||
public void testCreatePayNotifyTask_order() {
|
||||
PayNotifyServiceImpl payNotifyService = mock(PayNotifyServiceImpl.class);
|
||||
try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
|
||||
springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PayNotifyServiceImpl.class)))
|
||||
.thenReturn(payNotifyService);
|
||||
|
||||
// 准备参数
|
||||
Integer type = PayNotifyTypeEnum.ORDER.getType();
|
||||
Long dataId = 1L;
|
||||
// mock 方法(order)
|
||||
PayOrderDO order = randomPojo(PayOrderDO.class);
|
||||
when(orderService.getOrder(eq(1L))).thenReturn(order);
|
||||
// mock 方法(lock)
|
||||
mockLock(null); // null 的原因,是咱没办法拿到 taskId 新增
|
||||
|
||||
// 调用
|
||||
notifyService.createPayNotifyTask(type, dataId);
|
||||
// 断言,task
|
||||
PayNotifyTaskDO dbTask = notifyTaskMapper.selectOne(null);
|
||||
assertNotNull(dbTask.getNextNotifyTime());
|
||||
assertThat(dbTask)
|
||||
.extracting("type", "dataId", "status", "notifyTimes", "maxNotifyTimes",
|
||||
"appId", "merchantOrderId", "notifyUrl")
|
||||
.containsExactly(type, dataId, PayNotifyStatusEnum.WAITING.getStatus(), 0, 9,
|
||||
order.getAppId(), order.getMerchantOrderId(), order.getNotifyUrl());
|
||||
// 断言,调用
|
||||
verify(payNotifyService).executeNotify0(eq(dbTask));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatePayNotifyTask_refund() {
|
||||
PayNotifyServiceImpl payNotifyService = mock(PayNotifyServiceImpl.class);
|
||||
try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
|
||||
springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PayNotifyServiceImpl.class)))
|
||||
.thenReturn(payNotifyService);
|
||||
|
||||
// 准备参数
|
||||
Integer type = PayNotifyTypeEnum.REFUND.getType();
|
||||
Long dataId = 1L;
|
||||
// mock 方法(refund)
|
||||
PayRefundDO refund = randomPojo(PayRefundDO.class);
|
||||
when(refundService.getRefund(eq(1L))).thenReturn(refund);
|
||||
// mock 方法(lock)
|
||||
mockLock(null); // null 的原因,是咱没办法拿到 taskId 新增
|
||||
|
||||
// 调用
|
||||
notifyService.createPayNotifyTask(type, dataId);
|
||||
// 断言,task
|
||||
PayNotifyTaskDO dbTask = notifyTaskMapper.selectOne(null);
|
||||
assertNotNull(dbTask.getNextNotifyTime());
|
||||
assertThat(dbTask)
|
||||
.extracting("type", "dataId", "status", "notifyTimes", "maxNotifyTimes",
|
||||
"appId", "merchantOrderId", "notifyUrl")
|
||||
.containsExactly(type, dataId, PayNotifyStatusEnum.WAITING.getStatus(), 0, 9,
|
||||
refund.getAppId(), refund.getMerchantOrderId(), refund.getNotifyUrl());
|
||||
// 断言,调用
|
||||
verify(payNotifyService).executeNotify0(eq(dbTask));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecuteNotify() throws InterruptedException {
|
||||
// mock 数据(notify)
|
||||
PayNotifyTaskDO dbTask01 = randomPojo(PayNotifyTaskDO.class,
|
||||
o -> o.setStatus(PayNotifyStatusEnum.WAITING.getStatus())
|
||||
.setNextNotifyTime(addTime(Duration.ofMinutes(-1))));
|
||||
notifyTaskMapper.insert(dbTask01);
|
||||
PayNotifyTaskDO dbTask02 = randomPojo(PayNotifyTaskDO.class,
|
||||
o -> o.setStatus(PayNotifyStatusEnum.REQUEST_SUCCESS.getStatus())
|
||||
.setNextNotifyTime(addTime(Duration.ofMinutes(-1))));
|
||||
notifyTaskMapper.insert(dbTask02);
|
||||
PayNotifyTaskDO dbTask03 = randomPojo(PayNotifyTaskDO.class,
|
||||
o -> o.setStatus(PayNotifyStatusEnum.REQUEST_FAILURE.getStatus())
|
||||
.setNextNotifyTime(addTime(Duration.ofMinutes(-1))));
|
||||
notifyTaskMapper.insert(dbTask03);
|
||||
PayNotifyTaskDO dbTask04 = randomPojo(PayNotifyTaskDO.class, // 不满足状态
|
||||
o -> o.setStatus(PayNotifyStatusEnum.FAILURE.getStatus())
|
||||
.setNextNotifyTime(addTime(Duration.ofMinutes(-1))));
|
||||
notifyTaskMapper.insert(dbTask04);
|
||||
PayNotifyTaskDO dbTask05 = randomPojo(PayNotifyTaskDO.class, // 不满足状态
|
||||
o -> o.setStatus(PayNotifyStatusEnum.SUCCESS.getStatus())
|
||||
.setNextNotifyTime(addTime(Duration.ofMinutes(-1))));
|
||||
notifyTaskMapper.insert(dbTask05);
|
||||
PayNotifyTaskDO dbTask06 = randomPojo(PayNotifyTaskDO.class, // 不满足时间
|
||||
o -> o.setStatus(PayNotifyStatusEnum.SUCCESS.getStatus())
|
||||
.setNextNotifyTime(addTime(Duration.ofMinutes(1))));
|
||||
notifyTaskMapper.insert(dbTask06);
|
||||
// mock 方法(lock)
|
||||
mockLock(dbTask01.getId());
|
||||
mockLock(dbTask02.getId());
|
||||
mockLock(dbTask03.getId());
|
||||
|
||||
// 调用
|
||||
int count = notifyService.executeNotify();
|
||||
// 断言,数量
|
||||
assertEquals(count, 3);
|
||||
}
|
||||
|
||||
@Test // 由于 HttpUtil 不好 mock,所以只测试异常的情况
|
||||
public void testExecuteNotify0_exception() {
|
||||
// mock 数据(task)
|
||||
PayNotifyTaskDO task = randomPojo(PayNotifyTaskDO.class, o -> o.setType(-1)
|
||||
.setNotifyTimes(0).setMaxNotifyTimes(9));
|
||||
notifyTaskMapper.insert(task);
|
||||
|
||||
// 调用
|
||||
notifyService.executeNotify0(task);
|
||||
// 断言,task
|
||||
PayNotifyTaskDO dbTask = notifyTaskMapper.selectById(task.getId());
|
||||
assertNotEquals(task.getNextNotifyTime(), dbTask.getNextNotifyTime());
|
||||
assertNotEquals(task.getLastExecuteTime(), dbTask.getNextNotifyTime());
|
||||
assertEquals(dbTask.getNotifyTimes(), 1);
|
||||
assertEquals(dbTask.getStatus(), PayNotifyStatusEnum.REQUEST_FAILURE.getStatus());
|
||||
// 断言,log
|
||||
PayNotifyLogDO dbLog = notifyLogMapper.selectOne(null);
|
||||
assertEquals(dbLog.getTaskId(), task.getId());
|
||||
assertEquals(dbLog.getNotifyTimes(), 1);
|
||||
assertTrue(dbLog.getResponse().contains("未知的通知任务类型:"));
|
||||
assertEquals(dbLog.getStatus(), PayNotifyStatusEnum.REQUEST_FAILURE.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProcessNotifyResult_success() {
|
||||
// mock 数据(task)
|
||||
PayNotifyTaskDO task = randomPojo(PayNotifyTaskDO.class,
|
||||
o -> o.setNotifyTimes(0).setMaxNotifyTimes(9));
|
||||
notifyTaskMapper.insert(task);
|
||||
// 准备参数
|
||||
CommonResult<?> invokeResult = CommonResult.success(randomString());
|
||||
|
||||
// 调用
|
||||
notifyService.processNotifyResult(task, invokeResult, null);
|
||||
// 断言
|
||||
PayNotifyTaskDO dbTask = notifyTaskMapper.selectById(task.getId());
|
||||
assertEquals(task.getNextNotifyTime(), dbTask.getNextNotifyTime());
|
||||
assertNotEquals(task.getLastExecuteTime(), dbTask.getNextNotifyTime());
|
||||
assertEquals(dbTask.getNotifyTimes(), 1);
|
||||
assertEquals(dbTask.getStatus(), PayNotifyStatusEnum.SUCCESS.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProcessNotifyResult_failure() {
|
||||
// mock 数据(task)
|
||||
PayNotifyTaskDO task = randomPojo(PayNotifyTaskDO.class,
|
||||
o -> o.setNotifyTimes(8).setMaxNotifyTimes(9));
|
||||
notifyTaskMapper.insert(task);
|
||||
// 准备参数
|
||||
CommonResult<?> invokeResult = CommonResult.error(BAD_REQUEST);
|
||||
|
||||
// 调用
|
||||
notifyService.processNotifyResult(task, invokeResult, null);
|
||||
// 断言
|
||||
PayNotifyTaskDO dbTask = notifyTaskMapper.selectById(task.getId());
|
||||
assertEquals(task.getNextNotifyTime(), dbTask.getNextNotifyTime());
|
||||
assertNotEquals(task.getLastExecuteTime(), dbTask.getNextNotifyTime());
|
||||
assertEquals(dbTask.getNotifyTimes(), 9);
|
||||
assertEquals(dbTask.getStatus(), PayNotifyStatusEnum.FAILURE.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProcessNotifyResult_requestFailure() {
|
||||
// mock 数据(task)
|
||||
PayNotifyTaskDO task = randomPojo(PayNotifyTaskDO.class,
|
||||
o -> o.setNotifyTimes(0).setMaxNotifyTimes(9));
|
||||
notifyTaskMapper.insert(task);
|
||||
// 准备参数
|
||||
CommonResult<?> invokeResult = CommonResult.error(BAD_REQUEST);
|
||||
|
||||
// 调用
|
||||
notifyService.processNotifyResult(task, invokeResult, null);
|
||||
// 断言
|
||||
PayNotifyTaskDO dbTask = notifyTaskMapper.selectById(task.getId());
|
||||
assertNotEquals(task.getNextNotifyTime(), dbTask.getNextNotifyTime());
|
||||
assertNotEquals(task.getLastExecuteTime(), dbTask.getNextNotifyTime());
|
||||
assertEquals(dbTask.getNotifyTimes(), 1);
|
||||
assertEquals(dbTask.getStatus(), PayNotifyStatusEnum.REQUEST_SUCCESS.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProcessNotifyResult_requestSuccess() {
|
||||
// mock 数据(task)
|
||||
PayNotifyTaskDO task = randomPojo(PayNotifyTaskDO.class,
|
||||
o -> o.setNotifyTimes(0).setMaxNotifyTimes(9));
|
||||
notifyTaskMapper.insert(task);
|
||||
// 准备参数
|
||||
CommonResult<?> invokeResult = CommonResult.error(BAD_REQUEST);
|
||||
RuntimeException invokeException = new RuntimeException();
|
||||
|
||||
// 调用
|
||||
notifyService.processNotifyResult(task, invokeResult, invokeException);
|
||||
// 断言
|
||||
PayNotifyTaskDO dbTask = notifyTaskMapper.selectById(task.getId());
|
||||
assertNotEquals(task.getNextNotifyTime(), dbTask.getNextNotifyTime());
|
||||
assertNotEquals(task.getLastExecuteTime(), dbTask.getNextNotifyTime());
|
||||
assertEquals(dbTask.getNotifyTimes(), 1);
|
||||
assertEquals(dbTask.getStatus(), PayNotifyStatusEnum.REQUEST_FAILURE.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetNotifyTask() {
|
||||
// mock 数据(task)
|
||||
PayNotifyTaskDO task = randomPojo(PayNotifyTaskDO.class);
|
||||
notifyTaskMapper.insert(task);
|
||||
// 准备参数
|
||||
Long id = task.getId();
|
||||
|
||||
// 调用
|
||||
PayNotifyTaskDO dbTask = notifyService.getNotifyTask(id);
|
||||
// 断言
|
||||
assertPojoEquals(dbTask, task);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetNotifyTaskPage() {
|
||||
// mock 数据
|
||||
PayNotifyTaskDO dbTask = randomPojo(PayNotifyTaskDO.class, o -> { // 等会查询到
|
||||
o.setAppId(1L);
|
||||
o.setType(PayNotifyTypeEnum.REFUND.getType());
|
||||
o.setDataId(100L);
|
||||
o.setStatus(PayNotifyStatusEnum.SUCCESS.getStatus());
|
||||
o.setMerchantOrderId("P110");
|
||||
o.setCreateTime(buildTime(2023, 2, 3));
|
||||
});
|
||||
notifyTaskMapper.insert(dbTask);
|
||||
// 测试 appId 不匹配
|
||||
notifyTaskMapper.insert(cloneIgnoreId(dbTask, o -> o.setAppId(2L)));
|
||||
// 测试 type 不匹配
|
||||
notifyTaskMapper.insert(cloneIgnoreId(dbTask, o -> o.setType(PayNotifyTypeEnum.ORDER.getType())));
|
||||
// 测试 dataId 不匹配
|
||||
notifyTaskMapper.insert(cloneIgnoreId(dbTask, o -> o.setDataId(200L)));
|
||||
// 测试 status 不匹配
|
||||
notifyTaskMapper.insert(cloneIgnoreId(dbTask, o -> o.setStatus(PayNotifyStatusEnum.FAILURE.getStatus())));
|
||||
// 测试 merchantOrderId 不匹配
|
||||
notifyTaskMapper.insert(cloneIgnoreId(dbTask, o -> o.setMerchantOrderId(randomString())));
|
||||
// 测试 createTime 不匹配
|
||||
notifyTaskMapper.insert(cloneIgnoreId(dbTask, o -> o.setCreateTime(buildTime(2023, 1, 1))));
|
||||
// 准备参数
|
||||
PayNotifyTaskPageReqVO reqVO = new PayNotifyTaskPageReqVO();
|
||||
reqVO.setAppId(1L);
|
||||
reqVO.setType(PayNotifyTypeEnum.REFUND.getType());
|
||||
reqVO.setDataId(100L);
|
||||
reqVO.setStatus(PayNotifyStatusEnum.SUCCESS.getStatus());
|
||||
reqVO.setMerchantOrderId("P110");
|
||||
reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
|
||||
|
||||
// 调用
|
||||
PageResult<PayNotifyTaskDO> pageResult = notifyService.getNotifyTaskPage(reqVO);
|
||||
// 断言
|
||||
assertEquals(1, pageResult.getTotal());
|
||||
assertEquals(1, pageResult.getList().size());
|
||||
assertPojoEquals(dbTask, pageResult.getList().get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetNotifyLogList() {
|
||||
// mock 数据
|
||||
PayNotifyLogDO dbLog = randomPojo(PayNotifyLogDO.class);
|
||||
notifyLogMapper.insert(dbLog);
|
||||
PayNotifyLogDO dbLog02 = randomPojo(PayNotifyLogDO.class);
|
||||
notifyLogMapper.insert(dbLog02);
|
||||
// 准备参数
|
||||
Long taskId = dbLog.getTaskId();
|
||||
|
||||
// 调用
|
||||
List<PayNotifyLogDO> logList = notifyService.getNotifyLogList(taskId);
|
||||
// 断言
|
||||
assertEquals(logList.size(), 1);
|
||||
assertPojoEquals(dbLog, logList.get(0));
|
||||
}
|
||||
|
||||
private void mockLock(Long id) {
|
||||
RLock lock = mock(RLock.class);
|
||||
if (id == null) {
|
||||
when(redissonClient.getLock(anyString()))
|
||||
.thenReturn(lock);
|
||||
} else {
|
||||
when(redissonClient.getLock(eq("pay_notify:lock:" + id)))
|
||||
.thenReturn(lock);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,699 @@
|
||||
package cn.iocoder.yudao.module.pay.service.refund;
|
||||
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.pay.enums.PayChannelEnum;
|
||||
import cn.iocoder.yudao.module.pay.framework.pay.core.client.PayClient;
|
||||
import cn.iocoder.yudao.module.pay.framework.pay.core.client.dto.refund.PayRefundRespDTO;
|
||||
import cn.iocoder.yudao.module.pay.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseDbAndRedisUnitTest;
|
||||
import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO;
|
||||
import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundExportReqVO;
|
||||
import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundPageReqVO;
|
||||
import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO;
|
||||
import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO;
|
||||
import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
|
||||
import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO;
|
||||
import cn.iocoder.yudao.module.pay.dal.mysql.refund.PayRefundMapper;
|
||||
import cn.iocoder.yudao.module.pay.dal.redis.no.PayNoRedisDAO;
|
||||
import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum;
|
||||
import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
|
||||
import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum;
|
||||
import cn.iocoder.yudao.module.pay.framework.pay.config.PayProperties;
|
||||
import cn.iocoder.yudao.module.pay.service.app.PayAppService;
|
||||
import cn.iocoder.yudao.module.pay.service.channel.PayChannelService;
|
||||
import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService;
|
||||
import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
|
||||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;
|
||||
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
|
||||
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
|
||||
import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* {@link PayRefundServiceImpl} 的单元测试类
|
||||
*
|
||||
* @author 芋艿
|
||||
*/
|
||||
@Import({PayRefundServiceImpl.class, PayNoRedisDAO.class})
|
||||
public class PayRefundServiceTest extends BaseDbAndRedisUnitTest {
|
||||
|
||||
@Resource
|
||||
private PayRefundServiceImpl refundService;
|
||||
|
||||
@Resource
|
||||
private PayRefundMapper refundMapper;
|
||||
|
||||
@MockBean
|
||||
private PayProperties payProperties;
|
||||
@MockBean
|
||||
private PayOrderService orderService;
|
||||
@MockBean
|
||||
private PayAppService appService;
|
||||
@MockBean
|
||||
private PayChannelService channelService;
|
||||
@MockBean
|
||||
private PayNotifyService notifyService;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
when(payProperties.getRefundNotifyUrl()).thenReturn("http://127.0.0.1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRefund() {
|
||||
// mock 数据
|
||||
PayRefundDO refund = randomPojo(PayRefundDO.class);
|
||||
refundMapper.insert(refund);
|
||||
// 准备参数
|
||||
Long id = refund.getId();
|
||||
|
||||
// 调用
|
||||
PayRefundDO dbRefund = refundService.getRefund(id);
|
||||
// 断言
|
||||
assertPojoEquals(dbRefund, refund);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRefundCountByAppId() {
|
||||
// mock 数据
|
||||
PayRefundDO refund01 = randomPojo(PayRefundDO.class);
|
||||
refundMapper.insert(refund01);
|
||||
PayRefundDO refund02 = randomPojo(PayRefundDO.class);
|
||||
refundMapper.insert(refund02);
|
||||
// 准备参数
|
||||
Long appId = refund01.getAppId();
|
||||
|
||||
// 调用
|
||||
Long count = refundService.getRefundCountByAppId(appId);
|
||||
// 断言
|
||||
assertEquals(count, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRefundPage() {
|
||||
// mock 数据
|
||||
PayRefundDO dbRefund = randomPojo(PayRefundDO.class, o -> { // 等会查询到
|
||||
o.setAppId(1L);
|
||||
o.setChannelCode(PayChannelEnum.WX_PUB.getCode());
|
||||
o.setMerchantOrderId("MOT0000001");
|
||||
o.setMerchantRefundId("MRF0000001");
|
||||
o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus());
|
||||
o.setChannelOrderNo("CH0000001");
|
||||
o.setChannelRefundNo("CHR0000001");
|
||||
o.setCreateTime(buildTime(2021, 1, 10));
|
||||
});
|
||||
refundMapper.insert(dbRefund);
|
||||
// 测试 appId 不匹配
|
||||
refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setAppId(2L)));
|
||||
// 测试 channelCode 不匹配
|
||||
refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setChannelCode(PayChannelEnum.ALIPAY_APP.getCode())));
|
||||
// 测试 merchantOrderId 不匹配
|
||||
refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setMerchantOrderId(randomString())));
|
||||
// 测试 merchantRefundId 不匹配
|
||||
refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setMerchantRefundId(randomString())));
|
||||
// 测试 channelOrderNo 不匹配
|
||||
refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setChannelOrderNo(randomString())));
|
||||
// 测试 channelRefundNo 不匹配
|
||||
refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setChannelRefundNo(randomString())));
|
||||
// 测试 status 不匹配
|
||||
refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus())));
|
||||
// 测试 createTime 不匹配
|
||||
refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setCreateTime(buildTime(2021, 1, 1))));
|
||||
// 准备参数
|
||||
PayRefundPageReqVO reqVO = new PayRefundPageReqVO();
|
||||
reqVO.setAppId(1L);
|
||||
reqVO.setChannelCode(PayChannelEnum.WX_PUB.getCode());
|
||||
reqVO.setMerchantOrderId("MOT0000001");
|
||||
reqVO.setMerchantRefundId("MRF0000001");
|
||||
reqVO.setStatus(PayOrderStatusEnum.SUCCESS.getStatus());
|
||||
reqVO.setChannelOrderNo("CH0000001");
|
||||
reqVO.setChannelRefundNo("CHR0000001");
|
||||
reqVO.setCreateTime(buildBetweenTime(2021, 1, 9, 2021, 1, 11));
|
||||
|
||||
// 调用
|
||||
PageResult<PayRefundDO> pageResult = refundService.getRefundPage(reqVO);
|
||||
// 断言
|
||||
assertEquals(1, pageResult.getTotal());
|
||||
assertEquals(1, pageResult.getList().size());
|
||||
assertPojoEquals(dbRefund, pageResult.getList().get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRefundList() {
|
||||
// mock 数据
|
||||
PayRefundDO dbRefund = randomPojo(PayRefundDO.class, o -> { // 等会查询到
|
||||
o.setAppId(1L);
|
||||
o.setChannelCode(PayChannelEnum.WX_PUB.getCode());
|
||||
o.setMerchantOrderId("MOT0000001");
|
||||
o.setMerchantRefundId("MRF0000001");
|
||||
o.setStatus(PayOrderStatusEnum.SUCCESS.getStatus());
|
||||
o.setChannelOrderNo("CH0000001");
|
||||
o.setChannelRefundNo("CHR0000001");
|
||||
o.setCreateTime(buildTime(2021, 1, 10));
|
||||
});
|
||||
refundMapper.insert(dbRefund);
|
||||
// 测试 appId 不匹配
|
||||
refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setAppId(2L)));
|
||||
// 测试 channelCode 不匹配
|
||||
refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setChannelCode(PayChannelEnum.ALIPAY_APP.getCode())));
|
||||
// 测试 merchantOrderId 不匹配
|
||||
refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setMerchantOrderId(randomString())));
|
||||
// 测试 merchantRefundId 不匹配
|
||||
refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setMerchantRefundId(randomString())));
|
||||
// 测试 channelOrderNo 不匹配
|
||||
refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setChannelOrderNo(randomString())));
|
||||
// 测试 channelRefundNo 不匹配
|
||||
refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setChannelRefundNo(randomString())));
|
||||
// 测试 status 不匹配
|
||||
refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setStatus(PayOrderStatusEnum.WAITING.getStatus())));
|
||||
// 测试 createTime 不匹配
|
||||
refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setCreateTime(buildTime(2021, 1, 1))));
|
||||
// 准备参数
|
||||
PayRefundExportReqVO reqVO = new PayRefundExportReqVO();
|
||||
reqVO.setAppId(1L);
|
||||
reqVO.setChannelCode(PayChannelEnum.WX_PUB.getCode());
|
||||
reqVO.setMerchantOrderId("MOT0000001");
|
||||
reqVO.setMerchantRefundId("MRF0000001");
|
||||
reqVO.setStatus(PayOrderStatusEnum.SUCCESS.getStatus());
|
||||
reqVO.setChannelOrderNo("CH0000001");
|
||||
reqVO.setChannelRefundNo("CHR0000001");
|
||||
reqVO.setCreateTime(buildBetweenTime(2021, 1, 9, 2021, 1, 11));
|
||||
|
||||
// 调用
|
||||
List<PayRefundDO> list = refundService.getRefundList(reqVO);
|
||||
// 断言
|
||||
assertEquals(1, list.size());
|
||||
assertPojoEquals(dbRefund, list.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateRefund_orderNotFound() {
|
||||
PayRefundCreateReqDTO reqDTO = randomPojo(PayRefundCreateReqDTO.class,
|
||||
o -> o.setAppKey("demo"));
|
||||
// mock 方法(app)
|
||||
PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L));
|
||||
when(appService.validPayApp(eq("demo"))).thenReturn(app);
|
||||
|
||||
// 调用,并断言异常
|
||||
assertServiceException(() -> refundService.createRefund(reqDTO),
|
||||
PAY_ORDER_NOT_FOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateRefund_orderWaiting() {
|
||||
testCreateRefund_orderWaitingOrClosed(PayOrderStatusEnum.WAITING.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateRefund_orderClosed() {
|
||||
testCreateRefund_orderWaitingOrClosed(PayOrderStatusEnum.CLOSED.getStatus());
|
||||
}
|
||||
|
||||
private void testCreateRefund_orderWaitingOrClosed(Integer status) {
|
||||
// 准备参数
|
||||
PayRefundCreateReqDTO reqDTO = randomPojo(PayRefundCreateReqDTO.class,
|
||||
o -> o.setAppKey("demo").setMerchantOrderId("100"));
|
||||
// mock 方法(app)
|
||||
PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L));
|
||||
when(appService.validPayApp(eq("demo"))).thenReturn(app);
|
||||
// mock 数据(order)
|
||||
PayOrderDO order = randomPojo(PayOrderDO.class, o -> o.setStatus(status));
|
||||
when(orderService.getOrder(eq(1L), eq("100"))).thenReturn(order);
|
||||
|
||||
// 调用,并断言异常
|
||||
assertServiceException(() -> refundService.createRefund(reqDTO),
|
||||
PAY_ORDER_REFUND_FAIL_STATUS_ERROR);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateRefund_refundPriceExceed() {
|
||||
// 准备参数
|
||||
PayRefundCreateReqDTO reqDTO = randomPojo(PayRefundCreateReqDTO.class,
|
||||
o -> o.setAppKey("demo").setMerchantOrderId("100").setPrice(10));
|
||||
// mock 方法(app)
|
||||
PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L));
|
||||
when(appService.validPayApp(eq("demo"))).thenReturn(app);
|
||||
// mock 数据(order)
|
||||
PayOrderDO order = randomPojo(PayOrderDO.class, o ->
|
||||
o.setStatus(PayOrderStatusEnum.REFUND.getStatus())
|
||||
.setPrice(10).setRefundPrice(1));
|
||||
when(orderService.getOrder(eq(1L), eq("100"))).thenReturn(order);
|
||||
|
||||
// 调用,并断言异常
|
||||
assertServiceException(() -> refundService.createRefund(reqDTO),
|
||||
REFUND_PRICE_EXCEED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateRefund_orderHasRefunding() {
|
||||
// 准备参数
|
||||
PayRefundCreateReqDTO reqDTO = randomPojo(PayRefundCreateReqDTO.class,
|
||||
o -> o.setAppKey("demo").setMerchantOrderId("100").setPrice(10));
|
||||
// mock 方法(app)
|
||||
PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L));
|
||||
when(appService.validPayApp(eq("demo"))).thenReturn(app);
|
||||
// mock 数据(order)
|
||||
PayOrderDO order = randomPojo(PayOrderDO.class, o ->
|
||||
o.setStatus(PayOrderStatusEnum.REFUND.getStatus())
|
||||
.setPrice(10).setRefundPrice(1));
|
||||
when(orderService.getOrder(eq(1L), eq("100"))).thenReturn(order);
|
||||
// mock 数据(refund 在退款中)
|
||||
PayRefundDO refund = randomPojo(PayRefundDO.class, o ->
|
||||
o.setOrderId(order.getId()).setStatus(PayOrderStatusEnum.WAITING.getStatus()));
|
||||
refundMapper.insert(refund);
|
||||
|
||||
// 调用,并断言异常
|
||||
assertServiceException(() -> refundService.createRefund(reqDTO),
|
||||
REFUND_PRICE_EXCEED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateRefund_channelNotFound() {
|
||||
// 准备参数
|
||||
PayRefundCreateReqDTO reqDTO = randomPojo(PayRefundCreateReqDTO.class,
|
||||
o -> o.setAppKey("demo").setMerchantOrderId("100").setPrice(9));
|
||||
// mock 方法(app)
|
||||
PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L));
|
||||
when(appService.validPayApp(eq("demo"))).thenReturn(app);
|
||||
// mock 数据(order)
|
||||
PayOrderDO order = randomPojo(PayOrderDO.class, o ->
|
||||
o.setStatus(PayOrderStatusEnum.REFUND.getStatus())
|
||||
.setPrice(10).setRefundPrice(1)
|
||||
.setChannelId(1L).setChannelCode(PayChannelEnum.ALIPAY_APP.getCode()));
|
||||
when(orderService.getOrder(eq(1L), eq("100"))).thenReturn(order);
|
||||
// mock 方法(channel)
|
||||
PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L)
|
||||
.setCode(PayChannelEnum.ALIPAY_APP.getCode()));
|
||||
when(channelService.validPayChannel(eq(1L))).thenReturn(channel);
|
||||
|
||||
// 调用,并断言异常
|
||||
assertServiceException(() -> refundService.createRefund(reqDTO),
|
||||
CHANNEL_NOT_FOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateRefund_refundExists() {
|
||||
// 准备参数
|
||||
PayRefundCreateReqDTO reqDTO = randomPojo(PayRefundCreateReqDTO.class,
|
||||
o -> o.setAppKey("demo").setMerchantOrderId("100").setPrice(9)
|
||||
.setMerchantRefundId("200").setReason("测试退款"));
|
||||
// mock 方法(app)
|
||||
PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L));
|
||||
when(appService.validPayApp(eq("demo"))).thenReturn(app);
|
||||
// mock 数据(order)
|
||||
PayOrderDO order = randomPojo(PayOrderDO.class, o ->
|
||||
o.setStatus(PayOrderStatusEnum.REFUND.getStatus())
|
||||
.setPrice(10).setRefundPrice(1)
|
||||
.setChannelId(1L).setChannelCode(PayChannelEnum.ALIPAY_APP.getCode()));
|
||||
when(orderService.getOrder(eq(1L), eq("100"))).thenReturn(order);
|
||||
// mock 方法(channel)
|
||||
PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L)
|
||||
.setCode(PayChannelEnum.ALIPAY_APP.getCode()));
|
||||
when(channelService.validPayChannel(eq(1L))).thenReturn(channel);
|
||||
// mock 方法(client)
|
||||
PayClient<?> client = mock(PayClient.class);
|
||||
when(channelService.getPayClient(eq(10L))).thenReturn(client);
|
||||
// mock 数据(refund 已存在)
|
||||
PayRefundDO refund = randomPojo(PayRefundDO.class, o ->
|
||||
o.setAppId(1L).setMerchantRefundId("200"));
|
||||
refundMapper.insert(refund);
|
||||
|
||||
// 调用,并断言异常
|
||||
assertServiceException(() -> refundService.createRefund(reqDTO),
|
||||
REFUND_EXISTS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateRefund_invokeException() {
|
||||
// 准备参数
|
||||
PayRefundCreateReqDTO reqDTO = randomPojo(PayRefundCreateReqDTO.class,
|
||||
o -> o.setAppKey("demo").setMerchantOrderId("100").setPrice(9)
|
||||
.setMerchantRefundId("200").setReason("测试退款"));
|
||||
// mock 方法(app)
|
||||
PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L));
|
||||
when(appService.validPayApp(eq("demo"))).thenReturn(app);
|
||||
// mock 数据(order)
|
||||
PayOrderDO order = randomPojo(PayOrderDO.class, o ->
|
||||
o.setStatus(PayOrderStatusEnum.REFUND.getStatus())
|
||||
.setPrice(10).setRefundPrice(1)
|
||||
.setChannelId(10L).setChannelCode(PayChannelEnum.ALIPAY_APP.getCode()));
|
||||
when(orderService.getOrder(eq(1L), eq("100"))).thenReturn(order);
|
||||
// mock 方法(channel)
|
||||
PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L)
|
||||
.setCode(PayChannelEnum.ALIPAY_APP.getCode()));
|
||||
when(channelService.validPayChannel(eq(10L))).thenReturn(channel);
|
||||
// mock 方法(client)
|
||||
PayClient<?> client = mock(PayClient.class);
|
||||
when(channelService.getPayClient(eq(10L))).thenReturn(client);
|
||||
// mock 方法(client 调用发生异常)
|
||||
when(client.unifiedRefund(any(PayRefundUnifiedReqDTO.class))).thenThrow(new RuntimeException());
|
||||
|
||||
// 调用
|
||||
Long refundId = refundService.createRefund(reqDTO);
|
||||
// 断言
|
||||
PayRefundDO refundDO = refundMapper.selectById(refundId);
|
||||
assertPojoEquals(reqDTO, refundDO);
|
||||
assertNotNull(refundDO.getNo());
|
||||
assertThat(refundDO)
|
||||
.extracting("orderId", "orderNo", "channelId", "channelCode",
|
||||
"notifyUrl", "channelOrderNo", "status", "payPrice", "refundPrice")
|
||||
.containsExactly(order.getId(), order.getNo(), channel.getId(), channel.getCode(),
|
||||
app.getRefundNotifyUrl(), order.getChannelOrderNo(), PayRefundStatusEnum.WAITING.getStatus(),
|
||||
order.getPrice(), reqDTO.getPrice());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateRefund_invokeSuccess() {
|
||||
PayRefundServiceImpl payRefundServiceImpl = mock(PayRefundServiceImpl.class);
|
||||
try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
|
||||
springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PayRefundServiceImpl.class)))
|
||||
.thenReturn(payRefundServiceImpl);
|
||||
|
||||
// 准备参数
|
||||
PayRefundCreateReqDTO reqDTO = randomPojo(PayRefundCreateReqDTO.class,
|
||||
o -> o.setAppKey("demo").setMerchantOrderId("100").setPrice(9)
|
||||
.setMerchantRefundId("200").setReason("测试退款"));
|
||||
// mock 方法(app)
|
||||
PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L));
|
||||
when(appService.validPayApp(eq("demo"))).thenReturn(app);
|
||||
// mock 数据(order)
|
||||
PayOrderDO order = randomPojo(PayOrderDO.class, o ->
|
||||
o.setStatus(PayOrderStatusEnum.REFUND.getStatus())
|
||||
.setPrice(10).setRefundPrice(1)
|
||||
.setChannelId(10L).setChannelCode(PayChannelEnum.ALIPAY_APP.getCode()));
|
||||
when(orderService.getOrder(eq(1L), eq("100"))).thenReturn(order);
|
||||
// mock 方法(channel)
|
||||
PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L)
|
||||
.setCode(PayChannelEnum.ALIPAY_APP.getCode()));
|
||||
when(channelService.validPayChannel(eq(10L))).thenReturn(channel);
|
||||
// mock 方法(client)
|
||||
PayClient<?> client = mock(PayClient.class);
|
||||
when(channelService.getPayClient(eq(10L))).thenReturn(client);
|
||||
// mock 方法(client 成功)
|
||||
PayRefundRespDTO refundRespDTO = randomPojo(PayRefundRespDTO.class);
|
||||
when(client.unifiedRefund(argThat(unifiedReqDTO -> {
|
||||
assertNotNull(unifiedReqDTO.getOutRefundNo());
|
||||
assertThat(unifiedReqDTO)
|
||||
.extracting("payPrice", "refundPrice", "outTradeNo",
|
||||
"notifyUrl", "reason")
|
||||
.containsExactly(order.getPrice(), reqDTO.getPrice(), order.getNo(),
|
||||
"http://127.0.0.1/10", reqDTO.getReason());
|
||||
return true;
|
||||
}))).thenReturn(refundRespDTO);
|
||||
|
||||
// 调用
|
||||
Long refundId = refundService.createRefund(reqDTO);
|
||||
// 断言
|
||||
PayRefundDO refundDO = refundMapper.selectById(refundId);
|
||||
assertPojoEquals(reqDTO, refundDO);
|
||||
assertNotNull(refundDO.getNo());
|
||||
assertThat(refundDO)
|
||||
.extracting("orderId", "orderNo", "channelId", "channelCode",
|
||||
"notifyUrl", "channelOrderNo", "status", "payPrice", "refundPrice")
|
||||
.containsExactly(order.getId(), order.getNo(), channel.getId(), channel.getCode(),
|
||||
app.getRefundNotifyUrl(), order.getChannelOrderNo(), PayRefundStatusEnum.WAITING.getStatus(),
|
||||
order.getPrice(), reqDTO.getPrice());
|
||||
// 断言调用
|
||||
verify(payRefundServiceImpl).notifyRefund(same(channel), same(refundRespDTO));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyRefund() {
|
||||
PayRefundServiceImpl payRefundServiceImpl = mock(PayRefundServiceImpl.class);
|
||||
try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
|
||||
springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PayRefundServiceImpl.class)))
|
||||
.thenReturn(payRefundServiceImpl);
|
||||
|
||||
// 准备参数
|
||||
Long channelId = 10L;
|
||||
PayRefundRespDTO refundRespDTO = randomPojo(PayRefundRespDTO.class);
|
||||
// mock 方法(channel)
|
||||
PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L));
|
||||
when(channelService.validPayChannel(eq(10L))).thenReturn(channel);
|
||||
|
||||
// 调用
|
||||
refundService.notifyRefund(channelId, refundRespDTO);
|
||||
// 断言
|
||||
verify(payRefundServiceImpl).notifyRefund(same(channel), same(refundRespDTO));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyRefundSuccess_notFound() {
|
||||
// 准备参数
|
||||
PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L).setAppId(1L));
|
||||
PayRefundRespDTO refundRespDTO = randomPojo(PayRefundRespDTO.class,
|
||||
o -> o.setStatus(PayRefundStatusEnum.SUCCESS.getStatus()).setOutRefundNo("R100"));
|
||||
|
||||
// 调用,并断言异常
|
||||
assertServiceException(() -> refundService.notifyRefund(channel, refundRespDTO),
|
||||
REFUND_NOT_FOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyRefundSuccess_isSuccess() {
|
||||
// 准备参数
|
||||
PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L).setAppId(1L));
|
||||
PayRefundRespDTO refundRespDTO = randomPojo(PayRefundRespDTO.class,
|
||||
o -> o.setStatus(PayRefundStatusEnum.SUCCESS.getStatus()).setOutRefundNo("R100"));
|
||||
// mock 数据(refund + 已支付)
|
||||
PayRefundDO refund = randomPojo(PayRefundDO.class, o -> o.setAppId(1L).setNo("R100")
|
||||
.setStatus(PayRefundStatusEnum.SUCCESS.getStatus()));
|
||||
refundMapper.insert(refund);
|
||||
|
||||
// 调用
|
||||
refundService.notifyRefund(channel, refundRespDTO);
|
||||
// 断言,refund 没有更新,因为已经退款成功
|
||||
assertPojoEquals(refund, refundMapper.selectById(refund.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyRefundSuccess_failure() {
|
||||
// 准备参数
|
||||
PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L).setAppId(1L));
|
||||
PayRefundRespDTO refundRespDTO = randomPojo(PayRefundRespDTO.class,
|
||||
o -> o.setStatus(PayRefundStatusEnum.SUCCESS.getStatus()).setOutRefundNo("R100"));
|
||||
// mock 数据(refund + 已支付)
|
||||
PayRefundDO refund = randomPojo(PayRefundDO.class, o -> o.setAppId(1L).setNo("R100")
|
||||
.setStatus(PayRefundStatusEnum.FAILURE.getStatus()));
|
||||
refundMapper.insert(refund);
|
||||
|
||||
// 调用,并断言异常
|
||||
assertServiceException(() -> refundService.notifyRefund(channel, refundRespDTO),
|
||||
REFUND_STATUS_IS_NOT_WAITING);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyRefundSuccess_updateOrderException() {
|
||||
// 准备参数
|
||||
PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L).setAppId(1L));
|
||||
PayRefundRespDTO refundRespDTO = randomPojo(PayRefundRespDTO.class,
|
||||
o -> o.setStatus(PayRefundStatusEnum.SUCCESS.getStatus()).setOutRefundNo("R100"));
|
||||
// mock 数据(refund + 已支付)
|
||||
PayRefundDO refund = randomPojo(PayRefundDO.class, o -> o.setAppId(1L).setNo("R100")
|
||||
.setStatus(PayRefundStatusEnum.WAITING.getStatus())
|
||||
.setOrderId(100L).setRefundPrice(23));
|
||||
refundMapper.insert(refund);
|
||||
// mock 方法(order + 更新异常)
|
||||
doThrow(new RuntimeException()).when(orderService)
|
||||
.updateOrderRefundPrice(eq(100L), eq(23));
|
||||
|
||||
// 调用,并断言异常
|
||||
assertThrows(RuntimeException.class, () -> refundService.notifyRefund(channel, refundRespDTO));
|
||||
// 断言,refund 没有更新,因为事务回滚了
|
||||
assertPojoEquals(refund, refundMapper.selectById(refund.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyRefundSuccess_success() {
|
||||
// 准备参数
|
||||
PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L).setAppId(1L));
|
||||
PayRefundRespDTO refundRespDTO = randomPojo(PayRefundRespDTO.class,
|
||||
o -> o.setStatus(PayRefundStatusEnum.SUCCESS.getStatus()).setOutRefundNo("R100"));
|
||||
// mock 数据(refund + 已支付)
|
||||
PayRefundDO refund = randomPojo(PayRefundDO.class, o -> o.setAppId(1L).setNo("R100")
|
||||
.setStatus(PayRefundStatusEnum.WAITING.getStatus())
|
||||
.setOrderId(100L).setRefundPrice(23));
|
||||
refundMapper.insert(refund);
|
||||
|
||||
// 调用
|
||||
refundService.notifyRefund(channel, refundRespDTO);
|
||||
// 断言,refund
|
||||
refund.setSuccessTime(refundRespDTO.getSuccessTime())
|
||||
.setChannelRefundNo(refundRespDTO.getChannelRefundNo())
|
||||
.setStatus(PayRefundStatusEnum.SUCCESS.getStatus())
|
||||
.setChannelNotifyData(toJsonString(refundRespDTO));
|
||||
assertPojoEquals(refund, refundMapper.selectById(refund.getId()),
|
||||
"updateTime", "updater");
|
||||
// 断言,调用
|
||||
verify(orderService).updateOrderRefundPrice(eq(100L), eq(23));
|
||||
verify(notifyService).createPayNotifyTask(eq(PayNotifyTypeEnum.REFUND.getType()),
|
||||
eq(refund.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyRefundFailure_notFound() {
|
||||
// 准备参数
|
||||
PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L).setAppId(1L));
|
||||
PayRefundRespDTO refundRespDTO = randomPojo(PayRefundRespDTO.class,
|
||||
o -> o.setStatus(PayRefundStatusEnum.FAILURE.getStatus()).setOutRefundNo("R100"));
|
||||
|
||||
// 调用,并断言异常
|
||||
assertServiceException(() -> refundService.notifyRefund(channel, refundRespDTO),
|
||||
REFUND_NOT_FOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyRefundFailure_isFailure() {
|
||||
// 准备参数
|
||||
PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L).setAppId(1L));
|
||||
PayRefundRespDTO refundRespDTO = randomPojo(PayRefundRespDTO.class,
|
||||
o -> o.setStatus(PayRefundStatusEnum.FAILURE.getStatus()).setOutRefundNo("R100"));
|
||||
// mock 数据(refund + 退款失败)
|
||||
PayRefundDO refund = randomPojo(PayRefundDO.class, o -> o.setAppId(1L).setNo("R100")
|
||||
.setStatus(PayRefundStatusEnum.FAILURE.getStatus()));
|
||||
refundMapper.insert(refund);
|
||||
|
||||
// 调用
|
||||
refundService.notifyRefund(channel, refundRespDTO);
|
||||
// 断言,refund 没有更新,因为已经退款失败
|
||||
assertPojoEquals(refund, refundMapper.selectById(refund.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyRefundFailure_isSuccess() {
|
||||
// 准备参数
|
||||
PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L).setAppId(1L));
|
||||
PayRefundRespDTO refundRespDTO = randomPojo(PayRefundRespDTO.class,
|
||||
o -> o.setStatus(PayRefundStatusEnum.FAILURE.getStatus()).setOutRefundNo("R100"));
|
||||
// mock 数据(refund + 已支付)
|
||||
PayRefundDO refund = randomPojo(PayRefundDO.class, o -> o.setAppId(1L).setNo("R100")
|
||||
.setStatus(PayRefundStatusEnum.SUCCESS.getStatus()));
|
||||
refundMapper.insert(refund);
|
||||
|
||||
// 调用,并断言异常
|
||||
assertServiceException(() -> refundService.notifyRefund(channel, refundRespDTO),
|
||||
REFUND_STATUS_IS_NOT_WAITING);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotifyRefundFailure_success() {
|
||||
// 准备参数
|
||||
PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L).setAppId(1L));
|
||||
PayRefundRespDTO refundRespDTO = randomPojo(PayRefundRespDTO.class,
|
||||
o -> o.setStatus(PayRefundStatusEnum.FAILURE.getStatus()).setOutRefundNo("R100"));
|
||||
// mock 数据(refund + 已支付)
|
||||
PayRefundDO refund = randomPojo(PayRefundDO.class, o -> o.setAppId(1L).setNo("R100")
|
||||
.setStatus(PayRefundStatusEnum.WAITING.getStatus())
|
||||
.setOrderId(100L).setRefundPrice(23));
|
||||
refundMapper.insert(refund);
|
||||
|
||||
// 调用
|
||||
refundService.notifyRefund(channel, refundRespDTO);
|
||||
// 断言,refund
|
||||
refund.setChannelRefundNo(refundRespDTO.getChannelRefundNo())
|
||||
.setStatus(PayRefundStatusEnum.FAILURE.getStatus())
|
||||
.setChannelNotifyData(toJsonString(refundRespDTO))
|
||||
.setChannelErrorCode(refundRespDTO.getChannelErrorCode())
|
||||
.setChannelErrorMsg(refundRespDTO.getChannelErrorMsg());
|
||||
assertPojoEquals(refund, refundMapper.selectById(refund.getId()),
|
||||
"updateTime", "updater");
|
||||
// 断言,调用
|
||||
verify(notifyService).createPayNotifyTask(eq(PayNotifyTypeEnum.REFUND.getType()),
|
||||
eq(refund.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSyncRefund_notFound() {
|
||||
// 准备参数
|
||||
PayRefundDO refund = randomPojo(PayRefundDO.class, o -> o.setAppId(1L)
|
||||
.setStatus(PayRefundStatusEnum.WAITING.getStatus()));
|
||||
refundMapper.insert(refund);
|
||||
|
||||
// 调用
|
||||
int count = refundService.syncRefund();
|
||||
// 断言
|
||||
assertEquals(count, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSyncRefund_waiting() {
|
||||
assertEquals(testSyncRefund_waitingOrSuccessOrFailure(PayRefundStatusEnum.WAITING.getStatus()), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSyncRefund_success() {
|
||||
assertEquals(testSyncRefund_waitingOrSuccessOrFailure(PayRefundStatusEnum.SUCCESS.getStatus()), 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSyncRefund_failure() {
|
||||
assertEquals(testSyncRefund_waitingOrSuccessOrFailure(PayRefundStatusEnum.FAILURE.getStatus()), 1);
|
||||
}
|
||||
|
||||
private int testSyncRefund_waitingOrSuccessOrFailure(Integer status) {
|
||||
PayRefundServiceImpl payRefundServiceImpl = mock(PayRefundServiceImpl.class);
|
||||
try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
|
||||
springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PayRefundServiceImpl.class)))
|
||||
.thenReturn(payRefundServiceImpl);
|
||||
|
||||
// 准备参数
|
||||
PayRefundDO refund = randomPojo(PayRefundDO.class, o -> o.setAppId(1L).setChannelId(10L)
|
||||
.setStatus(PayRefundStatusEnum.WAITING.getStatus())
|
||||
.setOrderNo("P110").setNo("R220"));
|
||||
refundMapper.insert(refund);
|
||||
// mock 方法(client)
|
||||
PayClient<?> client = mock(PayClient.class);
|
||||
when(channelService.getPayClient(eq(10L))).thenReturn(client);
|
||||
// mock 方法(client 返回指定状态)
|
||||
PayRefundRespDTO respDTO = randomPojo(PayRefundRespDTO.class, o -> o.setStatus(status));
|
||||
when(client.getRefund(eq("P110"), eq("R220"))).thenReturn(respDTO);
|
||||
// mock 方法(channel)
|
||||
PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L));
|
||||
when(channelService.validPayChannel(eq(10L))).thenReturn(channel);
|
||||
|
||||
// 调用
|
||||
return refundService.syncRefund();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSyncRefund_exception() {
|
||||
// 准备参数
|
||||
PayRefundDO refund = randomPojo(PayRefundDO.class, o -> o.setAppId(1L).setChannelId(10L)
|
||||
.setStatus(PayRefundStatusEnum.WAITING.getStatus())
|
||||
.setOrderNo("P110").setNo("R220"));
|
||||
refundMapper.insert(refund);
|
||||
// mock 方法(client)
|
||||
PayClient<?> client = mock(PayClient.class);
|
||||
when(channelService.getPayClient(eq(10L))).thenReturn(client);
|
||||
// mock 方法(client 抛出异常)
|
||||
when(client.getRefund(eq("P110"), eq("R220"))).thenThrow(new RuntimeException());
|
||||
|
||||
// 调用
|
||||
int count = refundService.syncRefund();
|
||||
// 断言
|
||||
assertEquals(count, 0);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,48 @@
|
||||
spring:
|
||||
main:
|
||||
lazy-initialization: true # 开启懒加载,加快速度
|
||||
banner-mode: off # 单元测试,禁用 Banner
|
||||
|
||||
--- #################### 数据库相关配置 ####################
|
||||
|
||||
spring:
|
||||
# 数据源配置项
|
||||
datasource:
|
||||
name: ruoyi-vue-pro
|
||||
url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value; # MODE 使用 MySQL 模式;DATABASE_TO_UPPER 配置表和字段使用小写
|
||||
driver-class-name: org.h2.Driver
|
||||
username: sa
|
||||
password:
|
||||
druid:
|
||||
async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度
|
||||
initial-size: 1 # 单元测试,配置为 1,提升启动速度
|
||||
sql:
|
||||
init:
|
||||
schema-locations: classpath:/sql/create_tables.sql
|
||||
|
||||
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
|
||||
data:
|
||||
redis:
|
||||
host: 127.0.0.1 # 地址
|
||||
port: 16379 # 端口(单元测试,使用 16379 端口)
|
||||
database: 0 # 数据库索引
|
||||
|
||||
mybatis:
|
||||
lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试
|
||||
|
||||
--- #################### 定时任务相关配置 ####################
|
||||
|
||||
--- #################### 配置中心相关配置 ####################
|
||||
|
||||
--- #################### 服务保障相关配置 ####################
|
||||
|
||||
# Lock4j 配置项(单元测试,禁用 Lock4j)
|
||||
|
||||
--- #################### 监控相关配置 ####################
|
||||
|
||||
--- #################### 芋道相关配置 ####################
|
||||
|
||||
# 芋道配置项,设置当前项目所有自定义的配置
|
||||
yudao:
|
||||
info:
|
||||
base-package: cn.iocoder.yudao.module
|
4
yudao-module-pay/src/test/resources/logback.xml
Normal file
4
yudao-module-pay/src/test/resources/logback.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<configuration>
|
||||
<!-- 引用 Spring Boot 的 logback 基础配置 -->
|
||||
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
|
||||
</configuration>
|
7
yudao-module-pay/src/test/resources/sql/clean.sql
Normal file
7
yudao-module-pay/src/test/resources/sql/clean.sql
Normal file
@@ -0,0 +1,7 @@
|
||||
DELETE FROM pay_app;
|
||||
DELETE FROM pay_channel;
|
||||
DELETE FROM pay_order;
|
||||
DELETE FROM pay_order_extension;
|
||||
DELETE FROM pay_refund;
|
||||
DELETE FROM pay_notify_task;
|
||||
DELETE FROM pay_notify_log;
|
147
yudao-module-pay/src/test/resources/sql/create_tables.sql
Normal file
147
yudao-module-pay/src/test/resources/sql/create_tables.sql
Normal file
@@ -0,0 +1,147 @@
|
||||
CREATE TABLE IF NOT EXISTS "pay_app" (
|
||||
"id" number NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
"app_key" varchar(64) NOT NULL,
|
||||
"name" varchar(64) NOT NULL,
|
||||
"status" tinyint NOT NULL,
|
||||
"remark" varchar(255) DEFAULT NULL,
|
||||
`order_notify_url` varchar(1024) NOT NULL,
|
||||
`refund_notify_url` varchar(1024) NOT NULL,
|
||||
"creator" varchar(64) DEFAULT '',
|
||||
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updater" varchar(64) DEFAULT '',
|
||||
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
"deleted" bit(1) NOT NULL DEFAULT FALSE,
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT = '支付应用';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "pay_channel" (
|
||||
"id" number NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
"code" varchar(32) NOT NULL,
|
||||
"status" tinyint(4) NOT NULL,
|
||||
"remark" varchar(255) DEFAULT NULL,
|
||||
"fee_rate" double NOT NULL DEFAULT 0,
|
||||
"app_id" bigint(20) NOT NULL,
|
||||
"config" varchar(10240) NOT NULL,
|
||||
"creator" varchar(64) NULL DEFAULT '',
|
||||
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updater" varchar(64) NULL DEFAULT '',
|
||||
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
"deleted" bit(1) NOT NULL DEFAULT FALSE,
|
||||
"tenant_id" bigint not null default '0',
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT = '支付渠道';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `pay_order` (
|
||||
"id" number NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
`app_id` bigint(20) NOT NULL,
|
||||
`channel_id` bigint(20) DEFAULT NULL,
|
||||
`channel_code` varchar(32) DEFAULT NULL,
|
||||
`merchant_order_id` varchar(64) NOT NULL,
|
||||
`subject` varchar(32) NOT NULL,
|
||||
`body` varchar(128) NOT NULL,
|
||||
`notify_url` varchar(1024) NOT NULL,
|
||||
`price` bigint(20) NOT NULL,
|
||||
`channel_fee_rate` double DEFAULT 0,
|
||||
`channel_fee_price` bigint(20) DEFAULT 0,
|
||||
`status` tinyint(4) NOT NULL,
|
||||
`user_ip` varchar(50) NOT NULL,
|
||||
`expire_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`success_time` datetime(0) DEFAULT CURRENT_TIMESTAMP,
|
||||
`notify_time` datetime(0) DEFAULT CURRENT_TIMESTAMP,
|
||||
`extension_id` bigint(20) DEFAULT NULL,
|
||||
`no` varchar(64) NULL,
|
||||
`refund_price` bigint(20) NOT NULL,
|
||||
`channel_user_id` varchar(255) DEFAULT NULL,
|
||||
`channel_order_no` varchar(64) DEFAULT NULL,
|
||||
`creator` varchar(64) DEFAULT '',
|
||||
`create_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`updater` varchar(64) DEFAULT '',
|
||||
`update_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
`deleted` bit(1) NOT NULL DEFAULT FALSE,
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT = '支付订单';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `pay_order_extension` (
|
||||
"id" number NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
`no` varchar(64) NOT NULL,
|
||||
`order_id` bigint(20) NOT NULL,
|
||||
`channel_id` bigint(20) NOT NULL,
|
||||
`channel_code` varchar(32) NOT NULL,
|
||||
`user_ip` varchar(50) NULL DEFAULT NULL,
|
||||
`status` tinyint(4) NOT NULL,
|
||||
`channel_extras` varchar(1024) NULL DEFAULT NULL,
|
||||
`channel_error_code` varchar(64) NULL,
|
||||
`channel_error_msg` varchar(64) NULL,
|
||||
`channel_notify_data` varchar(1024) NULL,
|
||||
`creator` varchar(64) NULL DEFAULT '',
|
||||
`create_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`updater` varchar(64) NULL DEFAULT '',
|
||||
`update_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
`deleted` bit(1) NOT NULL DEFAULT FALSE,
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT = '支付订单拓展';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `pay_refund` (
|
||||
"id" number NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
`no` varchar(64) NOT NULL,
|
||||
`app_id` bigint(20) NOT NULL,
|
||||
`channel_id` bigint(20) NOT NULL,
|
||||
`channel_code` varchar(32) NOT NULL,
|
||||
`order_id` bigint(20) NOT NULL,
|
||||
`order_no` varchar(64) NOT NULL,
|
||||
`merchant_order_id` varchar(64) NOT NULL,
|
||||
`merchant_refund_id` varchar(64) NOT NULL,
|
||||
`notify_url` varchar(1024) NOT NULL,
|
||||
`status` tinyint(4) NOT NULL,
|
||||
`pay_price` bigint(20) NOT NULL,
|
||||
`refund_price` bigint(20) NOT NULL,
|
||||
`reason` varchar(256) NOT NULL,
|
||||
`user_ip` varchar(50) NULL DEFAULT NULL,
|
||||
`channel_order_no` varchar(64) NOT NULL,
|
||||
`channel_refund_no` varchar(64) NULL DEFAULT NULL,
|
||||
`success_time` datetime(0) NULL DEFAULT NULL,
|
||||
`channel_error_code` varchar(128) NULL DEFAULT NULL,
|
||||
`channel_error_msg` varchar(256) NULL DEFAULT NULL,
|
||||
`channel_notify_data` varchar(1024) NULL,
|
||||
`creator` varchar(64) NULL DEFAULT '',
|
||||
`create_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`updater` varchar(64) NULL DEFAULT '',
|
||||
`update_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
`deleted` bit(1) NOT NULL DEFAULT FALSE,
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT = '退款订单';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `pay_notify_task` (
|
||||
"id" number NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
`app_id` bigint(20) NOT NULL,
|
||||
`type` tinyint(4) NOT NULL,
|
||||
`data_id` bigint(20) NOT NULL,
|
||||
`merchant_order_id` varchar(64) NOT NULL,
|
||||
`status` tinyint(4) NOT NULL,
|
||||
`next_notify_time` datetime(0) NULL DEFAULT NULL,
|
||||
`last_execute_time` datetime(0) NULL DEFAULT NULL,
|
||||
`notify_times` int NOT NULL,
|
||||
`max_notify_times` int NOT NULL,
|
||||
`notify_url` varchar(1024) NOT NULL,
|
||||
`creator` varchar(64) NULL DEFAULT '',
|
||||
`create_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`updater` varchar(64) NULL DEFAULT '',
|
||||
`update_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
`deleted` bit(1) NOT NULL DEFAULT FALSE,
|
||||
`tenant_id` bigint(20) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT = '支付通知任务';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `pay_notify_log` (
|
||||
"id" number NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
`task_id` bigint(20) NOT NULL,
|
||||
`notify_times` int NOT NULL,
|
||||
`response` varchar(1024) NOT NULL,
|
||||
`status` tinyint(4) NOT NULL,
|
||||
`creator` varchar(64) NULL DEFAULT '',
|
||||
`create_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`updater` varchar(64) NULL DEFAULT '',
|
||||
`update_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
`deleted` bit(1) NOT NULL DEFAULT FALSE,
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT = '支付通知日志';
|
Reference in New Issue
Block a user