How to Take a Puppeteer Screenshot of a UPI Payment Confirmation Page
Capture an authorized UPI confirmation with Puppeteer, wait for the rendered receipt, choose the right capture area, and handle sensitive payment details safely.
Use Puppeteer’s page.screenshot() after the authorized UPI page has rendered its confirmation state. Navigation finishing does not necessarily mean the payment confirmation has appeared: wait for an app-specific success indicator when the page renders that state asynchronously. The title does not specify a UPI app or its selectors, so the example below uses placeholders you must replace with an authorized test page and the correct success selector.
1. Install Puppeteer and capture the confirmation
In a new project, install Puppeteer:
npm install puppeteer
Save this as capture-upi.js. Replace the example URL and [data-testid="payment-success"] with values from your own authorized test page. The selector is illustrative; it is not a universal UPI selector.
import puppeteer from 'puppeteer';
const url = process.env.UPI_TEST_URL;
const successSelector = process.env.SUCCESS_SELECTOR || '[data-testid="payment-success"]';
if (!url) {
throw new Error('Set UPI_TEST_URL to an authorized test or permitted page.');
}
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await page.setViewport({ width: 1280, height: 900, deviceScaleFactor: 1 });
await page.goto(url, {
waitUntil: 'networkidle2',
timeout: 30000,
});
// Navigation readiness is not proof that the receipt has rendered.
await page.waitForSelector(successSelector, { timeout: 15000 });
await page.screenshot({ path: 'upi-confirmation.png' });
} finally {
await browser.close();
}
Run it with your own permitted test URL and the selector that identifies a successful confirmation:
UPI_TEST_URL='https://your-authorized-test-page.example' \
SUCCESS_SELECTOR='[data-testid="payment-success"]' \
node capture-upi.js
Use a test or otherwise authorized workflow. Do not automate a payment or capture a real transaction unless you are permitted to access and handle that page. Confirmation receipts can contain sensitive transaction details.
2. Wait for the actual success state
page.goto() waits according to the chosen navigation condition. networkidle2 is a useful general setting, but it does not establish that an app-specific confirmation message has appeared. Single-page apps may render after navigation, and pages with ongoing network activity may never become idle. Prefer an observable success element when one is available:
await page.waitForSelector('[data-testid="payment-success"]', {
visible: true,
timeout: 15000,
});
If the page exposes a stable success heading instead of a test ID, use an appropriate locator or selector for that application. Avoid relying on a fixed sleep as the main readiness check: it can be too short on a slow run and waste time on a fast one. If the app has no stable success marker, a short delay can be a fallback, but it does not guarantee the page is correct.
Use only the access and authentication flow you are authorized to use. This guide cannot give an app-specific login procedure, success selector, or rendering condition because no provider or target page was identified.
3. Choose the screenshot area and output
Capture the viewport
The default screenshot captures the visible viewport. This is usually appropriate when the confirmation fits on screen:
await page.screenshot({ path: 'upi-confirmation.png' });
Capture the full page
Set fullPage: true when the receipt extends below the viewport. The resulting image may include surrounding page content as well as the receipt:
await page.screenshot({
path: 'upi-confirmation-full.png',
fullPage: true,
});
Capture only a receipt element
If the receipt is a distinct element and you know its selector, take an element screenshot to omit unrelated page content:
const receipt = await page.waitForSelector('[data-testid="receipt"]', {
visible: true,
timeout: 15000,
});
await receipt.screenshot({ path: 'upi-receipt.png' });
Replace the example selector with the actual receipt element. Puppeteer also supports a clip rectangle when you need a specific page region and know its coordinates. Element capture is often easier to maintain than hard-coded coordinates if the page layout changes.
Choose the image format
Puppeteer screenshots are images. Choose an image format and options supported by the installed Puppeteer version; for example, PNG is suitable when you want a lossless image, while JPEG can be useful when a smaller lossy image is acceptable. Check the official screenshot options reference for current options and format details. If the required deliverable is a PDF, use Puppeteer’s separate page.pdf() API; PDF generation uses print CSS media by default, so its appearance can differ from a screenshot.
4. Handle receipt data carefully
A sample BHIM-UPI confirmation in NPCI’s BHIM-UPI guidelines shows details such as a reference number, recipient, source account, amount, and transaction date. Other apps may show different fields and use different layouts. Treat the captured image as sensitive: use an authorized transaction, store it only where needed, and restrict access and sharing to the intended purpose.
If you need a screenshot for a test report or documentation, prefer a permitted test environment and test data. Review the output before saving or sharing it to make sure it contains only the information needed.
5. Troubleshoot common capture failures
| Symptom | Likely cause | What to try |
|---|---|---|
| The screenshot shows a loading page, not a success receipt. | Navigation completed before the app rendered its confirmation state. | Wait for the app’s real success element with waitForSelector or an equivalent application-specific condition. |
TimeoutError from goto(). |
The page did not meet the selected navigation condition before the timeout, or the page remained active. | Check that the URL is reachable in the authorized environment. If network-idle is unsuitable for that page, use a different documented navigation condition and separately wait for the success element. Increase the timeout only when the page legitimately needs longer. |
TimeoutError from waitForSelector(). |
The selector is wrong, the page did not reach success, or the content is inside a frame or otherwise rendered differently. | Inspect the authorized page structure and identify its actual success marker. Do not assume another provider uses the example selector. |
| The receipt is cut off. | The content exceeds the viewport. | Use fullPage: true or capture the receipt element directly. |
| The saved image is empty or not where expected. | The output path is relative to the process working directory, or the capture failed before writing. | Check the command’s working directory, ensure the process can write there, and inspect the thrown error. Use an explicit output path if needed. |
| The screenshot differs from the browser view. | Viewport, device scale, timing, or page state differs. | Set the viewport explicitly before navigation, wait for the success state, and use consistent capture settings between runs. |
6. Performance, reliability, and cost
Browser launch and page rendering are the main work in a Puppeteer capture. For one-off captures, close the browser in a finally block so it is cleaned up even if navigation or screenshotting fails. For repeated captures in a controlled service, reusing a browser process can avoid repeated launches, but isolate page state between jobs and close pages when finished. Use timeouts and explicit state checks so a stalled or incomplete page does not silently become a misleading receipt image.
Choose the smallest capture area and output format that meet the use case; full-page images can include more content and produce larger files. Puppeteer itself is an open-source browser automation library, but running captures still consumes compute and storage in your environment. The actual cost depends on your infrastructure and workload; no benchmark or fixed cost is implied here.
7. Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. Its one-call API can capture an authorized confirmation page without setting up Puppeteer locally. See the ScreenshotNeo API documentation for request options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://your-authorized-test-page.example -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://your-authorized-test-page.example"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://your-authorized-test-page.example',
});
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('shot.webp', res);
For Node.js outside Bun, write the response bytes with your preferred filesystem method. Use only pages you are authorized to capture, and check the returned response and headers as described in the docs.
- Cookie banners, newsletter popups, and chat widgets are removed before the shot; those cleanup steps can be turned off.
- Bot checks, blank pages, timeouts, and failed loads are never billed; response headers report the page verdict and billing status.
- An MCP server lets AI agents use
take_screenshot,get_page_info, andcapture_pdf. - The Free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.
Sign up for ScreenshotNeo’s free plan to capture up to 1,000 screenshots a month without a card.
8. FAQ
Can this capture every UPI app’s confirmation screen?
No single selector or rendering condition works for every app. The target page and its permitted access method determine how to identify the confirmation state.
Does a successful navigation mean the payment succeeded?
No. Browser navigation state and application payment state are separate. A screenshot records what rendered; it does not verify a transaction.
Should I use a screenshot or a PDF for a receipt?
Use a screenshot when you need an image of the rendered page. Use Puppeteer’s PDF API when the required artifact is a PDF, accounting for its print-media rendering behavior.


