> ## Documentation Index
> Fetch the complete documentation index at: https://www.checklyhq.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Monitor a checkout flow

> Cover browse, search, add to cart, and checkout on a shop with a Playwright Check Suite that asserts the order confirmation, uses safe test data, and runs against a per-environment base URL.

By the end of this guide, four Playwright tests cover the path from the shop's home page to a confirmed order, run every ten minutes from two regions, and fail if the confirmation text is missing.

![The Shop checkout flow Playwright Check Suite in Checkly, passing, running every 10 minutes from N. Virginia and Ireland](https://mintcdn.com/checkly-422f444a/TndAnv1H8AWsx1K8/images/guides/checkout-flow/outcome-check-detail.png?fit=max\&auto=format\&n=TndAnv1H8AWsx1K8\&q=85\&s=af2698e0b53cfd1ad6f4da8ec3d66132)

The [sample project](https://github.com/checkly/docs/tree/main/samples/guides/checkout-flow) this guide is built on runs against the [Danube demo shop](https://danube-web.shop) and includes the Terraform variant.

> **Let your agent do it**
>
> To run this guide from your terminal or your coding agent, run `npx checkly init` in your project first. It installs the Checkly CLI and [Checkly Skills](https://www.checklyhq.com/docs/ai/skills.md) for your agent. Then paste the prompt below into Claude Code, Cursor, Codex, or any agent that supports skills. It builds the same setup as this guide, proves it with `npx checkly test --record`, and stops for your confirmation before `npx checkly deploy`.
>
> ```txt Prompt theme={null}
> Monitor the checkout flow of this shop with Checkly.
>
> Goal: a Playwright Check Suite with one test per step, browse, search, add to cart, and checkout, running every 10 minutes from us-east-1 and eu-west-1.
>
> Success criteria:
> 1. Four spec files under `tests/`, one per step. The checkout test asserts the order confirmation text with `expect(page.getByText(...)).toBeVisible()`, not just that a page loaded.
> 2. All order data comes from one shared `tests/shopper.ts` with a marker in a free-text field that our order pipeline can filter out. Never create accounts or use real customer data.
> 3. `playwright.config.ts` reads `baseURL` from `SHOP_URL` and falls back to the local dev server. Set `screenshot: 'only-on-failure'` and `trace: 'retain-on-failure'`.
> 4. `checkly.config.ts` defines one entry in `playwrightChecks` that sets `SHOP_URL` to production in `environmentVariables`.
> 5. Run `npx checkly test --record` and show me the session link.
> 6. Stop. Show me `npx checkly deploy --preview` and wait for my confirmation before `npx checkly deploy`.
>
> Explain each file you changed and why.
> ```
>
> Every step below is what the agent just did, in the open, so you can read the result or do it by hand.

## Step 1: Write one test per step of the flow

A monitor that only runs the checkout tells you it broke, not where. Split the flow into the steps a shopper takes, so a failure names the step.

```ts tests/browse.spec.ts theme={null}
import { test, expect } from '@playwright/test'

test('browse to a product', async ({ page }) => {
  await page.goto('/')

  await expect(page.locator('.preview').first()).toBeVisible()
  await page.locator('.preview:nth-child(1) > .preview-author').click()

  await expect(page.getByRole('button', { name: 'Add to cart' })).toBeVisible()
})
```

```ts tests/search.spec.ts theme={null}
import { test, expect } from '@playwright/test'

test('search finds a book', async ({ page }) => {
  await page.goto('/')

  await page.locator('input[name="searchbar"]').fill('haben')
  await page.getByRole('button', { name: 'Search' }).click()

  await expect(page.locator('.preview').first()).toContainText('Haben oder haben')
})
```

```ts tests/cart.spec.ts theme={null}
import { test, expect } from '@playwright/test'

test('add to cart', async ({ page }) => {
  await page.goto('/')

  await page.locator('.preview:nth-child(1) > .preview-author').click()
  const title = await page.locator('.detail-wrapper h2').innerText()
  await page.getByRole('button', { name: 'Add to cart' }).click()
  await page.locator('#logo').click()

  await page.locator('#cart').click()
  await expect(page.locator('.cart')).toContainText(title)
})
```

The checkout test places an order from one shared identity, so every monitoring order looks the same to your backend.

```ts tests/shopper.ts theme={null}
// One synthetic identity for every monitoring order. The company field
// carries a marker your order pipeline can filter out of real sales.
export const shopper = {
  name: 'Checkly',
  surname: 'Monitor',
  address: 'Charlottenstr. 57',
  zipcode: '10117',
  city: 'Berlin',
  company: 'synthetic-monitoring',
}
```

```ts tests/checkout.spec.ts theme={null}
import { test, expect } from '@playwright/test'
import { shopper } from './shopper'

test('checkout completes', async ({ page }) => {
  await page.goto('/')

  await page.locator('.preview:nth-child(1) > .preview-author').click()
  await page.getByRole('button', { name: 'Add to cart' }).click()
  await page.locator('#logo').click()

  await page.locator('#cart').click()
  await page.getByRole('button', { name: 'Checkout' }).click()

  await page.getByPlaceholder('Name', { exact: true }).fill(shopper.name)
  await page.getByPlaceholder('Surname', { exact: true }).fill(shopper.surname)
  await page.getByPlaceholder('Address').fill(shopper.address)
  await page.getByPlaceholder('Zipcode').fill(shopper.zipcode)
  await page.getByPlaceholder('City').fill(shopper.city)
  await page.getByPlaceholder('Company (optional)').fill(shopper.company)
  await page.getByLabel('as soon as possible').check()
  await page.getByRole('button', { name: 'Buy' }).click()

  // A page that loads is not a page that worked. Assert the confirmation text.
  await expect(page.getByText('All good, order is on the way. Thank you!!')).toBeVisible()
})
```

Every ten minutes, from two regions, this places an order. Decide up front how those orders stay out of your numbers: a marker in a free-text field like the company above, a test SKU, a test payment method, or a flag that skips fulfilment for a known customer. Never sign up accounts from a monitor, and never use a real customer's data.

> [!TIP]
>
> On the demo shop, the "Company (optional)" field has to be filled or the Buy button does nothing. Run your own flow by hand once and note every required field.

## Step 2: Assert the confirmation text, not the page

The last line of the checkout test is the whole point of the monitor. A rendered page proves nothing about the order. The confirmation text does. `getByText` matches anywhere on the page, case-insensitive, substring included, which suits anything a customer has to see: an order number, a "thank you" message, a best seller on the home page.

```ts tests/checkout.spec.ts theme={null}
  await expect(page.getByText('All good, order is on the way. Thank you!!')).toBeVisible()
```

When the wording varies, pass a regular expression such as `/order (is on the way|confirmed)/i`. When the text can appear more than once, add `.first()`.

## Step 3: Point the tests at each environment

Keep the shop's address out of the tests. The Playwright config reads it from `SHOP_URL` and falls back to your dev server, so the same specs run locally, against staging in CI, and against production on Checkly.

```ts playwright.config.ts theme={null}
import { defineConfig, devices } from '@playwright/test'

export default defineConfig({
  testDir: './tests',
  retries: process.env.CHECKLY === '1' ? 2 : 0,
  use: {
    // SHOP_URL is set per environment: on the Checkly check for production,
    // in your shell or CI for staging. Local runs fall back to the dev server.
    baseURL: process.env.SHOP_URL ?? 'http://localhost:3000',
    trace: 'retain-on-failure',
    screenshot: 'only-on-failure',
    ...devices['Desktop Chrome'],
  },
})
```

`screenshot: 'only-on-failure'` is what puts a screenshot on the failed result in Checkly. Checkly records a trace on every run regardless of the `trace` setting, and sets `CHECKLY=1` on every run, which is what turns on the retries here. To run locally against the demo shop, prefix the command with `SHOP_URL=https://danube-web.shop`.

## Step 4: Define the monitor

Install the CLI if this repository does not have it yet, then describe the monitor next to your Playwright config. A Playwright Check Suite runs the whole suite with your config and shows one test case per spec. A Browser Check runs a single spec file on its own. Pick one.

```bash Terminal theme={null}
npm install --save-dev checkly
```

```ts checkly.config.ts (Playwright Check Suite) theme={null}
import { defineConfig } from 'checkly'
import { Frequency } from 'checkly/constructs'

export default defineConfig({
  projectName: 'Docs guide: Monitor a checkout flow',
  logicalId: 'docs-guide-checkout-flow',
  checks: {
    playwrightConfigPath: './playwright.config.ts',
    playwrightChecks: [
      {
        name: 'Shop checkout flow',
        logicalId: 'shop-checkout-flow',
        frequency: Frequency.EVERY_10M,
        locations: ['us-east-1', 'eu-west-1'],
        // The production base URL lives on the check, not in the tests.
        // Point a second check at staging by changing only this value.
        environmentVariables: [
          { key: 'SHOP_URL', value: 'https://danube-web.shop' },
        ],
      },
    ],
  },
  cli: {
    runLocation: 'eu-west-1',
  },
})
```

```ts checks/checkout.check.ts (Browser Check) theme={null}
import { BrowserCheck, Frequency } from 'checkly/constructs'
import * as path from 'path'

// The single-file alternative: one spec file becomes one Browser Check.
// Browser Checks run the file on its own, without playwright.config.ts,
// so the spec reads SHOP_URL itself instead of relying on baseURL.
new BrowserCheck('shop-checkout-browser-check', {
  name: 'Shop checkout (Browser Check)',
  frequency: Frequency.EVERY_10M,
  locations: ['us-east-1', 'eu-west-1'],
  environmentVariables: [
    { key: 'SHOP_URL', value: 'https://danube-web.shop' },
  ],
  code: {
    entrypoint: path.join(__dirname, 'checkout.spec.ts'),
  },
})
```

The base URL is a check-level environment variable in both paths. A staging monitor is the same entry with a different `SHOP_URL`, and the tests never change. The Browser Check path needs a copy of the checkout spec that opens `process.env.SHOP_URL` itself, kept in `checks/checkout.spec.ts` in the sample.

## Step 5: Run it on Checkly, then deploy

`npx checkly test` bundles the project, runs it on Checkly's infrastructure, and streams the result back. `--record` keeps the run so you can open it in the app.

```bash Terminal theme={null}
npx checkly test --record
```

```text Terminal theme={null}
Parsing your project... ✅
Validating project resources... ✅
Bundling project resources... ✅
Uploading Playwright tests... ✅

Test session ID: 01a0d48f-d7df-70dd-852d-ee314453c954
Open session: https://app.checklyhq.com/accounts/<account-id>/test-sessions/01a0d48f-d7df-70dd-852d-ee314453c954

Running 2 checks in eu-west-1.

checks/checkout.check.ts
  ✔ Shop checkout (Browser Check) (4s)
playwright.config.ts
  ✔ Shop checkout flow (30s)

2 passed, 2 total
```

Both variants ran because the sample contains both. The suite result lists the four test cases, each with a trace and video.

![A passing Shop checkout flow result in Checkly listing the browse, add to cart, checkout, and search test cases with trace and video attachments](https://mintcdn.com/checkly-422f444a/TndAnv1H8AWsx1K8/images/guides/checkout-flow/result-passing.png?fit=max\&auto=format\&n=TndAnv1H8AWsx1K8\&q=85\&s=4915687e19dc3c03039da92c460ea92e)

When the run is green, deploy.

```bash Terminal theme={null}
npx checkly deploy
```

```text Terminal theme={null}
Deploying project... ✅

Successfully deployed project "Docs guide: Monitor a checkout flow" to account "Checkly Marketing".
```

The suite now runs every ten minutes from both locations, and every later deploy updates it in place.

## Same thing in Terraform

If your team manages infrastructure with Terraform, the checkout flow is one `checkly_check` resource: a Browser Check with the self-contained spec and the same `SHOP_URL` variable.

```terraform terraform/main.tf theme={null}
# Provider block as in the Terraform overview, then:
resource "checkly_check" "shop_checkout" {
  name                      = "Shop checkout (Terraform)"
  type                      = "BROWSER"
  activated                 = true
  frequency                 = 10
  use_global_alert_settings = true
  run_parallel              = true

  locations = [
    "us-east-1",
    "eu-west-1"
  ]

  retry_strategy {
    type = "LINEAR"
  }

  environment_variable {
    key   = "SHOP_URL"
    value = "https://danube-web.shop"
  }

  script = file("${path.module}/../checks/checkout.spec.ts")
}
```

Set `TF_VAR_checkly_api_key` and `TF_VAR_checkly_account_id`, then run `terraform init` and `terraform plan`.

```text Terminal theme={null}
Terraform will perform the following actions:

  # checkly_check.shop_checkout will be created
  + resource "checkly_check" "shop_checkout" {
      + frequency                 = 10
      + locations                 = [
          + "eu-west-1",
          + "us-east-1",
        ]
      + name                      = "Shop checkout (Terraform)"
      + type                      = "BROWSER"

      + environment_variable {
          + key    = "SHOP_URL"
          + value  = "https://danube-web.shop"
        }
    }

Plan: 1 to add, 0 to change, 0 to destroy.
```

`terraform apply` creates the check. The provider also has a `checkly_playwright_check_suite` resource for the four-test split from step 1; see the [Terraform provider docs](https://www.checklyhq.com/docs/integrations/iac/terraform/overview.md).

## Verify it works

Break the checkout on purpose before production does. Rename the `Buy` button in `tests/checkout.spec.ts` to `Purchase` and run the suite again, without deploying.

```bash Terminal theme={null}
npx checkly test --record --grep "Shop checkout flow"
```

```text Terminal theme={null}
Running 1 checks in eu-west-1.

playwright.config.ts
  ✖ Shop checkout flow (105s)

    14:03:58 INFO  Running 4 tests using 4 workers
    14:04:01 INFO  1 ✓ browse.spec.ts > browse to a product
    14:04:02 INFO  2 ✓ cart.spec.ts > add to cart
    14:04:02 INFO  3 ✓ search.spec.ts > search finds a book
    14:04:30 ERROR Error: locator.click: Test timeout of 30000ms exceeded.
    Call log:
      - waiting for getByRole('button', { name: 'Purchase' })

        at tests/checkout.spec.ts:21:56
    14:05:35 INFO  4 ✘ checkout.spec.ts > checkout completes

    View result: https://chkly.link/l/3GNVN

1 failed, 1 total
```

Browse, search, and add to cart still passed, so the failure is scoped to checkout before anyone opens a trace. The result page shows the failing line, the error, a screenshot at the moment of failure, and the trace.

![The failed checkout test case in Checkly showing three failed attempts, the Playwright error with the Purchase button line highlighted, and the View Trace and Screenshots buttons](https://mintcdn.com/checkly-422f444a/TndAnv1H8AWsx1K8/images/guides/checkout-flow/result-failing.png?fit=max\&auto=format\&n=TndAnv1H8AWsx1K8\&q=85\&s=2c8f460b2e8978170a101127aa113b70)

Put `Buy` back. The deployed monitor never saw the broken version, because only `npx checkly deploy` changes the monitor.

> [!NOTE]
>
> `--grep` matches the check's `name`, not its `logicalId`.

## Next

[Monitor a shop with Terraform](https://www.checklyhq.com/docs/guides/monitoring-ecommerce-apps-using-terraform.md): if your team manages infrastructure with Terraform, declare the shop's checks, group, and alert channel there instead.

## Reference

* [Playwright Check Suites overview](https://www.checklyhq.com/docs/detect/synthetic-monitoring/playwright-checks/overview.md)
* [Playwright Check Suite configuration](https://www.checklyhq.com/docs/detect/synthetic-monitoring/playwright-checks/configuration.md)
* [Playwright Check Suite environment variables](https://www.checklyhq.com/docs/detect/synthetic-monitoring/playwright-checks/environment-variables.md)
* [`PlaywrightCheck` construct](https://www.checklyhq.com/docs/constructs/playwright-check)
* [`BrowserCheck` construct](https://www.checklyhq.com/docs/constructs/browser-check)
* [Environment variables and secrets](https://www.checklyhq.com/docs/platform/variables.md)
* [Terraform provider](https://www.checklyhq.com/docs/integrations/iac/terraform/overview.md)
* [`npx checkly test`](https://www.checklyhq.com/docs/cli/checkly-test) and [`npx checkly deploy`](https://www.checklyhq.com/docs/cli/checkly-deploy)
* [Checkly Skills](https://www.checklyhq.com/docs/ai/skills.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.