import test from 'node:test'; import assert from 'node:assert/strict'; import { fulfillOrder, MiaoguoVirtualPayNotify500 } from '../src/api/virtualPay/virtualPayController.js'; function orderXml({ productId = '166_199', env = 0, goodsPrice = 19900, detail = {} } = {}) { return `` + `` + `` + ``; } function createFakeConnection({ order, user }) { const state = { began: 0, committed: 0, rolledBack: 0, released: 0, queries: [] }; const connection = { async beginTransaction() { state.began += 1; }, async commit() { state.committed += 1; }, async rollback() { state.rolledBack += 1; }, release() { state.released += 1; }, async query(sql, params) { state.queries.push({ sql, params }); if (sql.includes('FROM ProductPayInfo')) return [[order]]; if (sql.includes('FROM MiaoguoWXUsers')) return [[user]]; return [{ affectedRows: 1 }]; } }; return { connection, state }; } function dependencies(connection, postCalls = []) { return { getConnection: async () => connection, schedulePostCommit: callback => callback(), runPostPayActions: async payload => { postCalls.push(payload); } }; } function paidMemberOrder(overrides = {}) { return { ID: 101, TradeNo: 'VP_TEST_199', PayType: 7, OpenID: 'openid-test', Money: 19900, Status: 0, XMLPre: orderXml({ detail: { EndTime: '2027-08-27 23:59:59' } }), ...overrides }; } const memberUser = { UserID: 1, OpenID: 'openid-test', IsMember: 1, ProductServiceTime: '2026-08-27 23:59:59' }; test('支付成功后在一个事务中更新会员、优惠券和订单', async () => { const { connection, state } = createFakeConnection({ order: paidMemberOrder(), user: memberUser }); const postCalls = []; const result = await fulfillOrder('VP_TEST_199', { openid: 'openid-test', env: 0, productId: '166_199', quantity: 1, originalPrice: 19900, actualPrice: 19900, paidTime: 1787846400, raw: { source: 'test' } }, dependencies(connection, postCalls)); await new Promise(resolve => setImmediate(resolve)); assert.equal(result.alreadyFulfilled, false); assert.equal(state.began, 1); assert.equal(state.committed, 1); assert.equal(state.rolledBack, 0); assert.equal(state.released, 1); assert.ok(state.queries.some(item => item.sql.includes('UPDATE MiaoguoWXUsers SET'))); assert.ok(state.queries.some(item => item.sql.includes('UPDATE MiaoguoCoupon SET IsUse=1'))); assert.ok(state.queries.some(item => item.sql.includes('SET PayEndTime=') && item.sql.includes('Status=1'))); assert.equal(postCalls.length, 1); }); test('测试用户的 1 元购买执行与正式购买相同的会员完成逻辑', async () => { const testOrder = paidMemberOrder({ TradeNo: 'VP_TEST_BUY_1', Money: 100, XMLPre: orderXml({ productId: '166_1', goodsPrice: 100, detail: { EndTime: '2027-08-27 23:59:59' } }) }); const { connection, state } = createFakeConnection({ order: testOrder, user: memberUser }); const result = await fulfillOrder('VP_TEST_BUY_1', { openid: 'openid-test', env: 0, productId: '166_1', originalPrice: 100, actualPrice: 100 }, dependencies(connection)); assert.equal(result.memberUpdate.paidMember, true); assert.ok(state.queries.some(item => item.sql.includes('UPDATE MiaoguoCoupon SET IsUse=1'))); assert.ok(state.queries.some(item => item.sql.includes('Status=1'))); }); test('重复发货通知不会重复增加会员有效期', async () => { const { connection, state } = createFakeConnection({ order: paidMemberOrder({ Status: 1 }), user: memberUser }); const result = await fulfillOrder('VP_TEST_199', {}, dependencies(connection)); assert.equal(result.alreadyFulfilled, true); assert.equal(state.committed, 1); assert.equal(state.rolledBack, 0); assert.equal(state.queries.filter(item => item.sql.includes('UPDATE ')).length, 0); }); test('微信实付金额不一致时回滚且不发放权益', async () => { const { connection, state } = createFakeConnection({ order: paidMemberOrder(), user: memberUser }); await assert.rejects( fulfillOrder('VP_TEST_199', { openid: 'openid-test', env: 0, productId: '166_199', orderFee: 100 }, dependencies(connection)), /金额与本地订单不一致/ ); assert.equal(state.committed, 0); assert.equal(state.rolledBack, 1); assert.equal(state.released, 1); assert.equal(state.queries.filter(item => item.sql.includes('UPDATE ')).length, 0); }); test('1 元试用只记录申请和支付订单,不开通付费会员', async () => { const trialOrder = paidMemberOrder({ TradeNo: 'VP_TEST_1', PayType: 9, Money: 100, XMLPre: orderXml({ productId: '166_1', goodsPrice: 100, detail: { PayType: 9 } }) }); const { connection, state } = createFakeConnection({ order: trialOrder, user: memberUser }); const result = await fulfillOrder('VP_TEST_1', { openid: 'openid-test', env: 0, productId: '166_1', originalPrice: 100, actualPrice: 100 }, dependencies(connection)); assert.equal(result.memberUpdate.paidMember, false); assert.equal(result.memberUpdate.IsApply, 1); assert.ok(state.queries.some(item => item.sql.includes('UPDATE MiaoguoWXUsers SET'))); assert.equal(state.queries.some(item => item.sql.includes('UPDATE MiaoguoCoupon')), false); assert.ok(state.queries.some(item => item.sql.includes('Status=1'))); }); test('可在本地模拟微信发货通知并进入统一完成函数', async () => { const calls = []; const ctx = { query: {}, request: { headers: { 'content-type': 'application/json' }, body: { Event: 'xpay_goods_deliver_notify', OpenId: 'openid-test', OutTradeNo: 'VP_NOTIFY_TEST', Env: 0, GoodsInfo: { ProductId: '166_199', Quantity: 1, OrigPrice: 19900, ActualPrice: 19900 }, WeChatPayInfo: { TransactionId: 'wx-test-transaction', PaidTime: 1787846400 } } } }; await MiaoguoVirtualPayNotify500(ctx, { isValidMessageSignature: () => true, fulfillOrder: async (tradeNo, payment) => { calls.push({ tradeNo, payment }); } }); assert.deepEqual(ctx.body, { ErrCode: 0, ErrMsg: 'success' }); assert.equal(calls.length, 1); assert.equal(calls[0].tradeNo, 'VP_NOTIFY_TEST'); assert.equal(calls[0].payment.productId, '166_199'); assert.equal(calls[0].payment.actualPrice, 19900); });