mirror of
https://github.com/Such-Software/wownerogue.git
synced 2026-10-21 16:18:33 +02:00
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).
This commit is contained in:
@@ -55,6 +55,19 @@ describe('PaymentHandlers basic', () => {
|
||||
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
|
||||
});
|
||||
});
|
||||
|
||||
@@ -72,6 +72,51 @@ describe('BTCPayProvider (Greenfield)', () => {
|
||||
const p = new BTCPayProvider({ fetchImpl: async () => ({}) });
|
||||
await expect(p.getInvoiceStatus('x')).rejects.toThrow(/not configured/);
|
||||
});
|
||||
|
||||
describe('getWalletStatus (raw shape the confirmation callback consumes)', () => {
|
||||
const mk = (invStatus, methods, chains) => new BTCPayProvider({
|
||||
baseUrl: 'x', storeId: 's', apiKey: 'k', chains,
|
||||
fetchImpl: recordingFetch([
|
||||
{ method: 'GET', match: /\/invoices\/inv1$/, body: { id: 'inv1', status: invStatus } },
|
||||
{ method: 'GET', match: /\/invoices\/inv1\/payment-methods$/, body: methods }
|
||||
])
|
||||
});
|
||||
|
||||
test('Settled -> confirmed+complete with paid/required in atomic units (XMR, 12 decimals)', async () => {
|
||||
const st = await mk('Settled', [{ cryptoCode: 'XMR', destination: 'a', totalPaid: '1.5', amount: '1.5' }], ['XMR']).getWalletStatus('inv1');
|
||||
expect(st.confirmed).toBe(true);
|
||||
expect(st.complete).toBe(true);
|
||||
expect(st.in_mempool).toBe(false);
|
||||
expect(st.amount).toBe(1500000000000); // 1.5 * 1e12
|
||||
expect(st.required).toBe(1500000000000);
|
||||
});
|
||||
|
||||
test('WOW uses 11 decimals', async () => {
|
||||
const st = await mk('Settled', [{ cryptoCode: 'WOW', totalPaid: '1.5', amount: '1.5' }], ['WOW']).getWalletStatus('inv1');
|
||||
expect(st.amount).toBe(150000000000); // 1.5 * 1e11
|
||||
});
|
||||
|
||||
test('Processing -> in_mempool, not confirmed', async () => {
|
||||
const st = await mk('Processing', [{ cryptoCode: 'XMR', totalPaid: '0.5', amount: '1.5' }], ['XMR']).getWalletStatus('inv1');
|
||||
expect(st.in_mempool).toBe(true);
|
||||
expect(st.confirmed).toBe(false);
|
||||
expect(st.complete).toBe(false);
|
||||
expect(st.amount).toBe(500000000000);
|
||||
});
|
||||
|
||||
test('New -> pending, no amount', async () => {
|
||||
const st = await mk('New', [], ['XMR']).getWalletStatus('inv1');
|
||||
expect(st.confirmed).toBe(false);
|
||||
expect(st.in_mempool).toBe(false);
|
||||
expect(st.amount).toBe(0);
|
||||
});
|
||||
|
||||
test('Expired -> terminal flag so the watcher stops', async () => {
|
||||
const st = await mk('Expired', [], ['XMR']).getWalletStatus('inv1');
|
||||
expect(st._terminal).toBe(true);
|
||||
expect(st.complete).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('NativeMoneroProvider wraps walletRPCService', () => {
|
||||
@@ -97,4 +142,16 @@ describe('NativeMoneroProvider wraps walletRPCService', () => {
|
||||
expect(po.txids).toEqual(['tx1']);
|
||||
expect(wallet.processBatchPayout).toHaveBeenCalledWith([{ address: 'Wo3ydest', amount: '50000000000' }]);
|
||||
});
|
||||
|
||||
test('startWatch passes the RAW wallet status through untouched (native path preserved)', () => {
|
||||
let captured;
|
||||
const w = { startPaymentMonitoring: jest.fn((addr, cb) => { captured = cb; }) };
|
||||
const np = new NativeMoneroProvider({ walletService: w });
|
||||
const seen = [];
|
||||
np.startWatch('Wo3xaddr', (s) => seen.push(s), 2000);
|
||||
expect(w.startPaymentMonitoring).toHaveBeenCalledWith('Wo3xaddr', expect.any(Function), 2000);
|
||||
const raw = { in_mempool: true, confirmed: false, complete: false, amount: 42, required: 100, confirmations: 0 };
|
||||
captured(raw);
|
||||
expect(seen[0]).toBe(raw); // same object, not normalized — callback sees exactly what the wallet emitted
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user