How to Test Payment Flow on Web (Complete Guide)
Payment processing is the moment where a user’s intent to buy meets the business’s ability to collect money. A failure here directly translates to lost revenue, damaged trust, and potential regulatory
Why Payment Flow Testing Matters
Payment processing is the moment where a user’s intent to buy meets the business’s ability to collect money. A failure here directly translates to lost revenue, damaged trust, and potential regulatory penalties. Unlike UI glitches that may be merely annoying, a broken payment step can abort a transaction, leave a charge in a pending state, or expose sensitive data. Because payment flows involve third‑party gateways, asynchronous callbacks, and varying compliance rules (PCI‑DSS, PSD2, GDPR), they are among the most complex user journeys to test. A single missed edge case—such as a network timeout after the gateway returns an authorization but before the confirmation page loads—can cause duplicate charges or orphaned orders. Therefore, testing payment flows must be treated as a critical quality gate, not an optional after‑thought.
Common Payment Flow Failures in Production
Production environments expose conditions that are difficult to reproduce in a staging sandbox. Typical failure patterns include:
- Gateway latency spikes that cause timeout handling to fall back to an error page instead of retrying.
- Mismatched currency formatting where the frontend sends a value like “1,234.56” while the gateway expects “1234.56”.
- Duplicate submission when a user double‑clicks the pay button or the browser retries a failed request.
- 3D Secure challenge frames that are blocked by pop‑up blockers or incorrectly sized iframes, leaving the user stuck.
- Session expiration during the redirect to the gateway, resulting in a lost cart after a successful payment.
- Webhook delivery failures where the order‑fulfillment service never receives the payment‑confirmed event.
- Accessibility barriers such as missing ARIA labels on the card‑number input, preventing screen‑reader users from completing the flow.
- Security oversights like transmitting card data over HTTP instead of HTTPS, or storing raw PAN in localStorage.
Each of these issues can slip through unit tests because they depend on timing, network behavior, or external service contracts.
Building a Comprehensive Test Matrix
A test matrix helps you enumerate the dimensions you need to cover. Below is a matrix that separates *what* to test (test categories) from *how* the test varies (variables). Use it as a checklist when designing both manual and automated suites.
| Test Category | Sub‑category | Variables to Vary | Expected Outcome |
|---|---|---|---|
| Happy Path | Standard purchase | Item quantity, shipping method, promo code | Order created, payment captured, confirmation shown |
| Error Paths | Declined card | Card number (test suffixes), CVV, expiry | Friendly decline message, cart unchanged |
| Insufficient funds | Low‑balance test card | Same as declined | |
| Expired card | Past expiry date | Same as declined | |
| Invalid format | Letters in card number, wrong length | Inline validation error | |
| Gateway timeout | Simulated latency > 30s | Timeout UI, option to retry | |
| 3DS challenge failure | Wrong OTP, closed iframe | Challenge error, stay on payment page | |
| Edge Cases | Duplicate submit | Rapid double‑click, network retry | Idempotent request, single charge |
| Currency mismatch | Different locale, decimal separator | Amount correctly converted or rejected | |
| Session expiry mid‑flow | Short server timeout | User redirected to login, cart preserved | |
| Webhook loss | Mock webhook endpoint returns 500 | Order status stays pending, alert triggered | |
| Mobile‑only UI | Touch events, virtual keyboard | All fields accessible, no overlap | |
| Accessibility | Screen reader navigation | NVDA, VoiceOver | All controls announced, focus order logical |
| Keyboard only | Tab navigation | No trap, all actions reachable | |
| Color contrast | WCAG AA | Text/background ratio ≥ 4.5:1 | |
| Security/Privacy | TLS enforcement | Force HTTP | Request blocked, upgrade to HTTPS |
| Card data leakage | Check network logs, storage | No PAN in URLs, localStorage, or console | |
| CSP violations | Inline script, eval | No blocked console errors | |
| Rate limiting | Rapid successive attempts | Throttling response, no brute‑force enable |
How to Use the Matrix
- Pick a base scenario (e.g., happy path with a promo code).
- Select one variable column to iterate (e.g., card number test suffixes).
- Run the test for each value in that column while keeping other variables constant.
- Repeat for each test category, gradually adding more variables to uncover interaction bugs (e.g., promo code + expired card).
This systematic approach reduces the chance of missing a combination that only appears under load or with a specific gateway response.
Manual Testing Approach (Step‑by‑Step)
Even when automation is in place, a manual exploratory pass catches issues that scripted checks assume away. Follow this procedure on a clean browser profile (no extensions, cache cleared).
- Preparation
- Obtain a set of test card numbers from your gateway’s documentation (e.g., Stripe test cards).
- Enable developer tools, network throttling (Slow 3G), and device emulation if needed.
- Log out of any existing session to start from an anonymous state.
- Happy Path Walk‑through
- Add an item to the cart, proceed to checkout.
- Fill in shipping details using a valid address.
- Choose a payment method (card).
- Enter a successful test card (e.g., 4242 4242 4242 4242).
- Submit the form.
- Verify that the URL changes to a confirmation page, the order number appears, and a success toast is shown.
- Check the backend (admin panel or DB) to confirm the order status is *paid* and inventory is decremented.
- Error Path Injection
- Replace the card number with a known decline card (e.g., 4000 0000 0000 0002).
- Submit and confirm that the inline error appears without navigating away from the payment page.
- Repeat for expired card, insufficient funds, and invalid format.
- For each case, ensure the cart remains unchanged and the user can correct the field and retry.
- Network Condition Simulation
- In DevTools → Network, enable offline mode, then go back online after clicking pay.
- Observe whether the UI shows a clear “connection lost” message and offers a retry button.
- Test with latency profiles (e.g., 200ms, 2s) to see if timeout handling fires too early or too late.
- Duplicate Submission Test
- Click the pay button twice quickly (or use a macro to send two requests within 100 ms).
- Check server logs for idempotency key usage; only one charge should be recorded.
- Confirm the UI does not show two success messages.
- 3D Secure Flow
- Use a test card that triggers a challenge (e.g., 4000 0000 0000 0025).
- Verify that the challenge iframe appears, is focusable, and can be dismissed.
- Enter a wrong OTP, ensure the error stays inside the iframe and does not break the parent page.
- Complete with correct OTP and confirm the payment succeeds.
- Session Expiry Mid‑Flow
- Set the server session timeout to a very low value (e.g., 30 seconds).
- After entering card details, wait beyond the timeout before submitting.
- Expect a redirect to login, with the cart restored upon re‑login.
- Accessibility Check
- Navigate the form using only Tab + Shift+Tab.
- Ensure every input receives a visible focus indicator.
- Run a screen reader (NVDA on Windows or VoiceOver on macOS) and confirm that each field announces its purpose, required status, and any error messages.
- Verify contrast ratios with a tool like axe‑core.
- Security Scan
- Confirm the page is served over HTTPS (look for the lock icon).
- In the Network tab, filter for any request containing “card”, “number”, or “cvc” and ensure the payload is encrypted (no query‑string parameters).
- Check localStorage and sessionStorage for any stray PAN fragments.
- Run a quick CSP report‑only header test to see if any inline scripts are blocked.
- Post‑Payment Verification
- After a successful payment, verify that the order confirmation email (if applicable) contains the correct amount and no sensitive data.
- Ensure the thank‑you page does not expose the raw gateway response in the UI or source code.
When you complete these steps, you have exercised the most common failure modes while also validating that the basic journey works.
Automated Testing Approaches for Web
Manual checks are essential but do not scale. Automation provides repeatability, enables CI gating, and can simulate conditions that are tedious to reproduce by hand (e.g., thousands of concurrent users). The following tools and patterns work well for web‑based payment flows.
Choosing a Framework
- Playwright (Microsoft) – native support for multiple browsers, built‑in tracing, and easy handling of iframes and dialogs.
- Cypress – excellent developer experience, automatic waiting, and rich debugging UI, but limited cross‑origin iframe handling (requires plugins).
- WebDriverIO with Appium – useful if you need to test hybrid web views inside a native container, but heavier for pure web.
For most pure‑web applications, Playwright offers the best balance of power and simplicity, especially when dealing with payment gateways that open third‑party iframes.
Test Architecture
- Page Object Model (POM) – encapsulate selectors and actions for each step (cart, shipping, payment, confirmation).
- Environment Variables – store gateway test credentials, base URLs, and feature flags outside the code.
- Test Data Management – use a fixture file that lists test card numbers and expected outcomes; parameterize tests over this list.
- Mocking vs. Real Gateway – for fast unit‑like tests, mock the gateway endpoint with tools like MSW (Mock Service Worker) or network interception in Playwright. For end‑to‑end confidence, run a subset against the real sandbox gateway.
Sample Playwright Test (TypeScript)
Below is a complete example that tests the happy path, a declined card, and a network timeout scenario. It assumes a simple checkout flow with the following selectors:
#cart-count– shows number of items in the cart#checkout-btn– proceeds to checkout page#shipping-form– contains address fields#payment-method-card– selects card payment#card-number,#card-expiry,#card-cvc,#pay-btn– payment fields.error-message– container for inline validation errors#order-confirmation– appears after successful payment
// tests/payment-flow.spec.ts
import { test, expect } from '@playwright/test';
// Helper to load test card data from a JSON fixture
type TestCard = { number: string; expiry: string; cvc: string; shouldPass: boolean };
const testCards: TestCard[] = JSON.parse(
require('../fixtures/test-cards.json')
);
test.beforeEach(async ({ page }) => {
await page.goto('/');
// Add a product to cart
await page.click('.add-to-cart[data-sku="TSHIRT-01"]');
await expect(page.locator('#cart-count')).toHaveText('1');
});
test('Happy path with valid test card', async ({ page }) => {
await page.click('#checkout-btn');
// Fill shipping
await page.fill('#shipping-name', 'Ada Lovelace');
await page.fill('#shipping-address', '123 Algorithm St');
await page.fill('#shipping-city', 'London');
await page.fill('#shipping-postal', 'WC2N 5DU');
await page.click('#shipping-continue');
// Choose card
await page.click('#payment-method-card');
// Pull first passing card from fixture
const { number, expiry, cvc } = testCards.find(c => c.shouldPass)!;
await page.fill('#card-number', number);
await page.fill('#card-expiry', expiry);
await page.fill('#card-cvc', cvc);
await page.click('#pay-btn');
// Wait for confirmation
await expect(page.locator('#order-confirmation')).toBeVisible({ timeout: 15000 });
await expect(page.locator('#order-confirmation')).toContainText('Thank you');
});
test.describe('Error paths', () => {
testCards.filter(c => !c.shouldPass).forEach(card => {
test(`Declined/invalid card ${card.number}`, async ({ page }) => {
await page.click('#checkout-btn');
// skip shipping for brevity – assume pre‑filled
await page.click('#payment-method-card');
await page.fill('#card-number', card.number);
await page.fill('#card-expiry', card.expiry);
await page.fill('#card-cvc', card.cvc);
await page.click('#pay-btn');
// Expect inline error, not navigation away
await expect(page.locator('.error-message')).toBeVisible();
await expect(page.locator('#order-confirmation')).not.toBeVisible();
});
});
});
test('Network timeout handling', async ({ page }) => {
await page.route('https://api.example-gateway.com/v1/charges', route => {
// Simulate a gateway that never responds
return new Promise(() => {}); // pending forever
});
await page.click('#checkout-btn');
// … fill shipping and card details as in happy path …
await page.click('#payment-method-card');
await page.fill('#card-number', '4242424242424242');
await page.fill('#card-expiry', '12/30');
await page.fill('#card-cvc', '123');
await page.click('#pay-btn');
// UI should show a timeout message after a client‑side timeout (e.g., 10s)
const timeoutMsg = page.locator('text=Connection timeout. Please try again.');
await expect(timeoutMsg).toBeVisible({ timeout: 12000 });
// Provide a retry button
await expect(page.locator('button:has-text("Retry")')).toBeEnabled();
});
Explanation of the snippet
- The
beforeEachhook ensures a clean cart state. - The happy‑path test pulls a known good card from a fixture, fills the form, and asserts on the confirmation element.
- The error‑path test iterates over all failing cards, verifying that an inline error appears without leaving the payment page.
- The timeout test uses Playwright’s route interception to hang the gateway request, then checks that the UI displays a user‑friendly timeout message and offers a retry.
You can extend this pattern to cover 3D Secure iframes, duplicate submission (by invoking page.click('#pay-btn') twice with a short delay), and session expiry (by setting a cookie with a short expires attribute before the test).
Cypress Variant (for teams already using Cypress)
Cypress cannot directly interact with cross‑origin iframes, but many gateways expose a fallback endpoint for testing. If your gateway supports a “tokenization” endpoint that returns a fake token, you can bypass the iframe entirely:
// cypress/integration/payment_spec.js
describe('Payment flow', () => {
const goodCard = { number: '4242424242424242', expiry: '12/30', cvc: '123' };
beforeEach(() => {
cy.visit('/');
cy.get('.add-to-cart[data-sku="TSHIRT-01"]').click();
cy.get('#cart-count').should('have.text', '1');
});
it('completes a successful purchase', () => {
cy.get('#checkout-btn').click();
// fill shipping (omitted for brevity)
cy.get('#payment-method-card').click();
cy.get('#card-number').type(goodCard.number);
cy.get('#card-expiry').type(goodCard.expiry);
cy.get('#card-cvc').type(goodCard.cvc);
cy.get('#pay-btn').click();
cy.get('#order-confirmation').should('contain.text', 'Thank you');
});
it('shows error for declined card', () => {
cy.get('#checkout-btn').click();
cy.get('#payment-method-card').click();
cy.get('#card-number').type('4000000000000002'); // Stripe decline
cy.get('#card-expiry').type('12/30');
cy.get('#card-cvc').type('123');
cy.get('#pay-btn').click();
cy.get('.error-message').should('be.visible');
cy.get('#order-confirmation').should('not.exist');
});
});
If you need to test the actual 3DS challenge, consider using a service like TestCard.com that provides a test iframe you can interact with via Cypress’s cy.frameLoaded and cy.iframe() commands (available through the cypress-iframe plugin).
Integrating Mocks for Fast Feedback
For PR checks, you may want to avoid hitting the sandbox gateway altogether. Playwright’s route API lets you mock the POST to /v1/charges and return a predetermined JSON:
await page.route('https://api.example-gateway.com/v1/charges', async route => {
const body = JSON.parse((await route.request().postDataJSON) || '{}');
// Simulate different outcomes based on card number
if (body.number.startsWith('4000000000000002')) {
await route.fulfill({ status: 402, json: { error: { message: 'Your card was declined.' } } });
} else {
await route.fulfill({ status: 200, json: { id: 'ch_test_123', status: 'succeeded' } });
}
});
This approach yields sub‑second test runs while still exercising your frontend error‑handling logic.
Edge Cases That Appear Only in Production
Even with thorough staging tests, certain conditions only manifest under real‑world load, mixed‑device traffic, or specific gateway behaviors. Below are the most common production‑only pitfalls and how to detect them early.
1. Intermittent Gateway Network Errors
Production gateways occasionally return HTTP 502 or 504 due to internal scaling events. If your frontend treats any non‑2xx as a generic “something went wrong” and shows a stale spinner, users may abandon the cart.
Detection:
- Use a chaos‑engineering tool (e.g., Gremlin or Toxiproxy) in a pre‑prod environment to inject 5xx responses at a low rate (1‑2%).
- Observe whether the UI shows a retryable error and preserves form data.
2. Currency Conversion Rounding Differences
When a shopper pays in a non‑base currency, the gateway may apply its own rounding rules (e.g., rounding to the nearest 0.05 USD). If your frontend calculates tax or discounts using a different rule, the final amount sent to the gateway can mismatch the amount displayed, leading to a validation error.
Detection:
- Create a matrix of cart totals, tax rates, and discount coupons across the currencies you support.
- Compare the amount shown on the review screen with the amount sent in the payment request (capture via network logs).
3. Browser‑Specific Popup Blocker Interference
Some gateways open the 3DS authentication in a new window rather than an iframe. Users with aggressive popup blockers may see the window close instantly, leaving them on the payment page with no indication of failure.
Detection:
- Test with popular browser extensions (uBlock Origin, AdBlock Plus) enabled.
- Verify that a fallback to an iframe is offered or that a clear message instructs the user to disable the blocker.
4. Mobile Safari’s “Prevent Cross‑Site Tracking”
Intelligent Tracking Prevention (ITP) can block third‑party cookies that some gateways rely on for session state after a redirect. If the gateway sets a cookie on its domain and the browser blocks it, the post‑redirect landing page may treat the session as new, causing the order to appear as unpaid.
Detection:
- Use Safari’s Web Inspector → Storage → Cookies to confirm whether the gateway’s cookie is set after the redirect.
- If missing, consider switching to a token‑based flow that does not rely on third‑party cookies.
5. Race Condition Between Order Creation and Payment Webhook
Your backend may create an order record *before* redirecting to the gateway, expecting a webhook to mark it as paid. If the webhook is delayed (e.g., due to network glitch) and the user refreshes the confirmation page, the frontend might read the order as still pending and show an error.
Detection:
- Introduce an artificial delay (e.g., 5 seconds) in the webhook endpoint in a staging environment.
- Verify that the order page eventually transitions to “paid” without requiring a manual refresh.
6. Local Tax Jurisdiction Overrides
Certain regions (e.g., certain US states) require that tax be calculated on the *shipping* address rather than the billing address. If your checkout defaults to billing‑address tax, you may under‑collect tax and later face compliance issues.
Detection:
- Maintain a rule‑engine test suite that feeds addresses from each jurisdiction and asserts the calculated tax matches the official rate table.
7. Third‑Party Library Version Drift
Payment SDKs (e.g., Stripe Elements, PayPal Buttons) frequently release minor updates that change CSS class names or event signatures. If you lock your integration to a specific version via a CDN without a lockfile, a silent update can break styling or event handling.
Detection:
- Subscribe to the SDK’s changelog RSS feed.
- Run a weekly CI job that installs the latest version and runs your smoke test suite; fail if any visual regression is detected (using tools like Chromatic or Applitools).
By deliberately reproducing these conditions in a controlled pre‑prod environment, you can turn “production‑only” bugs into reproducible failures that your CI pipeline can catch.
Accessibility and Security Considerations
Payment flows are high‑risk areas for both accessibility violations and security lapses. Treating them as separate concerns leads to gaps; instead, integrate checks into your test strategy.
Accessibility Checklist (WCAG 2.1 AA)
| Criterion | How to Test | Tool / Method |
|---|---|---|
| 1.3.1 Info and Relationships | Ensure form fields have associated elements or aria-label. | axe‑core, manual inspection |
| 2.1.1 Keyboard | Tab through the entire checkout; no focus traps. | Keyboard-only navigation |
| 2.4.7 Focus Visible | Each interactive item shows a visible outline when focused. | CSS inspection |
| 3.3.2 Labels or Instructions | Provide inline format hints (e.g., “MM/YY”) that are announced. | Screen‑reader test |
| 3.3.3 Error Suggestion | When a card number fails Luhn check, suggest the correct format. | Inline validation inspection |
| 4.1.2 Name, Role, Value | Custom components (e.g., custom dropdown for card type) expose correct role. | axe‑core, VoiceOver/NVDA |
Automate the bulk of these checks with axe‑core integrated into your Playwright test suite:
import { injectAxe, checkA11y } from 'playwright-axe';
test.beforeEach(async ({ page }) => {
await injectAxe(page);
});
test('payment page has no accessibility violations', async ({ page }) => {
await page.goto('/checkout/payment');
await checkA11y(page, { detailedReport: true, detailedReportOptions: { html: true } });
});
If any violation appears, the test fails and outputs an HTML report you can attach to your CI artifact.
Security Testing Checklist
| Control | Test Procedure | Expected Result |
|---|---|---|
| TLS Enforcement | Attempt to load the page via http:// (or use curl -I). | Redirect to HTTPS or hard failure. |
| Card Data Exposure | Search network payloads for patterns \d{13,19} (possible PAN). | No matches in request URL, query string, or response body. |
| Storage Safety | After submitting the form, inspect localStorage and sessionStorage. | No card number, CVV, or expiration stored. |
| CSP | Load page with a strict CSP header that disallows inline scripts and eval. | No console errors about blocked scripts. |
| Rate Limiting | Send 20 rapid payment attempts with different test cards. | Receive HTTP 429 or a UI throttle message after a configurable threshold. |
| HTTP Security Headers | Verify presence of Strict-Transport-Security, X-Content-Type-Options, X-Frame-Options. | All headers present with correct values. |
| Subresource Integrity (SRI) | Check that any third‑party payment SDK script includes an integrity attribute. | Attribute present and matches the released hash. |
You can automate many of these with OWASP ZAP or Netsparker in a nightly scan, but lightweight assertions in your E2E suite give rapid feedback:
test('no card data leaked in URLs or storage', async ({ page }) => {
await page.route('**/*', route => {
const url = route.request().url();
if (/\d{13,19}/.test(url)) {
throw new Error('Possible PAN in URL: ' + url);
}
route.continue();
});
await page.goto('/checkout/payment');
await page.fill('#card-number', '4242424242424242');
await page.click('#pay-btn');
// after navigation, check storage
const local = await page.evaluate(() => JSON.stringify(window.localStorage));
const session = await page.evaluate(() => JSON.stringify(window.sessionStorage));
expect(local).not.toMatch(/\d{13,19}/);
expect(session).not.toMatch(/\d{13,19}/);
});
Autonomous, Persona‑Driven Exploration (Mention SUSA)
Scripted tests excel at verifying known paths, but they cannot anticipate the unpredictable ways real users interact with a checkout. Autonomous testing platforms that simulate varied user personas can surface issues that remain hidden in deterministic suites.
How it works:
An autonomous agent loads the application, then explores it using a behavior model that corresponds to a selected persona (e.g., *impatient* users who rapidly click, *elderly* users who move slowly and rely on larger touch targets, *adversarial* users who attempt to input malformed data). The agent records each screen, action, and response, building a graph of reachable states. When it encounters a dead end (e.g., a button that does nothing or a modal that traps focus), it flags it as a potential defect. Over successive runs, the agent learns which paths lead to errors and prioritizes them, improving its defect‑finding efficiency.
Practical Benefits for Payment Flow Testing
- Discovery of hidden navigation loops: A *curious* persona might repeatedly open the promo‑code field, type random strings, and close it, revealing that the apply button does not disable after a successful application, leading to multiple promo applications.
- Detection of timing‑sensitive bugs: An *impatient* persona may double‑tap the pay button before the first request finishes, exposing missing idempotency protection that a scripted test that waits for navigation would never trigger.
- Uncovering accessibility gaps: An *elderly* persona that relies on zoom and large tap targets might find that the CVC input becomes obscured when the keyboard appears on a device with a small viewport, a problem invisible in desktop‑only tests.
- Identifying security‑adjacent misuse: An *adversarial* persona could attempt to submit SQL‑like strings in the address fields, surfacing insufficient server‑side validation that could lead to injection flaws if the data is ever logged or reused.
Integrating SUSA Into Your Workflow
If you already use a CI pipeline, you can add a step that runs the SUSA agent against a preview deployment:
# Install the agent (once per CI runner)
pip install susatest-agent
# Run a 10‑minute exploratory session with a mix of personas
susatest run \
--url https://preview.example.com/checkout \
--apk-or-url '' \
--personas curious impatient elderly adversarial \
--duration 10m \
--output susa-report.json \
--format json
The agent will produce a JSON report containing:
- A list of discovered screens and transitions.
- Any observed JavaScript errors, console warnings, or network failures.
- Detected accessibility violations (WCAG) via its built‑in axe engine.
- Potential security findings such as reflected XSS in error messages.
You can then fail the build if the report contains any high‑severity items (e.g., a crash, an accessibility failure that blocks keyboard navigation, or a leaked PAN). Because the agent learns from prior runs, subsequent executions become faster and focus on newly‑added code paths.
Note: SUSA is mentioned here only to illustrate how autonomous, persona‑driven testing complements traditional scripted approaches. The concepts apply equally to other exploratory testing tools or manual exploratory sessions.
Checklist for Payment Flow Testing
Use this concise list before marking a payment‑flow feature as ready for release. Each item can be mapped to a test case in your manual or automated suite.
| ✅ Item | Description | How to Verify |
|---|---|---|
| Happy path succeeds with all supported payment methods | End‑to‑end purchase completes, order recorded, confirmation shown. | Manual walk‑through + automated smoke test. |
| Every declined card shows a clear inline error | No navigation away, error message explains the issue, cart unchanged. | Parameterized test over gateway’s decline cards. |
| Network timeout and intermittent 5xx are handled gracefully | UI shows retryable message, preserves form data, does not duplicate charge. | Latency simulation + chaos injection. |
| Duplicate submission is idempotent | Only one charge recorded regardless of rapid clicks or retries. | Click‑twice test + backend idempotency check. |
| 3DS flow works in iframe and popup modes | Challenge appears, is focusable, accepts correct OTP, rejects wrong OTP. | Manual test with test cards that trigger challenge. |
| Session expiration mid‑flow preserves cart | User redirected to login, after login cart is restored and can continue. | Short‑session cookie test. |
| All form fields are accessible via keyboard and screen reader | Logical tab order, visible focus, ARIA labels, error announcements. | Keyboard navigation + axe + VoiceOver/NVDA test. |
| No card data leaks in URLs, headers, storage, or logs | PAN never appears in query strings, fragment, localStorage, sessionStorage, or console. | Network inspection + storage inspection after submit. |
| TLS enforced, strong security headers present | All traffic uses HTTPS, HSTS, CSP, X‑Frame‑Options, etc. | curl -I or automated header check. |
| Currency conversion and tax calculation are correct per jurisdiction | Amount shown matches amount sent to gateway after applying locale‑specific rules. | Matrix of locales, tax rates, coupons. |
| Popup blockers do not break 3DS fallback | If gateway uses pop‑up, a clear message appears when blocked; otherwise iframe works. | Install uBlock/AdBlock, test. |
| Webhook receipt updates order status reliably | Simulated delayed webhook eventually marks order as paid; no stale pending state. | Inject delay in webhook endpoint, poll order status. |
| No JavaScript errors or uncaught promises on payment page | Console clean during all interactions. | page.on('console', msg => {...}) assertion in E2E test. |
| Visual regression baseline passes |
Test Your App Autonomously
Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts.
Try SUSA Free