From 3c38333dd1066c296a70d972a01f111e879d114f Mon Sep 17 00:00:00 2001 From: Jennie Robinson Faber Date: Mon, 27 Apr 2026 19:31:59 +0100 Subject: [PATCH] fix(reconcile): pass customerCode (not helcimCustomerId) to Helcim transactions API --- server/api/internal/reconcile-payments.post.js | 11 +++++++++-- tests/server/api/reconcile-payments-route.test.js | 10 +++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/server/api/internal/reconcile-payments.post.js b/server/api/internal/reconcile-payments.post.js index f59e838..869f6f1 100644 --- a/server/api/internal/reconcile-payments.post.js +++ b/server/api/internal/reconcile-payments.post.js @@ -69,10 +69,12 @@ export default defineEventHandler(async (event) => { // Opportunistic backfill: members predating the helcimCustomerCode field // get it filled in here so the daily cron acts as the migration. Only on // the missing path — no overwrite, no extra API call once populated. - if (!member.helcimCustomerCode) { + let customerCode = member.helcimCustomerCode + if (!customerCode) { try { const customer = await getHelcimCustomer(member.helcimCustomerId) if (customer?.customerCode) { + customerCode = customer.customerCode await Member.findByIdAndUpdate( member._id, { $set: { helcimCustomerCode: customer.customerCode } }, @@ -85,9 +87,14 @@ export default defineEventHandler(async (event) => { } } + if (!customerCode) { + console.warn(`[reconcile] no customerCode for member=${member._id}; skipping`) + return { error: false, txExamined: 0, created: 0, existed: 0, skipped: 0 } + } + let txs try { - txs = await listTransactionsWithRetry(member.helcimCustomerId) + txs = await listTransactionsWithRetry(customerCode) } catch (err) { console.error(`[reconcile] member=${member._id}: ${err?.message || err}`) return { error: true } diff --git a/tests/server/api/reconcile-payments-route.test.js b/tests/server/api/reconcile-payments-route.test.js index 6b7c4c1..5fcd9cc 100644 --- a/tests/server/api/reconcile-payments-route.test.js +++ b/tests/server/api/reconcile-payments-route.test.js @@ -167,8 +167,8 @@ describe('POST /api/internal/reconcile-payments', () => { // m1 succeeds first try, m2 fails all 3 retries, m3 succeeds first try. // Keyed by customerCode so it works regardless of call order (chunked Promise.all). listHelcimCustomerTransactions.mockImplementation((customerCode) => { - if (customerCode === 'cust-1') return Promise.resolve([{ id: 'tx1', status: 'paid', amount: 5 }]) - if (customerCode === 'cust-3') return Promise.resolve([{ id: 'tx3', status: 'paid', amount: 7 }]) + if (customerCode === 'CST-1') return Promise.resolve([{ id: 'tx1', status: 'paid', amount: 5 }]) + if (customerCode === 'CST-3') return Promise.resolve([{ id: 'tx3', status: 'paid', amount: 7 }]) return Promise.reject(new Error('helcim 503')) }) upsertPaymentFromHelcim.mockResolvedValue({ created: true, payment: { _id: 'p' } }) @@ -291,6 +291,7 @@ describe('POST /api/internal/reconcile-payments', () => { { $set: { helcimCustomerCode: 'CST-NEW' } }, { runValidators: false } ) + expect(listHelcimCustomerTransactions).toHaveBeenCalledWith('CST-NEW') }) it('skips backfill when helcimCustomerCode is already present', async () => { @@ -308,6 +309,7 @@ describe('POST /api/internal/reconcile-payments', () => { expect(getHelcimCustomer).not.toHaveBeenCalled() expect(Member.findByIdAndUpdate).not.toHaveBeenCalled() + expect(listHelcimCustomerTransactions).toHaveBeenCalledWith('CST-EXISTING') }) it('does not fail the run when getHelcimCustomer throws during backfill', async () => { @@ -325,7 +327,9 @@ describe('POST /api/internal/reconcile-payments', () => { const result = await reconcileHandler(event) expect(Member.findByIdAndUpdate).not.toHaveBeenCalled() - expect(result.memberErrors).toBe(0) // backfill failure is best-effort, not fatal + // Without a customerCode, we skip the transactions API entirely (best-effort, not error). + expect(listHelcimCustomerTransactions).not.toHaveBeenCalled() + expect(result.memberErrors).toBe(0) }) }) })