# Salesforce Platform Events & CDC

This guide documents all Platform Events and CDC (Change Data Capture) channels used for real-time communication between Salesforce and the Customer Portal.

## Overview

The BFF subscribes to Salesforce events using the Pub/Sub API and reacts by:

1. **Invalidating caches** - Ensuring fresh data on next request
2. **Sending SSE events** - Notifying connected portal clients to refetch
3. **Creating in-app notifications** - For significant status changes

## Event Types

| Type                | Channel Prefix | Purpose                              |
| ------------------- | -------------- | ------------------------------------ |
| **Platform Events** | `/event/`      | Custom events from Salesforce Flows  |
| **CDC**             | `/data/`       | Standard object change notifications |

## Architecture

```
Salesforce Record Change
    ↓
┌─────────────────────────────────────────┐
│ Platform Events: Record-Triggered Flow  │
│ CDC: Automatic change tracking          │
└─────────────────────────────────────────┘
    ↓
BFF Pub/Sub Subscriber
    ↓
┌────────────────────────────────────────┐
│ 1. Invalidate Redis cache              │
│ 2. Send SSE to connected portal        │
│ 3. Create notification (if applicable) │
└────────────────────────────────────────┘
```

---

## Platform Events

### 1. Account Update Event

**Purpose:** Notify portal when eligibility or verification status changes.

**Channel:** `SF_ACCOUNT_EVENT_CHANNEL` (default: `/event/Account_Eligibility_Update__e`)

#### Fields

| Field               | API Name                 | Type      | Required  | When to Include                                     |
| ------------------- | ------------------------ | --------- | --------- | --------------------------------------------------- |
| Account ID          | `Account_Id__c`          | Text(255) | ✅ Always | Always                                              |
| Eligibility Status  | `Eligibility_Status__c`  | Text(255) | Optional  | Only if `ISCHANGED(Internet_Eligibility_Status__c)` |
| Verification Status | `Verification_Status__c` | Text(255) | Optional  | Only if `ISCHANGED(Id_Verification_Status__c)`      |
| Rejection Message   | `Rejection_Message__c`   | Text(255) | Optional  | Only if Verification_Status\_\_c = "Rejected"       |

#### Salesforce Flow Logic

**Trigger:** Record update on Account when ANY of these fields change:

- `Internet_Eligibility__c`
- `Internet_Eligibility_Status__c`
- `Id_Verification_Status__c`
- `Id_Verification_Rejection_Message__c`

**Event Payload:**

```
Account_Id__c = {!$Record.Id}

IF ISCHANGED({!$Record.Internet_Eligibility_Status__c}) THEN
  Eligibility_Status__c = {!$Record.Internet_Eligibility_Status__c}
END IF

IF ISCHANGED({!$Record.Id_Verification_Status__c}) THEN
  Verification_Status__c = {!$Record.Id_Verification_Status__c}
  IF {!$Record.Id_Verification_Status__c} = "Rejected" THEN
    Rejection_Message__c = {!$Record.Id_Verification_Rejection_Message__c}
  END IF
END IF
```

#### BFF Behavior

| Scenario                               | Cache Action    | SSE Event            | Notification              |
| -------------------------------------- | --------------- | -------------------- | ------------------------- |
| Eligibility value changes (not status) | Invalidate both | ✅ `account.updated` | ❌                        |
| Eligibility status → Pending           | Invalidate both | ✅ `account.updated` | ❌                        |
| Eligibility status → Eligible          | Invalidate both | ✅ `account.updated` | ✅ ELIGIBILITY_ELIGIBLE   |
| Eligibility status → Ineligible        | Invalidate both | ✅ `account.updated` | ✅ ELIGIBILITY_INELIGIBLE |
| Verification status → Submitted        | Invalidate both | ✅ `account.updated` | ❌                        |
| Verification status → Verified         | Invalidate both | ✅ `account.updated` | ✅ VERIFICATION_VERIFIED  |
| Verification status → Rejected         | Invalidate both | ✅ `account.updated` | ✅ VERIFICATION_REJECTED  |
| Rejection message changes only         | Invalidate both | ✅ `account.updated` | ❌                        |

---

### 2. Case Status Update Event

**Purpose:** Notify portal when a support case status changes.

**Channel:** `SF_CASE_EVENT_CHANNEL` (default: `/event/Case_Status_Update__e`)

#### Fields

| Field      | API Name        | Type      | Required  |
| ---------- | --------------- | --------- | --------- |
| Account ID | `Account_Id__c` | Text(255) | ✅ Always |
| Case ID    | `Case_Id__c`    | Text(255) | ✅ Always |

#### Salesforce Flow Logic

**Trigger:** Record update on Case when Status changes (and Origin = "Portal Support")

**Event Payload:**

```
Account_Id__c = {!$Record.AccountId}
Case_Id__c = {!$Record.Id}
```

#### BFF Behavior

| Action                                 | Description               |
| -------------------------------------- | ------------------------- |
| Invalidate `support:cases:{accountId}` | Clear case list cache     |
| Invalidate `support:messages:{caseId}` | Clear case messages cache |
| SSE `support.case.changed`             | Notify portal to refetch  |

---

### 3. Order Provision Requested Event

**Purpose:** Trigger order fulfillment when Salesforce Order is approved.

**Channel:** `/event/OrderProvisionRequested__e`

See `docs/how-it-works/order-fulfillment.md` for details.

---

## CDC (Change Data Capture)

### 1. Product2 CDC

**Purpose:** Detect product catalog changes.

**Channel:** `SF_CATALOG_PRODUCT_CDC_CHANNEL` (default: `/data/Product2ChangeEvent`)

#### BFF Behavior

| Action                     | Description                       |
| -------------------------- | --------------------------------- |
| Invalidate product cache   | Clear affected products           |
| Fallback full invalidation | If no specific products found     |
| SSE `services.changed`     | Notify portals to refetch catalog |

---

### 2. PricebookEntry CDC

**Purpose:** Detect pricing changes.

**Channel:** `SF_CATALOG_PRICEBOOKENTRY_CDC_CHANNEL` (default: `/data/PricebookEntryChangeEvent`)

#### BFF Behavior

| Action                   | Description                           |
| ------------------------ | ------------------------------------- |
| Filter by pricebook      | Only process portal pricebook changes |
| Invalidate product cache | Clear affected products               |
| SSE `services.changed`   | Notify portals to refetch catalog     |

---

### 3. Order CDC

**Purpose:** Detect order changes + trigger provisioning.

**Channel:** `SF_ORDER_CDC_CHANNEL` (default: `/data/OrderChangeEvent`)

#### BFF Behavior

| Scenario                              | Action                         |
| ------------------------------------- | ------------------------------ |
| `Activation_Status__c` → "Activating" | Enqueue provisioning job       |
| Customer-facing field changes         | Invalidate order cache + SSE   |
| Internal field changes only           | Ignore (no cache invalidation) |

**Internal fields (ignored):** `Activation_Status__c`, `WHMCS_Order_ID__c`, `Activation_Error_*`

---

### 4. OrderItem CDC

**Purpose:** Detect order line item changes.

**Channel:** `SF_ORDER_ITEM_CDC_CHANNEL` (default: `/data/OrderItemChangeEvent`)

#### BFF Behavior

| Scenario                      | Action                         |
| ----------------------------- | ------------------------------ |
| Customer-facing field changes | Invalidate order cache + SSE   |
| Internal field changes only   | Ignore (no cache invalidation) |

**Internal fields (ignored):** `WHMCS_Service_ID__c`

---

## Environment Variables

```bash
# Platform Events
SF_ACCOUNT_EVENT_CHANNEL=/event/Account_Eligibility_Update__e
SF_CASE_EVENT_CHANNEL=/event/Case_Status_Update__e

# Catalog CDC
SF_CATALOG_PRODUCT_CDC_CHANNEL=/data/Product2ChangeEvent
SF_CATALOG_PRICEBOOKENTRY_CDC_CHANNEL=/data/PricebookEntryChangeEvent

# Order CDC
SF_ORDER_CDC_CHANNEL=/data/OrderChangeEvent
SF_ORDER_ITEM_CDC_CHANNEL=/data/OrderItemChangeEvent

# Enable/disable all CDC subscriptions (Platform Events always enabled)
SF_EVENTS_ENABLED=true
```

---

## BFF Implementation

### Subscribers

| Subscriber                | Events                   | Channel   | File                           |
| ------------------------- | ------------------------ | --------- | ------------------------------ |
| `AccountEventsSubscriber` | Account                  | `/event/` | `account-events.subscriber.ts` |
| `CaseEventsSubscriber`    | Case                     | `/event/` | `case-events.subscriber.ts`    |
| `CatalogCdcSubscriber`    | Product2, PricebookEntry | `/data/`  | `catalog-cdc.subscriber.ts`    |
| `OrderCdcSubscriber`      | Order, OrderItem         | `/data/`  | `order-cdc.subscriber.ts`      |

### Shared Utilities (`shared/`)

| File                | Purpose                              |
| ------------------- | ------------------------------------ |
| `pubsub.service.ts` | Salesforce Pub/Sub client management |
| `pubsub.utils.ts`   | Payload extraction helpers           |
| `pubsub.types.ts`   | TypeScript types                     |
| `index.ts`          | Public exports                       |

---

## Testing

1. **Salesforce Workbench:** Use "REST Explorer" to publish test events
2. **BFF Logs:** Watch for event received messages
3. **Redis:** Verify cache keys are deleted after event

## Troubleshooting

| Issue                    | Cause                     | Solution                                  |
| ------------------------ | ------------------------- | ----------------------------------------- |
| Events not received      | Pub/Sub connection failed | Check BFF logs for connection errors      |
| Cache not invalidated    | Wrong ID in payload       | Verify Flow is sending correct ID         |
| Notification not created | Status not in final state | Only final states trigger notifications   |
| Duplicate notifications  | Same event re-delivered   | Dedupe logic handles this (1 hour window) |
