| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237 |
- 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 `<xml>`
- + `<detail><![CDATA[${JSON.stringify(detail)}]]></detail>`
- + `<virtual_pay><![CDATA[${JSON.stringify({ productId, env, goodsPrice })}]]></virtual_pay>`
- + `</xml>`;
- }
- 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('会员有效期兼容小程序提交的斜杠日期且不触发 Moment 回退解析', async () => {
- const slashDateOrder = paidMemberOrder({
- TradeNo: 'VP_TEST_SLASH_DATE',
- Money: 100,
- XMLPre: orderXml({
- productId: '166_1',
- goodsPrice: 100,
- detail: { EndTime: '2032/05/14' }
- })
- });
- const { connection } = createFakeConnection({ order: slashDateOrder, user: memberUser });
- const result = await fulfillOrder('VP_TEST_SLASH_DATE', {
- openid: 'openid-test',
- env: 0,
- productId: '166_1',
- originalPrice: 100,
- actualPrice: 100
- }, dependencies(connection));
- assert.equal(result.memberUpdate.ProductServiceTime, '2032-05-14 00:00:00');
- });
- 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);
- });
|