|
@@ -19,6 +19,8 @@ import { config } from 'dotenv';
|
|
|
import { resolve } from 'path';
|
|
import { resolve } from 'path';
|
|
|
// 导入微信支付SDK用于模拟
|
|
// 导入微信支付SDK用于模拟
|
|
|
import WxPay from 'wechatpay-node-v3';
|
|
import WxPay from 'wechatpay-node-v3';
|
|
|
|
|
+// 导入测试数据工厂
|
|
|
|
|
+import { PaymentTestFactory } from '../factories/payment-test.factory.js';
|
|
|
|
|
|
|
|
// 在测试环境中加载环境变量
|
|
// 在测试环境中加载环境变量
|
|
|
config({ path: resolve(process.cwd(), '.env.test') });
|
|
config({ path: resolve(process.cwd(), '.env.test') });
|
|
@@ -30,9 +32,15 @@ setupIntegrationDatabaseHooksWithEntities([PaymentMtEntity, UserEntityMt, FileMt
|
|
|
|
|
|
|
|
describe('支付回调API集成测试 - 多租户版本', () => {
|
|
describe('支付回调API集成测试 - 多租户版本', () => {
|
|
|
let client: ReturnType<typeof testClient<typeof PaymentMtRoutes>>;
|
|
let client: ReturnType<typeof testClient<typeof PaymentMtRoutes>>;
|
|
|
- let testUser: UserEntityMt;
|
|
|
|
|
- let testPayment: PaymentMtEntity;
|
|
|
|
|
- let testOrder: OrderMt;
|
|
|
|
|
|
|
+ let testFactory: PaymentTestFactory;
|
|
|
|
|
+ let testData: {
|
|
|
|
|
+ user: UserEntityMt;
|
|
|
|
|
+ merchant: MerchantMt;
|
|
|
|
|
+ supplier: SupplierMt;
|
|
|
|
|
+ address: DeliveryAddressMt;
|
|
|
|
|
+ order: OrderMt;
|
|
|
|
|
+ payment: PaymentMtEntity;
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
// 使用真实的微信支付回调数据 - 直接使用原始请求体字符串
|
|
// 使用真实的微信支付回调数据 - 直接使用原始请求体字符串
|
|
|
const rawBody = '{"id":"495e231b-9fd8-54a1-8a30-2a38a807744c","create_time":"2025-10-25T12:48:11+08:00","resource_type":"encrypt-resource","event_type":"TRANSACTION.SUCCESS","summary":"支付成功","resource":{"original_type":"transaction","algorithm":"AEAD_AES_256_GCM","ciphertext":"tl1/8FRRn6g0gRq8IoVR8+95VuIADYBDOt6N9PKiHVhiD6l++W5g/wg6VlsCRIZJ+KWMYTaf5FzQHMjCs8o9otIkLLuJA2aZC+kCQtGxNfyVBwxool/tLT9mHd0dFGThqbj8vb/lm+jjNcmmiWHz+J1ZRvGl7mH4I714vudok7JRt5Q0u0tYaLWr76TTXuQErlA7T4KbeVeGAj8iMpu2ErCpR9QRif36Anc5ARjNYrIWfraXlmUXVbXermDyJ8r4o/4QCFfGk8L1u1WqNYASrRTQvQ8OPqj/J21OkDxbPPrOiEmAX1jOvONvIVEe9Lbkm6rdhW4aLRoZYtiusAk/Vm7MI/UYPwRZbyuc4wwdA1T1D4RdJd/m2I4KSvZHQgs0DM0tLqlb0z3880XYNr8iPFnyu2r8Z8LGcXD+COm06vc7bvNWh3ODwmMrmZQkym/Y/T3X/h/4MZj7+1h2vYHqnnrsgtNPHc/2IwWC/fQlPwtSrLh6iUxSd0betFpKLSq08CaJZvnenpDf1ORRMvd8EhTtIJJ4mV4v+VzCOYNhIcBhKp9XwsuhxIdkpGGmNPpow2c2BXY=","associated_data":"transaction","nonce":"sTnWce32BTQP"}}';
|
|
const rawBody = '{"id":"495e231b-9fd8-54a1-8a30-2a38a807744c","create_time":"2025-10-25T12:48:11+08:00","resource_type":"encrypt-resource","event_type":"TRANSACTION.SUCCESS","summary":"支付成功","resource":{"original_type":"transaction","algorithm":"AEAD_AES_256_GCM","ciphertext":"tl1/8FRRn6g0gRq8IoVR8+95VuIADYBDOt6N9PKiHVhiD6l++W5g/wg6VlsCRIZJ+KWMYTaf5FzQHMjCs8o9otIkLLuJA2aZC+kCQtGxNfyVBwxool/tLT9mHd0dFGThqbj8vb/lm+jjNcmmiWHz+J1ZRvGl7mH4I714vudok7JRt5Q0u0tYaLWr76TTXuQErlA7T4KbeVeGAj8iMpu2ErCpR9QRif36Anc5ARjNYrIWfraXlmUXVbXermDyJ8r4o/4QCFfGk8L1u1WqNYASrRTQvQ8OPqj/J21OkDxbPPrOiEmAX1jOvONvIVEe9Lbkm6rdhW4aLRoZYtiusAk/Vm7MI/UYPwRZbyuc4wwdA1T1D4RdJd/m2I4KSvZHQgs0DM0tLqlb0z3880XYNr8iPFnyu2r8Z8LGcXD+COm06vc7bvNWh3ODwmMrmZQkym/Y/T3X/h/4MZj7+1h2vYHqnnrsgtNPHc/2IwWC/fQlPwtSrLh6iUxSd0betFpKLSq08CaJZvnenpDf1ORRMvd8EhTtIJJ4mV4v+VzCOYNhIcBhKp9XwsuhxIdkpGGmNPpow2c2BXY=","associated_data":"transaction","nonce":"sTnWce32BTQP"}}';
|
|
@@ -47,97 +55,12 @@ describe('支付回调API集成测试 - 多租户版本', () => {
|
|
|
// 创建测试客户端
|
|
// 创建测试客户端
|
|
|
client = testClient(PaymentMtRoutes);
|
|
client = testClient(PaymentMtRoutes);
|
|
|
|
|
|
|
|
- // 创建测试用户
|
|
|
|
|
|
|
+ // 创建测试数据工厂
|
|
|
const dataSource = await IntegrationTestDatabase.getDataSource();
|
|
const dataSource = await IntegrationTestDatabase.getDataSource();
|
|
|
|
|
+ testFactory = new PaymentTestFactory(dataSource);
|
|
|
|
|
|
|
|
- const userRepository = dataSource.getRepository(UserEntityMt);
|
|
|
|
|
- testUser = userRepository.create({
|
|
|
|
|
- username: `test_user_${Date.now()}`,
|
|
|
|
|
- password: 'test_password',
|
|
|
|
|
- nickname: '测试用户',
|
|
|
|
|
- openid: 'oJy1-16IIG18XZLl7G32k1hHMUFg',
|
|
|
|
|
- tenantId: 1
|
|
|
|
|
- });
|
|
|
|
|
- await userRepository.save(testUser);
|
|
|
|
|
-
|
|
|
|
|
- // 创建商户记录
|
|
|
|
|
- const merchantRepository = dataSource.getRepository(MerchantMt);
|
|
|
|
|
- const testMerchant = merchantRepository.create({
|
|
|
|
|
- tenantId: 1,
|
|
|
|
|
- name: '测试商户',
|
|
|
|
|
- username: `m${Date.now()}`.slice(-19), // 确保不超过20字符
|
|
|
|
|
- password: 'test_password',
|
|
|
|
|
- state: 1,
|
|
|
|
|
- createdBy: testUser.id,
|
|
|
|
|
- updatedBy: testUser.id
|
|
|
|
|
- });
|
|
|
|
|
- await merchantRepository.save(testMerchant);
|
|
|
|
|
-
|
|
|
|
|
- // 创建供货商记录
|
|
|
|
|
- const supplierRepository = dataSource.getRepository(SupplierMt);
|
|
|
|
|
- const testSupplier = supplierRepository.create({
|
|
|
|
|
- tenantId: 1,
|
|
|
|
|
- name: '测试供货商',
|
|
|
|
|
- username: `s${Date.now()}`.slice(-49), // 确保不超过50字符
|
|
|
|
|
- password: 'test_password',
|
|
|
|
|
- state: 1,
|
|
|
|
|
- createdBy: testUser.id,
|
|
|
|
|
- updatedBy: testUser.id
|
|
|
|
|
- });
|
|
|
|
|
- await supplierRepository.save(testSupplier);
|
|
|
|
|
-
|
|
|
|
|
- // 创建配送地址记录
|
|
|
|
|
- const addressRepository = dataSource.getRepository(DeliveryAddressMt);
|
|
|
|
|
- const testAddress = addressRepository.create({
|
|
|
|
|
- tenantId: 1,
|
|
|
|
|
- userId: testUser.id,
|
|
|
|
|
- name: '测试收货人',
|
|
|
|
|
- mobile: '13800138000',
|
|
|
|
|
- province: 110000,
|
|
|
|
|
- city: 110100,
|
|
|
|
|
- district: 110101,
|
|
|
|
|
- town: 110101001,
|
|
|
|
|
- address: '测试地址',
|
|
|
|
|
- isDefault: 1,
|
|
|
|
|
- createdBy: testUser.id,
|
|
|
|
|
- updatedBy: testUser.id
|
|
|
|
|
- });
|
|
|
|
|
- await addressRepository.save(testAddress);
|
|
|
|
|
-
|
|
|
|
|
- // 创建测试订单
|
|
|
|
|
- const orderRepository = dataSource.getRepository(OrderMt);
|
|
|
|
|
- testOrder = orderRepository.create({
|
|
|
|
|
- tenantId: 1,
|
|
|
|
|
- orderNo: `ORD${Date.now()}`,
|
|
|
|
|
- userId: testUser.id,
|
|
|
|
|
- amount: 1,
|
|
|
|
|
- costAmount: 0.5,
|
|
|
|
|
- payAmount: 1,
|
|
|
|
|
- orderType: 1,
|
|
|
|
|
- payType: 2,
|
|
|
|
|
- payState: 0, // 未支付
|
|
|
|
|
- state: 0,
|
|
|
|
|
- addressId: testAddress.id,
|
|
|
|
|
- merchantId: testMerchant.id,
|
|
|
|
|
- supplierId: testSupplier.id,
|
|
|
|
|
- createdBy: testUser.id,
|
|
|
|
|
- updatedBy: testUser.id
|
|
|
|
|
- });
|
|
|
|
|
- await orderRepository.save(testOrder);
|
|
|
|
|
-
|
|
|
|
|
- // 创建测试支付记录,使用与真实回调数据一致的金额
|
|
|
|
|
- const paymentRepository = dataSource.getRepository(PaymentMtEntity);
|
|
|
|
|
- testPayment = paymentRepository.create({
|
|
|
|
|
- externalOrderId: testOrder.id, // 使用订单ID作为外部订单ID
|
|
|
|
|
- userId: testUser.id,
|
|
|
|
|
- totalAmount: 1, // 1分钱,与真实回调数据一致
|
|
|
|
|
- description: '测试支付',
|
|
|
|
|
- paymentStatus: PaymentStatus.PROCESSING, // 设置为处理中状态,模拟已发起支付
|
|
|
|
|
- openid: testUser.openid!,
|
|
|
|
|
- outTradeNo: `ORDER_${testOrder.id}_${Date.now()}`,
|
|
|
|
|
- tenantId: 1
|
|
|
|
|
- });
|
|
|
|
|
- await paymentRepository.save(testPayment);
|
|
|
|
|
|
|
+ // 创建完整的测试数据
|
|
|
|
|
+ testData = await testFactory.createCompleteTestData(1);
|
|
|
|
|
|
|
|
// 设置微信支付SDK的全局mock
|
|
// 设置微信支付SDK的全局mock
|
|
|
const mockWxPay = {
|
|
const mockWxPay = {
|
|
@@ -150,7 +73,7 @@ describe('支付回调API集成测试 - 多租户版本', () => {
|
|
|
}),
|
|
}),
|
|
|
verifySign: vi.fn().mockResolvedValue(true),
|
|
verifySign: vi.fn().mockResolvedValue(true),
|
|
|
decipher_gcm: vi.fn().mockReturnValue(JSON.stringify({
|
|
decipher_gcm: vi.fn().mockReturnValue(JSON.stringify({
|
|
|
- out_trade_no: testPayment.outTradeNo, // 使用数据库中保存的 outTradeNo
|
|
|
|
|
|
|
+ out_trade_no: testData.payment.outTradeNo, // 使用数据库中保存的 outTradeNo
|
|
|
trade_state: 'SUCCESS',
|
|
trade_state: 'SUCCESS',
|
|
|
transaction_id: 'test_transaction_id',
|
|
transaction_id: 'test_transaction_id',
|
|
|
amount: {
|
|
amount: {
|
|
@@ -286,94 +209,9 @@ describe('支付回调API集成测试 - 多租户版本', () => {
|
|
|
|
|
|
|
|
it('应该验证多租户数据隔离', async () => {
|
|
it('应该验证多租户数据隔离', async () => {
|
|
|
// 创建第二个租户的测试数据
|
|
// 创建第二个租户的测试数据
|
|
|
- const dataSource = await IntegrationTestDatabase.getDataSource();
|
|
|
|
|
-
|
|
|
|
|
- const userRepository = dataSource.getRepository(UserEntityMt);
|
|
|
|
|
- const testUser2 = userRepository.create({
|
|
|
|
|
- username: `test_user2_${Date.now()}`,
|
|
|
|
|
- password: 'test_password',
|
|
|
|
|
- nickname: '测试用户2',
|
|
|
|
|
- openid: 'oJy1-16IIG18XZLl7G32k1hHMUFg2',
|
|
|
|
|
- tenantId: 2
|
|
|
|
|
- });
|
|
|
|
|
- await userRepository.save(testUser2);
|
|
|
|
|
-
|
|
|
|
|
- // 创建第二个租户的商户记录
|
|
|
|
|
- const merchantRepository = dataSource.getRepository(MerchantMt);
|
|
|
|
|
- const testMerchant2 = merchantRepository.create({
|
|
|
|
|
- tenantId: 2,
|
|
|
|
|
- name: '测试商户2',
|
|
|
|
|
- username: `test_merchant2_${Date.now()}`,
|
|
|
|
|
- password: 'test_password',
|
|
|
|
|
- state: 1,
|
|
|
|
|
- createdBy: testUser2.id,
|
|
|
|
|
- updatedBy: testUser2.id
|
|
|
|
|
- });
|
|
|
|
|
- await merchantRepository.save(testMerchant2);
|
|
|
|
|
-
|
|
|
|
|
- // 创建第二个租户的供货商记录
|
|
|
|
|
- const supplierRepository = dataSource.getRepository(SupplierMt);
|
|
|
|
|
- const testSupplier2 = supplierRepository.create({
|
|
|
|
|
- tenantId: 2,
|
|
|
|
|
- name: '测试供货商2',
|
|
|
|
|
- username: `test_supplier2_${Date.now()}`,
|
|
|
|
|
- password: 'test_password',
|
|
|
|
|
- state: 1,
|
|
|
|
|
- createdBy: testUser2.id,
|
|
|
|
|
- updatedBy: testUser2.id
|
|
|
|
|
- });
|
|
|
|
|
- await supplierRepository.save(testSupplier2);
|
|
|
|
|
-
|
|
|
|
|
- // 创建第二个租户的配送地址记录
|
|
|
|
|
- const addressRepository = dataSource.getRepository(DeliveryAddressMt);
|
|
|
|
|
- const testAddress2 = addressRepository.create({
|
|
|
|
|
- tenantId: 2,
|
|
|
|
|
- userId: testUser2.id,
|
|
|
|
|
- name: '测试收货人2',
|
|
|
|
|
- mobile: '13800138001',
|
|
|
|
|
- province: 110000,
|
|
|
|
|
- city: 110100,
|
|
|
|
|
- district: 110101,
|
|
|
|
|
- town: 110101001,
|
|
|
|
|
- address: '测试地址2',
|
|
|
|
|
- isDefault: 1,
|
|
|
|
|
- createdBy: testUser2.id,
|
|
|
|
|
- updatedBy: testUser2.id
|
|
|
|
|
- });
|
|
|
|
|
- await addressRepository.save(testAddress2);
|
|
|
|
|
-
|
|
|
|
|
- const orderRepository = dataSource.getRepository(OrderMt);
|
|
|
|
|
- const testOrder2 = orderRepository.create({
|
|
|
|
|
- tenantId: 2,
|
|
|
|
|
- orderNo: `ORD${Date.now()}_2`,
|
|
|
|
|
- userId: testUser2.id,
|
|
|
|
|
- amount: 1,
|
|
|
|
|
- costAmount: 0.5,
|
|
|
|
|
- payAmount: 1,
|
|
|
|
|
- orderType: 1,
|
|
|
|
|
- payType: 2,
|
|
|
|
|
- payState: 0,
|
|
|
|
|
- state: 0,
|
|
|
|
|
- addressId: testAddress2.id,
|
|
|
|
|
- merchantId: testMerchant2.id,
|
|
|
|
|
- supplierId: testSupplier2.id,
|
|
|
|
|
- createdBy: testUser2.id,
|
|
|
|
|
- updatedBy: testUser2.id
|
|
|
|
|
- });
|
|
|
|
|
- await orderRepository.save(testOrder2);
|
|
|
|
|
-
|
|
|
|
|
- const paymentRepository = dataSource.getRepository(PaymentMtEntity);
|
|
|
|
|
- const testPayment2 = paymentRepository.create({
|
|
|
|
|
- externalOrderId: testOrder2.id,
|
|
|
|
|
- userId: testUser2.id,
|
|
|
|
|
- totalAmount: 1,
|
|
|
|
|
- description: '测试支付2',
|
|
|
|
|
- paymentStatus: PaymentStatus.PROCESSING,
|
|
|
|
|
- openid: testUser2.openid!,
|
|
|
|
|
- outTradeNo: `ORDER_${testOrder2.id}_${Date.now()}`,
|
|
|
|
|
- tenantId: 2
|
|
|
|
|
- });
|
|
|
|
|
- await paymentRepository.save(testPayment2);
|
|
|
|
|
|
|
+ const multiTenantData = await testFactory.createMultiTenantTestData();
|
|
|
|
|
+ const tenant1Data = multiTenantData.tenant1;
|
|
|
|
|
+ const tenant2Data = multiTenantData.tenant2;
|
|
|
|
|
|
|
|
// 处理租户1的支付回调
|
|
// 处理租户1的支付回调
|
|
|
const response = await client.payment.callback.$post({
|
|
const response = await client.payment.callback.$post({
|
|
@@ -388,14 +226,16 @@ describe('支付回调API集成测试 - 多租户版本', () => {
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.status).toBe(200);
|
|
|
|
|
|
|
|
// 验证租户1的订单状态已更新
|
|
// 验证租户1的订单状态已更新
|
|
|
|
|
+ const dataSource = await IntegrationTestDatabase.getDataSource();
|
|
|
|
|
+ const orderRepository = dataSource.getRepository(OrderMt);
|
|
|
const updatedOrder1 = await orderRepository.findOne({
|
|
const updatedOrder1 = await orderRepository.findOne({
|
|
|
- where: { id: testOrder.id, tenantId: 1 }
|
|
|
|
|
|
|
+ where: { id: tenant1Data.order.id, tenantId: 1 }
|
|
|
});
|
|
});
|
|
|
expect(updatedOrder1?.payState).toBe(2); // 已支付
|
|
expect(updatedOrder1?.payState).toBe(2); // 已支付
|
|
|
|
|
|
|
|
// 验证租户2的订单状态未受影响
|
|
// 验证租户2的订单状态未受影响
|
|
|
const updatedOrder2 = await orderRepository.findOne({
|
|
const updatedOrder2 = await orderRepository.findOne({
|
|
|
- where: { id: testOrder2.id, tenantId: 2 }
|
|
|
|
|
|
|
+ where: { id: tenant2Data.order.id, tenantId: 2 }
|
|
|
});
|
|
});
|
|
|
expect(updatedOrder2?.payState).toBe(0); // 仍为未支付
|
|
expect(updatedOrder2?.payState).toBe(0); // 仍为未支付
|
|
|
});
|
|
});
|