Add OAuth token exchange to BoA sandbox test script

Use correct sandbox base URL (api-sb.bofa.com), proper OAuth
client-credentials flow with applicationID, and routing number
for transaction inquiries. Both APIs now return 200 in sandbox.
This commit is contained in:
Adam Moussa 2026-04-13 16:08:57 -04:00
parent a9ad5f7dd1
commit 957fb726ab

View file

@ -1,8 +1,11 @@
/**
* 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.
* Reads credentials from SSM Parameter Store, exchanges them for
* OAuth Bearer tokens, then makes test API calls to both Check
* Management and Reporting APIs.
*
* Prints full responses so you can capture the client ID, timestamp,
* and transactionIdentification for BoA production onboarding.
*
* Usage:
* node scripts/test-boa-sandbox.js
@ -11,7 +14,8 @@
import { SSMClient, GetParameterCommand } from "@aws-sdk/client-ssm";
const ssm = new SSMClient();
const BOA_BASE_URL = "https://developer.bankofamerica.com";
const SANDBOX_BASE = "https://api-sb.bofa.com";
const AUTH_URL = `${SANDBOX_BASE}/authn/v1/client-authentication`;
async function getSSMParam(name) {
const { Parameter } = await ssm.send(
@ -20,31 +24,88 @@ async function getSSMParam(name) {
return Parameter.Value;
}
async function getAccessToken(applicationID, clientId, clientSecret) {
console.log(` Requesting token for ${applicationID}...`);
const res = await fetch(AUTH_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
applicationID,
authn: {
client_id: clientId,
client_secret: clientSecret,
},
}),
});
const text = await res.text();
console.log(` Auth response (${res.status}):`, text, "\n");
if (!res.ok) {
throw new Error(`Auth failed for ${applicationID}: ${res.status} - ${text}`);
}
const data = JSON.parse(text);
return data.access_token;
}
async function main() {
console.log("Loading credentials from SSM...\n");
const [checkMgmtToken, accountInfoToken, accountNumber, companyId] = await Promise.all([
const [
checkMgmtClientId,
checkMgmtSecret,
accountInfoClientId,
accountInfoSecret,
accountNumber,
companyId,
] = await Promise.all([
getSSMParam("/payments-dashboard/boa-check-mgmt-client-id"),
getSSMParam("/payments-dashboard/boa-check-mgmt-token"),
getSSMParam("/payments-dashboard/boa-account-info-client-id"),
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");
console.log("Credentials loaded.\n");
// --- Test 1: Check Issue (add_issue with a test check) ---
// --- Step 1: Get OAuth tokens for both APIs ---
console.log("=".repeat(60));
console.log("TEST 1: Check Issue (add_issue)");
console.log("STEP 1: OAuth Token Exchange");
console.log("=".repeat(60));
console.log("\n[Check Management]");
const checkMgmtBearerToken = await getAccessToken(
"app_SeaHavenIndustries_Checkmanagement_SB",
checkMgmtClientId,
checkMgmtSecret
);
console.log("[Account Info / Reporting]");
const accountInfoBearerToken = await getAccessToken(
"app_SeaHavenIndustries_Reporting_SB",
accountInfoClientId,
accountInfoSecret
);
console.log("Both tokens acquired.\n");
// --- Step 2: 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,
issueAction: "add_Issue",
checkNumber: "999999",
amount: "1.00",
issueAction: "add_issue",
issueDate: new Date().toISOString().split("T")[0],
payee: "Sandbox Test",
},
],
};
@ -53,12 +114,12 @@ async function main() {
try {
const issueRes = await fetch(
`${BOA_BASE_URL}/cashpro/checkmanagement/v1/check-issues`,
`${SANDBOX_BASE}/cashpro/checkmanagement/v1/check-issues`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${checkMgmtToken}`,
Authorization: `Bearer ${checkMgmtBearerToken}`,
companyId,
},
body: JSON.stringify(issuePayload),
@ -75,7 +136,7 @@ async function main() {
console.error("Check Issue request failed:", err.message);
}
// --- Test 2: Previous Day Transaction Inquiry ---
// --- Step 3: Previous Day Transaction Inquiry ---
console.log("\n" + "=".repeat(60));
console.log("TEST 2: Previous Day Transaction Inquiry");
console.log("=".repeat(60));
@ -85,21 +146,26 @@ async function main() {
const dateStr = yesterday.toISOString().split("T")[0];
const inquiryPayload = {
accounts: [{ accountNumber, bankId: "BOFAFRPP" }],
fromDate: dateStr,
toDate: dateStr,
accounts: [
{
accountNumber,
bankId: "021000322",
},
],
};
console.log("Request:", JSON.stringify(inquiryPayload, null, 2), "\n");
try {
const inquiryRes = await fetch(
`${BOA_BASE_URL}/cashpro/reporting/v1/transaction-inquiries/previous-day`,
`${SANDBOX_BASE}/cashpro/reporting/v1/transaction-inquiries/previous-day`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${accountInfoToken}`,
Authorization: `Bearer ${accountInfoBearerToken}`,
},
body: JSON.stringify(inquiryPayload),
}