121 lines
4.0 KiB
TypeScript
121 lines
4.0 KiB
TypeScript
import type { Page } from "playwright-core";
|
|
|
|
/**
|
|
* Self-contained Playwright function for completing a Stripe setup payment checkout.
|
|
* NO external imports — this function must be serializable via fn.toString()
|
|
* for Kernel Playwright Execution. (type imports are fine — Bun strips them)
|
|
*
|
|
* Handles setup mode checkout (mode: "setup") for adding payment methods.
|
|
* Uses checkout.stripe.com direct DOM selectors, CVC is "100",
|
|
* and unchecks the Stripe Link "Save my info" checkbox if present.
|
|
*/
|
|
export const setupPayment = async ({
|
|
page,
|
|
url,
|
|
}: {
|
|
page: Page;
|
|
url: string;
|
|
}) => {
|
|
await page.goto(url, { waitUntil: "domcontentloaded", timeout: 60000 });
|
|
console.log("[setupPayment] Page loaded");
|
|
|
|
// Wait for the checkout page to render
|
|
await page.waitForTimeout(3000);
|
|
|
|
// Select the Card payment method. The hidden radio input is covered by an
|
|
// AccordionButton overlay (expandedClickArea). Target that button via
|
|
// data-testid and use a JS click since it has no visible text content.
|
|
try {
|
|
const cardBtn = page.locator('[data-testid="card-accordion-item-button"]');
|
|
if ((await cardBtn.count()) > 0) {
|
|
await cardBtn.evaluate((el) => (el as HTMLElement).click());
|
|
console.log("[setupPayment] Card selected via accordion button JS click");
|
|
await page.waitForTimeout(1000);
|
|
} else {
|
|
// Fallback: try the accordion ID directly via JS
|
|
await page.evaluate(() => {
|
|
const radio = document.getElementById(
|
|
"payment-method-accordion-item-title-card",
|
|
);
|
|
if (radio) radio.click();
|
|
});
|
|
console.log("[setupPayment] Card selected via radio JS click fallback");
|
|
await page.waitForTimeout(1000);
|
|
}
|
|
} catch {
|
|
// Card might already be selected
|
|
}
|
|
|
|
const cardNumber = page.locator("#cardNumber");
|
|
await cardNumber.waitFor({ timeout: 10000 });
|
|
await cardNumber.pressSequentially("4242424242424242");
|
|
console.log("[setupPayment] Card number filled");
|
|
|
|
const cardExpiry = page.locator("#cardExpiry");
|
|
await cardExpiry.waitFor({ timeout: 5000 });
|
|
await cardExpiry.pressSequentially("1228");
|
|
console.log("[setupPayment] Expiry filled");
|
|
|
|
const cardCvc = page.locator("#cardCvc");
|
|
await cardCvc.waitFor({ timeout: 5000 });
|
|
await cardCvc.pressSequentially("100");
|
|
console.log("[setupPayment] CVC filled");
|
|
|
|
const billingName = page.locator("#billingName");
|
|
await billingName.waitFor({ timeout: 5000 });
|
|
await billingName.pressSequentially("Test Customer");
|
|
console.log("[setupPayment] Billing name filled");
|
|
|
|
// Uncheck "Save my information for faster checkout" (Stripe Link) if checked.
|
|
// This must be unchecked or the submit button stays greyed out.
|
|
// The checkbox input is hidden behind custom styling, so use JS click directly.
|
|
try {
|
|
const wasChecked = await page.evaluate(() => {
|
|
const cb = document.getElementById("enableStripePass") as HTMLInputElement | null;
|
|
if (cb && cb.checked) {
|
|
cb.click();
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
if (wasChecked) {
|
|
console.log("[setupPayment] Stripe Link checkbox unchecked");
|
|
await page.waitForTimeout(500);
|
|
}
|
|
} catch {
|
|
// Stripe Link checkbox not present
|
|
}
|
|
|
|
// Force country to US so a postal code input is always shown
|
|
try {
|
|
const countrySelect = page.locator("#billingCountry");
|
|
if ((await countrySelect.count()) > 0) {
|
|
await countrySelect.selectOption("US");
|
|
console.log("[setupPayment] Country set to US");
|
|
await page.waitForTimeout(500);
|
|
}
|
|
} catch {
|
|
// Country selector not present
|
|
}
|
|
|
|
try {
|
|
const postalCode = page.locator("#billingPostalCode");
|
|
if ((await postalCode.count()) > 0) {
|
|
await postalCode.pressSequentially("10001");
|
|
console.log("[setupPayment] Postal code filled");
|
|
}
|
|
} catch {
|
|
// Postal code field not present
|
|
}
|
|
|
|
const submitBtn = page.locator(".SubmitButton-TextContainer").first();
|
|
if ((await submitBtn.count()) > 0) {
|
|
await submitBtn.evaluate((el) => (el as HTMLElement).click());
|
|
console.log("[setupPayment] Submit clicked");
|
|
}
|
|
|
|
// Wait for form submission to complete
|
|
await page.waitForTimeout(7000);
|
|
console.log("[setupPayment] Setup payment complete");
|
|
};
|