Assist_Design/docs/how-it-works/COMPLETE-GUIDE.md
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

28 KiB
Raw Blame History

Customer Portal Technical Operations Guide

A comprehensive guide for supervisors explaining how the Customer Portal integrates with WHMCS, Salesforce, and other systems.


Contents

  1. System Architecture
  2. Data Ownership and Flow
  3. Account Creation and Linking
  4. Profile and Address Management
  5. Password Management
  6. Product Catalog and Eligibility
  7. Order Creation
  8. Order Fulfillment and Provisioning
  9. Billing and Payments
  10. Subscriptions and Services
  11. SIM Management
  12. Support Cases
  13. Dashboard and Summary Data
  14. Realtime Events
  15. Caching Strategy
  16. Error Handling
  17. Rate Limiting and Security

System Architecture

The portal consists of two main components:

  • Frontend: Next.js application serving the customer UI
  • BFF (Backend-for-Frontend): NestJS API that orchestrates calls to external systems

Connected Systems

System Role Integration Method
WHMCS Billing system of record REST API (API actions)
Salesforce CRM and order management REST API + Change Data Capture (CDC)
Freebit SIM/MVNO provisioning REST API
SFTP (fs.mvno.net) Call/SMS detail records SFTP file download
PostgreSQL Portal user accounts and ID mappings Direct connection
Redis Caching and pub/sub for realtime events Direct connection

ID Mapping

The portal maintains a mapping table (id_mappings in PostgreSQL) linking:

  • user_id (portal UUID) ↔ whmcs_client_id (integer) ↔ sf_account_id (Salesforce 18-char ID)

This mapping is validated on every cross-system operation. The mapping is cached in Redis and can be looked up by any of the three IDs.


Data Ownership and Flow

Data System of Record Portal Behavior
User credentials Portal (PostgreSQL) Passwords hashed with Argon2; no WHMCS/SF credentials stored
Client profile & address WHMCS Portal reads/writes to WHMCS; clears cache on update
Product catalog & prices Salesforce (Pricebook) Portal reads from portal pricebook (configured via PORTAL_PRICEBOOK_ID)
Orders & order status Salesforce (Order object) Portal creates orders; Salesforce CDC triggers fulfillment
Invoices & payment methods WHMCS Portal reads only; payments via WHMCS SSO
Subscriptions/Services WHMCS (tblhosting) Portal reads only
Support cases Salesforce (Case object) Portal creates/reads cases with Origin = "Portal Website"
SIM details & usage Freebit Portal reads/writes via Freebit API

Account Creation and Linking

Salesforce Account Lookup

The portal finds a Salesforce Account using the Customer Number:

SELECT Id, Name, WH_Account__c
FROM Account
WHERE SF_Account_No__c = '{customerNumber}'
Field Purpose
SF_Account_No__c Customer Number field used for lookup
Id The Salesforce Account ID (18-char) stored in id_mappings
WH_Account__c If populated, indicates account is already linked to WHMCS

New Customer Sign-Up

Validation Steps:

  1. Check if email already exists in portal users table
    • If exists with mapping → "You already have an account. Please sign in."
    • If exists without mapping → "Please sign in to continue setup."
  2. Query Salesforce Account by Customer Number (SF_Account_No__c)
    • If not found → "Salesforce account not found for Customer Number"
  3. Check if WH_Account__c field is already populated on Salesforce Account
    • If populated → "You already have an account. Please use the login page."
  4. Check if email exists in WHMCS via GetClientsDetails
    • If found with portal mapping → "You already have an account. Please sign in."
    • If found without mapping → "We found an existing billing account. Please link your account instead."

Creation Steps (if validation passes):

  1. Create WHMCS client via AddClient API action with:
    • Contact info: firstname, lastname, email, phonenumber
    • Address fields: address1, address2, city, state, postcode, country
    • Custom field 198 (configurable): Customer Number
    • Optional custom fields: DOB, Gender, Nationality (field IDs configurable)
    • Password (synced to WHMCS for SSO compatibility)
  2. Create portal user record in PostgreSQL (password hashed with Argon2)
  3. Create ID mapping in same transaction: user_idwhmcs_client_idsf_account_id
  4. Update Salesforce Account with portal fields (see below)

Salesforce Account Fields Updated:

Field Value Notes
Portal_Status__c "Active" Configurable via ACCOUNT_PORTAL_STATUS_FIELD
Portal_Registration_Source__c "Portal" Configurable via ACCOUNT_PORTAL_STATUS_SOURCE_FIELD
Portal_Last_SignIn__c ISO timestamp Configurable via ACCOUNT_PORTAL_LAST_SIGNED_IN_FIELD
WH_Account__c WHMCS Client ID Configurable via ACCOUNT_WHMCS_FIELD

Rollback Behavior:

  • If WHMCS client creation fails → no portal record created
  • If portal DB transaction fails after WHMCS creation → WHMCS client marked Inactive for manual cleanup

Linking Existing WHMCS Account

Flow:

  1. Customer submits WHMCS email and password
  2. Portal validates credentials via WHMCS ValidateLogin API action
  3. Check if WHMCS client is already mapped → "This billing account is already linked. Please sign in."
  4. Portal reads Customer Number from WHMCS custom field 198 via GetClientsDetails
  5. Portal queries Salesforce Account by Customer Number (SF_Account_No__c)
  6. Portal creates local user (without password needs to be set)
  7. Portal inserts ID mapping
  8. Portal updates Salesforce Account with portal flags (Portal_Status__c = "Active", etc.)
  9. Customer is prompted to set a portal password

Profile and Address Management

Data Source

  • All profile/address data is read from WHMCS via GetClientsDetails API action
  • Portal caches client profile for 30 minutes

Updates

  • Profile updates are written to WHMCS via UpdateClient API action
  • Cache is invalidated immediately after successful update
  • Salesforce only receives address snapshots at order creation time (not live synced)

Available Fields

Field WHMCS Field Notes
First Name firstname
Last Name lastname
Email email
Phone phonenumber Format: +CC.NNNNNNNN
Address Line 1 address1
Address Line 2 address2 Optional
City city
State/Prefecture state
Postcode postcode
Country country 2-letter ISO code

Password Management

Portal Password

  • Stored in PostgreSQL hashed with Argon2
  • Completely separate from WHMCS credentials after initial sync
  • WHMCS password is set during signup for SSO compatibility but not synced thereafter

Password Reset Flow

  1. Customer requests reset via email
  2. Portal generates time-limited token (stored in DB)
  3. Email sent with reset link
  4. Customer submits new password with token
  5. All existing sessions are invalidated (token blacklist)
  6. Customer must log in again

Rate Limiting

  • Password reset requests: 5 per 15 minutes per IP
  • Response always: "If an account exists, a reset email has been sent" (prevents enumeration)

Product Catalog and Eligibility

Catalog Source

  • Products pulled from Salesforce Pricebook configured via PORTAL_PRICEBOOK_ID
  • Only products marked for portal visibility are shown
  • Categories: Internet, SIM/Mobile, VPN

Caching

  • Catalog cached in Redis without TTL
  • Invalidated via Salesforce CDC when PricebookEntry records change
  • Volatile/dynamic data uses 60-second TTL

SIM Family Plans

  • Portal checks WHMCS for existing active SIM subscriptions via GetClientsProducts
  • Filter: status = Active AND product in SIM group
  • If active SIM exists → family/discount SIM plans are shown
  • If no active SIM → family plans are hidden

Internet Eligibility

  • Portal queries Salesforce for account-specific eligibility
  • Eligibility result cached per sf_account_id (no TTL, invalidated on Salesforce change)
  • Checks for duplicate active Internet services in WHMCS before allowing order

Order Creation

Pre-Checkout Validation

  1. User must have valid ID mapping (whmcs_client_id exists)
  2. User must have at least one payment method in WHMCS (via GetPayMethods)
  3. For Internet orders: no existing active Internet service in WHMCS

Salesforce Order Structure

Order object fields:

Field Value
AccountId From ID mapping (sf_account_id)
EffectiveDate Today's date
Status "Pending Review"
Pricebook2Id Portal pricebook ID
Type__c Internet / SIM / VPN
Activation_Type__c Immediate / Scheduled
Activation_Schedule__c Date if scheduled
Address fields Snapshot from WHMCS profile

OrderItem records:

  • Created via Salesforce Composite API
  • PricebookEntryId from catalog lookup by SKU
  • Quantity and UnitPrice from pricebook

No Payment Storage

  • Portal verifies payment method exists but does not store or process card data
  • Actual payment occurs in WHMCS after fulfillment creates invoice

Order Fulfillment and Provisioning

Trigger

  • Salesforce CDC detects Order status change (e.g., to "Approved" or "Reactivate")
  • Event is published and picked up by the portal's provisioning queue (BullMQ)
  • Idempotency key prevents duplicate processing

Provisioning Steps

  1. Update Salesforce Activation_Status__c = "Activating"
  2. Map Salesforce OrderItems to WHMCS products
  3. Call WHMCS AddOrder API action:
    • Creates WHMCS order, invoice, and hosting/subscription records
    • paymentmethod: "stripe"
    • promocode: applied if configured
    • noinvoiceemail: true (portal handles notifications)
  4. For SIM orders: call Freebit activation API
  5. Update Salesforce Order with:
    • WHMCS_Order_ID__c
    • Activation_Status__c = "Active" or error status
    • Error codes/messages if failed
  6. Invalidate order cache
  7. Publish realtime event for UI live update

Distributed Transaction

The fulfillment uses a distributed transaction pattern with rollback:

  • If WHMCS creation fails after Salesforce status update → rollback Salesforce status
  • No partial orders are left in WHMCS

Error Handling

Scenario Behavior
Missing payment method Pause with PAYMENT_METHOD_MISSING in Salesforce
WHMCS API failure Mark failed; rollback Salesforce status
Freebit failure Mark failed; error written to Salesforce
Transient failure Retry via BullMQ queue with backoff

Billing and Payments

Invoice Data

  • Fetched from WHMCS via GetInvoices and GetInvoice API actions
  • List cached 90 seconds; individual invoice cached 5 minutes
  • Invalidated by WHMCS webhooks and portal write operations

Payment Methods

  • Fetched from WHMCS via GetPayMethods API action
  • Cached 15 minutes per user
  • Portal transforms WHMCS response to normalized format
  • First payment method marked as default

Payment Gateways

  • Fetched from WHMCS via GetPaymentMethods API action
  • Cached 1 hour (rarely changes)
  • Portal generates WHMCS SSO token via CreateSsoToken API action
  • Destination: index.php?rp=/invoice/{id}/pay
  • Token valid ~60 seconds
  • Payment method or gateway can be pre-selected via URL params
  • Portal normalizes redirect URL to configured WHMCS base URL

Subscriptions and Services

Data Source

  • WHMCS GetClientsProducts API action
  • Returns hosting/subscription records from tblhosting

Cached Fields

Field WHMCS Source
ID id
Product Name name / groupname
Status status (Active, Pending, Suspended, Cancelled, etc.)
Registration Date regdate
Next Due Date nextduedate
Amount amount
Billing Cycle billingcycle

Caching

  • List cached 5 minutes
  • Individual subscription cached 10 minutes
  • Invalidated on WHMCS webhooks or profile updates

SIM Management

For subscriptions identified as SIM products, additional management is available via Freebit API.

Identifying SIM Subscriptions

  • Portal checks if product name contains "SIM" (case-insensitive)
  • SIM management UI only shown for matching subscriptions

Data Retrieval

Account Details (PA05-01 getAcnt / master/getAcnt):

Portal Field Freebit Field
MSISDN msisdn
ICCID iccid
IMSI imsi
EID eid
Plan Code planCode
Status status (active/suspended/cancelled/pending)
SIM Type simType (physical/esim/standard/nano/micro)
Remaining Quota (KB) remainingQuotaKb
Voice Mail voiceMailEnabled
Call Waiting callWaitingEnabled
International Roaming internationalRoamingEnabled
Network Type networkType (4G/5G)

Usage (getTrafficInfo):

  • Today's usage (MB)
  • Monthly usage (MB)
  • Recent daily breakdown

Available Operations

Operation Freebit API Effect Timing
Top-Up Data addSpec / eachQuota Immediate or scheduled
Change Plan PA05-21 changePlan 1st of following month (requires runTime in YYYYMMDD)
Update Voice Features PA05-06 talkoption/changeOrder Immediate
Update Network Type PA05-38 contractline/change Immediate
Cancel SIM Plan PA05-04 releasePlan Scheduled
Cancel SIM Account PA02-04 cnclAcnt Scheduled (requires runDate)
Reissue eSIM reissueEsim / PA05-41 addAcct As scheduled

Voice Feature Values

Feature Enable Value Disable Value
Voice Mail "10" "20"
Call Waiting "10" "20"
World Wing (Roaming) "10" "20"

When enabling World Wing, worldWingCreditLimit is set to "50000" (minimum permitted).

Operation Timing Constraints

Critical rule: 30-minute minimum gap between these operations:

  • Voice feature changes (PA05-06)
  • Network type changes (PA05-38)
  • Plan changes (PA05-21)

Additional constraints:

  • PA05-21 (plan change) and PA02-04 (cancellation) cannot coexist
  • After PA02-04 cancellation is sent, any PA05-21 call will cancel the cancellation
  • Voice/network changes should be made before 25th of month for billing cycle alignment

The portal enforces these constraints with in-memory operation timestamps per Freebit account. Stale entries are cleaned up periodically (entries older than 35 minutes, except cancellations which persist).

Call/SMS History

  • Retrieved via SFTP from fs.mvno.net
  • Files: PASI_talk-detail-YYYYMM.csv, PASI_sms-detail-YYYYMM.csv
  • Available 2 months behind current date (e.g., November can access September)
  • Connection uses SSH key fingerprint verification in production

Support Cases

Salesforce Case Integration

Create Case:

Field Value
AccountId From ID mapping (sf_account_id)
Origin "Portal Website"
Subject Customer input (required)
Description Customer input (required)
Type Customer selection (optional)
Priority Customer selection (optional)
Status "New"

Read Cases:

  • Portal queries Cases where AccountId matches mapped account
  • No caching always live read from Salesforce

Security

  • Cases are strictly filtered to the customer's linked Account
  • If case not found or belongs to different account → "case not found" response

Dashboard and Summary Data

The dashboard aggregates data from multiple sources:

Metric Source Query
Recent Orders Salesforce Orders for account, last 30 days
Pending Invoices WHMCS Invoices with status Unpaid/Overdue
Active Services WHMCS Subscriptions with status Active
Open Cases Salesforce Cases for account with Status ≠ Closed
Next Invoice WHMCS First unpaid invoice by due date
Activity Feed Aggregated Recent invoices, orders, cases combined

Realtime Events

SSE Endpoint

  • Single endpoint: GET /api/events
  • Server-Sent Events (SSE) connection
  • Requires authentication
  • Backed by Redis pub/sub for multi-instance delivery

Event Streams

Channel Events
account:sf:{sf_account_id} Order status updates, fulfillment events
global:catalog Catalog/pricebook invalidation

Connection Management

  • Heartbeat every 30 seconds (account.stream.heartbeat)
  • Ready event on connection (account.stream.ready)
  • Per-user connection limit enforced
  • Rate limit: 30 connection attempts per minute
  • Connection limiter prevents resource exhaustion

Caching Strategy

Redis Key Scoping

All cache keys include user identifier to prevent data mix-ups between customers.

TTL Summary

Data TTL Invalidation Trigger
Catalog None (event-driven) Salesforce CDC on PricebookEntry
Eligibility None Salesforce CDC on Account changes
Orders None (event-driven) Salesforce CDC on Order/OrderItem
ID Mappings None Create/update/delete operations
Invoices (list) 90 seconds WHMCS webhook, write operations
Invoice (detail) 5 minutes WHMCS webhook, write operations
Subscriptions (list) 5 minutes WHMCS webhook, profile update
Subscription (single) 10 minutes WHMCS webhook, profile update
Payment Methods 15 minutes Add/remove operations
Payment Gateways 1 hour Rarely changes
Client Profile 30 minutes Profile/address update
Signup Account Lookup 30 seconds N/A
Support Cases None (live) N/A

Fallback Behavior

  • If cache read fails → fall back to live API call
  • Failures are not cached (prevents stale error states)

Error Handling

General Principles

  • Fail safe with clear messages
  • No partial writes operations are atomic where possible
  • Errors written back to Salesforce for visibility (fulfillment)
  • Generic error messages to customers to avoid information leakage

Error Responses by Area

Area Error Scenario Response
Sign-up Email exists with mapping "You already have an account. Please sign in."
Sign-up Email exists without mapping "Please sign in to continue setup."
Sign-up Customer Number not found "Salesforce account not found for Customer Number"
Sign-up WH_Account__c already set "You already have an account. Please use the login page."
Sign-up Email exists in WHMCS "We found an existing billing account. Please link your account instead."
Sign-up WHMCS client creation fails "Failed to create billing account" (no portal record created)
Sign-up DB transaction fails WHMCS client marked Inactive for cleanup
Link WHMCS client already mapped "This billing account is already linked. Please sign in."
Checkout No payment method Block with "Add payment method" prompt
Checkout Duplicate Internet service Block with explanation
Fulfillment Payment method missing Pause, write PAYMENT_METHOD_MISSING to Salesforce
Billing Invoice not found "Invoice not found" (no data leakage)
Billing WHMCS unavailable "Billing system unavailable, try later"
SIM 30-minute rule violation "Please wait 30 minutes between changes"
SIM Pending cancellation "Plan changes not allowed after cancellation"
SIM Not a SIM subscription "This subscription is not a SIM service"
Support Case not found/wrong account "Case not found"

Rate Limiting and Security

API Rate Limits

Endpoint Category Limit Window
General API 100 requests 60 seconds
Login 3 attempts 15 minutes
Signup 5 attempts 15 minutes
Password Reset 5 attempts 15 minutes
Token Refresh 10 attempts 5 minutes
SSE Connect 30 attempts 60 seconds
Order Creation 5 attempts 60 seconds
Signup Validation 20 attempts 10 minutes

Rate Limit Key

  • Composed of: IP address + User-Agent hash
  • Prevents bypass via User-Agent rotation alone
  • Uses Redis-backed rate-limiter-flexible

Upstream Throttling

  • WHMCS requests queued with concurrency limit
  • Salesforce requests queued with concurrency limit
  • Respects Salesforce daily API limits
  • Graceful degradation when limits approached

Security Features

  • CAPTCHA integration available (Turnstile/hCaptcha)
  • Configurable via AUTH_CAPTCHA_PROVIDER, AUTH_CAPTCHA_SECRET
  • Can be enabled after threshold of failed auth attempts
  • Password reset responses are generic (prevents account enumeration)
  • Cross-account data access returns "not found" (prevents data leakage)
  • Token blacklist for invalidated sessions
  • CSRF protection on state-changing operations

Environment Configuration

Key environment variables for system integration:

Variable Purpose Default
WHMCS_API_URL WHMCS API endpoint -
WHMCS_API_IDENTIFIER WHMCS API credentials -
WHMCS_API_SECRET WHMCS API credentials -
WHMCS_CUSTOMER_NUMBER_FIELD_ID Custom field for Customer Number "198"
SALESFORCE_LOGIN_URL Salesforce auth endpoint -
PORTAL_PRICEBOOK_ID Salesforce Pricebook for catalog -
ACCOUNT_PORTAL_STATUS_FIELD SF Account field for status "Portal_Status__c"
ACCOUNT_WHMCS_FIELD SF Account field for WHMCS ID "WH_Account__c"
FREEBIT_API_URL Freebit API endpoint -
SFTP_HOST MVNO SFTP server "fs.mvno.net"

Last updated: December 2025