Skip to content

Get started

Most read

The payment pages

PostShip reads your site, finds the pages that lose money when they break — pricing, sign-up, log-in, checkout —, checks each one, and you choose.

Updated on 25 September 2026

On this page

PostShip finds on your site the URLs that matter most to your revenue and adds them, with checks suited to each: the price still shows on the pricing page, the login form is still there, Stripe.js loads on the checkout. Those are the 3 disappearances that cost money without visibly breaking anything — the page answers 200, and nobody can pay anymore.

What PostShip checks

The pack creates ordinary HTTP targets, with a 200 status expected and, in addition, assertions specific to each page:

PageWhat confirms itAssertion set
HomeThe project's origin, alwaysNo particular assertion
PricingAt least one price shown in the pageThe price (the text you indicate, € by default) appears in the page
Sign-upA form with an email or password field, and a sign-up heading (“create an account”, “sign up”…)An email or password field
Log-inA form with an email or password field, and a log-in heading (“log in”, “sign in”…)An email or password field
CheckoutA payment script loaded (Stripe, PayPal…), or a real cart: subtotal, amount and formThe page loads js.stripe.com, if Stripe is what was seen

How pages are found

PostShip doesn't guess an address: it reads your site. The home page's links first — menu, footer —, with their text, in French and English; then the sitemap; and, only for a role left without a candidate, a few usual addresses (/pricing, /login, /checkout…). A pricing page called /our-plans or a log-in on app.your-site.com/login is therefore found.

Then each candidate is opened and only confirmed if its content matches the role (table above). An address that redirects to the log-in page — a /checkout reserved to logged-in customers — is not a checkout page. Product presentation, article or documentation pages (/product/…, /blog/…, /docs/…) are set aside: they talk about payment without taking any.

Each page found is marked verified (ticked by default) or likely (unticked: the link was clear, but the page showed nothing — a log-in rendered in JavaScript, for instance). Nothing is added without your consent.

Assertions are evaluated on every run, on the HTML as served, without a browser. A pricing page whose price is injected by JavaScript therefore has no price for PostShip: in that case, use a server-rendered element as the text.

When payment happens after an account is created

Many sites don't take payment on a public page: you create an account, then the payment panel opens. There's then no /checkout to request. PostShip recognises the provider another way, and the “Which pages?” screen shows it under the pages found, with what gives it away:

  • the site's security header (Content-Security-Policy), which must allow the provider — js.stripe.com, api.stripe.com;
  • a script loaded by a page, or a link to a hosted payment page (buy.stripe.com, checkout.paddle.com…);
  • the site's own code (the provider's library, a pk_live_… publishable key).

Providers recognised: Stripe, PayPal, Paddle, Lemon Squeezy, Mollie, Braintree, Adyen, Chargebee.

Two switches follow:

  • Monitor payment adds a “Payment” check: on every run, the site still loads and allows the provider, and the provider responds. A CSP tightened in the last deploy that drops js.stripe.com, and nobody can pay anymore: that's what it catches. It doesn't appear on the public status page.
  • Pricing → sign-up journey adds a payment journey: the price on pricing, then the sign-up form.

To go as far as real payments, connect your Stripe account read-only.

The price canary: T+0 against T+8

On the pricing target, PostShip also records the visible amounts — “29 €”, “$49”, “79 EUR” — on every deploy run: at T+0, then on the T+2 and T+8 re-checks. A price present at T+0 and gone on the next run becomes a mutation alert: “The 29 € price has disappeared (T+8)”. That's what a CDN cache, ISR or a stale page served after the ship produce, and what the HTTP status never sees. A price that appears doesn't alert; a price that changes amount is reported by the old one. 20 amounts per page at most, only what carries a currency. Like the other deploy-quality alerts, Pro and above.

Set it up

Project → URLs → “Monitor…” → “The payment pages”:

  1. Which pages? PostShip reads the site for a few seconds, then shows what it found: each page's role, its address, what confirmed it, and a verified or likely badge. Tick or untick.
  2. The price to find: € by default. Put the real displayed price (“€49”, “29.90”) so the alert goes out the day it's that price that disappears, not only the symbol.
  3. “Add these pages”.

If your host puts an anti-bot challenge in front of PostShip, the screen says so: see Allow PostShip in your firewall.

The feedback message says what happened: “3 pages that sell added.”; “No URL to add (already present or no option ticked).”; or, if the plan's URL quota is reached along the way, “2 pages of 3 added — quota reached, go Pro to cover the others.”.

A page already monitored over HTTP isn't added twice. A page monitored by a site check (indexability is set on the home page) doesn't count as already monitored: the pack still adds the home page's HTTP check.

Payment

On the Overview, the “Payment” card answers “can people still pay?” with the criteria that are green — “3 of 4 green” —, all read from what the payment pages and the Security check already collect. It is no longer a score out of 100: PostShip's only two scores are the Ship Score, where payment is the heaviest category, and the security score.

CriterionWhat checks it
Login form presentThe payment pages' login target (login_form)
Stripe.js loadedThe checkout target (stripe_js), or the “Payment” check when payment happens after an account is created
Price visibleThe pricing target (price_token)
No mixed contentThe Security check (contenu_mixte)

A project that still watches a Stripe return page keeps a fifth criterion, “success_url reachable”. A criterion you don't check says so, with the button that sets it up; one that doesn't apply to you can be marked Not applicable and leaves the count. The count is on every plan; the per-criterion detail, with the failing URL, is open from Pro. The daily summary on Slack or Discord carries the same line: “Payment: 3 of 4 criteria green.”

What triggers an alert

In addition to the usual HTTP gaps (status, assets, content), 3 codes specific to the assertions:

CodeScreen sentenceAlert sentence
price_tokenThe price no longer appears on this page.The price no longer appears on https://acme.fr/pricing.
login_formThe login form cannot be found on this page.The login form cannot be found on https://acme.fr/login.
stripe_jsStripe.js is no longer loaded on this page.Stripe.js is no longer loaded on https://acme.fr/checkout.

A failing payment page costs 40 points on the Ship Score — it's the heaviest category, and 2 failing pages cost twice 40. On the Deployments page, the deploy diff also shows the third-party domain that disappeared (“this ship removed js.stripe.com from /checkout”).

Limits and plans

Finding your payment pages is locked by no plan: the button is present everywhere. What bounds it is the URL quota, counted over all your projects:

DiscoverProPro Plus
Monitored URLs525100
Full pack (home, pricing, login, checkout)Partial, 3 of 4YesYes

To check not 4 isolated pages but the chain linking them — pricing, then checkout, then thank you, in order — see the payment journey (Pro and Pro Plus).

Troubleshooting

Why wasn't the pricing page added?

Either no link on the site looked like a pricing page, or the page found showed no price in its HTML (price injected by JavaScript). Add the page by hand (“Monitor…” → “A page”) and, in “Must contain”, put a server-rendered text: you get the same check.

Why is login failing when the form is there?

The form is probably rendered by JavaScript, without an email or password field in the served HTML, and without a link or button whose text contains one of the recognised words. Add a server-rendered “Sign in” link, or monitor the page over plain HTTP with an expected content.

The price changed, how do I update the assertion?

Delete the pricing target and run the pack again with the new text, or add the page as an HTTP target with “Must contain” — that field, for its part, can be edited on the URL's detail.

Payment journey

Available on the Pro and Pro Plus plans.

The chain that leads to payment, checked in order, stopped at the first broken step.

The payment pages are monitored one by one, 4 isolated pages. A payment journey is a chain: pricing, then checkout, then thank you. A chain can be green page by page and broken anyway — the pricing page's button points to a /checkout that renders 404, the 3 individual checks stay green, and nobody pays anymore. The payment journey requests the pages one after the other and tells you which one breaks.

The payment journey, replayed in order, stopped at the first broken step.

What PostShip checks

A journey is a list of 2 to 6 URLs of your site, in order. On every run:

  1. PostShip requests the first step exactly like an HTTP target: 200 status expected (redirects are followed, so a /checkout that redirects cleanly passes), assets checked, and the payment pages' assertions if you set any.
  2. If it passes, it requests the next one. Otherwise it stops: the following steps are reported “not reached”, never green. We didn't check them, and a green we didn't earn would be a lie in a product whose subject is precisely that.
  3. The journey's verdict is the first broken step's; its response time is the sum of the steps travelled.
  4. When the whole chain passes, each step is reopened in a real browser (Chromium): a page that answers 200 then renders blank, crashes (“Application error”), or whose script or stylesheet from your own site answers an error breaks the step. JavaScript errors alone are noted as a warning, with no alert: most come from third-party scripts.

2 assertions cover what really breaks:

  • the price text to find on the first step — it's the page that announces it, and that's where its disappearance shows first;
  • the step number that must load Stripe.js (js.stripe.com), that is, the one that takes the payment.

Where to read it

Project → Monitoring → Journeys: each journey with its last pass step by step (the status, what is missing, each step's time to first byte as a bar), success over 30 days, the median total time and the curve of the last passes. “New journey” leads to the form below.

Set it up

Project → Monitoring → Journeys → “New journey” → “Addresses”:

  1. The steps, in order: one URL per line, 2 to 6, all on the project's origin. The “Find my journey” button reads your home page and suggests a list — it's a guess, re-read it before validating.
  2. What must be readable there (optional): the price text on the first step — “€49”, “29.90”, what the page displays — and the step that loads Stripe.js (0 for none, otherwise 1 to 6).
  3. “Monitor this journey”.

Possible refusals: “A journey needs at least 2 steps, one per line.”, “6 steps at most.”, “Step 2: …” when a line isn't an https URL of your domain, or the quota message if the plan is full.

What triggers an alert

The journey fails when a step fails, for any HTTP reason (status, asset, price, Stripe.js, timeout) or a rendering one (blank page, crash, a site script failing). The sentence names the step:

SituationSentence
Everything passesJourney complete: 3 steps checked.
The second breaksStep 2 of 4 broken (https://acme.fr/checkout) — 2 following steps not reached.

The broken step's gap is an HTTP target's — Stripe.js is no longer loaded on https://acme.fr/checkout., The price no longer appears on https://acme.fr/pricing., https://acme.fr/checkout answers 404..

The run's fingerprint carries the step number: “2 of 4 is broken” and “3 of 4 is broken” are 2 different outages, and the second isn't swallowed by the first's deduplication. A failing journey costs 40 points on the Ship Score, like a payment page — it's payment by definition.

Limits and plans

DiscoverProPro Plus
Journeys per project0210
Steps per journey—2 to 62 to 6
Place in the URL quota—1 URL per journey1 URL per journey

A journey counts as a single URL of your quota, not 4: it's a single line, whatever its length. On Discover, the “A checkout journey” choice leads to the plans, and the action answers “Money journeys are available from the Pro plan.”.

Troubleshooting

Why is the “thank you” page failing when it's only reachable after payment?

Because PostShip requests it without having paid, and it probably answers 302 to the home page, or 200 with an error message. Only put in the journey pages that answer 200 to an anonymous visitor; for the Stripe return page, see Stripe.

Why does “Find my funnel” suggest nothing?

It reads the home page's links and looks for addresses that look like pricing, a checkout, a sign-up. A site whose navigation is rendered by JavaScript gives it nothing to read. Type the URLs yourself.

How do I edit a journey?

Delete it from Payment journeys (⋯ menu) and recreate it with “New journey”: the steps aren't edited one by one. A journey takes no URL slot; only the per-project journey limit counts.

Canary form

Check that your contact form's endpoint still accepts a submission, without polluting your CRM.

/api/contact and /api/lead are the first victims of a refactor: a route file moved, an export renamed, a middleware that now swallows POSTs. The page carrying the form still answers 200, nothing else notices, and the leads simply stop arriving. The canary form sends visibly fake data to the endpoint and reads the response code.

What PostShip checks

On every run, PostShip does a POST on the target's URL with:

  • the body _postship=canary&email=canary@invalid, as application/x-www-form-urlencoded;
  • the header X-PostShip-Canary: 1, so you can exclude these submissions from your statistics and your rate limiting without guessing from the User-Agent;
  • the same 12-second timeout and the same 5 redirects at most as the other checks. The body and the method are re-sent on each hop; each hop is checked against private addresses.

The address canary@invalid is deliberately impossible to route: the .invalid domain is reserved precisely so that nothing ever tries to deliver to it.

What PostShip does not check: that a lead was recorded. It would take a real address and a mailbox to read, and it would put dummy data in your CRM. The canary reads the status, nothing else.

Set it up

Project → URLs → “Monitor…” → “A form”. The address is the endpoint's (https://acme.fr/api/contact), not the page that displays the form. Then the “How does it answer a dummy submission?” screen:

ChoiceMeaning
It refuses dummy data (default)The endpoint validates its inputs and answers 4xx to an invalid email. A 2xx is also accepted: many forms answer 200 whatever you send them, and failing them would be noise.
It accepts the submission and answers OKThe endpoint must answer 2xx. Any other status is a failure.

The first choice is the right one for an ordinary contact form. The second one is for an endpoint that is a relay that must always accept (an inbound webhook, a form without validation).

What triggers an alert

CodeSentenceWhen
form_5xxThe form submission returns a server error.Status 500 or more, whatever the setting.
form_goneThe form endpoint disappeared or no longer accepts submissions (404 / 405).404 or 405 — the exact shape a moved route file takes.
form_not_acceptedThe endpoint was supposed to accept the submission, it refused it.“accepts” setting, status outside 2xx.
form_not_rejectedThe form endpoint doesn't return a usable response.“rejects” setting, 3xx response with no followable destination.

With the “rejects” setting, a 2xx and a 4xx are both green. The alert follows the project's confirmation and goes out once per outage; “Recovered” follows the return to green. A failing form costs 10 points on the Ship Score.

Limits and plans

Available on all plans; a canary target counts as one URL of the quota (Discover 5, Pro 25, Pro Plus 100). The frequency is the cycle's: 5 minutes on Discover, 2 on Pro, 1 on Pro Plus, plus every deploy. The Pro Plus 30-second probe only watches pages: it never submits a form.

Troubleshooting

Why is the endpoint failing with a 403?

With the “accepts” setting, anything that isn't 2xx fails — and a 403 often comes from an anti-bot protection (Turnstile, reCAPTCHA) or an origin check (Origin, CSRF token). Switch to the “rejects” setting: the 403 becomes a healthy response, and a 404, 405 or 5xx remains a failure.

Why do I receive an email on every run?

Your endpoint processes the submission before validating the address. Reject canary@invalid (or anything carrying X-PostShip-Canary) before the notification. It's also a good guard against bot submissions.

Can the canary test a GET or JSON form?

No: it's always a form-encoded POST. For a JSON endpoint, monitor it as an API contract if it's a GET, or as an HTTP target with the matching expected status.

Stripe

The Payment check is on every plan; reading your real payments is on Pro and Pro Plus.

Know that your customers can still pay, and hear about it when their payments start failing.

Since 26 September 2026, everything about Stripe comes down to two things: the Payment check, set up when PostShip finds a payment provider on your site, and your read-only Stripe account. The former return-page check (success_url) is no longer offered; existing ones keep running.

What PostShip checks

On every pass, the Payment check makes sure the provider is loaded on the page where people pay, allowed by the site's security header, and reachable. See When payment happens after an account is created.

What it does not do: it creates no payment session, replays no webhook and never touches your account. To watch your webhook route (/api/stripe/webhook), add it as an HTTP target with the status your server returns to an unsigned GET — often 405 or 400.

Read-only account

To be alerted when your real payments start failing, connect your Stripe account in Settings → Integrations → Stripe account, read-only:

  1. In Stripe: Developers → API keys → Create restricted key.
  2. Give it Read on PaymentIntents, and None everywhere else.
  3. Paste it into PostShip: it starts with rk_live_.

PostShip checks the key by reading one payment, then the project's Payment check looks at the last hour's payments on every pass. It alerts beyond three failed payments, and no fewer failures than successes: 3 of 5 payments failed in the last hour. A key revoked in Stripe says so too: The Stripe key given to PostShip was revoked: payments are no longer read.

A full secret key (sk_…) is refused: it could refund and charge, and PostShip doesn't need that. Disconnect erases the key from PostShip; revoke it in Stripe too.

Limits and plans

DiscoverProPro Plus
Payment checkYesYesYes
Read-only Stripe account—YesYes

Troubleshooting

How do I watch the page shown after payment?

As an ordinary HTTP target, if it answers 200 to a visitor with no Stripe session. If your page does redirect('/') or returns 400 when session_id is missing, make it tolerant — a generic message, 200 in every case: a customer who refreshes or comes back through history lands on the same thing PostShip sees.

Can I watch the cancel page too?

Yes, as an ordinary HTTP target (status 200): the cancel_url is a public page like any other.