Assist_Design/docs/PORTAL-ORDERING-PROVISIONING.md
T. Narantuya 0bf872e249 Refactor code formatting and improve documentation clarity
- Adjusted YAML and JSON files for consistent formatting, including healthcheck commands and package exports.
- Enhanced readability in various TypeScript files by standardizing string quotes and improving line breaks.
- Updated documentation across multiple files to improve clarity and consistency, including address system and logging levels.
- Removed unnecessary package-lock.json from shared package directory to streamline dependencies.
2025-09-09 18:19:54 +09:00

25 KiB
Raw Blame History

Portal Ordering & Provisioning Complete Reference

This document consolidates the complete ordering and provisioning specification, integrating architecture, flows, and implementation details.

Related Documents:

  • ORDER-FULFILLMENT-COMPLETE-GUIDE.md Complete implementation guide with examples
  • SALESFORCE-WHMCS-MAPPING-REFERENCE.md Comprehensive field mapping reference
  • PORTAL-DATA-MODEL.md Field mappings and data structures
  • PRODUCT-CATALOG-ARCHITECTURE.md SKU architecture and catalog implementation
  • SALESFORCE-PRODUCTS.md Complete product setup guide

📖 For complete implementation details, see ORDER-FULFILLMENT-COMPLETE-GUIDE.md

  • Backend: NestJS BFF (apps/bff) with existing integrations: WHMCS, Salesforce
  • Frontend: Next.js portal (apps/portal)
  • Billing: WHMCS (invoices, payment methods, subscriptions)
  • Control plane: Salesforce (review/approval, provisioning trigger)
  • Logging: centralized logger "dino" do not introduce alternate loggers

We require a Customer Number (SF Number) at signup and gate checkout on the presence of a WHMCS payment method. Orchestration runs in the BFF; Salesforce reviews and triggers provisioning.

0) Architecture at a Glance

  • Source of truth
    • Salesforce: Catalog (Product2 + PricebookEntry with portal fields), eligibility, order review/trigger, reporting
    • WHMCS: Customer profile, payment methods, invoices, subscriptions (authoritative)
    • Portal BFF: Orchestration + ID mappings (no customer data authority)
  • Salesforce data model (three-object pattern)
    • Product2 (+ PricebookEntry) with portal fields (catalog + WHMCS mapping)
    • Order (header; one per checkout)
    • OrderItem (child; one per selected product) → references Product2 (and PricebookEntry)
  • Provisioning
    • Operator approves in Salesforce → RecordTriggered Flow publishes Platform Event → BFF reads Order + OrderItems, dereferences Product2 portal fields, calls WHMCS AddOrderAcceptOrder, then writes back WHMCS IDs to Order/OrderItems

1) Customer Experience

  1. Signup (Customer Number required)

    • User provides: Email, Confirm Email, Password, Confirm Password, First/Last Name, optional Company/Phone, and Customer Number (SF Number).
    • Portal validates and links to the existing Salesforce Account using the SF Number; if email differs, proceed and auto-create a Salesforce Case for CS (details in data model doc).
    • WHMCS client is always created on signup (no pre-existing clients expected). WHMCS custom field for Customer Number must be set to the SF Number.
    • Mapping is stored: portalUserId ↔ whmcsClientId ↔ sfAccountId.
  2. Add payment method (required before checkout)

    • Portal shows an “Add payment method” CTA that opens WHMCS payment methods via SSO (POST /auth/sso-linkindex.php?rp=/account/paymentmethods).
    • Portal checks GET /billing/payment-methods/summary to confirm presence before enabling checkout.
  3. Browse catalog and configure

    • /catalog lists products from BFF GET /catalog (reads Salesforce Product2 via BFF).
    • Product detail pages collect configurable options; checkout button disabled until payment method exists.
  4. Place order

    • POST /orders creates a Salesforce Order (Pending Review) and stores orchestration state in BFF. Portal shows “Awaiting review”.
  5. Review & Provision (operator in Salesforce)

  • Recommended (async): Operator reviews/approves. A Record-Triggered Flow publishes Platform Event OrderProvisionRequested__e.
  • BFF subscribes to the event and enqueues a provisioning job. Worker validates payment method, (for eSIM) calls activation API, then AddOrder and AcceptOrder in WHMCS, updates Salesforce Order fields/status.
  • Legacy (webhook): previously called POST /orders/{sfOrderId}/fulfill. This path has been removed.
  1. Completion
    • Subscriptions and invoices appear in portal (/subscriptions, /billing/invoices). Pay via WHMCS SSO links.

1.1 Email Notifications

We will send operational emails at key events (no email validation step required at signup):

  • Signup success: send Welcome email to customer; CC support.
  • eSIM activation: send Activation email to customer; CC support.
  • Order activated: send Activated/Next steps email to customer.

Implementation notes:

  • Reuse centralized logger; no sensitive data in logs.
  • Add a lightweight EmailService abstraction in BFF using existing modules style; queue via BullMQ jobs for reliability (Jobs module already present). Transport to be configured (SMTP/SendGrid) via env.
  • Templates stored server-side; configurable CC list via env.

2) Backend Contracts (BFF)

2.1 Auth & Identity

  • Modify POST /auth/signup (exists) to require sfNumber: string (Customer Number).

    • Steps:
      • Validate request; check portal user exists by email; if exists → error (prompt login/reset).
      • Salesforce: find Account by Customer Number (SF Number). If not found → error.
      • WHMCS: create client unconditionally for this flow. Set Customer Number custom field to SF Number.
      • Create portal user and store mapping.
      • Send Welcome email (to customer) with support on CC.
      • If email mismatch is detected between inputs/systems (e.g., SF Account email vs signup email), automatically create a Salesforce Case to notify CS team and include both emails and Account reference.
    • Security: sanitize errors; never log passwords; use centralized logger.
  • POST /auth/claim (optional future): If we ever separate claim flow from signup.

2.2 Identity & Data Mapping (Portal ↔ Salesforce ↔ WHMCS)

  • Systems of Record

    • Salesforce: Catalog (Product2/PricebookEntry) and process control (Order approvals/status), account eligibility fields. Read-only from portal at runtime except Order creations/updates.
    • WHMCS: Customer details, payment methods, invoices, subscriptions, provisioning outcomes. Source of truth for customer contact/billing data.
    • Portal (BFF): Orchestration state and ID mappings only.
  • Mapping Records (existing):

    • portalUserId ↔ whmcsClientId ↔ sfAccountId stored in BFF mappings service.
    • Customer Number (SF Number) is provided once at signup to create the mapping; we do not ask again.
  • Portal User → Salesforce Account (lookup only)

    • Authoritative lookup key: Customer Number on Account (provided via SF Number at signup).
    • We do not sync profile/address changes from portal to Salesforce.
  • Portal User → WHMCS Client (authoritative for customer profile)

    • Email → email
    • First/Last Name → firstname, lastname
    • Company → companyname (optional)
    • Phone → phonenumber
    • Address: address1, address2, city, state, postcode, country (ISO 2-letter)
    • Custom Field (Customer Number) → set to SF Number (id/name TBD; currently used in mapping)
    • Notes (optional) → include correlation IDs / SF refs
  • Discrepancy handling

    • If SF Account email differs from signup email, proceed and auto-create a Salesforce Case for CS with both emails and Account reference (for review). No sync/write to SF.

2.3 Address Capture (WHMCS only)

  • Capture Requirements

    • Required: street, city, state, postalCode, country
    • Optional: addressLine2, buildingName, roomNumber, phone
    • Validation: client-side + server-side; normalize country to ISO code.
  • UX Flow

    • After signup, prompt to complete Address before catalog/checkout.
    • Dashboard banner if address incomplete.
  • API Usage

    • Extend PATCH /api/me/billing to update WHMCS address fields only. No write to Salesforce.
    • Centralized logging; redact PII.

2.4 Billing

  • GET /billing/payment-methods/summary (new)

    • Returns { hasPaymentMethod: boolean } using WHMCS GetPayMethods for mapped client.
  • POST /auth/sso-link (exists)

    • Used to open WHMCS payment methods and invoice/pay pages.

2.5 Catalog (Salesforce Product2 as Source of Truth)

We will not expose WHMCS catalog directly. Instead, Salesforce Product2 (with PricebookEntry) will be the catalog, augmented with a small set of custom fields used by the portal and BFF.

Custom fields on Product2 (proposal; confirm API names):

  • Identity & Display
    • Product2Categories1__c (Picklist): Internet | SIM | VPN | Other
    • Portal_Description__c (Long Text)
    • Portal_Feature_Bullets__c (Long Text)
    • Portal_Hero_Image_URL__c (URL)
    • Portal_Tags__c (Text)
    • Portal_Sort_Order__c (Number)
    • Portal_Catalog__c (Checkbox, default false)
    • Portal_Valid_From__c / Portal_Valid_Until__c (Date)
  • Terms/Options
    • Portal_Billing_Cycle__c (Picklist): Monthly | Onetime (default)
    • Portal_Max_Quantity__c (Number, default 1)
    • Portal_Requires_Payment_Method__c (Checkbox, default true)
    • Portal_ConfigOptions_JSON__c (Long Text) defaults and allowed values
  • Eligibility (Internet personalization)
    • Portal_Eligibility_Dwelling__c (Picklist): Home | Apartment | Any
    • Portal_Eligibility_Tier__c (Picklist): 1G | 100Mb | Any
    • Portal_Eligibility_Region__c (Text) (optional)
  • WHMCS Mapping
    • WHMCS_Product_Id__c (Number)
    • WHMCS_Notes_Template__c (Long Text)
    • eSIM_Settings_JSON__c (Long Text)

Endpoints (BFF)

  • GET /catalog (exists): return public offerings from Product2 where Portal_Catalog__c = true and within validity dates; price via PricebookEntry for the portal pricebook.

  • GET /catalog/personalized (new):

    • Authenticated: infer sfAccountId from mapping. We only check the SF Number once during signup to create the mapping.
    • Query Product2 filtered by Portal_Catalog__c and validity, then apply eligibility filters using Account fields (e.g., dwelling/tier). eSIM/VPN are always included.
  • Caching & Invalidation

    • Cache global catalog 15m; cache personalized results per sfAccountId 5m.
    • Optional Salesforce CDC/Platform Event to bust cache on Product_Offering__c changes.

2.6 Orders & Provisioning

  • POST /orders (new)

    • Body: { items: { productId, billingCycle, configOptions?, notes? }[], promoCode?, notes? }
    • Server-side checks: require WHMCS mapping; require hasPaymentMethod=true.
    • Actions: Create Salesforce Order (Pending Review), persist orchestration record (sfOrderId, items/config, status=Pending Review, idempotency), return { sfOrderId, status }.
  • GET /orders/:sfOrderId (new)

    • Returns orchestration status and relevant IDs; portal polls for updates.
  • Async Provisioning (Platform Events)

    • Event: OrderProvisionRequested__e with { OrderId__c, IdemKey__c?, CorrelationId__c? }
    • BFF autosubscribes when SF_EVENTS_ENABLED=true; enqueues provisioning job; returns 202 immediately (no inbound SF call).
    • Worker performs the same steps as above and updates Salesforce.

3) Salesforce

3.1 Account matching

  • Personalization Fields (Internet Eligibility)

    • Use the Accounts serviceability/plan eligibility field(s) to decide which Internet product variants to show.
    • Examples (to confirm API names and values):
      • Dwelling_Type__c: Home | Apartment
      • Internet_Tier__c: 1G | 100Mb
    • The BFF personalization endpoint maps these to curated catalog SKUs.
  • Customer Number (SF Number) is authoritative. Signup requires it. We find Account by that number.

  • Mirror SF Number to WHMCS client custom field.

  • If a discrepancy is found (e.g., Account has a different email than signup), create a Salesforce Case automatically with context so CS can triage; proceed with signup (no hard block), but flag the portal user for review.

3.2 Order fields

  • Add the following fields to Order:
    • Activation_Status__c (Pending Review, Activating, Provisioned, Failed)
    • WHMCS_Order_ID__c
    • Optional (if needed): ESIM_ICCID__c

3.2.1 Salesforce Order API & Required Fields (to confirm)

  • Object: Order
  • Required fields for creation (proposal):
    • AccountId (from SF Number lookup)
    • EffectiveDate (today)
    • Status (org-specific; code currently sets "Pending Review" — use your org's draft/review value)
    • Description (optional: include product summary)
  • Custom: Activation_Status__c = Not Started
    • Optional link: OpportunityId (if created/available)
  • On updates during provisioning:
    • Set Activation_Status__c → Activating → Activated/Failed
    • Store WHMCS_Order_ID__c
    • For eSIM: masked ESIM_ICCID__c

3.2.2 Order Line Representation (Salesforce-side, to confirm)

Options (pick one):

  1. Use standard OrderItem with Product2 and Pricebooks (recommended)

    • Pros: native SF pricing and reporting; clean standard model
    • Cons: maintain Product2 and PricebookEntry for all offerings
    • Fields per OrderItem (standard):
      • OrderId, Product2Id, PricebookEntryId, Quantity, UnitPrice
    • Custom fields to add on OrderItem:
      • (Derived billing cycle from SKU class; no custom field)
      • ConfigOptions_JSON__c (Long Text)
  2. Custom child object Order_Offering__c

    • Not used; we standardize on OrderItem.

Decision: Use standard OrderItem with Product2 and portal fields for mapping.

We will build the BFF payload for WHMCS from these line records plus the Order header.

3.2.3 Salesforce ↔ WHMCS Order Mapping

  • Header mapping

    • SF Order.Id → included in WHMCS notes as sfOrderId=<Id>
    • SF AccountId → via portal mapping to whmcsClientIdAddOrder.clientid
    • SF Promo_Code__c (if on header) → AddOrder.promocode
    • SF Activation_Status__c controls operator flow; not sent to WHMCS
  • Line mapping (per OrderItem)

    • Product2 WHMCS_Product_Id__cAddOrder.pid[]
    • Derived billing cycle (Service=Monthly, Activation/Install=Onetime) → AddOrder.billingcycle
    • SF ConfigOptions_JSON__cAddOrder.configoptions
    • Quantity → replicate product ID in pid[] or use config option/quantity if applicable
  • After AddOrder:

    • Call AcceptOrder to provision; capture orderid from response
    • Update SF WHMCS_Order_ID__c; set Activation_Status__c = Activated on success
    • On error, set Activation_Status__c = Failed

3.3 Flow (Provisioning Trigger)

  • RecordTriggered Flow publishes OrderProvisionRequested__e on Order approval.

3.4 UI

3.5 Catalog → Order → Provisioning Linkage (Clean Mapping)

  • Single source of mapping truth: Product2 portal fields

    • WHMCS_Product_Id__c, Portal_ConfigOptions_JSON__c, and Provisioning_Flow__c live on Product2.
    • Do not duplicate these fields on OrderItem; each line references Product2 and price from PricebookEntry.
    • Snapshot only what can change over time: UnitPrice and Quantity on the line.
  • Order construction (by portal at checkout)

  • Create Order header with Activation_Status__c = Not Started.

    • For each cart item, create a line (either OrderItem with custom fields or Order_Offering__c) that includes:
      • Product2Id and PricebookEntryId
      • Quantity, UnitPrice__c
    • Item type is inferred from SKU class (Service vs Activation/Install); no custom field needed.
      • Optional overrides in ConfigOptions_JSON__c (e.g., size, add-ons) based on user selection
  • Provisioning (triggered from Salesforce)

    • BFF receives sfOrderId, loads Order and its lines.
    • For each line, dereference Product2 to fetch WHMCS_Product_Id__c and default config options, then merge with any line-level overrides in ConfigOptions_JSON__c.
    • Build AddOrder payload using the mapping above; place sfOrderId in WHMCS notes.
    • After AcceptOrder, write back:
      • Header: WHMCS_Order_ID__c
      • Header: Activation_Status__c = Activated on success
  • Subscriptions linkage

    • The authoritative subscription record lives in WHMCS.

This keeps the mapping clean and centralized in Product2 portal fields, while Orders/OrderItems act as a snapshot of the customers selection and price at time of checkout.

3.5 Flow Sanity Check

  1. Catalog comes from Salesforce Product2 (filtered/personalized by Account eligibility).
  2. Customer signs up with SF Number; portal creates WHMCS client and mapping; address/profile managed in WHMCS.
  3. Checkout creates an SF Order and child lines (no provisioning yet).
  4. Operator approves in SF; Flow publishes Platform Event.
  5. BFF subscriber enqueues job and provisions: recheck payment method (WHMCS), handle eSIM activation if needed, then AddOrder + AcceptOrder in WHMCS using mappings from Product2 portal fields referenced by the OrderItems.
  6. BFF updates SF Order fields (WHMCS_Order_ID__c, etc.) and status; emails are sent as required.
  7. Customer sees completed order; subscriptions/invoices appear from WHMCS data in the portal.
  • LWC on Order to display provisioning status, errors, WHMCS IDs, and a Retry button.

4) Frontend (Portal)

  • Signup page: add sfNumber field; validation and error messages for missing/invalid SF Number.
  • Payment banner: dashboard shows CTA to add a payment method if none.
  • Catalog: /catalog page using existing BFF endpoint.
  • Product detail + Checkout:
    • Checkout button disabled until hasPaymentMethod=true (via GET /billing/payment-methods/summary).
    • On submit, call POST /orders and redirect to order status page with polling.
  • Order status page: shows statuses (Not Started → Activating → Activated/Failed), with links to Subscriptions and Invoices.

4.1 eSIM Self-service Actions (Service Detail)

  • Actions available on an active eSIM subscription:
    • Reissue eSIM: triggers BFF endpoint to call activation provider for a new profile, updates WHMCS notes/custom fields, sends email to customer.
    • Top-up: triggers BFF to call provider top-up API; invoice/charges handled via WHMCS (AddOrder for add-on or gateway charge depending on implementation), sends email confirmation.
  • UI: buttons gated by subscription status; confirmations and progress states.

5) Security, Idempotency, Observability

  • Secrets in env/KMS, HTTPS-only, strict timeouts and retries with backoff in BFF external calls.
  • Signed Salesforce → BFF requests with short TTL; IP allowlisting of Salesforce egress ranges.
  • Idempotency keys for order creation and provisioning; include sfOrderId marker in WHMCS order notes.
  • Logging: use centralized logger "dino" only; redact sensitive values; no payment data.
  • Metrics: activation latency, WHMCS API error rates, provisioning success/failure, retries; alerts on anomalies.

6) Data Storage (minimal in BFF)

  • Orchestration record: sfOrderId, items/config, status, masked eSIM identifiers, WHMCS order/service IDs, timestamps, idempotency keys.
  • Mappings: userId ↔ whmcsClientId ↔ sfAccountId.
  • No PANs, CVVs, or gateway tokens stored.

7) Work Items

Prerequisites for WHMCS provisioning

  • Each Salesforce Product2 used by the portal must map to a WHMCS Product (pid): set Product2.WHMCS_Product_Id__c accordingly for:
    • Main service SKUs (Internet, SIM/eSIM, VPN)
    • Installation/activation SKUs (Internet install variants, VPN activation)
  • BFF uses Product2.StockKeepingUnit to select PricebookEntry and Product2.WHMCS_Product_Id__c to build the pid[] list for WHMCS AddOrder.
  1. Auth: require sfNumber in SignupDto and signup flow; lookup SF Account by Customer Number; align WHMCS custom field.
  2. Billing: add GET /billing/payment-methods/summary and frontend gating.
  3. Catalog UI: /catalog + product details pages.
  4. Orders API: implement POST /orders, GET /orders/:sfOrderId.
  5. Salesforce: fields, RecordTriggered Flow to publish OrderProvisionRequested__e; LWC for status.
  6. WHMCS: add wrappers for AddOrder, AcceptOrder, GetPayMethods (if not already exposed).
  7. Observability: correlation IDs, metrics, alerts; CDC or Platform Events for cache busting (optional).
  8. Email: implement EmailService with provider; add BullMQ jobs for async sending; add templates for Signup, eSIM Activation, Activated.
  9. eSIM Actions: implement POST /subscriptions/:id/reissue-esim and POST /subscriptions/:id/topup endpoints with BFF provider calls and WHMCS updates.
  10. Future: Cancellations form → Salesforce Cancellations object submission (no immediate service cancel by customer).

8) Acceptance Criteria

  • Signup requires Customer Number (SF Number) and links to the correct Salesforce Account and WHMCS client.
  • Portal blocks checkout until a WHMCS payment method exists; SSO to WHMCS to add card.
  • Orders are created in Salesforce and provisioned via BFF after operator trigger; idempotent and retriable.
  • Customer sees clear order status and resulting subscriptions/invoices; sensitive details are not exposed.

9) WHMCS Field Mapping (Order Creation)

  • AddOrder parameters to use:
    • clientid: from user mapping
    • pid[]: array of WHMCS product IDs (map from our catalog selection)
    • billingcycle: monthly | quarterly | semiannually | annually | etc.
    • configoptions: key/value for configurable options (from product detail form)
    • customfields: include Customer Number (SF Number) and any order-specific data
    • paymentmethod: WHMCS gateway system name (optional if default)
    • promocode: if provided
    • notes: include sfOrderId=<Salesforce Order Id> for idempotency tracing
    • noinvoice / noemail: set to 0 to allow normal invoice + emails unless we handle emails ourselves
  • After creation, call AcceptOrder to provision services and generate invoice/subscription as per WHMCS settings.

9.1 WHMCS Updates for eSIM Actions

  • On Reissue:
    • Update service custom fields (store masked new ICCID/EID if applicable), append to service notes with correlation ID and SF Order/Case references if any.
    • Optionally create a zero-priced order for traceability or a billable add-on as business rules dictate.
  • On Top-up:
    • Create an add-on order or billable item/invoice through WHMCS; capture payment via existing payment method.
    • Record top-up details in notes/custom fields.

10) Endpoint DTOs (Proposed)

  • POST /auth/signup

    • Request: { email, password, firstName, lastName, company?, phone?, sfNumber }
    • Response: { user, accessToken, refreshToken }
  • GET /billing/payment-methods/summary

    • Response: { hasPaymentMethod: boolean }
  • POST /orders

    • Request: { items: { productId: number; billingCycle: string; configOptions?: Record<string,string>; notes?: string }[]; promoCode?: string; notes?: string }
    • Response: { sfOrderId: string; status: 'Pending Review' }
  • GET /orders/:sfOrderId

    • Response: { sfOrderId, status, whmcsOrderId?, whmcsServiceIds?: number[], lastUpdatedAt }

// No SF → portal endpoint is required; provisioning is triggered via Platform Events.

  • POST /subscriptions/:id/reissue-esim

    • Request: { reason?: string }
    • Response: { status: 'InProgress' | 'Completed' | 'Failed', activationRef?, maskedIccid?, errorMessage? }
  • POST /subscriptions/:id/topup

    • Request: { amount?: number; packageCode?: string }
    • Response: { status: 'Completed' | 'Failed', invoiceId?, errorMessage? }

11) Email Requirements

  • Transport: configurable (SMTP/SendGrid) via env; no secrets logged.
  • Events & templates (to be provided):
    • Signup Welcome (customer, CC support)
    • eSIM Activation (customer, CC support)
    • Order Provisioned (customer)
  • Include correlation ID and minimal order/service context; no sensitive values.

11.1 Email Provider Recommendation

  • Primary: SendGrid API (robust deliverability, templates, analytics). Use API key via env; send via BullMQ job for resiliency.
  • Fallback: SMTP (e.g., SES SMTP or company SMTP relay) for environments without SendGrid.
  • Rationale: SendGrid simplifies templating and CC/BCC handling; API-based sending reduces SMTP variability. Keep centralized logging without leaking PII.

12) Open Questions (to confirm)

  1. Salesforce
    • Confirm the Customer Number field API name on Account used for lookup.
    • Confirm the exact custom field API names on Order (Provisioning_Status__c, etc.).
    • Should OpportunityId be mandatory for Orders we create?
  2. WHMCS
    • Confirm the custom field id/name for Customer Number (currently used in mapping; assumed id 198).
    • Provide product ID mapping for Home Internet/eSIM/VPN and their configurable options keys.
    • Preferred default paymentmethod gateway system name.
  3. Email
    • Preferred provider (SMTP vs SendGrid) and from/reply-to addresses.
    • Support CC distribution list for ops; any BCC requirements?
    • Provide or approve email templates (copy + branding).
  4. eSIM Activation API
    • Endpoint(s), auth scheme, required payload, success/failed response shapes.
    • Which identifiers to store/mask (ICCID, EID, MSISDN) and masking rules.
  5. Provisioning Trigger
    • Trigger via Flow on status change to Approved; optional manual retry publishes event again.
    • Retry/backoff limits expected from SF side?
  6. Cancellations
    • Cancellation object API name in Salesforce; required fields; desired intake fields in portal form; who should be notified.