Layout 3 — Stripe-style Two-Column
A complete checkout form example that demonstrates a modern two-column payment layout with order summary and payment form side by side. This example showcases how to integrate the justifi-modular-checkout wrapper with justifi-card-form and justifi-card-billing-form-simple components to create a professional payment experience.
The layout includes:
- Header Bar: Back arrow, brand name, and test mode indicator
- Left Column: Order summary with product details, pricing breakdown, and footer
- Right Column: Payment form with email, card details, billing address, and submit button
- Card Form: Secure iframe inputs for card number, expiration, and CVV
- Billing Form: ZIP code input for billing information (using justifi-card-billing-form-simple)
This example uses CSS parts to style the web components consistently with the overall design theme and demonstrates proper integration of the modular checkout components, including the preCompleteHook for custom validation or server updates before submission.
Example Usage
←
JustiFi Store
Pay JustiFi Store
$39.99
📚
Fintech Deployhandbook Test
How to deploy Fintech app...
$39.99
Subtotal
$39.99
Tax
Enter address to calculate
Total due
$39.99
<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0" />
<title>justifi-modular-checkout</title>
<script type="module" src="https://cdn.jsdelivr.net/npm/@justifi/webcomponents@>=6.12.3/dist/webcomponents/webcomponents.esm.js"></script>
<script
nomodule
src="https://cdn.jsdelivr.net/npm/@justifi/webcomponents@>=6.12.3/dist/webcomponents/webcomponents.js"
></script>
<style>
.checkout-container * {
display: block;
box-sizing: border-box;
}
.checkout-container {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
margin: 0;
padding: 0;
background-color: #f8f9fa;
color: #333;
min-height: 100vh;
}
.checkout-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 40px;
background-color: white;
border-bottom: 1px solid #e0e0e0;
}
.checkout-brand {
display: flex;
align-items: center;
gap: 10px;
}
.checkout-back-arrow { font-size: 20px; }
.checkout-brand-name { font-size: 18px; font-weight: 500; }
.checkout-main-container {
display: flex;
max-width: 1200px;
margin: 0 auto;
padding: 40px 20px;
gap: 40px;
}
.checkout-order-summary {
display: flex;
flex-direction: column;
gap: 30px;
flex: 0 0 45%;
padding: 15px;
}
.checkout-pay-header { margin-bottom: 30px; }
.checkout-pay-title {
font-size: 24px;
font-weight: 600;
color: #333;
margin: 0 0 10px 0;
}
.checkout-pay-amount {
font-size: 32px;
font-weight: bold;
color: #333;
}
.checkout-product-item {
display: flex;
align-items: center;
gap: 15px;
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 1px solid #e0e0e0;
}
.checkout-product-image {
width: 60px;
height: 80px;
background-color: #f8f9fa;
border: 1px solid #e0e0e0;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
color: #666;
}
.checkout-product-details { flex: 1; }
.checkout-product-name {
font-weight: 500;
color: #333;
margin-bottom: 5px;
}
.checkout-product-description { font-size: 14px; color: #666; }
.checkout-product-price { font-weight: 600; color: #333; }
.checkout-summary-breakdown {
display: flex;
flex-direction: column;
gap: 10px;
margin-bottom: 30px;
}
.checkout-summary-row {
display: flex;
justify-content: space-between;
margin-bottom: 10px;
font-size: 14px;
}
.checkout-summary-row.tax { align-items: center; }
.checkout-tax-info {
display: flex;
align-items: center;
gap: 5px;
}
.checkout-tax-icon { font-size: 12px; color: #666; }
.checkout-tax-placeholder { color: #666; }
.checkout-summary-row.total {
font-size: 16px;
font-weight: bold;
color: #333;
padding-top: 10px;
border-top: 1px solid #e0e0e0;
}
.checkout-footer {
margin-top: auto;
font-size: 12px;
color: #666;
}
.checkout-powered-by { margin-bottom: 10px; }
.checkout-justifi-text { font-weight: 600; }
.checkout-footer-links { display: flex; gap: 20px; }
.checkout-footer-link {
color: #007bff;
text-decoration: none;
}
.checkout-form-field justifi-card-form { margin-bottom: 15px; }
.checkout-payment-form {
display: flex;
flex-direction: column;
gap: 15px;
flex: 0 0 50%;
background-color: white;
border-radius: 8px;
padding: 30px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.checkout-form-title {
font-size: 24px;
font-weight: 600;
color: #333;
margin: 0 0 30px 0;
}
.checkout-form-field { margin-bottom: 25px; }
.checkout-form-label {
display: block;
margin-bottom: 8px;
font-size: 14px;
font-weight: 500;
color: #333;
}
.checkout-form-input {
width: 100%;
padding: 12px 15px;
border: 1px solid #e0e0e0;
border-radius: 6px;
font-size: 14px;
background-color: white;
}
.checkout-form-select {
width: 100%;
padding: 12px 15px;
border: 1px solid #e0e0e0;
border-radius: 6px;
font-size: 14px;
background-color: white;
color: #333;
}
.checkout-manual-link {
color: #007bff;
text-decoration: none;
font-size: 14px;
}
.checkout-checkbox-container { margin-bottom: 30px; }
.checkout-pay-button {
width: 100%;
background-color: #007bff;
color: white;
border: none;
padding: 15px;
border-radius: 6px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
}
.checkout-pay-button:hover { background-color: #0056b3; }
::part(font-family) {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
::part(color) { color: #333; }
::part(background-color) { background-color: white; }
::part(input) {
border-color: #e0e0e0;
border-width: 1px;
border-radius: 6px;
border-style: solid;
box-shadow: none;
font-size: 14px;
font-weight: normal;
line-height: 1.5;
padding: 12px 15px;
background-color: white;
}
::part(input-focused) {
border-color: #007bff;
box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.1);
}
::part(input-invalid) {
border-color: #dc3545;
box-shadow: 0 0 0 2px rgba(220, 53, 69, 0.1);
}
::part(label) {
font-size: 14px;
font-weight: 500;
color: #333;
margin-bottom: 8px;
}
::part(button) {
padding: 12px 15px;
font-size: 14px;
box-shadow: none;
border-radius: 6px;
line-height: 1.5;
text-transform: none;
}
::part(button-primary) {
color: #ffffff;
background-color: #007bff;
border-color: #007bff;
}
::part(button-primary):hover {
background-color: #0056b3;
border-color: #0056b3;
color: #ffffff;
}
</style>
</head>
<body>
<justifi-modular-checkout auth-token="authToken" checkout-id="cho_123">
<div class="checkout-container">
<div class="checkout-header">
<div class="checkout-brand">
<span class="checkout-back-arrow">←</span>
<span class="checkout-brand-name">JustiFi Store</span>
</div>
</div>
<div class="checkout-main-container">
<div class="checkout-order-summary">
<div class="checkout-pay-header">
<h1 class="checkout-pay-title">Pay JustiFi Store</h1>
<div class="checkout-pay-amount">$39.99</div>
</div>
<div class="checkout-product-item">
<div class="checkout-product-image">📚</div>
<div class="checkout-product-details">
<div class="checkout-product-name">
Fintech Deployhandbook Test
</div>
<div class="checkout-product-description">
How to deploy Fintech app...
</div>
</div>
<div class="checkout-product-price">$39.99</div>
</div>
<div class="checkout-summary-breakdown">
<div class="checkout-summary-row">
<span>Subtotal</span>
<span>$39.99</span>
</div>
<div class="checkout-summary-row tax">
<div class="checkout-tax-info">
<span>Tax</span>
<span class="checkout-tax-icon">ⓘ</span>
</div>
<span class="checkout-tax-placeholder">
Enter address to calculate
</span>
</div>
<div class="checkout-summary-row total">
<span>Total due</span>
<span>$39.99</span>
</div>
</div>
<div class="checkout-footer">
<div class="checkout-powered-by">
Powered by <span class="checkout-justifi-text">JustiFi</span>
</div>
<div class="checkout-footer-links">
<a href="#" class="checkout-footer-link">
Terms
</a>
<a href="#" class="checkout-footer-link">
Privacy
</a>
</div>
</div>
</div>
<div class="checkout-payment-form">
<h2 class="checkout-form-title">Pay with card</h2>
<div class="checkout-form-field">
<label class="checkout-form-label">Email</label>
<input
type="email"
placeholder="Enter your email"
class="checkout-form-input"
/>
</div>
<div class="checkout-form-field">
<justifi-card-form></justifi-card-form>
<justifi-card-billing-form-simple></justifi-card-billing-form-simple>
</div>
<div class="checkout-form-field">
<label class="checkout-form-label">Name on card</label>
<input
type="text"
placeholder="Enter cardholder name"
class="checkout-form-input"
/>
</div>
<div class="checkout-form-field">
<label class="checkout-form-label">Billing address</label>
<div style="margin-bottom: 45px;">
<select class="checkout-form-select">
<option>United States</option>
<option>Canada</option>
</select>
</div>
<div style="margin-bottom: 15px;">
<input
type="text"
placeholder="Address"
class="checkout-form-input"
/>
</div>
<a href="#" class="checkout-manual-link">
Enter address manually
</a>
</div>
<div class="checkout-checkbox-container">
<justifi-save-new-payment-method label="Save this for next time"></justifi-save-new-payment-method>
</div>
<button class="checkout-pay-button" id="submit-button">
Pay
</button>
</div>
</div>
</div>
</justifi-modular-checkout>
</body>
<script>
(function() {
const modularCheckout = document.querySelector('justifi-modular-checkout');
const submitButton = document.querySelector('#submit-button');
modularCheckout.preCompleteHook = (state, resolve, reject) => {
// You could check the payment method type
// if (state.selectedPaymentMethod?.type === 'new_card') { ... }
// You could update records on your server before submission
// await fetch('/api/pre-checkout', {
// method: 'POST',
// body: JSON.stringify(state)
// });
// You could perform custom validation
// const isValid = myCustomValidation(state);
// if (isValid) { resolve(state); } else { reject(); }
// For this example, always proceed
resolve(state);
};
submitButton.addEventListener('click', async () => {
await modularCheckout.submitCheckout();
});
modularCheckout.addEventListener('submit-event', (event) => {
console.log('Checkout completed successfully!', event.detail);
});
modularCheckout.addEventListener('error-event', (event) => {
console.error('Checkout error:', event.detail);
});
modularCheckout.addEventListener('checkout-changed', (event) => {
console.log('Checkout changed:', event.detail);
});
})();
</script>
</html>