Files
wownerogue/test/paymentHandlers.test.js
T
jwinterm f7c9fb7366 feat(payments): wire gateway invoice monitoring through the provider registry
Completes gateway support (code side). network/paymentHandlers._monitorAddress
now selects the provider for the chain and calls provider.startWatch; the whole
confirmation callback is extracted to _handlePaymentStatus so native and gateway
payments share the exact same logic. stopMonitoringForSocket stops via the
provider too.

- nativeMoneroProvider.startWatch passes the RAW wallet status through untouched,
  so native monitoring is byte-for-byte the legacy path (getInvoiceStatus still
  returns the normalized contract shape for other callers).
- btcpayProvider.getWalletStatus maps a Greenfield invoice to that same raw shape:
  Settled -> confirmed+complete, Processing -> in_mempool, with paid/required
  amounts read from the invoice payment-methods (totalPaid/amount) and converted
  to atomic units via ChainProfile decimals. startWatch polls it and stops on
  complete/expired/invalid.
- paymentHandlers falls back to walletService.startPaymentMonitoring when no
  registry is present, so existing behavior/tests are preserved.

Behavior-preserving: no gateway env => native provider selected => identical
path. Tests: +7 (getWalletStatus status/decimal mapping x5, native raw
passthrough, gateway monitoring routing). Suite 53 suites / 372 tests green.
Not yet live-verified against the real box (needs a real store API key).
2026-07-10 18:06:24 -04:00

74 lines
2.6 KiB
JavaScript

const PaymentHandlers = require('../src/network/paymentHandlers');
function makeMocks() {
const user = { id: 'user-1', clientId: 'client1', payout_address: 'wow1address' };
return {
io: { to: () => ({ emit: () => {} }) },
gameModeManager: {
gameMode: 'PAID_SINGLE',
cryptoType: 'WOW',
currencyDecimals: 11,
singleGamePrice: 1000,
creditsPayoutEnabled: false,
formatAtomicHuman: jest.fn(() => '0.001'),
getOrCreateUser: jest.fn(async () => user),
createPaymentRequest: async () => ({
id: 'pid123',
address: 'addr',
amount: 1000,
amountFormatted: '0.001',
expiresAt: new Date()
})
},
walletService: {
startPaymentMonitoring: jest.fn(),
stopPaymentMonitoring: jest.fn()
},
debugManager: { getCurrentBlockHeight: () => 123, CONSOLE_LOGGING: false },
queueManager: {
getUserBySocket: () => user,
getPlayerIndex: () => -1,
getQueueLength: () => 0,
addPlayer: jest.fn(),
markConfirmed: jest.fn(),
startGameImmediately: jest.fn(() => false)
},
broadcastManager: { sendStatusUpdate: jest.fn() }
};
}
describe('PaymentHandlers basic', () => {
let ph;
afterEach(() => {
if (ph && typeof ph.dispose === 'function') {
ph.dispose();
}
ph = null;
});
test('createAndShowPaymentRequest emits payment_created', async () => {
const mocks = makeMocks();
const emitted = {};
mocks.io.to = () => ({ emit: (event, data) => { emitted[event] = data; } });
ph = new PaymentHandlers(mocks);
await ph.createAndShowPaymentRequest({ id: 'sock1' });
expect(emitted.payment_created).toBeDefined();
expect(emitted.payment_created.paymentId).toBe('pid123');
// No paymentProviders registry on the mock -> legacy fallback to direct wallet monitoring.
expect(mocks.walletService.startPaymentMonitoring).toHaveBeenCalled();
});
test('monitoring routes through a routed gateway provider instead of the wallet', async () => {
const mocks = makeMocks();
const gateway = { id: 'wowcheckout', startWatch: jest.fn(), stopWatch: jest.fn() };
mocks.gameModeManager.paymentProviders = { getProvider: jest.fn(() => gateway) };
mocks.io.to = () => ({ emit: () => {} });
ph = new PaymentHandlers(mocks);
await ph.createAndShowPaymentRequest({ id: 'sock2' });
expect(mocks.gameModeManager.paymentProviders.getProvider).toHaveBeenCalledWith('WOW');
expect(gateway.startWatch).toHaveBeenCalled();
expect(mocks.walletService.startPaymentMonitoring).not.toHaveBeenCalled(); // routed away from native
});
});