You can test Stripe Checkout with Playwright by creating a test-mode Checkout Session, opening its URL, completing payment with a Stripe test card, and verifying both the redirect and server-side session status. Keep the full payment test in a test environment. A production monitor should stop before it submits a charge.
Stripe's hosted and embedded Checkout options place payment UI on Stripe-controlled pages or inside an iframe. Playwright can work across those boundaries with frame locators, while Stripe test mode prevents the test from moving real money.
Key takeaways
- Create a new test-mode Checkout Session for each run.
- Use
frameLocator()only when the selected Stripe integration renders fields inside an iframe. - Verify payment on the server as well as in the browser.
- Include a 3D Secure scenario in CI, but keep a short happy-path check for routine monitoring.
- Never put a Stripe secret key or card data in the test source.
Which Stripe integration are you testing?
The correct Playwright flow depends on how Checkout appears in the application.
| Stripe UI | Browser behavior | Playwright approach |
|---|---|---|
| Hosted Checkout | Browser redirects to a Stripe URL | Interact with the new page normally |
| Embedded Checkout | Stripe inserts Checkout into an iframe | Scope locators with frameLocator() |
| Stripe Elements | Individual payment fields render in one or more iframes | Locate each relevant frame |
Stripe's Embedded Checkout documentation confirms that the payment form is inserted into an iframe. Do not assume that every Stripe integration uses the same frame title or number of frames.
How do you create a fresh Checkout Session?
Creating the session through the backend removes stale links and test-data collisions. This example uses the standard fetch API so the setup remains explicit:
async function createCheckoutSession() {
const response = await fetch('https://api.stripe.com/v1/checkout/sessions', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.STRIPE_TEST_SECRET_KEY}`,
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
mode: 'payment',
'line_items[0][price]': process.env.STRIPE_TEST_PRICE_ID,
'line_items[0][quantity]': '1',
success_url: `${process.env.TEST_APP_URL}/checkout/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.TEST_APP_URL}/checkout/cancel`,
}),
})
if (!response.ok) {
throw new Error(`Stripe session creation failed: ${response.status}`)
}
return response.json()
}Use a restricted test key where possible. The price must also belong to the same Stripe test-mode account.
Testing hosted Stripe Checkout
Hosted Checkout is usually the simpler browser path. The application sends the browser to a URL under checkout.stripe.com, so Playwright continues on the same page object after the redirect.
import { test, expect } from '@playwright/test'
test('hosted Stripe Checkout accepts a test payment', async ({ page }) => {
test.setTimeout(90_000)
const session = await createCheckoutSession()
await page.goto(session.url, { waitUntil: 'domcontentloaded' })
await expect(page).toHaveURL(/checkout\.stripe\.com/)
await page.getByLabel(/email/i).fill('checkout-test@example.com')
await page.getByLabel(/card number/i).fill('4242424242424242')
await page.getByLabel(/expiration/i).fill('1230')
await page.getByLabel(/security code|cvc/i).fill('123')
await page.getByLabel(/cardholder name/i).fill('Checkout Monitor')
await page.getByRole('button', { name: /pay/i }).click()
await expect(page).toHaveURL(/\/checkout\/success/, { timeout: 30_000 })
})The accessible labels can vary with locale, enabled payment methods, and Checkout configuration. Record your flow with Playwright Codegen and prefer roles or labels over Stripe's internal CSS classes.
Testing embedded Stripe Checkout
Embedded Checkout needs a frame locator. Start with a locator that identifies the Checkout iframe rather than its generated name:
const checkout = page.frameLocator('iframe[title*="Secure payment"]')
await checkout.getByLabel(/email/i).fill('checkout-test@example.com')
await checkout.getByLabel(/card number/i).fill('4242424242424242')
await checkout.getByLabel(/expiration/i).fill('1230')
await checkout.getByLabel(/security code|cvc/i).fill('123')
await checkout.getByRole('button', { name: /pay/i }).click()Inspect the live iframe title before adopting this selector. If the title is not stable, select the frame by a narrow src pattern and keep the assertion focused on visible labels inside it.
How do you test 3D Secure?
Stripe publishes test cards and payment methods for authentication scenarios. Use a test card that requires 3D Secure, submit the payment, and interact with the test challenge UI.
The challenge can appear in another nested frame:
const challenge = page
.frameLocator('iframe[name^="__privateStripeFrame"]')
.frameLocator('iframe#challengeFrame')
await challenge.getByRole('button', { name: /complete|authorize/i }).click()
await expect(page).toHaveURL(/\/checkout\/success/, { timeout: 30_000 })Frame structure can change, so treat generated names as a fallback. Run this scenario in CI rather than every minute. It is slower and tests a specialized branch rather than the default purchase path.
Why is the success page not enough?
A browser redirect proves that the customer reached your success URL. It does not prove that your backend processed the expected Stripe event.
After the redirect, retrieve the session through a backend endpoint or Stripe's API and check its status:
const response = await page.request.get(
`${process.env.TEST_APP_URL}/api/checkout/session/${session.id}`
)
expect(response.ok()).toBeTruthy()
const result = await response.json()
expect(result.payment_status).toBe('paid')If the application depends on checkout.session.completed, add a test-only endpoint that reports whether the webhook was received. Poll with a bounded timeout because the webhook and browser redirect can arrive in either order.
What belongs in production monitoring?
Stripe test cards cannot be used with live-mode Checkout Sessions. A production Browser Check should verify the real path until the final payment action:
- Open the product or pricing page.
- Choose the paid plan or product.
- Confirm that the application creates a live Checkout Session.
- Verify that hosted or embedded Checkout renders the correct product and amount.
- Stop before entering or submitting payment details.
This covers broken pricing buttons, session-creation errors, invalid redirect configuration, blocked Stripe scripts, and missing Checkout UI without creating a live charge.
How to use Hyperping for Stripe Checkout monitoring
1. Create a test Checkout Session
Use Stripe test-mode keys for the full payment script. Keep the secret key and test price ID in environment variables.
2. Open Checkout with Playwright
Navigate to the returned URL. Use normal locators for hosted Checkout or a frame locator for embedded Checkout.
3. Complete the test payment
Fill Stripe's documented test card details. Add a separate CI scenario for a 3D Secure challenge.
4. Verify the result
Assert the success URL and query your backend for the Checkout Session or processed webhook status.
5. Schedule safe production coverage
Create a separate Hyperping Browser Check that follows the live customer path to Checkout and stops before payment. Store URLs and credentials as environment variables.
Test payment behavior and monitor payment availability
Use test mode to prove that payment completion and webhooks work. Use production monitoring to prove that customers can reach a correctly configured Stripe Checkout page. Together they cover the transaction without charging a card on every scheduled run.
The network interception guide explains how to inspect failed session requests, and the Browser Check templates include a simpler checkout journey you can adapt.
FAQ
Can Playwright test Stripe Checkout? ▼
Yes. Playwright can drive hosted Stripe Checkout and locate fields inside embedded Stripe iframes. Run payment submissions with Stripe test-mode keys and test cards rather than live credentials.
How do I access Stripe card fields in Playwright? ▼
Use page.frameLocator() with a stable iframe title or source pattern, then locate the input inside that frame. Stripe's exact iframe structure depends on whether you use Checkout or Elements, so inspect your integration before choosing the locator.
Can I test Stripe 3D Secure with Playwright? ▼
Yes. Stripe supplies test payment methods that trigger a 3D Secure challenge in test mode. The test should complete the challenge and then verify the final payment status or success page.
Should I run Stripe test payments against production? ▼
No. Stripe test cards work only with test-mode resources. Run the complete payment test against a test deployment, and use a production browser check that stops before submitting payment.



