Initial commit

This commit is contained in:
2026-03-07 11:07:45 -03:00
commit 9d523f8b6a
65 changed files with 17311 additions and 0 deletions

View File

@@ -0,0 +1,486 @@
# Checkout Page Layout
## Contents
- [Overview](#overview)
- [Decision: Single-Page vs Multi-Step](#decision-single-page-vs-multi-step)
- [Guest vs Logged-In Checkout](#guest-vs-logged-in-checkout)
- [Component Architecture](#component-architecture-recommended)
- [Checkout Flow](#checkout-flow)
- [Key Ecommerce Considerations](#key-ecommerce-considerations)
- [Backend Integration](#backend-integration)
- [Mobile Checkout](#mobile-checkout)
- [Trust and Conversion Optimization](#trust-and-conversion-optimization)
- [Error Handling](#error-handling)
- [Checklist](#checklist)
## Overview
Final step in conversion funnel where customers provide shipping and payment information. Must be optimized for completion with minimal friction.
**⚠️ CRITICAL: Always fetch shipping methods AND payment methods from backend. Users must be able to select from available options - never skip payment method selection.**
### Key Requirements
- Clear steps and progress indication
- Guest checkout option (if backend supports it)
- Shipping address and method selection
- **Shipping methods fetched from backend (vary by address/region)**
- **Payment methods fetched from backend (user must select preferred method)**
- Payment processing
- Order review before submission
- Trust signals throughout
- Mobile-optimized (60%+ traffic is mobile)
- Fast loading and submission
## Decision: Single-Page vs Multi-Step
**Use Single-Page Checkout when:**
- Simple products with few shipping options
- Mobile-heavy traffic (>60% mobile users)
- Fewer form fields required (<15 total)
- Startup or new store (minimize friction)
- Fast checkout is prioritized
- Low average order value (<$50)
**Benefits:**
- Fewer clicks (no step navigation)
- User sees full scope upfront
- Faster on mobile (no page loads)
- Lower perceived friction
**Use Multi-Step Checkout when:**
- Complex shipping (international, multiple carriers)
- B2B customers (need detailed information)
- Many form fields required (>15 total)
- High-value products (>$100, thoroughness expected)
- Established brand (customers trust process)
- Need clear progress indication
**Benefits:**
- Less overwhelming (one step at a time)
- Progress indicator reduces anxiety
- Easier step-by-step validation
- Better for complex forms
**Recommended: Hybrid Approach**
- Single-page scroll layout on desktop
- Accordion-based sections on mobile (expand/collapse)
- Progressive disclosure of sections
- Best of both worlds
**Common steps:**
1. Shipping Information (address)
2. Delivery (shipping method selection)
3. Payment (payment method and details)
4. Review (final review before submission)
## Guest vs Logged-In Checkout
**IMPORTANT:** Guest checkout availability depends on backend support.
**Guest checkout (recommended if backend supports it):**
- Reduces friction (no signup barrier)
- "Checkout as Guest" option prominent
- Email required for order confirmation
- Optional "Create account?" checkbox after order
- Don't force account creation
**Logged-in checkout:**
- Pre-fill saved addresses and payment methods
- "Returning customer? Log in" link at top
- Allow seamless switch between guest/login
## Component Architecture (RECOMMENDED)
**Build separate components for each checkout step for better maintainability and reusability.**
**Create individual step components:**
- `ShippingInformationStep` - Contact and shipping address form
- `DeliveryMethodStep` - Shipping method selection
- `PaymentInformationStep` - Payment method and details
- `OrderReviewStep` - Final review before submission
**Benefits of component separation:**
- **Maintainability**: Fix bugs or update one step without affecting others
- **Reusability**: Reuse shipping address component in account settings, checkout, etc.
- **Testability**: Test each step independently
- **Code organization**: Clearer separation of concerns (validation, submission logic per step)
- **Easier debugging**: Isolate issues to specific steps
- **Flexibility**: Easy to reorder steps or add/remove steps based on requirements
- **Performance**: Lazy load steps or split bundles for faster initial load
**What to separate:**
- Main checkout page/container component
- Individual step components (ShippingInformationStep, DeliveryMethodStep, etc.)
- Reusable order summary component (shown on all steps)
**Component communication:**
Each step component should accept:
- Current step data (form values)
- Callback to update data (e.g., `onShippingUpdate`)
- Callback to proceed to next step (e.g., `onContinue`)
- Loading/error states
- Validation errors
**Shared components:**
- Address form (used in shipping and billing)
- Payment method selector
- Order summary (sidebar, shown on all steps)
**Works for both single-page and multi-step:**
- Single-page: Render all steps at once, scroll-based navigation
- Multi-step: Show one component at a time, controlled by step state
- Accordion: Expand/collapse components as sections
**Common mistake:**
- ❌ Building entire checkout as one massive component with all form fields, logic, and validation mixed together
- ✅ Separate components for each step, shared state management in parent
## Checkout Flow
### Complete Checkout Flow Diagram
```
┌─────────────────────────────────────────────────────────────────────┐
│ CHECKOUT PROCESS │
└─────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────┐
│ Optional: Guest Checkout vs Login │
│ • Guest: Enter email only │
│ • Logged-in: Pre-fill saved data │
└────────────────────┬─────────────────────────────────┘
┌──────────────────────────────────────────────────────┐
│ STEP 1: Shipping Information │
│ ├─ Contact: Email, Phone │
│ ├─ Shipping Address: Name, Address, City, etc. │
│ └─ Billing Address: □ Same as shipping / Different │
└────────────────────┬─────────────────────────────────┘
┌──────────────────────────────────────────────────────┐
│ STEP 2: Delivery │
│ • Fetch shipping methods from backend │
│ • Display: Standard, Express, Overnight │
│ • Show: Cost + Delivery estimate │
│ • Update order total │
└────────────────────┬─────────────────────────────────┘
┌──────────────────────────────────────────────────────┐
│ STEP 3: Payment Information │
│ • Fetch payment methods from backend │
│ • Options: Card, PayPal, Apple Pay, etc. │
│ • Enter: Card details (tokenized) │
│ • Use billing address from Step 1 │
└────────────────────┬─────────────────────────────────┘
┌──────────────────────────────────────────────────────┐
│ STEP 4: Order Review │
│ • Review: Items, addresses, shipping, payment │
│ • Optional: □ Agree to Terms and Conditions │
│ • Click: [Place Order] Button │
│ → Payment processing triggered │
└────────────────────┬─────────────────────────────────┘
┌──────────────────────────────────────────────────────┐
│ Loading: Processing payment... │
│ • Authorize/capture payment via gateway │
│ • Create order in backend │
│ • Send confirmation email │
└────────────────────┬─────────────────────────────────┘
┌────┴────┐
│ │
Success Failure
│ │
▼ ▼
┌───────────────────┐ ┌──────────────────────┐
│ Order Confirmation│ │ Show Error Message │
│ • Order number │ │ • Retry payment │
│ • Details │ │ • Keep form data │
│ • Tracking link │ │ • Suggest solutions │
└───────────────────┘ └──────────────────────┘
```
## Key Ecommerce Considerations
### Shipping Address Collection
Collect:
- Required: email, name, address, city, state/zip, country
- Optional: phone.
**Key ecommerce considerations:**
- Email placement: First if guest checkout (identifies customer)
- Country placement: Early if shipping methods vary by country (affects available shipping)
- Phone: Optional to reduce friction, but recommended for delivery coordination
- Billing address: "Same as shipping" checkbox (default checked)
**For Medusa backends:**
- Country dropdown: Show only countries from cart's region (don't show all countries globally)
- Get countries from: `cart.region.countries` or `sdk.store.region.retrieve(cart.region_id)`
- Medusa regions contain specific countries - limiting options ensures correct pricing and shipping
- If user needs different country, they must change region first (typically via country selector component)
### Shipping Method Selection
**Fetch from backend after address provided** (shipping methods vary by address/region):
- Display as radio buttons with cost + delivery estimate
- Update order total immediately when method changes
- Highlight free shipping if available
- Show "Add $X for free shipping" if close to threshold
- Handle unavailable shipping: show message, suggest alternatives
### Payment Method Selection
**CRITICAL: Always fetch payment methods from backend and allow user to select from available options.**
Payment methods vary by store configuration (backend settings). NEVER assume which payment methods are available or hardcode payment options. Users MUST be able to choose their preferred payment method.
**Fetch available methods from backend:**
```typescript
// ALWAYS fetch payment providers from backend
// For Medusa:
const { payment_providers } = await sdk.store.payment.listPaymentProviders()
// For other backends:
// Change based on the integrated backend
const paymentMethods = await fetch(`${apiUrl}/payment-methods`)
// Returns: card, paypal, apple_pay, google_pay, stripe, etc.
```
**Display payment method selection UI:**
- Show all enabled payment providers returned by backend
- Allow user to select their preferred method (radio buttons or cards)
- Don't skip selection step - user must actively choose
- Map backend codes to display names in the storefront. For example `pp_system_manual` -> `Manual payment`.
- Common options: Credit/Debit Card, PayPal, Apple Pay, Google Pay, Buy Now Pay Later
**Available payment methods (examples, actual options come from backend):**
- Credit/Debit Card (most common, via Stripe/Braintree/other gateway)
- PayPal (redirect or in-context)
- Apple Pay (Safari, iOS only)
- Google Pay (Chrome, Android)
- Buy Now Pay Later (Affirm, Klarna - if enabled by store)
- Manual payment (bank transfer, cash on delivery - if enabled)
**Why backend fetching is required:**
- Store admin controls which payment providers are enabled
- Payment methods vary by region, currency, order value
- Test vs production mode affects available methods
- Can't assume all stores use the same payment gateway
**For Medusa backends - Payment flow:**
1. **List available payment providers:**
```typescript
const { payment_providers } = await sdk.store.payment.listPaymentProviders({
region_id: cart.region_id // Required to get region-specific providers
})
```
2. **Display providers and allow user to select:**
Show payment providers as radio buttons or cards. User must actively select one.
3. **Initialize payment session after selection:**
```typescript
// When user selects a provider
await sdk.store.payment.initiatePaymentSession(cart, {
provider_id: selectedProvider.id // e.g., "pp_stripe_stripe", "pp_system_default"
})
// Re-fetch cart to get updated payment session data
const { cart: updatedCart } = await sdk.store.cart.retrieve(cart.id)
```
4. **Render provider-specific UI:**
- Stripe providers (`pp_stripe_*`): Render Stripe Elements card UI
- Manual payment (`pp_system_default`): No additional UI needed
- Other providers: Implement according to provider requirements
**Important:** Payment provider IDs are returned from the backend (e.g., `pp_stripe_stripe`, `pp_system_manual`). Map these to user-friendly display names in your UI.
**Digital wallets (mobile priority):**
- Apple Pay / Google Pay should be prominent on mobile
- One-click payment (pre-filled shipping)
- Significantly faster checkout
- Higher conversion on mobile
**Card payment:**
- Use payment gateway (Stripe Elements, Braintree)
- Never handle raw card data (PCI compliance)
- Tokenize card data before submission
- Auto-detect card type (show logo)
### Order Review
**Display for final confirmation:**
Cart items, addresses, shipping method/cost, payment method, order total breakdown.
**Key elements:**
- "Edit" link next to each section (returns to step or edits inline)
- Terms checkbox (if required): "I agree to Terms and Conditions"
- Place Order button: Large (48-56px), shows total, loading state on submit
### Order Summary Sidebar
**Desktop:** Sticky sidebar with items, prices, totals. Updates in real-time.
**Mobile:** Collapsible at top ("Show order summary" toggle). Keeps focus on form.
## Backend Integration
**Address validation (optional):**
- Use address lookup APIs (Google, SmartyStreets) for higher accuracy
- Tradeoff: accuracy vs friction. Consider for high-value orders.
**Payment processing flow:**
1. Frontend tokenizes payment (Stripe Elements, Braintree)
2. Send token + order details to backend
3. Backend authorizes/captures payment & creates order
4. Redirect to confirmation page
**Never:** Send raw card data, store cards without PCI compliance, process payments client-side.
**On payment failure:** Show specific error, keep form data, allow retry without re-entering.
### Order Completion and Cart Cleanup (CRITICAL)
**After order is successfully placed, you MUST reset the cart state:**
**Common issue:** Cart popup and cart state still show old cart content after order is placed. This happens because the global cart state (Context, Zustand, Redux) isn't cleared after checkout completion.
**Required actions on successful order:**
1. **Clear cart from global state:**
- Reset cart state in Context/Zustand/Redux to null or empty
- Update cart count to 0 in navbar
- Prevent old cart items from showing in cart popup
2. **Clear localStorage cart ID:**
- Remove cart ID from localStorage: `localStorage.removeItem('cart_id')`
- Or create new cart and update cart ID in localStorage
- Ensures fresh cart for next shopping session
3. **Invalidate cart queries (if using TanStack Query):**
- `queryClient.invalidateQueries({ queryKey: ['cart'] })`
- Or `queryClient.removeQueries({ queryKey: ['cart', cartId] })`
- Prevents stale cart data from cache
4. **Redirect to order confirmation page:**
- Navigate to `/order-confirmation/[order_id]` or `/thank-you/[order_id]`
- Show order details, tracking info, confirmation
**Pattern:**
```typescript
// After successful order placement
async function onOrderSuccess(order) {
// 1. Clear cart state
setCart(null) // or clearCart() from context
// 2. Clear localStorage
localStorage.removeItem('cart_id')
// 3. Invalidate queries (if using TanStack Query)
queryClient.invalidateQueries({ queryKey: ['cart'] })
// 4. Redirect to confirmation
router.push(`/order-confirmation/${order.id}`)
}
```
**Why this is critical:**
- Without clearing cart state, cart popup shows old items after order
- User sees "phantom cart" if they click cart icon after checkout
- Creates confusion and poor UX
- May prevent user from starting new shopping session
## Mobile Checkout
**Key optimizations:**
- Digital wallets prominent (Apple Pay/Google Pay) - significantly faster checkout
- Single-column layout, 44-48px touch targets
- Appropriate keyboard types, autocomplete attributes enabled
- Collapsible order summary at top (shows total, expands on tap)
- Sticky Place Order button at bottom (always accessible, shows total)
- Accordion sections (one step at a time, reduces cognitive load)
**For detailed mobile patterns and safe area insets**, see `reference/mobile-responsiveness.md`.
## Trust and Conversion Optimization
**Trust signals (critical for conversion):**
- "Secure Checkout" badge, payment provider logos (Visa, Mastercard)
- Return policy link visible, customer support contact
- Near Place Order: "100% secure checkout", guarantees/free returns if offered
- For new brands: Customer review count, social proof
**Reduce abandonment:**
- Progress indicator (shows steps remaining)
- Auto-save form data, clear pricing (no surprise fees)
- Minimal required fields, smart defaults, autocomplete enabled
**Reduce perceived friction:**
- "No account required" (guest checkout)
- "Free shipping" highlighted
- Time estimate: "Less than 2 minutes"
## Error Handling
**Form validation:**
- Validate on blur, show error below field
- User-friendly messages ("Please enter a valid email address")
- Scroll to first error on submit
**Payment errors:**
- Card declined: "Your card was declined. Please try another payment method."
- Keep form data, suggest alternatives (try another card, PayPal)
- Network timeout: Show retry option without re-entering data
**Stock availability errors:**
- Out of stock: Remove item, recalculate, allow continue with remaining items
- Quantity reduced: Update automatically, show message, allow continue
## Checklist
**Essential checkout features:**
- [ ] **RECOMMENDED: Separate components created for each checkout step**
- [ ] Components: ShippingInformationStep, DeliveryMethodStep, PaymentInformationStep, OrderReviewStep
- [ ] Decision made: Single-page or multi-step (based on complexity)
- [ ] Guest checkout option (if backend supports it)
- [ ] Email field first (if guest checkout)
- [ ] Shipping address form with autocomplete attributes
- [ ] "Billing same as shipping" checkbox (default checked)
- [ ] Shipping methods fetched from backend dynamically
- [ ] Shipping cost updates order total in real-time
- [ ] **CRITICAL: Payment methods fetched from backend (NEVER assume or hardcode)**
- [ ] **CRITICAL: Payment method selection UI shown to user (user must select from available options)**
- [ ] Payment methods: show only enabled providers returned by backend
- [ ] For Medusa: Payment session initialized after user selects provider (sdk.store.payment.initiatePaymentSession)
- [ ] For Medusa: Country dropdown limited to cart's region countries
- [ ] Digital wallets prominent on mobile (Apple Pay, Google Pay)
- [ ] Payment tokenization (never send raw card data)
- [ ] Order review section before submission
- [ ] Order summary sidebar (sticky on desktop, collapsible on mobile)
- [ ] Promo code input (if not applied in cart)
- [ ] Trust signals throughout (secure checkout, return policy)
- [ ] Terms and conditions checkbox (if required)
- [ ] Place Order button prominent (48-56px, shows total)
- [ ] Loading state during payment processing
- [ ] Progress indicator (if multi-step)
- [ ] Clear error messages for validation failures
- [ ] Error handling for payment failures (keep form data)
- [ ] Stock availability check before order creation
- [ ] Mobile optimized (44-48px touch targets, single column)
- [ ] Autocomplete enabled on all form fields
- [ ] Keyboard accessible (tab through fields, enter to submit)
- [ ] ARIA labels on form fields (aria-required, aria-invalid)
- [ ] Redirect to order confirmation on success
- [ ] **CRITICAL: Clear cart state after successful order** (reset cart in Context/Zustand, remove cart ID from localStorage, invalidate cart queries)
- [ ] Cart popup shows empty cart after order completion (not old items)