Skip to content
KrokoPay
Back to the blog

Product

Anatomy of a checkout that converts

Few fields, a total that never moves, one clear method choice, declines explained in plain words and a state for every outcome. A walk through what a payment page must get right, with our own checkout as the example.

Written by KrokoPay teamPublished 7 min read

A checkout is the only page on your site where every visitor has already decided to pay. It does not need to persuade anyone. Its job is to stay out of the way: answer the buyer's questions before they are asked, accept the payment, and say clearly what happened.

There are no conversion numbers here: we have not measured any we would trust. What follows is reasoning, plus the decisions we made while building KrokoPay's own hosted checkout. Treat it as a checklist to argue with.

Show the total before anything else

The first three questions a buyer has are how much, for what and to whom. Put the answers at the top: the seller's name, a one-line description of the item and the amount, written in the buyer's notation. A Brazilian reader expects R$ 1.234,50; an English reader is used to R$1,234.50. The currency stays the same; only the way it is written changes.

Then keep that total still. If the button and the header disagree, the buyer stops to work out why. When something legitimately changes the total, say so next to the amount, not in a footnote.

In our checkout the order summary shows the seller, the item and the total, and the pay button repeats the same amount: Pay R$189.00, not just Pay. On narrow screens the order details fold away behind a toggle, but the item and the total stay visible.

Ask only for what the payment needs

Every field is a question, and every question is a chance to hesitate. Before adding one, write down who will read the answer and when. If nobody can name a use, the field goes.

FieldWhy it is thereWhen to ask
EmailReceipt and supportAlways, as one field
Full nameReceipt and reconciliationAlways
Card number, expiry, security codeThe card payment itselfOnly after the buyer picks card
Tax ID (CPF or CNPJ)Some invoices and business flows in BrazilOnly if your business needs it, clearly marked
PhoneRarely needed to payOnly if someone will actually call

The fields that remain should do work for the buyer. Use the right keyboard (inputmode="numeric" for card digits), the right autofill hints (autocomplete="cc-number", "email"), and format as people type. Detect the card brand instead of asking for it, and check the number's checksum before submitting, so a typo is caught on the page instead of coming back as a decline.

Information: Card data is not yours to keep

In our checkout the card number and security code live only on the page. They are never written to storage; only the brand and the last four digits are kept for the receipt.

Make choosing a method a single decision

Pix and card are not two skins of the same thing. With Pix the buyer leaves for their bank app, approves there and comes back; with card they fill a form and get an answer on the spot. Presenting them as identical tabs hides the one thing the buyer needs to know: what happens after the click.

  • Pix: "Pay with the QR code or the copy-and-paste code in your bank app." The page then shows the code, a countdown to its expiry and a waiting state that updates on its own.
  • Card: "Pay with a credit card." The form appears in place, and the answer is shown on the same page.

One line under each option is enough. If you only accept one method, do not show a choice at all: our checkout renders the single method directly when a payment link is configured that way. A question with one possible answer is noise.

Explain declines in words people can act on

"Payment failed" may be the most expensive sentence on the page. It leaves the buyer guessing whether they were charged. Most declines carry a reason, and most reasons have an obvious next step. Say both, without blaming anyone and without leaking internal error text.

failure_codeWhat the buyer readsNext step offered
card_declinedThe issuer declined without giving a reasonAnother card, or Pix
insufficient_fundsNot enough available limit for this amountAnother card, or Pix
expired_cardThe issuer says the card has expiredCheck the date, or another card
incorrect_cvcThe security code doesn't matchCheck the 3 or 4 digits and retry
processing_errorIt couldn't be processed this timeTry again in a moment

Every decline screen in our checkout also says, in its own line, that nothing was charged. That sentence answers the question the buyer is too anxious to ask. On your side the attempt arrives as a charge.failed event with the same failure_code, so support can see exactly what the buyer saw.

Success: Try every decline yourself

The test environment has a fictional card for each outcome: 4000 0000 0000 0002 is declined, 4000 0000 0000 9995 has insufficient funds. Nothing real is charged.

Design every state, not only the happy path

A checkout is a state machine the buyer can see. The happy path gets designed because it is in the mockup; the others get a generic error box. Write them all down, and give each its own sentence and its own way forward.

  • Waiting for payment: the Pix code, its countdown, and the reassurance that the page updates by itself.
  • Processing: the button keeps its width, shows a spinner and cannot be pressed twice.
  • Paid: a receipt with the amount, method, date and a receipt id the buyer can quote to support.
  • Expired: the Pix code ran out; no payment was made; one button generates a new code for the same order.
  • Declined: the reason, "nothing was charged", and the next step.
  • Unavailable: the seller turned the link off. Say so, and show how to reach them.

The forgotten ones are expired and unavailable: a link shared months ago in a group chat will be opened long after you switched it off, and deserves a clear sentence, not a 404. And when a buyer reopens a link they already paid, show the receipt instead of a second form. Paying twice by accident is the worst outcome a checkout can produce.

One page, many states: the summary and the total stay fixed while the form beneath it changes.

Assume a phone, and a bank app one swipe away

Many buyers will open your checkout on the same phone they will pay with. A QR code is hard to scan with the device showing it, so the copy-and-paste code must be just as prominent, with a thumb-sized copy button and a fallback when copying is blocked.

  • Touch targets of at least 44px, with space between them.
  • No horizontal scroll at 360px wide, and labels that wrap instead of being cut off. Portuguese copy runs longer than English.
  • Inputs at 16px or larger, so mobile browsers do not zoom in on focus.

Trust without the badge theatre

Buyers trust what they can check, not rows of padlocks: whose name is on the page, what they are paying for and who receives the money. Our checkout shows the seller's brand and legal name, and tells Pix payers to confirm that the recipient in their bank app matches it.

A checkout earns trust by being predictable: the same amount on every screen, no surprises, and a clear answer at the end.

Do not add badges for certifications you do not hold, and do not invent urgency. A countdown belongs on a Pix code because the code really expires; on a product page, it is pressure.

A checklist you can run today

  1. Can a buyer tell the seller, the item and the total within five seconds?
  2. Does the amount on the button match the amount in the summary?
  3. Does each payment method say, in one line, what happens next?
  4. Does every decline name a reason, a next step and "nothing was charged"?
  5. Do expired, unavailable and already-paid links each have their own screen?
  6. Does the page work at 360px, with long Portuguese labels, using only a thumb?

You can walk through all of this in our checkout demo or read how the hosted page fits your integration in the checkout docs. On the server side, pair it with idempotency keys and a webhook consumer that can take duplicates, which is the subject of Webhooks without surprises.

  • Engineering8 min read

    Webhooks without surprises

    Retries, duplicates and events that arrive out of order are not edge cases; they are what at-least-once delivery means. How to build a consumer that verifies, acknowledges fast, processes once and reconciles.

  • Payments7 min read

    Pix explained for global teams

    Never shipped Pix before? Here is how it works from the payer's side, why it turns your checkout into a waiting room, which states and events to handle, and how to test the whole flow without moving money.

  • Product7 min read

    Designing subscription billing states

    Recurring billing is not a boolean. It is a small state machine with five states, a handful of events and a lot of product decisions. Here is how to model it, what to do in each state and the mistakes that cost the most.

Try it in the test environment

Create an account to get test keys, open the hosted checkout and send yourself a signed webhook. Nothing real is charged.

Commercial terms are to be announced. See what pricing covers so far