Payment Card Details

Follow this pattern when you need to collect credit or debit card details to take a payment.

When to use this pattern

Use this pattern when you need to collect payment card details from users. Present all fields on a single page, in the order in which they appear on a credit or debit card. This arrangement helps users transcribe information directly from their payment card.

Interactive example

Try entering a card number to see the card type detection in action:

VisaMastercardAmerican ExpressMaestro
For example, 03/26
The last 3 digits on the back of your card

How it works

Field order

Present fields in the order they appear on a physical card:

  1. Card number — the long number on the front
  2. Expiry date — usually below the card number
  3. Name on card — the cardholder's name
  4. Card security code — on the back (or front for Amex)

Accepted cards

Show which cards you accept using recognisable logos. When the user starts entering their card number, identify the card type and highlight only that logo while greying out others.

Accepted cards:

VisaMastercardAmerican ExpressMaestro
<div class="payment-card-icons">
  <img src="/icons/payment/visa.svg" alt="Visa">
  <img src="/icons/payment/mastercard.svg" alt="Mastercard">
  <img src="/icons/payment/american-express.svg" alt="American Express">
  <img src="/icons/payment/maestro.svg" alt="Maestro">
</div>

Card number formatting

Allow users to enter payment card numbers in whatever format is familiar to them. Allow additional spaces, hyphens and dashes. Format the display as the user types for easier reading.

function formatCardNumber(value) {
  // Remove all non-digits
  const digits = value.replace(/\D/g, '');

  // Add space every 4 digits
  const formatted = digits.match(/.{1,4}/g)?.join(' ') || digits;

  return formatted.slice(0, 19); // Max 16 digits + 3 spaces
}

// Example: 4111111111111111 → 4111 1111 1111 1111

Card type detection

Use the first digits of the card number (IIN/BIN) to detect the card type and provide appropriate validation:

Card typeStarts withLengthSecurity code
Visa416 digits3 digits (back)
Mastercard51-55, 2221-272016 digits3 digits (back)
American Express34, 3715 digits4 digits (front)
Maestro5018, 5020, 5038, etc.12-19 digits3 digits (back)

Security code hint

Do not use acronyms like "CVV" or "CVC". Instead, describe where to find the code in plain language. Update the hint based on detected card type:

  • Most cards: "The last 3 digits on the back of your card"
  • American Express: "The 4 digits on the front of your card"

Available payment icons

VisaVisa
MastercardMastercard
AmexAmex
MaestroMaestro
DiscoverDiscover
JCBJCB
Diners ClubDiners Club
UnionPayUnionPay
PayPalPayPal
Apple PayApple Pay
Google PayGoogle Pay
StripeStripe
KlarnaKlarna
AlipayAlipay
WeChat PayWeChat Pay
Direct DebitDirect Debit

Error messages

Use clear, specific error messages:

ScenarioError message
Card number is empty"Enter your card number"
Card number is invalid"Enter a valid card number"
Card type not accepted"We do not accept this type of card. Try another card."
Expiry date is empty"Enter the expiry date"
Expiry date format wrong"Enter the expiry date in the format MM/YY"
Card has expired"Card has expired. Check the expiry date or try another card."
Name on card is empty"Enter the name on your card"
Security code is empty"Enter the card security code"
Security code wrong length"The security code should be 3 digits" (or 4 for Amex)
Payment declined"Your payment was declined. Check your card details or try a different card."

Security

Never store full card details. Use a PCI DSS compliant payment provider (like Stripe, Adyen, or PayPal) to handle card processing securely.

  • Use a PCI DSS compliant payment provider
  • Use HTTPS on all payment pages
  • Never log or store full card numbers
  • Show only last 4 digits in confirmations
  • Consider using payment provider's hosted fields or iframes

Autocomplete attributes

Use correct autocomplete values for better browser autofill:

FieldAutocomplete value
Card numbercc-number
Expiry date (combined)cc-exp
Expiry monthcc-exp-month
Expiry yearcc-exp-year
Security codecc-csc
Name on cardcc-name
Card typecc-type

Accessibility

  • Use inputmode="numeric" for number fields to show numeric keyboard on mobile
  • Provide clear labels and hint text
  • Do not disable paste functionality — users may use password managers
  • Allow sufficient time to complete payment (session timeouts should be generous)
  • Make error messages clear and specific
  • Do not assume the cardholder's name matches any other name in the service

HTML structure

<fieldset class="form-group">
  <legend class="form-label form-label-lg">Card details</legend>

  <!-- Accepted cards -->
  <div class="payment-card-icons">
    <img src="/icons/payment/visa.svg" alt="Visa">
    <img src="/icons/payment/mastercard.svg" alt="Mastercard">
  </div>

  <!-- Card number - full width -->
  <div class="form-group">
    <label class="form-label" for="card-number">Card number</label>
    <input class="form-input" id="card-number" type="text"
           inputmode="numeric" autocomplete="cc-number">
  </div>

  <!-- Expiry date and Security code - side by side -->
  <div class="form-row form-row-1-1">
    <div class="form-group">
      <label class="form-label" for="card-expiry">Expiry date</label>
      <span class="form-hint">For example, 03/26</span>
      <input class="form-input" id="card-expiry" type="text"
             inputmode="numeric" autocomplete="cc-exp" maxlength="5">
    </div>

    <div class="form-group">
      <label class="form-label" for="card-cvc">Card security code</label>
      <span class="form-hint">The last 3 digits on the back of your card</span>
      <input class="form-input" id="card-cvc" type="text"
             inputmode="numeric" autocomplete="cc-csc" maxlength="4">
    </div>
  </div>

  <!-- Name on card - full width -->
  <div class="form-group">
    <label class="form-label" for="card-name">Name on card</label>
    <input class="form-input" id="card-name" type="text"
           autocomplete="cc-name">
  </div>
</fieldset>