mirror of
https://github.com/Sea-Haven-Industries/payments-dashboard.git
synced 2026-10-04 09:02:08 +00:00
Add BoA CashPro sandbox test script and update seed script
- Add standalone test script to validate sandbox API connectivity for check management and account info endpoints - Update seed-bank-status to persist amount, issueDate, and method fields - Add data/ to gitignore
This commit is contained in:
parent
fc37c6e8f9
commit
a9ad5f7dd1
3 changed files with 164 additions and 6 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,3 +1,4 @@
|
||||||
node_modules/
|
node_modules/
|
||||||
.aws-sam/
|
.aws-sam/
|
||||||
samconfig.toml
|
samconfig.toml
|
||||||
|
data/
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ function toISODate(dateStr) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateStatus(checkNumber, status, paidDate) {
|
async function updateStatus(checkNumber, status, { paidDate, amount, issueDate, method } = {}) {
|
||||||
const pk = `payment#${checkNumber}`;
|
const pk = `payment#${checkNumber}`;
|
||||||
const expr = ["#status = :s"];
|
const expr = ["#status = :s"];
|
||||||
const names = { "#status": "status" };
|
const names = { "#status": "status" };
|
||||||
|
|
@ -62,6 +62,23 @@ async function updateStatus(checkNumber, status, paidDate) {
|
||||||
expr.push("paid_date = :pd");
|
expr.push("paid_date = :pd");
|
||||||
values[":pd"] = paidDate;
|
values[":pd"] = paidDate;
|
||||||
}
|
}
|
||||||
|
if (amount != null) {
|
||||||
|
expr.push("amount_usd = :amt");
|
||||||
|
values[":amt"] = amount;
|
||||||
|
}
|
||||||
|
if (issueDate) {
|
||||||
|
expr.push("send_payment_on = :sd");
|
||||||
|
values[":sd"] = issueDate;
|
||||||
|
}
|
||||||
|
if (checkNumber) {
|
||||||
|
expr.push("check_number = :cn");
|
||||||
|
values[":cn"] = checkNumber;
|
||||||
|
}
|
||||||
|
if (method) {
|
||||||
|
expr.push("#method = :m");
|
||||||
|
names["#method"] = "method";
|
||||||
|
values[":m"] = method;
|
||||||
|
}
|
||||||
|
|
||||||
await ddb.send(
|
await ddb.send(
|
||||||
new UpdateCommand({
|
new UpdateCommand({
|
||||||
|
|
@ -87,7 +104,9 @@ async function main() {
|
||||||
for (const row of rows) {
|
for (const row of rows) {
|
||||||
const checkNumber = row[0]?.trim();
|
const checkNumber = row[0]?.trim();
|
||||||
if (!checkNumber) continue;
|
if (!checkNumber) continue;
|
||||||
statusMap.set(checkNumber, { status: "Issued", paidDate: null });
|
const amount = parseFloat(String(row[4] || "0").replace(/,/g, ""));
|
||||||
|
const issueDate = row[1]?.trim() || null;
|
||||||
|
statusMap.set(checkNumber, { status: "Issued", paidDate: null, amount: isNaN(amount) ? 0 : amount, issueDate, method: "Check" });
|
||||||
}
|
}
|
||||||
console.log(` ${rows.length} positive pay entries`);
|
console.log(` ${rows.length} positive pay entries`);
|
||||||
}
|
}
|
||||||
|
|
@ -99,7 +118,18 @@ async function main() {
|
||||||
for (const row of rows) {
|
for (const row of rows) {
|
||||||
const checkNumber = (row["Check Number"] || "").trim();
|
const checkNumber = (row["Check Number"] || "").trim();
|
||||||
if (!checkNumber) continue;
|
if (!checkNumber) continue;
|
||||||
statusMap.set(checkNumber, { status: "Outstanding", paidDate: null });
|
const amount = parseFloat(String(row["Amount"] || "0").replace(/,/g, ""));
|
||||||
|
const rawDate = (row["Issue Date"] || "").trim();
|
||||||
|
// Normalize 2-digit year to 4-digit: "12/10/24" → "12/10/2024"
|
||||||
|
let issueDate = rawDate || null;
|
||||||
|
if (rawDate) {
|
||||||
|
const parts = rawDate.split("/");
|
||||||
|
if (parts.length === 3 && parts[2].length === 2) {
|
||||||
|
parts[2] = `20${parts[2]}`;
|
||||||
|
issueDate = parts.join("/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
statusMap.set(checkNumber, { status: "Outstanding", paidDate: null, amount: isNaN(amount) ? 0 : amount, issueDate, method: "Check" });
|
||||||
}
|
}
|
||||||
console.log(` ${rows.length} outstanding entries`);
|
console.log(` ${rows.length} outstanding entries`);
|
||||||
}
|
}
|
||||||
|
|
@ -120,15 +150,16 @@ async function main() {
|
||||||
}
|
}
|
||||||
seen.add(checkNumber);
|
seen.add(checkNumber);
|
||||||
const paidDate = toISODate(row["Paid Date"] || row["CD Volume Number"] || "");
|
const paidDate = toISODate(row["Paid Date"] || row["CD Volume Number"] || "");
|
||||||
statusMap.set(checkNumber, { status: "Cleared", paidDate });
|
const amount = parseFloat(String(row["Amount"] || "0").replace(/,/g, ""));
|
||||||
|
statusMap.set(checkNumber, { status: "Cleared", paidDate, amount: isNaN(amount) ? 0 : amount });
|
||||||
}
|
}
|
||||||
console.log(` ${seen.size} unique cleared checks (${deduped} duplicates skipped)`);
|
console.log(` ${seen.size} unique cleared checks (${deduped} duplicates skipped)`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write to DynamoDB
|
// Write to DynamoDB
|
||||||
let count = 0;
|
let count = 0;
|
||||||
for (const [checkNumber, { status, paidDate }] of statusMap) {
|
for (const [checkNumber, { status, paidDate, amount, issueDate, method }] of statusMap) {
|
||||||
await updateStatus(checkNumber, status, paidDate);
|
await updateStatus(checkNumber, status, { paidDate, amount, issueDate, method });
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
126
scripts/test-boa-sandbox.js
Normal file
126
scripts/test-boa-sandbox.js
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
/**
|
||||||
|
* One-off script to test BoA CashPro sandbox API connectivity.
|
||||||
|
* Reads credentials from SSM Parameter Store (same params as production),
|
||||||
|
* makes test API calls, and prints the full responses so you can capture
|
||||||
|
* the client ID, timestamp, and transactionIdentification for BoA onboarding.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* node scripts/test-boa-sandbox.js
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { SSMClient, GetParameterCommand } from "@aws-sdk/client-ssm";
|
||||||
|
|
||||||
|
const ssm = new SSMClient();
|
||||||
|
const BOA_BASE_URL = "https://developer.bankofamerica.com";
|
||||||
|
|
||||||
|
async function getSSMParam(name) {
|
||||||
|
const { Parameter } = await ssm.send(
|
||||||
|
new GetParameterCommand({ Name: name, WithDecryption: true })
|
||||||
|
);
|
||||||
|
return Parameter.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
console.log("Loading credentials from SSM...\n");
|
||||||
|
|
||||||
|
const [checkMgmtToken, accountInfoToken, accountNumber, companyId] = await Promise.all([
|
||||||
|
getSSMParam("/payments-dashboard/boa-check-mgmt-token"),
|
||||||
|
getSSMParam("/payments-dashboard/boa-account-info-token"),
|
||||||
|
getSSMParam("/payments-dashboard/boa-account-number"),
|
||||||
|
getSSMParam("/payments-dashboard/boa-company-id"),
|
||||||
|
]);
|
||||||
|
|
||||||
|
console.log("Credentials loaded. Testing sandbox endpoints...\n");
|
||||||
|
|
||||||
|
// --- Test 1: Check Issue (add_issue with a test check) ---
|
||||||
|
console.log("=".repeat(60));
|
||||||
|
console.log("TEST 1: Check Issue (add_issue)");
|
||||||
|
console.log("=".repeat(60));
|
||||||
|
|
||||||
|
const issuePayload = {
|
||||||
|
issueList: [
|
||||||
|
{
|
||||||
|
accountNumber,
|
||||||
|
checkNumber: "999999",
|
||||||
|
amount: "1.00",
|
||||||
|
issueAction: "add_issue",
|
||||||
|
issueDate: new Date().toISOString().split("T")[0],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log("Request:", JSON.stringify(issuePayload, null, 2), "\n");
|
||||||
|
|
||||||
|
try {
|
||||||
|
const issueRes = await fetch(
|
||||||
|
`${BOA_BASE_URL}/cashpro/checkmanagement/v1/check-issues`,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Authorization: `Bearer ${checkMgmtToken}`,
|
||||||
|
companyId,
|
||||||
|
},
|
||||||
|
body: JSON.stringify(issuePayload),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const issueHeaders = Object.fromEntries(issueRes.headers.entries());
|
||||||
|
const issueText = await issueRes.text();
|
||||||
|
|
||||||
|
console.log("Status:", issueRes.status);
|
||||||
|
console.log("Response Headers:", JSON.stringify(issueHeaders, null, 2));
|
||||||
|
console.log("Response Body:", issueText);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Check Issue request failed:", err.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Test 2: Previous Day Transaction Inquiry ---
|
||||||
|
console.log("\n" + "=".repeat(60));
|
||||||
|
console.log("TEST 2: Previous Day Transaction Inquiry");
|
||||||
|
console.log("=".repeat(60));
|
||||||
|
|
||||||
|
const yesterday = new Date();
|
||||||
|
yesterday.setDate(yesterday.getDate() - 1);
|
||||||
|
const dateStr = yesterday.toISOString().split("T")[0];
|
||||||
|
|
||||||
|
const inquiryPayload = {
|
||||||
|
accounts: [{ accountNumber, bankId: "BOFAFRPP" }],
|
||||||
|
fromDate: dateStr,
|
||||||
|
toDate: dateStr,
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log("Request:", JSON.stringify(inquiryPayload, null, 2), "\n");
|
||||||
|
|
||||||
|
try {
|
||||||
|
const inquiryRes = await fetch(
|
||||||
|
`${BOA_BASE_URL}/cashpro/reporting/v1/transaction-inquiries/previous-day`,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Authorization: `Bearer ${accountInfoToken}`,
|
||||||
|
},
|
||||||
|
body: JSON.stringify(inquiryPayload),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const inquiryHeaders = Object.fromEntries(inquiryRes.headers.entries());
|
||||||
|
const inquiryText = await inquiryRes.text();
|
||||||
|
|
||||||
|
console.log("Status:", inquiryRes.status);
|
||||||
|
console.log("Response Headers:", JSON.stringify(inquiryHeaders, null, 2));
|
||||||
|
console.log("Response Body:", inquiryText);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Transaction Inquiry request failed:", err.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("\n" + "=".repeat(60));
|
||||||
|
console.log("Done. Look for: client ID, timestamp, transactionIdentification");
|
||||||
|
console.log("=".repeat(60));
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch((err) => {
|
||||||
|
console.error("Fatal error:", err);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
Loading…
Add table
Reference in a new issue