import assert from 'node:assert/strict'; import { RechargeError, RechargeService } from '../dist/wallets/recharge-service.js'; const future = new Date(Date.now() + 86_400_000); const past = new Date(Date.now() - 86_400_000); function createHarness(overrides = {}) { const state = { plan: { id: '31', tenantId: '7', storeId: '11', name: '充 100 送 20', payAmountCents: 10000, giftAmountCents: 2000, scopeType: 'STORE', startsAt: past, endsAt: future, purchaseLimitPerUser: 2, status: 'ACTIVE', ...overrides.plan }, orders: [], nextOrderId: 900, purchaseCount: overrides.purchaseCount ?? 0, walletCredits: [] }; const connection = { async beginTransaction() {}, async commit() {}, async rollback() {}, release() {}, async execute(sql, params) { if (sql.includes('FROM qipai_recharge_orders') && sql.includes('client_request_id')) { const duplicate = state.orders.find((order) => order.tenantId === String(params[0]) && order.userId === String(params[1]) && order.clientRequestId === params[2] ); return [duplicate ? [toOrderRow(duplicate)] : [], []]; } if (sql.includes('FROM qipai_recharge_plans')) { if ( state.plan && state.plan.tenantId === String(params[0]) && state.plan.id === String(params[1]) ) { return [[state.plan], []]; } return [[], []]; } if (sql.includes('COUNT(*) AS total')) { return [[{ total: state.purchaseCount }], []]; } if (sql.includes('INSERT INTO qipai_recharge_orders')) { const order = { id: String(state.nextOrderId++), tenantId: String(params[0]), userId: String(params[1]), storeId: params[2] === null ? null : String(params[2]), planId: String(params[3]), rechargeNo: params[4], clientRequestId: params[5], payAmountCents: Number(params[6]), giftAmountCents: Number(params[7]), traceId: params[8], status: 'PENDING_PAYMENT', paymentId: null }; state.orders.push(order); return [{ insertId: order.id, affectedRows: 1 }, []]; } if (sql.includes('FROM qipai_recharge_orders') && sql.includes('FOR UPDATE')) { const order = state.orders.find((item) => item.tenantId === String(params[0]) && item.id === String(params[1]) ); return [order ? [toOrderRow(order)] : [], []]; } if (sql.includes("SET status = 'PAID'")) { const order = state.orders.find((item) => item.tenantId === String(params[1]) && item.id === String(params[2]) ); order.status = 'PAID'; order.paymentId = String(params[0]); return [{ affectedRows: 1 }, []]; } if (sql.includes("SET status = 'CREDITED'")) { const order = state.orders.find((item) => item.tenantId === String(params[0]) && item.id === String(params[1]) ); order.status = 'CREDITED'; return [{ affectedRows: 1 }, []]; } throw new Error(`Unexpected SQL: ${sql}`); } }; const pool = { async getConnection() { return connection; } }; const wallet = { async credit(input) { state.walletCredits.push(input); return { ledgerEntryId: String(state.walletCredits.length), cashBalanceCents: input.cashDeltaCents, giftBalanceCents: input.giftDeltaCents, idempotent: false }; } }; return { service: new RechargeService(pool, wallet), state }; } function toOrderRow(order) { return { id: order.id, userId: order.userId, storeId: order.storeId, planId: order.planId, rechargeNo: order.rechargeNo, payAmountCents: order.payAmountCents, giftAmountCents: order.giftAmountCents, status: order.status }; } const baseOrder = { tenantId: '7', userId: '21', planId: '31', storeId: '11', clientRequestId: 'client-001', traceId: 'recharge-test' }; { const { service, state } = createHarness(); const created = await service.createRechargeOrder(baseOrder); assert.equal(created.status, 'PENDING_PAYMENT'); assert.equal(created.payAmountCents, 10000); assert.equal(created.giftAmountCents, 2000); assert.equal(created.idempotent, false); assert.equal(state.orders.length, 1); const duplicate = await service.createRechargeOrder(baseOrder); assert.equal(duplicate.rechargeOrderId, created.rechargeOrderId); assert.equal(duplicate.idempotent, true); assert.equal(state.orders.length, 1); } { const { service } = createHarness({ plan: { status: 'DISABLED' } }); await assert.rejects( () => service.createRechargeOrder(baseOrder), (error) => error instanceof RechargeError && error.code === 'RECHARGE_PLAN_DISABLED' ); } { const { service } = createHarness({ plan: { endsAt: past } }); await assert.rejects( () => service.createRechargeOrder(baseOrder), (error) => error instanceof RechargeError && error.code === 'RECHARGE_PLAN_EXPIRED' ); } { const { service } = createHarness({ purchaseCount: 2 }); await assert.rejects( () => service.createRechargeOrder(baseOrder), (error) => error instanceof RechargeError && error.code === 'RECHARGE_LIMIT_REACHED' ); } { const { service, state } = createHarness(); const created = await service.createRechargeOrder(baseOrder); const credited = await service.markPaidAndCredit({ tenantId: '7', rechargeOrderId: created.rechargeOrderId, paymentId: '501', traceId: 'payment-callback' }); assert.equal(credited.status, 'CREDITED'); assert.equal(credited.idempotent, false); assert.equal(state.orders[0].status, 'CREDITED'); assert.equal(state.walletCredits.length, 1); assert.deepEqual( { businessType: state.walletCredits[0].businessType, businessId: state.walletCredits[0].businessId, cashDeltaCents: state.walletCredits[0].cashDeltaCents, giftDeltaCents: state.walletCredits[0].giftDeltaCents, storeId: state.walletCredits[0].storeId }, { businessType: 'RECHARGE', businessId: created.rechargeOrderId, cashDeltaCents: 10000, giftDeltaCents: 2000, storeId: '11' } ); const duplicateCallback = await service.markPaidAndCredit({ tenantId: '7', rechargeOrderId: created.rechargeOrderId, paymentId: '501', traceId: 'payment-callback' }); assert.equal(duplicateCallback.idempotent, true); assert.equal(state.walletCredits.length, 1); } console.log('PASS: M07-B recharge plans validate limits and credit paid callbacks idempotently.');