How to Test UPI Payment Status Pages with Website Screenshot Comparisons
Test UPI success, pending, and failure screens with synthetic fixtures, Playwright assertions, stable screenshot baselines, and independent backend checks.
Use controlled test data to render and compare separate successful, pending, and failed UPI payment states. In Playwright, assert the status and important guidance semantically first, then capture a stable screenshot of the status card or whole page. A screenshot shows what the page rendered; it cannot establish whether a bank account was debited or a payment completed. Verify payment state independently through the application’s backend or payment provider.
NPCI’s sample help screens distinguish approved, pending, and failed transactions, including pending progress stages. These are useful interface examples, not a binding layout or wording specification for every merchant or payment app. NPCI UPI Help Guidelines.
1. Define the states and safe test data
Keep status states explicit in your fixture data. Do not use real payments, UPI IDs, phone numbers, account details, transaction references, or genuine customer screenshots in public test artifacts. A synthetic reference should look plausible enough to exercise wrapping without identifying a real transaction.
| Fixture state | What the interface should communicate | Useful checks |
|---|---|---|
| success / approved | A clear confirmation that the application’s state is successful | Status label, amount, payee, reference presentation, timestamp, confirmation guidance |
| pending / processing | The outcome is still pending; do not present it as success or failure | Pending label and explanation, progress affordance if present, next step or help path |
| failed | A clear failure state and a useful next step | Failure label, explanation, retry/help guidance, no success-only messaging |
| timeout / reversal | Your product-specific outcome, when supported | Explicit copy for that state; do not collapse it into another state without product rules |
NPCI’s FAQ notes that beneficiary-bank processing can be delayed and describes a pending outcome separately from completed confirmation. The FAQ includes a 48-hour expectation, but customer advice and timelines can change; check the live FAQ and relevant bank or app guidance before publishing operational instructions. NPCI UPI FAQ.
2. Add deterministic page fixtures
One practical pattern is to have the page read a test-only fixture endpoint or query-selected fixture in a non-production test build. The fixture should be deterministic: freeze timestamps, IDs, amount formatting, locale, and payee names. The example below assumes the application renders a status card with accessible labels and a data-testid. Adapt those selectors to your product’s accessible interface.
// tests/upi-status.spec.ts
import { test, expect } from '@playwright/test';
const cases = [
{
state: 'success',
heading: 'Payment successful',
explanation: 'Your payment was completed.',
amount: '₹1,250.00',
payee: 'Example Store',
reference: 'TEST-UPI-000001',
timestamp: '4 Oct 2026, 10:30 am',
},
{
state: 'pending',
heading: 'Payment pending',
explanation: 'We are waiting for confirmation. Check again later.',
amount: '₹1,250.00',
payee: 'Example Store',
reference: 'TEST-UPI-000002',
timestamp: '4 Oct 2026, 10:30 am',
},
{
state: 'failed',
heading: 'Payment failed',
explanation: 'No confirmation was received. Contact support if you need help.',
amount: '₹1,250.00',
payee: 'Example Store',
reference: 'TEST-UPI-000003',
timestamp: '4 Oct 2026, 10:30 am',
},
] as const;
test.describe('UPI payment status visual states', () => {
for (const fixture of cases) {
test(`${fixture.state} state is clear and visually stable`, async ({ page }) => {
await page.goto(`/test/payment-status?fixture=${fixture.state}`);
const card = page.getByTestId('payment-status-card');
// Behavior and content assertions come before pixel comparison.
await expect(card.getByRole('heading', { name: fixture.heading })).toBeVisible();
await expect(card).toContainText(fixture.explanation);
await expect(card).toContainText(fixture.amount);
await expect(card).toContainText(fixture.payee);
await expect(card).toContainText(fixture.reference);
await expect(card).toContainText(fixture.timestamp);
await page.evaluate(() => document.fonts.ready);
await expect(card).toHaveScreenshot(`upi-${fixture.state}-card.png`, {
animations: 'disabled',
});
});
}
});
The status label is intentionally asserted as text rather than inferred from a green, amber, or red icon. If the product exposes an accessible status role, test that structure too. A color-only change can be visually important, but color is not a semantic assertion.
3. Configure Playwright and run the comparisons
Install the test runner in the project and create an initial baseline by running the test once. The first run stores reference images; later runs compare new captures with those references. Playwright waits for two consecutive screenshots to match before comparison, which helps with transient rendering but does not replace deterministic fixtures.
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
use: {
baseURL: 'http://127.0.0.1:3000',
locale: 'en-IN',
timezoneId: 'Asia/Kolkata',
colorScheme: 'light',
viewport: { width: 390, height: 844 },
deviceScaleFactor: 1,
...devices['Desktop Chrome'],
},
projects: [
{ name: 'chromium-mobile', use: { ...devices['Pixel 7'] } },
{ name: 'chromium-desktop', use: { ...devices['Desktop Chrome'], viewport: { width: 1280, height: 900 } } },
],
});
Run with npx playwright test. Review generated diffs and the test report. Commit approved reference images with the code if your team versions snapshots. Keep baselines for distinct browser engines, operating systems, and viewports where those environments are part of your support matrix; do not compare unlike renderers as if they were identical.
Choose the capture surface
- Status card: isolates the component and reduces noise from navigation, ads, or unrelated page content.
- Whole page: catches clipping, scrolling, responsive layout, footer/help content, and changes outside the component.
- Both: useful when the card has critical state content and the surrounding page can also regress.
Locator screenshot assertions use the same baseline model as page screenshots. For example, replace the card assertion with await expect(page).toHaveScreenshot('upi-pending-page.png') to capture the page. Keep separate snapshot names for each state and intended viewport.
4. Keep screenshots stable without hiding regressions
- Fix the viewport, browser project, locale, timezone, color scheme, and device scale factor. Rendering depends on browser version, OS, fonts, headless mode, hardware, and other conditions. Playwright calls out these sources of variation in its visual comparisons guide.
- Use fixture values for timestamps and generated references. If a value must remain dynamic, mask only that value and preserve its label and surrounding layout.
- Wait for critical data, images, and fonts. Prefer application readiness signals over arbitrary sleeps. If third-party content is irrelevant, stub it in the test environment.
- Disable or freeze animations and transitions for the screenshot. For a pending progress animation, assert the state and affordance semantically, then capture a deterministic frame or use a reduced-motion test mode.
- Do not mask the status, amount, payee, or customer guidance when those elements are under test. Masking these fields could hide the very regression the test should catch.
- Inspect every diff before updating a baseline. A new golden image represents a changed expectation; it is not proof that the change is correct.
Playwright offers configurable pixel-difference and color thresholds. Start conservatively in a stable environment, inspect actual diffs, and set thresholds to match observed product needs. There is no universally correct threshold: broad tolerance can conceal meaningful changes, while strict pixel equality may surface harmless rendering noise.
5. Test responsive and difficult content cases
For each supported state, add cases that challenge the layout and meaning without introducing real payment data:
- Long payee names and long synthetic references: check wrapping, truncation, copy affordances, and horizontal overflow.
- Amounts with supported currency and locale formats: confirm separators, decimals, and alignment.
- Small mobile width and desktop width: check that the status and next action remain visible and readable.
- Missing optional fields: ensure the card does not show empty labels or misleading placeholders.
- Pending that persists: verify it remains pending and presents the product’s intended follow-up guidance.
- Failure and timeout variants: verify each supported outcome has its own explanation and action.
- Slow or unavailable status data: check loading and error states separately; do not accidentally snapshot a transient loading screen as a final payment outcome.
NPCI describes technical declines as arising from technical reasons such as system unavailability or network issues, and describes deemed-approved cases in relation to missing online beneficiary-bank credit confirmation. This is a reminder that displayed status communicates a system outcome, not merely a visual theme. Use your product’s payment state model to define fixtures and copy. NPCI UPI ecosystem statistics glossary.
6. Keep visual and payment correctness separate
A useful test suite has two layers:
- UI layer: test state mapping, visible text, accessible semantics, amount and payee presentation, help content, and screenshot appearance using synthetic fixtures.
- Payment-system layer: test the backend/provider integration, webhook handling, reconciliation, and authoritative transaction state using a safe test environment and the provider’s supported methods.
Do not treat a success screenshot as proof of a successful bank transfer. Likewise, a pending screen does not by itself diagnose a real transaction dispute. NPCI’s FAQ includes questions about debited-but-not-credited and pending transactions; those are customer-support cases requiring authoritative transaction information, not screenshot comparison alone. Google Pay’s India web integration documentation discusses payment status checks and unique transaction IDs in its integration context; that does not make visual testing a payment verification mechanism. Google Pay India web integration.
7. Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. A one-call capture can provide a visual artifact for a URL that is already displaying a controlled test state; it does not replace semantic assertions or backend payment checks. See the ScreenshotNeo API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com/test/payment-status?fixture=pending -o pending.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={
"access_key": "YOUR_API_KEY",
"url": "https://example.com/test/payment-status?fixture=pending",
},
timeout=90,
)
r.raise_for_status()
open("pending.webp", "wb").write(r.content)
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://example.com/test/payment-status?fixture=pending',
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('pending.webp', new Uint8Array(await res.arrayBuffer()));
ScreenshotNeo removes cookie banners, popups, and chat widgets before the shot; bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000. The screenshot still only records the rendered page, so keep the fixture controlled and verify transaction correctness separately.
Create a free ScreenshotNeo account for 1,000 screenshots a month, with no card.
8. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Snapshots fail on every run | Browser, OS, font, viewport, device scale, or headless environment differs from the baseline | Run baselines and comparisons in the same pinned project/container and review the rendering environment. |
| Diffs show timestamps or IDs | Fixture data is generated from current time or randomness | Freeze values in the test fixture, or narrowly mask only the unstable value. |
| Screenshot captures loading UI | Capture begins before the status data and critical assets are ready | Wait for an explicit page-ready signal and font readiness; avoid guessing with long sleeps. |
| Pending looks like success | Test asserts only an icon or color, or fixture-to-copy mapping is wrong | Assert the pending heading and explanation text, and check the state mapping independently. |
| Small text shifts create noisy diffs | Font unavailable, fallback font used, or environment changed | Install and pin fonts in the runner; wait for document.fonts.ready. |
| Baseline update makes CI pass but concern remains | Snapshot was accepted without checking product intent | Inspect the old/new diff alongside the fixture and design expectation before updating. |
| Real payment result disagrees with screenshot | Visual capture reflects rendered UI only; backend state may differ or be stale | Check authoritative backend/provider status and event handling. Do not use the image to settle a payment outcome. |
9. Reliability, performance, and cost
Local browser snapshots avoid a per-capture screenshot service charge, but they consume CI time and require stable browser dependencies and stored baselines. Keep fixtures local or in a controlled test environment; avoid making a visual suite depend on live payment services or third-party content. For reliability, separate flaky data-loading failures from genuine visual diffs and retain test reports with the changed snapshots.
A remote screenshot API reduces the browser setup you maintain, but adds a network request and service usage to the capture path. Use it for review artifacts or repeatable URL captures where appropriate, and check the response verdict/billing information rather than assuming every request produced a usable page. ScreenshotNeo states that only clean shots are billed and responses identify page verdict and billing via headers; its plans are Free (1,000 monthly), Starter ($5/3,000), Growth ($15/15,000), Pro ($39/60,000), Scale ($99/250,000), and Business ($249/1,000,000), with two months free on yearly billing. All features are on every plan. These API captures do not replace local semantic browser assertions.
10. Review checklist
- Success, pending, and failure each have a deterministic synthetic fixture.
- Status text and critical fields are asserted semantically before capture.
- Amount, payee, reference presentation, timestamp, and guidance are checked where displayed.
- Viewport, locale, timezone, browser, and device scale are controlled.
- Dynamic content is frozen or narrowly masked; meaningful status content remains visible to the comparison.
- Mobile and desktop layouts are covered where supported.
- Every snapshot diff is reviewed before a baseline changes.
- Payment correctness is verified through backend/provider tests, not inferred from pixels.
- No real payment identifiers or customer payment screenshots enter public artifacts.
FAQ
Should pending be treated as a failed payment in a visual test?
No. Keep pending as its own fixture and verify its own explanation and next-step guidance. NPCI’s examples distinguish pending from approved and failed states.
Can a screenshot prove a UPI payment succeeded?
No. It proves only what the page rendered at capture time. Use an authoritative payment-system check for the actual transaction outcome.
Should I mask the transaction reference?
Use a synthetic reference by default. If a dynamic value must be masked, preserve the field label and layout, and do not use real identifiers in the test environment.
What threshold should I set for pixel differences?
Choose it from observed rendering noise in a controlled environment. Review diffs and use the least permissive tolerance that remains practical for your supported runners.


