Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
361 changes: 361 additions & 0 deletions cypress-tests/cypress/e2e/ach-bank-transfer-e2e-test.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,361 @@
/**
* ACH Bank Transfer E2E Tests
*
* Tests the ACH bank debit/transfer payment flow using Stripe connector.
* Stripe's ACH requires US billing address. This test covers:
* - ACH option visibility
* - Form fields (routing number, account number, account type)
* - Required field validation errors
* - Mandate text display
* - Successful submission and redirect
*
* Connector: Stripe (pro_5fVcCxU8MFTYozgtf0P8)
* Currency: USD
* Country: US
*
* NOTE: ACH only appears if the Stripe profile has ACH Bank Debit enabled.
*/
import * as testIds from "../../../src/Utilities/TestUtils.bs";
import { getClientURL } from "../support/utils";
import { createPaymentBody } from "../support/utils";
import {
changeObjectKeyValue,
connectorProfileIdMapping,
connectorEnum,
} from "../support/utils";
import { achBankTransferDetails } from "cypress/support/cards";

describe("ACH Bank Transfer — Form Render & Validation", () => {
const publishableKey = Cypress.env("HYPERSWITCH_PUBLISHABLE_KEY");
const secretKey = Cypress.env("HYPERSWITCH_SECRET_KEY");
let getIframeBody: () => Cypress.Chainable<JQuery<HTMLBodyElement>>;
let iframeSelector =
"#orca-payment-element-iframeRef-orca-elements-payment-element-payment-element";

changeObjectKeyValue(
createPaymentBody,
"profile_id",
connectorProfileIdMapping.get(connectorEnum.STRIPE),
);
changeObjectKeyValue(createPaymentBody, "currency", "USD");
changeObjectKeyValue(createPaymentBody, "customer_id", "new_user");

createPaymentBody.billing.address.country = "US";
createPaymentBody.billing.address.state = "New York";
createPaymentBody.billing.address.city = "New York";
createPaymentBody.billing.address.zip = "10001";
createPaymentBody.billing.address.first_name = "John";
createPaymentBody.billing.address.last_name = "Doe";
createPaymentBody.billing.email = "john.doe@example.com";

createPaymentBody.shipping.address.country = "US";
createPaymentBody.shipping.address.state = "California";

beforeEach(() => {
getIframeBody = () => cy.iframe(iframeSelector);
cy.createPaymentIntent(secretKey, createPaymentBody).then(() => {
cy.getGlobalState("clientSecret").then((clientSecret) => {
cy.visit(getClientURL(clientSecret, publishableKey));
});
});
});

it("title rendered correctly", () => {
cy.contains("Hyperswitch Unified Checkout").should("be.visible");
});

it("orca-payment-element iframe loaded", () => {
cy.get(iframeSelector)
.should("be.visible")
.its("0.contentDocument")
.its("body");
});

it("should show ACH Bank Debit as a payment method option", () => {
cy.wait(2000);
getIframeBody().then(($body) => {
const hasACH =
$body.find(
"[data-testid='ach_debit'], [data-testid='ACH Bank Debit'], [data-testid='ach_transfer']",
).length > 0 ||
$body.text().includes("ACH") ||
$body.text().includes("Bank Debit");

cy.log(
hasACH
? "ACH payment method found"
: "ACH not found — check Stripe profile has ACH Bank Debit enabled",
);
});
});

it("should show account number and routing number fields after selecting ACH", () => {
cy.wait(2000);

// Click addNewCard to show full payment method list if in saved-card mode
getIframeBody().then(($body) => {
if ($body.find(`[data-testid=${testIds.addNewCardIcon}]`).length > 0) {
cy.wrap($body).find(`[data-testid=${testIds.addNewCardIcon}]`).click();
cy.wait(500);
}

getIframeBody().then(($refreshed) => {
const $ach = $refreshed.find(
"[data-testid='ach_debit'], [data-testid='ACH Bank Debit']",
);

if ($ach.length > 0) {
cy.wrap($ach).first().click();
cy.wait(1000);

// ACH form should show routing and account number fields
getIframeBody()
.find(
"[data-testid='routingNumber'], [data-testid='bankRoutingNumber'], input[placeholder*='Routing']",
{ timeout: 4000 },
)
.should("be.visible");

getIframeBody()
.find(
"[data-testid='accountNumber'], [data-testid='bankAccountNumber'], input[placeholder*='Account']",
{ timeout: 4000 },
)
.should("be.visible");
} else {
cy.log("ACH not available — skipping form field visibility test");
}
});
});
});

it("should show validation error when submitting ACH form with empty fields", () => {
cy.wait(2000);

getIframeBody().then(($body) => {
if ($body.find(`[data-testid=${testIds.addNewCardIcon}]`).length > 0) {
cy.wrap($body).find(`[data-testid=${testIds.addNewCardIcon}]`).click();
cy.wait(500);
}

getIframeBody().then(($refreshed) => {
const $ach = $refreshed.find(
"[data-testid='ach_debit'], [data-testid='ACH Bank Debit']",
);

if ($ach.length > 0) {
cy.wrap($ach).first().click();
cy.wait(500);

// Submit without filling fields
getIframeBody().get("#submit").click();
cy.wait(2000);

// Error should appear
getIframeBody()
.find(".Error, .Error.pt-1, [class*='error']")
.should("be.visible");
} else {
cy.log("ACH not available — skipping validation test");
}
});
});
});

it("should show validation error for an invalid routing number", () => {
cy.wait(2000);

getIframeBody().then(($body) => {
if ($body.find(`[data-testid=${testIds.addNewCardIcon}]`).length > 0) {
cy.wrap($body).find(`[data-testid=${testIds.addNewCardIcon}]`).click();
cy.wait(500);
}

getIframeBody().then(($refreshed) => {
const $ach = $refreshed.find(
"[data-testid='ach_debit'], [data-testid='ACH Bank Debit']",
);

if ($ach.length > 0) {
cy.wrap($ach).first().click();
cy.wait(500);

// Enter invalid routing number
getIframeBody()
.find(
"[data-testid='routingNumber'], input[placeholder*='Routing']",
)
.first()
.type(achBankTransferDetails.invalid.routingNumber);

getIframeBody()
.find(
"[data-testid='accountNumber'], input[placeholder*='Account']",
)
.first()
.type(achBankTransferDetails.success.accountNumber);

getIframeBody().get("#submit").click();
cy.wait(2000);

getIframeBody()
.find(".Error, .Error.pt-1, [class*='error']")
.should("be.visible");
} else {
cy.log("ACH not available — skipping routing number validation test");
}
});
});
});
});

describe("ACH Bank Transfer — Mandate & Submission", () => {
const publishableKey = Cypress.env("HYPERSWITCH_PUBLISHABLE_KEY");
const secretKey = Cypress.env("HYPERSWITCH_SECRET_KEY");
let getIframeBody: () => Cypress.Chainable<JQuery<HTMLBodyElement>>;
let iframeSelector =
"#orca-payment-element-iframeRef-orca-elements-payment-element-payment-element";

changeObjectKeyValue(
createPaymentBody,
"profile_id",
connectorProfileIdMapping.get(connectorEnum.STRIPE),
);
changeObjectKeyValue(createPaymentBody, "currency", "USD");
changeObjectKeyValue(createPaymentBody, "customer_id", "new_user");

createPaymentBody.billing.address.country = "US";
createPaymentBody.billing.address.state = "New York";
createPaymentBody.billing.address.city = "New York";
createPaymentBody.billing.address.zip = "10001";
createPaymentBody.billing.address.first_name = "John";
createPaymentBody.billing.address.last_name = "Doe";
createPaymentBody.billing.email = "john.doe@example.com";

createPaymentBody.shipping.address.country = "US";
createPaymentBody.shipping.address.state = "California";

beforeEach(() => {
getIframeBody = () => cy.iframe(iframeSelector);
cy.createPaymentIntent(secretKey, createPaymentBody).then(() => {
cy.getGlobalState("clientSecret").then((clientSecret) => {
cy.visit(getClientURL(clientSecret, publishableKey));
});
});
});

it("should display mandate/authorization text before ACH submission", () => {
cy.wait(2000);

getIframeBody().then(($body) => {
if ($body.find(`[data-testid=${testIds.addNewCardIcon}]`).length > 0) {
cy.wrap($body).find(`[data-testid=${testIds.addNewCardIcon}]`).click();
cy.wait(500);
}

getIframeBody().then(($refreshed) => {
const $ach = $refreshed.find(
"[data-testid='ach_debit'], [data-testid='ACH Bank Debit']",
);

if ($ach.length > 0) {
cy.wrap($ach).first().click();
cy.wait(500);

getIframeBody()
.find(
"[data-testid='routingNumber'], input[placeholder*='Routing']",
)
.first()
.type(achBankTransferDetails.success.routingNumber);

getIframeBody()
.find(
"[data-testid='accountNumber'], input[placeholder*='Account']",
)
.first()
.type(achBankTransferDetails.success.accountNumber);

cy.wait(500);

// Mandate text should appear somewhere in the form
getIframeBody().then(($form) => {
const hasMandateText =
$form.text().toLowerCase().includes("authorize") ||
$form.text().toLowerCase().includes("debit") ||
$form.find("[class*='mandate'], [class*='terms']").length > 0;

cy.log(
hasMandateText
? "Mandate text found"
: "No explicit mandate text found — may be shown at confirm step",
);
});
} else {
cy.log("ACH not available — skipping mandate display test");
}
});
});
});

it("should complete ACH payment successfully with valid bank details", () => {
cy.wait(2000);

getIframeBody().then(($body) => {
if ($body.find(`[data-testid=${testIds.addNewCardIcon}]`).length > 0) {
cy.wrap($body).find(`[data-testid=${testIds.addNewCardIcon}]`).click();
cy.wait(500);
}

getIframeBody().then(($refreshed) => {
const $ach = $refreshed.find(
"[data-testid='ach_debit'], [data-testid='ACH Bank Debit']",
);

if ($ach.length > 0) {
cy.wrap($ach).first().click();
cy.wait(500);

getIframeBody()
.find(
"[data-testid='routingNumber'], input[placeholder*='Routing']",
)
.first()
.type(achBankTransferDetails.success.routingNumber);

getIframeBody()
.find(
"[data-testid='accountNumber'], input[placeholder*='Account']",
)
.first()
.type(achBankTransferDetails.success.accountNumber);

// Select account type if dropdown exists
getIframeBody().then(($form) => {
const $accountType = $form.find(
"[data-testid='accountType'], select[name='accountType']",
);
if ($accountType.length > 0) {
cy.wrap($accountType).select("checking");
}
});

getIframeBody().get("#submit").click();
cy.wait(3000);

// ACH returns a redirect URL to verify microdeposits or succeeds directly
cy.get("body").then(($b) => {
const succeeded = $b.text().includes("Thanks for your order!");
const redirected = !window.location.href.includes("localhost:9060");
cy.log(`ACH result — success: ${succeeded}, redirected: ${redirected}`);
expect(succeeded || redirected).to.be.true;
});
} else {
cy.log(
"ACH not available in this profile — test requires ACH Bank Debit enabled on Stripe profile",
);
}
});
});
});
});
Loading
Loading