barsa 7c929eb4dc Update Customer Portal Documentation and Remove Deprecated Files
- Streamlined the README.md for clarity and conciseness.
- Deleted outdated documentation files related to Freebit SIM management, SIM management API data flow, and various architectural guides to reduce clutter and improve maintainability.
- Updated the last modified date in the README to reflect the latest changes.
2025-12-23 15:43:36 +09:00

9.9 KiB

Address System Documentation

Overview

Our address system follows a clean, logical flow where address is required at signup and managed throughout the user lifecycle. This eliminates surprises during checkout and ensures data integrity.

🎯 Core Logic

Registration → Required Address → WHMCS Storage → Order Snapshots

Architecture

graph TD
    A[User Registration] --> B[Required Address Validation]
    B --> C[WHMCS Client Creation]
    C --> D[Complete Profile]

    D --> E[Checkout Flow]
    E --> F{Order Type?}
    F -->|Internet| G[Explicit Address Verification]
    F -->|Other| H[Auto-Confirm Address]

    G --> I[Address Snapshot]
    H --> I
    I --> J[Salesforce Order]

    D --> K[Profile Management]
    K --> L[Address Updates]
    L --> M[WHMCS Sync]

Key Components

Backend

  • SignupDto - Address required at registration
  • UsersService.updateAddress() - WHMCS sync
  • OrderBuilder.addAddressSnapshot() - Salesforce snapshots

Frontend

  • AddressConfirmation - Checkout verification
  • ProfileCompletionGuard - Handles incomplete profiles
  • /account/billing - Address management

Order Type Flows

Order Type Address Behavior
Internet Explicit verification required (technician visit)
SIM/VPN/Other Auto-confirm (exists from signup)

Implementation

1. Signup Flow (Required Address)

Backend Validation

// apps/bff/src/auth/dto/signup.dto.ts
export class AddressDto {
  @IsNotEmpty() line1: string; // Required
  @IsOptional() line2?: string; // Optional
  @IsNotEmpty() city: string; // Required
  @IsNotEmpty() state: string; // Required
  @IsNotEmpty() postalCode: string; // Required
  @IsNotEmpty() country: string; // Required
}

export class SignupDto {
  @ValidateNested()
  @Type(() => AddressDto)
  address: AddressDto; // ✅ REQUIRED - not optional
}

WHMCS Client Creation

// apps/bff/src/auth/auth.service.ts
await this.whmcsService.addClient({
  address1: address.line1, // Required field
  address2: address.line2 || "", // Optional field
  city: address.city, // Required field
  state: address.state, // Required field
  postcode: address.postalCode, // Required field
  country: address.country, // Required field
});

2. Checkout Flow

Address Confirmation Logic

// apps/portal/src/components/checkout/address-confirmation.tsx
// Since address is required at signup, it should always be complete
if (requiresAddressVerification) {
  // Internet orders: require explicit verification
  setAddressConfirmed(false);
  onAddressIncomplete(); // Keep disabled until confirmed
} else {
  // Other orders: auto-confirm since address exists from signup
  onAddressConfirmed(data.address);
  setAddressConfirmed(true);
}

Internet Order Verification

{isInternetOrder && !addressConfirmed && (
  <div className="bg-blue-50 border border-blue-200 rounded-lg p-4 mb-4">
    <p className="text-sm text-blue-800">
      <strong>Internet Installation Address Verification Required</strong>
    </p>
    <p className="text-sm text-blue-700 mt-1">
      Please verify this is the correct address for your internet installation.
      A technician will visit this location for setup.
    </p>
  </div>
)}

3. Address Updates & WHMCS Sync

// apps/bff/src/users/users.service.ts
async updateAddress(userId: string, address: UpdateAddressDto): Promise<void> {
  const mapping = await this.mappingsService.findByUserId(userId);

  // Prepare WHMCS update data
  const whmcsUpdateData = {
    address1: billingData.street,
    address2: billingData.streetLine2,
    city: billingData.city,
    state: billingData.state,
    postcode: billingData.postalCode,
    country: billingData.country,
  };

  // Update in WHMCS (authoritative source)
  await this.whmcsService.updateClient(mapping.whmcsClientId, whmcsUpdateData);
}

4. Order Address Snapshots

// apps/bff/src/orders/services/order-builder.service.ts
private async addAddressSnapshot(orderFields: Record<string, unknown>, userId: string, body: CreateOrderBody): Promise<void> {
  const address = await this.usersService.getAddress(userId);
  const orderAddress = (body.configurations as any)?.address;
  const addressChanged = !!(orderAddress);
  const addressToUse = orderAddress || billingInfo.address;

  // Combine street lines for Salesforce
  const fullStreet = [addressToUse?.street, addressToUse?.streetLine2]
    .filter(Boolean)
    .join(", ");

  // Always populate billing address fields
  orderFields["BillToStreet"] = fullStreet || "";
  orderFields["BillToCity"] = addressToUse?.city || "";
  orderFields["BillToState"] = addressToUse?.state || "";
  orderFields["BillToPostalCode"] = addressToUse?.postalCode || "";
  orderFields["BillToCountry"] = addressToUse?.country || "";

  // Set change flag
  orderFields["Address_Changed__c"] = addressChanged;
}

Field Mapping

WHMCS ↔ Internal ↔ Salesforce

WHMCS          → Internal      → Salesforce Order
address1       → street        → BillToStreet (combined)
address2       → streetLine2   → BillToStreet (combined)
city           → city          → BillToCity
state          → state         → BillToState
postcode       → postalCode    → BillToPostalCode
country        → country       → BillToCountry

// Change detection
N/A            → addressChanged → Address_Changed__c (boolean)

API Reference

Address Management

// Get current address
GET /api/users/billing
Response: {
  address: {
    street: string | null;
    streetLine2: string | null;
    city: string | null;
    state: string | null;
    postalCode: string | null;
    country: string | null;
  };
  isComplete: boolean;
}

// Update address
PATCH /api/users/billing
Body: {
  street?: string;
  streetLine2?: string;
  city?: string;
  state?: string;
  postalCode?: string;
  country?: string;
}

Quick Patterns

Protect Route with Profile Completion

<ProfileCompletionGuard requireComplete={true}>
  <YourComponent />
</ProfileCompletionGuard>

Handle Address in Checkout

<AddressConfirmation
  onAddressConfirmed={handleConfirmed}
  onAddressIncomplete={handleIncomplete}
  orderType={orderType}
/>

Check Profile Completion

const { isComplete, loading, redirectToCompletion } = useProfileCompletion();

User Flows

1. New User Registration

  1. User fills registration form with required address
  2. Backend validates all address fields
  3. WHMCS client created with complete address
  4. User can immediately access all features

2. Internet Order Checkout

  1. User selects Internet service
  2. Address confirmation component loads
  3. Shows current address with verification requirement
  4. User must explicitly confirm installation address
  5. Order created with address snapshot

3. Other Order Checkout

  1. User selects SIM/VPN/Other service
  2. Address confirmation component loads
  3. Auto-confirms address (exists from signup)
  4. Order created with address snapshot

4. Address Updates

  1. User goes to /account/billing
  2. Can edit and save address changes
  3. Changes sync to WHMCS immediately
  4. Future orders use updated address

Testing

Quick Commands

# Test required address validation
curl -X POST /api/auth/signup -d '{"address": {...}}'

# Get user address
curl -X GET /api/users/billing

# Update address
curl -X PATCH /api/users/billing -d '{"street": "New St"}'

Test Scenarios

  1. New User Signup: Verify address is required and validated
  2. Internet Order: Verify explicit address confirmation required
  3. SIM Order: Verify address auto-confirmed
  4. Address Update: Verify WHMCS sync works

Troubleshooting

Common Issues

"Address not found during checkout"

  • Cause: User somehow has incomplete profile
  • Solution: ProfileCompletionGuard will redirect to completion

"Internet order won't submit"

  • Cause: Address not explicitly confirmed
  • Solution: User must click "Confirm Installation Address"

"Address changes not saving"

  • Cause: WHMCS API connection issue
  • Solution: Check WHMCS credentials and API access

Quick Debug

  1. Address not required? → Check SignupDto.address is not @IsOptional()
  2. Internet order auto-confirms? → Check orderType === "Internet" logic
  3. Address not in Salesforce? → Check addAddressSnapshot() call
  4. WHMCS not updating? → Check API credentials and updateClient() method

Configuration

Environment Variables

# WHMCS API
WHMCS_API_URL=https://your-whmcs.com/includes/api.php
WHMCS_API_IDENTIFIER=your_api_identifier
WHMCS_API_SECRET=your_api_secret

# Salesforce Field Mapping (optional overrides)
ORDER_ADDRESS_CHANGED_FIELD=Address_Changed__c
ORDER_BILL_TO_STREET_FIELD=BillToStreet
ORDER_BILL_TO_CITY_FIELD=BillToCity
ORDER_BILL_TO_STATE_FIELD=BillToState
ORDER_BILL_TO_POSTAL_CODE_FIELD=BillToPostalCode
ORDER_BILL_TO_COUNTRY_FIELD=BillToCountry

Benefits

User Experience

  • No surprise address requirements during checkout
  • Clear completion flows for edge cases
  • Order-type specific verification (Internet vs Others)

Data Integrity

  • Address required at source (signup)
  • Single source of truth (WHMCS)
  • Point-in-time snapshots (Salesforce orders)

Performance

  • No unnecessary dashboard banners
  • Simplified checkout validation
  • Efficient profile completion checks

Maintainability

  • Logical flow from signup to order
  • Reusable profile completion components
  • Clean separation of concerns

This address system provides a clean, logical, and modern approach to address management with required addresses at signup and intelligent order-type specific flows.