diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java
index a6e88ccf1..1b8d05458 100755
--- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java
+++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java
@@ -19,7 +19,6 @@ 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.ErrorCodeConstants;
 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;
@@ -92,7 +91,6 @@ public class PayRefundServiceImpl implements PayRefundService {
     }
 
     @Override
-    @Transactional(rollbackFor = Exception.class)
     public Long createPayRefund(PayRefundCreateReqDTO reqDTO) {
         // 1.1 校验 App
         PayAppDO app = appService.validPayApp(reqDTO.getAppId());
@@ -109,7 +107,7 @@ public class PayRefundServiceImpl implements PayRefundService {
         PayRefundDO refund = refundMapper.selectByAppIdAndMerchantRefundId(
                 app.getId(), reqDTO.getMerchantRefundId());
         if (refund != null) {
-            throw exception(ErrorCodeConstants.REFUND_EXISTS);
+            throw exception(REFUND_EXISTS);
         }
 
         // 2.1 插入退款单
@@ -158,7 +156,7 @@ public class PayRefundServiceImpl implements PayRefundService {
     private PayOrderDO validatePayOrderCanRefund(PayRefundCreateReqDTO reqDTO) {
         PayOrderDO order = orderService.getOrder(reqDTO.getAppId(), reqDTO.getMerchantOrderId());
         if (order == null) {
-            throw exception(ErrorCodeConstants.ORDER_NOT_FOUND);
+            throw exception(ORDER_NOT_FOUND);
         }
         // 校验状态,必须是已支付、或者已退款
         if (!PayOrderStatusEnum.isSuccessOrRefund(order.getStatus())) {
@@ -167,12 +165,12 @@ public class PayRefundServiceImpl implements PayRefundService {
 
         // 校验金额,退款金额不能大于原定的金额
         if (reqDTO.getPrice() + order.getRefundPrice() > order.getPrice()){
-            throw exception(ErrorCodeConstants.REFUND_PRICE_EXCEED);
+            throw exception(REFUND_PRICE_EXCEED);
         }
         // 是否有退款中的订单
         if (refundMapper.selectCountByAppIdAndOrderId(reqDTO.getAppId(), order.getId(),
                 PayRefundStatusEnum.WAITING.getStatus()) > 0) {
-            throw exception(ErrorCodeConstants.REFUND_HAS_REFUNDING);
+            throw exception(REFUND_HAS_REFUNDING);
         }
         return order;
     }
diff --git a/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceTest.java b/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceTest.java
index fa01d6698..8ef92270f 100755
--- a/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceTest.java
+++ b/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceTest.java
@@ -1,12 +1,15 @@
 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.framework.pay.core.client.PayClient;
 import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
+import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
+import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
 import cn.iocoder.yudao.framework.test.core.ut.BaseDbAndRedisUnitTest;
-import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
-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.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;
@@ -21,12 +24,13 @@ 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 javax.annotation.Resource;
-import java.time.LocalDateTime;
 import java.util.List;
 
 import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
@@ -37,10 +41,12 @@ import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServic
 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.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.*;
 
 /**
  * {@link PayRefundServiceImpl} 的单元测试类
@@ -69,6 +75,41 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest {
     @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 数据
@@ -276,7 +317,7 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest {
         // 准备参数
         PayRefundCreateReqDTO reqDTO = randomPojo(PayRefundCreateReqDTO.class,
                 o -> o.setAppId(1L).setMerchantOrderId("100").setPrice(9)
-                        .setReason("测试退款"));
+                        .setMerchantRefundId("200").setReason("测试退款"));
         // mock 方法(app)
         PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L));
         when(appService.validPayApp(eq(1L))).thenReturn(app);
@@ -295,7 +336,7 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest {
         when(payClientFactory.getPayClient(eq(10L))).thenReturn(client);
         // mock 数据(refund 已存在)
         PayRefundDO refund = randomPojo(PayRefundDO.class, o ->
-                o.setOrderId(order.getId()).setStatus(PayOrderStatusEnum.WAITING.getStatus()));
+                o.setAppId(1L).setMerchantRefundId("200"));
         refundMapper.insert(refund);
 
         // 调用,并断言异常
@@ -305,12 +346,97 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest {
 
     @Test
     public void testCreateRefund_invokeException() {
+        // 准备参数
+        PayRefundCreateReqDTO reqDTO = randomPojo(PayRefundCreateReqDTO.class,
+                o -> o.setAppId(1L).setMerchantOrderId("100").setPrice(9)
+                        .setMerchantRefundId("200").setReason("测试退款"));
+        // mock 方法(app)
+        PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L));
+        when(appService.validPayApp(eq(1L))).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(payClientFactory.getPayClient(eq(10L))).thenReturn(client);
+        // mock 方法(client 调用发生异常)
+        when(client.unifiedRefund(any(PayRefundUnifiedReqDTO.class))).thenThrow(new RuntimeException());
 
+        // 调用
+        Long refundId = refundService.createPayRefund(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.setAppId(1L).setMerchantOrderId("100").setPrice(9)
+                            .setMerchantRefundId("200").setReason("测试退款"));
+            // mock 方法(app)
+            PayAppDO app = randomPojo(PayAppDO.class, o -> o.setId(1L));
+            when(appService.validPayApp(eq(1L))).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(payClientFactory.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.createPayRefund(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));
+        }
     }
 
 }
diff --git a/yudao-module-pay/yudao-module-pay-biz/src/test/resources/sql/create_tables.sql b/yudao-module-pay/yudao-module-pay-biz/src/test/resources/sql/create_tables.sql
index 7d7d2435c..7bcf0facc 100644
--- a/yudao-module-pay/yudao-module-pay-biz/src/test/resources/sql/create_tables.sql
+++ b/yudao-module-pay/yudao-module-pay-biz/src/test/resources/sql/create_tables.sql
@@ -87,6 +87,7 @@ CREATE TABLE IF NOT EXISTS `pay_refund` (
     `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,