| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227 |
- import { describe, it, expect, beforeEach, vi } from 'vitest';
- import { DataSource } from 'typeorm';
- import {
- IntegrationTestDatabase,
- setupIntegrationDatabaseHooksWithEntities
- } from '@d8d/shared-test-util';
- import { PaymentMtService } from '../../src/services/payment.mt.service.js';
- import { PaymentMtEntity } from '../../src/entities/payment.mt.entity.js';
- import { PaymentStatus } from '../../src/entities/payment.types.js';
- // Mock 微信支付SDK
- vi.mock('wechatpay-node-v3', () => {
- return {
- default: vi.fn().mockImplementation(() => ({
- refunds: vi.fn().mockResolvedValue({
- id: 'mock_refund_id_123',
- out_refund_no: 'REFUND_ORDER_123_1234567890',
- status: 'SUCCESS'
- }),
- verifySign: vi.fn().mockResolvedValue(true),
- decipher_gcm: vi.fn().mockReturnValue(JSON.stringify({
- out_refund_no: 'REFUND_ORDER_123_1234567890',
- refund_status: 'SUCCESS'
- }))
- }))
- };
- });
- // Mock 系统配置服务
- vi.mock('@d8d/core-module-mt/system-config-module-mt', () => ({
- SystemConfigServiceMt: vi.fn().mockImplementation(() => ({
- getConfigsByKeys: vi.fn().mockResolvedValue({
- 'wx.payment.merchant.id': 'mock_merchant_id',
- 'wx.mini.app.id': 'mock_app_id',
- 'wx.payment.v3.key': 'mock_v3_key',
- 'wx.payment.notify.url': 'mock_notify_url',
- 'wx.payment.cert.serial.no': 'mock_cert_serial_no',
- 'wx.payment.public.key': 'mock_public_key',
- 'wx.payment.private.key': 'mock_private_key'
- })
- }))
- }));
- // Mock 订单服务
- vi.mock('@d8d/orders-module-mt', () => ({
- OrderMtService: vi.fn().mockImplementation(() => ({})),
- OrderMt: vi.fn()
- }));
- // 设置集成测试钩子
- setupIntegrationDatabaseHooksWithEntities([PaymentMtEntity])
- describe('PaymentRefund Integration Tests', () => {
- let dataSource: DataSource;
- let paymentService: PaymentMtService;
- beforeEach(async () => {
- // 获取集成测试数据库连接
- dataSource = await IntegrationTestDatabase.getDataSource();
- paymentService = new PaymentMtService(dataSource);
- });
- describe('refund method', () => {
- it('应该成功处理已支付订单的退款', async () => {
- // 准备测试数据
- const payment = new PaymentMtEntity();
- payment.tenantId = 1;
- payment.externalOrderId = 1001;
- payment.userId = 1;
- payment.totalAmount = 1000; // 10元
- payment.description = '测试订单';
- payment.paymentStatus = PaymentStatus.PAID;
- payment.outTradeNo = 'PAYMENT_1001_1234567890';
- payment.openid = 'mock_openid';
- payment.wechatTransactionId = 'mock_transaction_id';
- await dataSource.getRepository(PaymentMtEntity).save(payment);
- // 执行退款
- const refundResult = await paymentService.refund(
- 1,
- 'PAYMENT_1001_1234567890',
- 1000,
- '测试退款'
- );
- // 验证退款结果
- expect(refundResult).toMatchObject({
- refundId: expect.stringContaining('REFUND_PAYMENT_1001_1234567890'),
- outRefundNo: expect.stringContaining('REFUND_PAYMENT_1001_1234567890'),
- refundStatus: 'SUCCESS',
- refundAmount: 1000,
- refundTime: expect.any(String)
- });
- // 验证支付记录已更新
- const updatedPayment = await dataSource.getRepository(PaymentMtEntity).findOne({
- where: { outTradeNo: 'PAYMENT_1001_1234567890', tenantId: 1 }
- });
- expect(updatedPayment).toBeDefined();
- expect(updatedPayment?.refundStatus).toBe(PaymentStatus.REFUNDED);
- expect(updatedPayment?.refundAmount).toBe(1000);
- expect(updatedPayment?.refundTime).toBeInstanceOf(Date);
- });
- it('应该对不存在的支付记录抛出错误', async () => {
- await expect(
- paymentService.refund(1, 'NON_EXISTENT_ORDER', 1000)
- ).rejects.toThrow('支付记录不存在');
- });
- it('应该对未支付订单抛出错误', async () => {
- const payment = new PaymentMtEntity();
- payment.tenantId = 1;
- payment.externalOrderId = 1002;
- payment.userId = 1;
- payment.totalAmount = 1000;
- payment.description = '测试订单';
- payment.paymentStatus = PaymentStatus.PENDING;
- payment.outTradeNo = 'PAYMENT_1002_1234567890';
- payment.openid = 'mock_openid';
- await dataSource.getRepository(PaymentMtEntity).save(payment);
- await expect(
- paymentService.refund(1, 'PAYMENT_1002_1234567890', 1000)
- ).rejects.toThrow('订单支付状态不正确');
- });
- it('应该对无效退款金额抛出错误', async () => {
- const payment = new PaymentMtEntity();
- payment.tenantId = 1;
- payment.externalOrderId = 1003;
- payment.userId = 1;
- payment.totalAmount = 1000;
- payment.description = '测试订单';
- payment.paymentStatus = PaymentStatus.PAID;
- payment.outTradeNo = 'PAYMENT_1003_1234567890';
- payment.openid = 'mock_openid';
- payment.wechatTransactionId = 'mock_transaction_id';
- await dataSource.getRepository(PaymentMtEntity).save(payment);
- // 测试退款金额为0
- await expect(
- paymentService.refund(1, 'PAYMENT_1003_1234567890', 0)
- ).rejects.toThrow('退款金额无效');
- // 测试退款金额超过支付金额
- await expect(
- paymentService.refund(1, 'PAYMENT_1003_1234567890', 2000)
- ).rejects.toThrow('退款金额无效');
- });
- });
- describe('handleRefundCallback method', () => {
- it('应该成功处理退款回调', async () => {
- const mockCallbackData = {
- resource: {
- ciphertext: 'mock_ciphertext',
- associated_data: '',
- nonce: 'mock_nonce'
- }
- };
- const mockHeaders = {
- 'wechatpay-timestamp': 'mock_timestamp',
- 'wechatpay-nonce': 'mock_nonce',
- 'wechatpay-signature': 'mock_signature',
- 'wechatpay-serial': 'mock_serial'
- };
- const mockRawBody = 'mock_raw_body';
- // 执行退款回调处理
- await expect(
- paymentService.handleRefundCallback(mockCallbackData, mockHeaders, mockRawBody)
- ).resolves.not.toThrow();
- });
- });
- describe('multi-tenant refund data isolation', () => {
- it('应该只退款特定租户的支付记录', async () => {
- // 创建租户1的支付记录
- const payment1 = new PaymentMtEntity();
- payment1.tenantId = 1;
- payment1.externalOrderId = 1004;
- payment1.userId = 1;
- payment1.totalAmount = 1000;
- payment1.description = '租户1订单';
- payment1.paymentStatus = PaymentStatus.PAID;
- payment1.outTradeNo = 'PAYMENT_1004_1234567890';
- payment1.openid = 'mock_openid';
- payment1.wechatTransactionId = 'mock_transaction_id';
- // 创建租户2的支付记录
- const payment2 = new PaymentMtEntity();
- payment2.tenantId = 2;
- payment2.externalOrderId = 1004;
- payment2.userId = 1;
- payment2.totalAmount = 1000;
- payment2.description = '租户2订单';
- payment2.paymentStatus = PaymentStatus.PAID;
- payment2.outTradeNo = 'PAYMENT_1004_1234567890';
- payment2.openid = 'mock_openid';
- payment2.wechatTransactionId = 'mock_transaction_id';
- await dataSource.getRepository(PaymentMtEntity).save([payment1, payment2]);
- // 为租户1执行退款
- await paymentService.refund(1, 'PAYMENT_1004_1234567890', 1000);
- // 验证租户1的支付记录已更新
- const tenant1Payment = await dataSource.getRepository(PaymentMtEntity).findOne({
- where: { outTradeNo: 'PAYMENT_1004_1234567890', tenantId: 1 }
- });
- expect(tenant1Payment?.refundStatus).toBe(PaymentStatus.REFUNDED);
- // 验证租户2的支付记录未受影响
- const tenant2Payment = await dataSource.getRepository(PaymentMtEntity).findOne({
- where: { outTradeNo: 'PAYMENT_1004_1234567890', tenantId: 2 }
- });
- expect(tenant2Payment?.refundStatus).toBeNull();
- });
- });
- });
|