- Introduced WhmcsAccountDiscoveryService to streamline client account discovery processes. - Expanded WhmcsCacheService to include caching for subscription invoices and client email mappings, improving data retrieval efficiency. - Updated WhmcsClientService to utilize caching for client ID lookups by email, enhancing performance. - Implemented new internet cancellation features in SubscriptionsController, allowing users to preview and submit cancellation requests for internet services. - Added validation schemas for internet cancellation requests, ensuring data integrity and user guidance during the cancellation process. - Refactored various components and services to integrate new cancellation functionalities, improving user experience and operational flow.
22 KiB
Shop & Checkout Process - Comprehensive Review & Recommendations
Date: 2024-12-19
Reviewer: AI Assistant
Scope: Complete shop-to-order flow, authentication handling, and system predictability
Executive Summary
This review analyzes the entire shop/checkout process from product selection through order submission, with a focus on:
- User Experience - How smooth and intuitive the flow is
- System Predictability - How reliable and consistent the system behaves
- Error Handling - How gracefully failures are handled
- Authentication Flow - How guest vs authenticated users are handled
Key Findings:
- ✅ Strong foundation with unified checkout supporting both guest and authenticated flows
- ⚠️ Several UX friction points that could reduce conversion
- ⚠️ Some unpredictable behaviors around state management and error recovery
- ⚠️ Complex multi-system registration flow with potential failure points
Current Flow Analysis
1. Product Selection & Configuration
Flow:
Public Catalog → Configure Product → Create Checkout Session → Redirect to /order
Current Implementation:
- ✅ Public catalog accessible without authentication
- ✅ Checkout session created server-side (2-hour TTL)
- ✅ Cart state persisted in localStorage via Zustand
- ✅ Supports both
/shop(public) and/account/shop(authenticated) routes
Issues Identified:
- Cart Staleness Detection: Cart can become stale if user changes product selection but doesn't refresh session
- Session Expiration: 2-hour TTL may be too short for users who take breaks
- No Cart Recovery: If user closes browser, cart is lost (only localStorage, no server-side backup)
2. Checkout Entry & Session Management
Flow:
CheckoutEntry → Load from URL params → Create/validate session → Initialize cart
Current Implementation:
- Session created via
POST /api/checkout/session - Cart built from session request
- Signature-based cart validation to detect changes
Issues Identified:
- Race Conditions: Multiple rapid clicks can create duplicate sessions
- Error Recovery: If session creation fails, user sees generic error
- No Session Refresh: Expired sessions require full restart
3. Account Step (Guest Flow)
Flow:
Identify Email → Check if exists → [New Account | Sign In | Set Password]
Current Implementation:
- ✅ Smart email identification with
checkPasswordNeededAPI - ✅ Embedded sign-in form
- ✅ Guest info stored in checkout store
- ✅ Registration deferred until Address step
Issues Identified:
- Password Requirements Not Shown Early: User only sees requirements after starting form
- No Email Verification: Registration happens without email confirmation
- Account Creation Timing: Registration happens at Address step, which may surprise users
- Error Messages: Generic "Registration failed" doesn't help user understand what went wrong
4. Address Step
Flow:
[Authenticated: Use saved address] OR [Guest: Enter address → Trigger registration]
Current Implementation:
- ✅ Address confirmation component for authenticated users
- ✅ Registration triggered after address submission
- ✅ Multi-system account creation (SF + WHMCS + Portal)
Critical Issues:
- Silent Registration: User doesn't know account is being created until it completes/fails
- No Progress Indication: Long-running registration (3-5 seconds) has no loading state
- Rollback Complexity: If registration fails, partial accounts may exist (SF Account not rolled back)
- Error Recovery: If registration fails, user loses address data and must re-enter
- WHMCS SSO Dependency: Payment method addition requires external redirect
5. Availability Step (Internet Only)
Flow:
Check Eligibility → [Not Requested | Pending | Eligible | Ineligible]
Current Implementation:
- Eligibility check via Salesforce Account fields
- Can request eligibility during checkout
- Blocks order submission if not eligible
Issues Identified:
- Timing: Eligibility check happens late in flow (after address)
- No Pre-check: User can configure product before knowing if eligible
- Pending State: User can't proceed if eligibility is pending review
- Error Handling: If eligibility check fails, user is stuck
6. Payment Step
Flow:
Check Payment Methods → [Has Method | Open WHMCS SSO → Poll for completion]
Current Implementation:
- ✅ Payment method polling (3-second interval)
- ✅ Window focus detection
- ✅ WHMCS SSO integration
- ✅ Residence card upload for SIM orders
Critical Issues:
- External Redirect Required: Forces user to leave checkout flow
- No Inline Payment: Must use WHMCS (can't add card directly)
- Polling Limitations: 3-second polling may miss rapid completions
- Window Management: Popup blockers may break flow
- No Payment Method Preview: User doesn't see what they're adding until after
- Residence Card Timing: SIM orders require residence card, but it's mixed with payment step
7. Review Step
Flow:
Review Details → Accept Terms → Submit Order → Redirect to Order Status
Current Implementation:
- ✅ Comprehensive order summary
- ✅ Terms acceptance checkbox
- ✅ Order creation via checkout session
Issues Identified:
- No Order Preview: User doesn't see final cart totals until review
- Error Handling: Generic error messages on submission failure
- Session Expiration: If session expired, user must restart
- No Draft Saving: Can't save and resume later
8. Order Creation
Flow:
POST /orders/from-checkout-session → Create SF Order → Delete Session
Current Implementation:
- ✅ Idempotent order creation
- ✅ Session cleanup after order
- ✅ Error handling with specific messages
Issues Identified:
- No Retry Logic: If order creation fails, user must restart
- Session Deletion: Session deleted immediately, can't retry
- Error Context: Limited error context for debugging
UX Pain Points & Recommendations
🔴 Critical Issues
1. Registration Happens Silently at Address Step
Problem:
- User enters address and clicks "Continue"
- System silently creates accounts in 3 systems (SF, WHMCS, Portal)
- No progress indication during 3-5 second operation
- If it fails, user loses address data
Impact: High - Users confused, data loss on failure
Recommendation:
// Show explicit registration step
<RegistrationProgress>
<Step status="creating">Creating your account...</Step>
<Step status="creating">Setting up billing...</Step>
<Step status="pending">Almost done...</Step>
</RegistrationProgress>
Implementation:
- Add explicit "Creating Account" step between Address and Availability
- Show progress indicators for each system
- Save address to store BEFORE registration
- Allow retry if registration fails
2. Payment Method Addition Requires External Redirect
Problem:
- User must leave checkout to add payment method
- Opens new tab/window (popup blockers may interfere)
- Must manually return and wait for polling
- No clear indication of what to do next
Impact: High - Major friction point, potential abandonment
Recommendation:
-
Short-term: Improve UX around WHMCS redirect
- Clear instructions: "We'll open a new tab. Complete payment setup there, then return here."
- Auto-detect when user returns (focus event + polling)
- Show countdown: "Waiting for payment method... (checking every 3 seconds)"
- Add "I've completed it" button to manually trigger check
-
Long-term: Consider inline payment (if WHMCS API supports it)
- Embed payment form directly in checkout
- Or use payment gateway that supports direct integration
3. No Cart Recovery or Draft Saving
Problem:
- If user closes browser, cart is lost (only localStorage)
- Can't save progress and resume later
- No email recovery for abandoned carts
Impact: Medium - Lost conversions
Recommendation:
- Save cart to server when session created
- Link cart to email (even before registration)
- Send abandoned cart email after 1 hour
- Allow cart recovery via email link
4. Eligibility Check Happens Too Late
Problem:
- User configures product, enters address, then finds out not eligible
- Wasted time and frustration
- No way to check eligibility before starting checkout
Impact: Medium - Poor user experience
Recommendation:
- Add eligibility check in catalog (before configuration)
- Show eligibility status on product cards
- Pre-check eligibility when user enters address
- Allow eligibility request from catalog page
🟡 Medium Priority Issues
5. Error Messages Are Too Generic
Problem:
- "Registration failed" doesn't tell user what went wrong
- "Checkout session expired" doesn't explain how to recover
- Network errors show technical messages
Recommendation:
- Map error codes to user-friendly messages
- Provide actionable recovery steps
- Log technical details server-side only
6. No Progress Persistence
Problem:
- If user refreshes page, some progress may be lost
- Step navigation relies on store state
- No server-side progress tracking
Recommendation:
- Save checkout progress to server
- Restore step on page load
- Show "Resume checkout" option
7. Session Expiration Not Communicated
Problem:
- 2-hour session expiration not shown to user
- No warning before expiration
- User loses progress if session expires
Recommendation:
- Show session expiration countdown
- Warn user 10 minutes before expiration
- Auto-refresh session if user is active
- Save progress before expiration
8. Residence Card Mixed with Payment
Problem:
- SIM orders require residence card, but it's in Payment step
- User may not realize requirement until late
- Can't proceed without residence card, but it's not clear upfront
Recommendation:
- Move residence card to separate step (before Payment)
- Or show requirement earlier (Account or Address step)
- Make requirement clear in product configuration
🟢 Low Priority Improvements
9. Password Requirements Not Shown Early
Recommendation:
- Show password requirements before user starts typing
- Add inline validation with helpful hints
10. No Order Summary Until Review
Recommendation:
- Show order summary sidebar throughout checkout
- Update totals in real-time as user progresses
- Highlight changes when user modifies selections
System Predictability Issues
🔴 Critical System Issues
1. Multi-System Registration Has Partial Failure Risk
Current Flow:
1. Create SF Account ✅
2. Create SF Contact ✅
3. Create WHMCS Client ✅
4. Update SF Account ✅
5. Create Portal User ✅
6. Create ID Mapping ✅
Problem:
- If step 3-6 fails, SF Account exists but no Portal user
- Rollback doesn't delete SF Account (intentional, but creates orphan)
- No idempotency checks - retry could create duplicates
Recommendation:
// Add idempotency checks
async registerForCheckout(data: CheckoutRegisterData, idempotencyKey: string) {
// Check if registration already completed
const existing = await this.findExistingRegistration(data.email, idempotencyKey);
if (existing) return existing;
// Use distributed transaction pattern
const transaction = await this.distributedTransaction.start();
try {
// All-or-nothing registration
await transaction.execute([
() => this.createSFAccount(data),
() => this.createSFContact(data),
() => this.createWHMCSClient(data),
() => this.createPortalUser(data),
() => this.createIDMapping(data),
]);
return transaction.commit();
} catch (error) {
await transaction.rollback();
throw error;
}
}
2. Checkout Session State Can Become Stale
Problem:
- Session created with cart snapshot
- If catalog prices change, cart becomes stale
- No validation that session cart matches current catalog
Recommendation:
- Re-validate cart on order creation
- Show price change warnings
- Allow user to refresh cart if stale
3. Payment Method Polling Can Miss Updates
Problem:
- 3-second polling interval may miss rapid payment additions
- Focus event may not fire if user uses different device
- No WebSocket/SSE for real-time updates
Recommendation:
- Reduce polling interval to 1 second when waiting
- Add WebSocket connection for real-time updates (if possible)
- Add manual "Check Now" button
- Use WHMCS webhook if available
4. No Retry Logic for Failed Operations
Problem:
- If order creation fails, user must restart entire flow
- No automatic retry for transient failures
- No exponential backoff
Recommendation:
- Add retry logic with exponential backoff
- Show retry button with attempt count
- Save order draft for manual retry
- Queue failed orders for background processing
🟡 Medium Priority System Issues
5. Error Codes Not Standardized
Problem:
- Different error formats from different services
- Hard to map errors to user messages
- No centralized error handling
Recommendation:
- Create error code enum
- Map all service errors to standard codes
- Centralized error translation service
6. No Circuit Breaker for External Services
Problem:
- If WHMCS is down, checkout completely blocked
- No fallback or degraded mode
- User sees generic error
Recommendation:
- Implement circuit breaker pattern
- Allow "save for later" if services down
- Show service status to user
- Queue orders for processing when service recovers
7. Session Cleanup Race Conditions
Problem:
- Session deleted immediately after order creation
- If order creation fails after deletion, can't retry
- Multiple tabs can create race conditions
Recommendation:
- Don't delete session until order confirmed successful
- Add session lock to prevent concurrent use
- Implement session state machine (created → in-use → completed → deleted)
Recommended Improvements (Prioritized)
Phase 1: Critical UX Fixes (1-2 weeks)
-
Add Registration Progress Indicator
- Show explicit "Creating Account" step
- Display progress for each system
- Save address before registration
-
Improve Payment Step UX
- Clear instructions for WHMCS redirect
- Better polling feedback
- Manual "Check Now" button
-
Better Error Messages
- User-friendly error messages
- Actionable recovery steps
- Hide technical details
-
Cart Recovery
- Save cart to server
- Email recovery link
- Abandoned cart emails
Phase 2: System Reliability (2-3 weeks)
-
Idempotent Registration
- Add idempotency keys
- Check for existing registrations
- Prevent duplicates
-
Session Management Improvements
- Session expiration warnings
- Auto-refresh active sessions
- Better session state tracking
-
Retry Logic
- Automatic retries for transient failures
- Manual retry buttons
- Order draft saving
-
Error Standardization
- Centralized error codes
- Error translation service
- Consistent error handling
Phase 3: Advanced Features (3-4 weeks)
-
Eligibility Pre-check
- Check eligibility in catalog
- Show status on product cards
- Early eligibility request
-
Inline Payment (if possible)
- Direct payment integration
- No external redirect
- Better payment UX
-
Real-time Updates
- WebSocket for payment status
- Real-time cart updates
- Live order status
-
Advanced Cart Features
- Save for later
- Multiple carts
- Cart sharing
Implementation Recommendations
1. Registration Flow Redesign
Current:
Address Step → [Silent Registration] → Continue
Recommended:
Address Step → Registration Step → Availability Step
Implementation:
// Add new RegistrationStep component
export function RegistrationStep() {
const [status, setStatus] = useState<'idle' | 'registering' | 'success' | 'error'>('idle');
const [progress, setProgress] = useState(0);
const handleRegister = async () => {
setStatus('registering');
try {
// Show progress for each system
await registerWithProgress({
onSFAccount: () => setProgress(20),
onSFContact: () => setProgress(40),
onWHMCS: () => setProgress(60),
onPortal: () => setProgress(80),
onMapping: () => setProgress(100),
});
setStatus('success');
} catch (error) {
setStatus('error');
}
};
return (
<RegistrationProgress
status={status}
progress={progress}
onRetry={handleRegister}
/>
);
}
2. Payment Step Improvements
Current:
- Opens WHMCS in new tab
- Polls every 3 seconds
- No manual check option
Recommended:
export function PaymentStep() {
const [waiting, setWaiting] = useState(false);
const [lastCheck, setLastCheck] = useState<Date | null>(null);
const handleOpenWHMCS = async () => {
const url = await getSSOLink();
window.open(url, '_blank');
setWaiting(true);
startPolling();
};
const handleManualCheck = async () => {
setLastCheck(new Date());
const hasPayment = await checkPaymentMethod();
if (hasPayment) {
setWaiting(false);
stopPolling();
}
};
return (
<div>
{waiting && (
<PaymentWaitingState
lastCheck={lastCheck}
onManualCheck={handleManualCheck}
onCancel={() => setWaiting(false)}
/>
)}
</div>
);
}
3. Error Handling Standardization
Recommended:
// Create error code enum
export enum CheckoutErrorCode {
SESSION_EXPIRED = "SESSION_EXPIRED",
REGISTRATION_FAILED = "REGISTRATION_FAILED",
PAYMENT_METHOD_MISSING = "PAYMENT_METHOD_MISSING",
ELIGIBILITY_CHECK_FAILED = "ELIGIBILITY_CHECK_FAILED",
ORDER_CREATION_FAILED = "ORDER_CREATION_FAILED",
}
// Error translation service
export const checkoutErrorMessages: Record<
CheckoutErrorCode,
{
title: string;
message: string;
action?: string;
}
> = {
[CheckoutErrorCode.SESSION_EXPIRED]: {
title: "Session Expired",
message: "Your checkout session has expired. Don't worry, your cart is saved.",
action: "Restart Checkout",
},
// ... more mappings
};
// Usage
function handleError(error: CheckoutError) {
const errorInfo = checkoutErrorMessages[error.code];
showError({
title: errorInfo.title,
message: errorInfo.message,
action: errorInfo.action ? () => handleAction(errorInfo.action) : undefined,
});
}
4. Session Management Improvements
Recommended:
// Add session expiration tracking
export function useCheckoutSession() {
const { checkoutSessionExpiresAt } = useCheckoutStore();
const [timeRemaining, setTimeRemaining] = useState<number | null>(null);
useEffect(() => {
if (!checkoutSessionExpiresAt) return;
const interval = setInterval(() => {
const remaining = new Date(checkoutSessionExpiresAt).getTime() - Date.now();
setTimeRemaining(remaining);
// Warn 10 minutes before expiration
if (remaining < 10 * 60 * 1000 && remaining > 0) {
showWarning("Your checkout session expires in 10 minutes");
}
// Auto-refresh if user is active
if (remaining < 5 * 60 * 1000 && isUserActive()) {
refreshSession();
}
}, 1000);
return () => clearInterval(interval);
}, [checkoutSessionExpiresAt]);
return { timeRemaining };
}
Testing Recommendations
1. E2E Test Scenarios
describe("Checkout Flow", () => {
it("should complete guest checkout for internet order", async () => {
// 1. Browse catalog
// 2. Configure product
// 3. Start checkout
// 4. Enter account info
// 5. Enter address (triggers registration)
// 6. Check eligibility
// 7. Add payment method
// 8. Review and submit
// 9. Verify order created
});
it("should handle registration failure gracefully", async () => {
// Mock registration failure
// Verify address is preserved
// Verify retry works
// Verify no partial accounts created
});
it("should recover from session expiration", async () => {
// Create session
// Wait for expiration
// Verify cart is saved
// Verify can restart checkout
});
});
2. Error Scenario Testing
- Registration failures at each step
- Payment method addition failures
- Session expiration during checkout
- Network failures
- Service outages (WHMCS, Salesforce)
3. Performance Testing
- Registration time (should be < 5 seconds)
- Payment polling responsiveness
- Session creation time
- Cart loading time
Metrics to Track
Conversion Funnel
- Catalog views → Product configuration
- Product configuration → Checkout start
- Checkout start → Account step completion
- Account step → Address step completion
- Address step → Registration completion
- Registration → Payment method added
- Payment method → Order submitted
Error Rates
- Registration failure rate
- Payment method addition failure rate
- Session expiration rate
- Order creation failure rate
User Behavior
- Average time in each step
- Drop-off points
- Retry rates
- Cart abandonment rate
Conclusion
The current checkout system has a solid foundation with good separation of concerns and support for both guest and authenticated flows. However, there are several areas where UX and system predictability can be significantly improved:
Top Priorities:
- Make registration explicit with progress indicators
- Improve payment method addition UX
- Add cart recovery and session management
- Standardize error handling
- Add retry logic and idempotency
Expected Impact:
- Conversion Rate: +15-25% (reduced friction, better error recovery)
- User Satisfaction: +30% (clearer progress, better feedback)
- Support Tickets: -40% (better error messages, self-service recovery)
- System Reliability: +50% (idempotency, retry logic, better error handling)
The recommended improvements are prioritized and can be implemented incrementally without disrupting the current system.