diff --git a/src/processPaymentCsv.js b/src/processPaymentCsv.js index d62dfcc..6ec6d9c 100644 --- a/src/processPaymentCsv.js +++ b/src/processPaymentCsv.js @@ -140,7 +140,17 @@ async function backfillBoA() { const transactionId = headers["transactionid"] || headers["x-transactionid"] || headers["x-correlation-id"] || headers["x-cashpro-transaction-id"] || null; - const data = JSON.parse(text); + let data = {}; + let parseError = null; + if (text.trim()) { + try { + data = JSON.parse(text); + } catch (err) { + parseError = err; + } + } else { + parseError = new Error("BoA returned an empty response body"); + } await ddb.send(new PutCommand({ TableName: TABLE_NAME, @@ -153,6 +163,7 @@ async function backfillBoA() { transaction_id: transactionId, check_numbers: items.map((i) => i.checkNumber), total_amount: items.reduce((sum, i) => sum + parseFloat(i.amount), 0).toFixed(2), + response_headers: JSON.stringify(headers), response_body: text, success: res.ok, processed_items: data.processedItems || 0, @@ -161,7 +172,11 @@ async function backfillBoA() { }, })); - return { ok: res.ok, status: res.status, data }; + if (res.ok && parseError) { + throw new Error(`Backfill ${label} failed: BoA returned invalid JSON (HTTP ${res.status})`); + } + + return { ok: res.ok, status: res.status, data, text, parseError }; }; for (let i = 0; i < toSubmit.length; i += BOA_BATCH_SIZE) { @@ -179,14 +194,17 @@ async function backfillBoA() { // Parse the error to separate duplicates from genuinely new items const dupeCheckNumbers = new Set(); - for (const item of result.data.issueList || []) { + for (const item of result.data?.issueList || []) { if (ALREADY_SUBMITTED.has(item.status)) { dupeCheckNumbers.add(item.checkNumber); } } if (dupeCheckNumbers.size === 0) { - throw new Error(`Backfill batch ${batchNum} failed with non-duplicate error: ${result.status}`); + const detail = result.parseError + ? "BoA returned a non-JSON response" + : "BoA returned a non-duplicate error"; + throw new Error(`Backfill batch ${batchNum} failed: ${detail} (HTTP ${result.status})`); } totalSkippedDuplicates += dupeCheckNumbers.size; @@ -198,7 +216,10 @@ async function backfillBoA() { const retryResult = await submitBatch(retryItems, `batch ${batchNum} retry`); if (!retryResult.ok) { console.error(`Backfill batch ${batchNum} retry failed:`, JSON.stringify(retryResult.data)); - throw new Error(`Backfill batch ${batchNum} retry failed: ${retryResult.status}`); + const detail = retryResult.parseError + ? "BoA returned a non-JSON response" + : `BoA returned ${JSON.stringify(retryResult.data)}`; + throw new Error(`Backfill batch ${batchNum} retry failed: ${detail} (HTTP ${retryResult.status})`); } totalProcessed += retryResult.data.processedItems; console.log(`Backfill batch ${batchNum} retry: ${retryResult.data.processedItems} processed`);