Giới thiệu
Checkout form là form quan trọng nhất ảnh hưởng conversion rate. Yêu cầu: bố cục 2 cột (thông tin thanh toán + tóm tắt đơn hàng), validation chặt chẽ, trust signals, tự động định dạng thẻ tín dụng, responsive thành 1 cột trên mobile.
Ví dụ hoàn chỉnh - Checkout Form 2 cột
<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Responsive Checkout Form</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
* { box-sizing: border-box; }
body { font-family: 'Segoe UI', system-ui, sans-serif; background: #f5f7fa; padding: 40px 20px; }
.checkout-grid {
max-width: 1000px;
margin: 0 auto;
display: grid;
grid-template-columns: 1fr 380px;
gap: 32px;
align-items: start;
}
@media (max-width: 900px) {
.checkout-grid { grid-template-columns: 1fr; }
.order-summary { position: sticky; top: 20px; }
}
.form-section { background: #fff; border-radius: 16px; box-shadow: 0 4px 24px rgba(0,0,0,0.06); padding: 32px; border: 1px solid #eef0f2; }
.section-title { display: flex; align-items: center; gap: 10px; margin: 0 0 24px; font-size: 20px; color: #1a1a1a; }
.section-title i { color: #1eadef; }
.form-row { display: flex; gap: 20px; margin-bottom: 20px; }
.form-group { flex: 1; min-width: 0; }
.form-group.full { flex-basis: 100%; }
label { display: block; margin-bottom: 8px; font-weight: 500; font-size: 14px; color: #333; }
.input-wrapper { position: relative; }
input, select {
width: 100%;
padding: 14px 16px;
font-size: 15px;
border: 1.5px solid #e0e0e0;
border-radius: 10px;
background: #fff;
transition: all 0.2s;
}
input:focus, select:focus { outline: none; border-color: #1eadef; box-shadow: 0 0 0 3px rgba(30,173,239,0.15); }
.input-icon { position: absolute; left: 16px; top: 50%; transform: translateY(-50%); color: #aaa; pointer-events: none; }
.input-wrapper:focus-within .input-icon { color: #1eadef; }
.input-with-icon input { padding-left: 48px; }
.card-inputs { display: flex; gap: 12px; }
.card-inputs .form-group { flex: 1; }
.btn-checkout {
width: 100%;
padding: 16px;
font-size: 16px;
font-weight: 600;
color: #fff;
background: linear-gradient(135deg, #4caf50, #2e7d32);
border: none;
border-radius: 10px;
cursor: pointer;
margin-top: 8px;
transition: transform 0.1s, box-shadow 0.2s;
}
.btn-checkout:hover { box-shadow: 0 6px 20px rgba(76,175,80,0.3); }
.btn-checkout:active { transform: scale(0.98); }
/* Order Summary */
.order-summary {
background: #fff;
border-radius: 16px;
box-shadow: 0 4px 24px rgba(0,0,0,0.06);
padding: 24px;
border: 1px solid #eef0f2;
height: fit-content;
}
.summary-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; padding-bottom: 16px; border-bottom: 1px solid #eee; }
.summary-header h3 { margin: 0; font-size: 18px; }
.item-count { color: #666; font-size: 14px; font-weight: 400; }
.cart-items { margin-bottom: 20px; }
.cart-item { display: flex; justify-content: space-between; padding: 12px 0; border-bottom: 1px solid #f0f0f0; }
.cart-item:last-child { border-bottom: none; }
.item-info { display: flex; flex-direction: column; gap: 4px; }
.item-name { font-weight: 500; font-size: 14px; }
.item-meta { font-size: 12px; color: #888; }
.item-price { font-weight: 600; color: #1a1a1a; white-space: nowrap; margin-left: 16px; }
.summary-totals { border-top: 1px solid #eee; padding-top: 16px; }
.total-row { display: flex; justify-content: space-between; margin-bottom: 10px; font-size: 14px; }
.total-row.final { font-size: 16px; font-weight: 700; color: #1a1a1a; padding-top: 10px; border-top: 1px dashed #eee; margin-top: 10px; }
.trust-badges { display: flex; gap: 16px; justify-content: center; margin-top: 24px; padding-top: 20px; border-top: 1px solid #eee; }
.trust-badge { display: flex; align-items: center; gap: 6px; font-size: 12px; color: #888; }
.trust-badge i { color: #4caf50; }
@media (max-width: 480px) {
.form-section { padding: 20px; }
.form-row { flex-direction: column; gap: 0; }
.card-inputs { flex-direction: column; gap: 0; }
}
</style>
</head>
<body>
<div class="checkout-grid">
<div class="form-section">
<h2 class="section-title"><i class="fas fa-user"></i> Thông tin thanh toán</h2>
<form id="checkoutForm" novalidate>
<div class="form-row">
<div class="form-group">
<label for="firstName">Họ</label>
<div class="input-wrapper input-with-icon">
<input type="text" id="firstName" name="firstName" placeholder="Nguyễn" required autocomplete="given-name">
<i class="fas fa-user input-icon" aria-hidden="true"></i>
</div>
</div>
<div class="form-group">
<label for="lastName">Tên</label>
<input type="text" id="lastName" name="lastName" placeholder="Văn An" required autocomplete="family-name">
</div>
</div>
<div class="form-group full">
<label for="email">Email</label>
<div class="input-wrapper input-with-icon">
<input type="email" id="email" name="email" placeholder="email@example.com" required autocomplete="email">
<i class="fas fa-envelope input-icon" aria-hidden="true"></i>
</div>
</div>
<div class="form-group full">
<label for="address">Địa chỉ giao hàng</label>
<input type="text" id="address" name="address" placeholder="Số nhà, tên đường" required autocomplete="street-address">
</div>
<div class="form-row">
<div class="form-group">
<label for="city">Tỉnh/Thành phố</label>
<select id="city" name="city" required>
<option value="">Chọn tỉnh/thành</option>
<option>Hà Nội</option>
<option>TP. Hồ Chí Minh</option>
<option>Đà Nẵng</option>
</select>
</div>
<div class="form-group">
<label for="district">Quận/Huyện</label>
<select id="district" name="district" required><option value="">Chọn quận/huyện</option></select>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label for="phone">Số điện thoại</label>
<input type="tel" id="phone" name="phone" placeholder="09x xxx xxxx" required autocomplete="tel">
</div>
<div class="form-group">
<label for="zip">Mã bưu chính</label>
<input type="text" id="zip" name="zip" placeholder="100000" autocomplete="postal-code">
</div>
</div>
<h2 class="section-title" style="margin-top:32px"><i class="fas fa-credit-card"></i> Thông tin thẻ</h2>
<div class="form-group full">
<label for="cardName">Tên trên thẻ</label>
<input type="text" id="cardName" name="cardName" placeholder="NGUYEN VAN AN" required autocomplete="cc-name" style="text-transform:uppercase">
</div>
<div class="form-group full">
<label for="cardNumber">Số thẻ</label>
<div class="input-wrapper input-with-icon">
<input type="text" id="cardNumber" name="cardNumber" placeholder="1234 5678 9012 3456" required autocomplete="cc-number" maxlength="19" inputmode="numeric">
<i class="fas fa-credit-card input-icon" aria-hidden="true"></i>
</div>
</div>
<div class="form-row card-inputs">
<div class="form-group">
<label for="expiry">Hết hạn (MM/YY)</label>
<input type="text" id="expiry" name="expiry" placeholder="MM/YY" required autocomplete="cc-exp" maxlength="5" inputmode="numeric">
</div>
<div class="form-group">
<label for="cvv">CVV</label>
<input type="text" id="cvv" name="cvv" placeholder="123" required autocomplete="cc-csc" maxlength="4" inputmode="numeric">
</div>
</div>
<button type="submit" class="btn-checkout">Thanh toán 299.000₫</button>
</form>
</div>
<aside class="order-summary">
<div class="summary-header">
<h3>Đơn hàng</h3>
<span class="item-count">2 sản phẩm</span>
</div>
<div class="cart-items">
<div class="cart-item">
<div class="item-info">
<span class="item-name">Áo thun cao cấp</span>
<span class="item-meta">Size M × 1</span>
</div>
<span class="item-price">199.000₫</span>
</div>
<div class="cart-item">
<div class="item-info">
<span class="item-name">Mũ lưỡi trai</span>
<span class="item-meta">Mặc định × 1</span>
</div>
<span class="item-price">80.000₫</span>
</div>
</div>
<div class="summary-totals">
<div class="total-row"><span>Tạm tính</span><span>279.000₫</span></div>
<div class="total-row"><span>Vận chuyển</span><span>20.000₫</span></div>
<div class="total-row"><span>Giảm giá</span><span style="color:#4caf50">-0₫</span></div>
<div class="total-row final"><span>Tổng cộng</span><span>299.000₫</span></div>
</div>
<div class="trust-badges">
<span class="trust-badge"><i class="fas fa-shield-alt"></i> Bảo mật SSL</span>
<span class="trust-badge"><i class="fas fa-lock"></i> Mã hóa 256-bit</span>
<span class="trust-badge"><i class="fas fa-undo"></i> Đổi trả 30 ngày</span>
</div>
</aside>
</div>
<script>
// Auto-format card number: 1234 5678 9012 3456
document.getElementById('cardNumber').addEventListener('input', (e) => {
let v = e.target.value.replace(/\D/g, '').replace(/(.{4})/g, '$1 ').trim();
e.target.value = v.substring(0, 19);
});
// Auto-format expiry: MM/YY
document.getElementById('expiry').addEventListener('input', (e) => {
let v = e.target.value.replace(/\D/g, '');
if (v.length >= 2) v = v.slice(0,2) + '/' + v.slice(2,4);
e.target.value = v;
});
// Detect card type by first digits
document.getElementById('cardNumber').addEventListener('input', (e) => {
const icon = e.target.nextElementSibling;
const val = e.target.value.replace(/\s/g, '');
if (val.startsWith('4')) icon.className = 'fas fa-cc-visa input-icon';
else if (val.startsWith('5')) icon.className = 'fas fa-cc-mastercard input-icon';
else if (val.startsWith('3')) icon.className = 'fas fa-cc-amex input-icon';
else icon.className = 'fas fa-credit-card input-icon';
});
</script>
</body>
</html>
Yếu tố then chốt của Checkout Form
| Yếu tố | Mô tả |
|---|---|
| Layout 2 cột | Form trái, Order summary phải (sticky trên mobile). |
| Auto-format card | Tự thêm space sau 4 chữ số, detect Visa/Master/Amex icon. |
| Expiry MM/YY | Tự thêm slash "/", chỉ cho phép số. |
| Autocomplete attrs | cc-name, cc-number, cc-exp, cc-csc, postal-code - giúp trình duyệt autofill. |
| Trust badges | SSL, mã hóa, đổi trả - giảm lo ngại bảo mật. |
| Sticky summary | Mobile: summary dính trên cùng khi scroll form dài. |
Mẹo nâng cao
- Dùng thư viện Cleave.js hoặc IMask cho input mask phức tạp.
- Tích hợp Stripe Elements / PayPal SDK - không tự xử lý số thẻ raw (PCI DSS).
- Luôn hiển thị tổng tiền cuối cùng rõ ràng trước khi bấm thanh toán.
- Cho phép guest checkout (không bắt buộc đăng ký).