# Gateway Lifecycle Workflow (ClickHouse-Only)

This document describes the complete Gateway Lifecycle flow used by the Billing UI page:

- URL: `/admin/billing-admin/gateway-lifecycle`
- Data source: **ClickHouse only** for runtime lifecycle/alert/incident views
- No runtime dependency on `gateway_incidents` table in PostgreSQL

---

## 1. Page Flow Overview

1. User opens Gateway Lifecycle page (Filament page).
2. Frontend fetches bearer token (`/admin/api/tokens/current`).
3. Frontend calls Billing APIs with tenant + filter parameters.
4. Billing controller resolves tenant (`app_id`) + auth.
5. Controller delegates lifecycle analytics to `GatewayJourneyClickhouseService`.
6. Service queries ClickHouse journey tables, derives status/reason/severity.
7. Frontend renders KPI cards + DataTables.

---

## 2. Runtime Data Sources (DB + Tables)

## ClickHouse Database
Configured in `config/services.php` under `services.clickhouse`:
- `CLICKHOUSE_SCHEME`
- `CLICKHOUSE_HOST`
- `CLICKHOUSE_PORT`
- `CLICKHOUSE_DATABASE`
- `CLICKHOUSE_USERNAME`
- `CLICKHOUSE_PASSWORD`
- `CLICKHOUSE_TIMEOUT_SECONDS`

## Tables used
1. `user_journey_logs`
2. `user_journey_event_master`

These are joined to build lifecycle events.

## Not used for runtime page rendering
- PostgreSQL `gateway_incidents` table

---

## 3. Backend Components

1. Page class
- `app/Filament/Resources/BillingAdminResource/Pages/GatewayLifecycle.php`

2. UI view + JS
- `resources/views/filament/resources/billing-admin-resource/pages/gateway-lifecycle.blade.php`

3. API controller
- `app/Http/Controllers/Billing/BillingWidgetController.php`

4. ClickHouse service (core logic)
- `app/Services/Billing/GatewayJourneyClickhouseService.php`

5. Routes
- `routes/api.php`

---

## 4. API Endpoints Used by the Page

1. Alerts banner
- `GET /admin/api/billing/gateway-alerts`
- Controller: `BillingWidgetController@getGatewayAlerts`
- Source: `GatewayJourneyClickhouseService::getGatewayAlertsSnapshot(...)`

2. Summary cards
- `GET /admin/api/billing/gateway-lifecycle/summary`
- Controller: `BillingWidgetController@getGatewayLifecycleSummary`
- Source:
  - `getLifecycleSummary(...)` for total/failed/resumed/revoked/hold
  - `getGatewayHealthKpis(...)` for warning/critical/recovery/healthy

3. Main lifecycle table
- `GET /admin/api/billing/gateway-lifecycle/get-listing`
- Controller: `BillingWidgetController@getGatewayLifecycleListing`
- Source: `getLifecycleListing(...)`

4. Incidents history table (computed incidents)
- `GET /admin/api/billing/gateway-incidents/get-listing`
- Controller: `BillingWidgetController@getGatewayIncidentsListing`
- Source: `getGatewayIncidentsListing(...)`

---

## 5. Filters and Query Parameters

Frontend sends:
- `app_id` (encrypted)
- `role_id`
- `date_from`
- `date_to`
- `payment_gateway`
- `status`
- `reason_code`
- `search`

Service filter handling:
- `payment_gateway` maps to `gateway`
- date range maps to `event_time`
- status maps to `lifecycle_status`
- reason maps to `reason_code`

---

## 6. Lifecycle Event Derivation Logic

Base query (`baseJourneyLifecycleSql`) joins:
- `user_journey_logs l`
- `user_journey_event_master em`

Join condition:
- `l.event` -> `em.id`

Only order lifecycle events are considered:
- `startsWith(lower(em.event), 'order_')`

Derived fields include:
- `gateway`
- `app_id`
- `event_name`
- `payment_ref`
- `order_ref`
- `amount`
- `event_time`
- `reason_text`
- `raw_payload`
- `lifecycle_status`
- `reason_code`

---

## 7. Status Mapping

`lifecycle_status` derivation:
- `order_failed` -> `failed`
- `order_resumed` -> `resumed`
- `order_revoked` -> `revoked`
- `order_paused` or `order_on_hold` -> `hold`
- anything else -> `info`

---

## 8. Reason Mapping

`reason_code` derivation (text match on event/details/others_info):
- contains `insufficient` -> `insufficient_funds`
- contains `declin` -> `card_declined`
- contains `expire` -> `card_expired`
- event is revoked or contains `revoke` -> `payment_method_revoked`
- paused/on-hold or contains `hold|pause|suspend` -> `subscription_paused`
- fallback -> `other`

UI label mapping:
- `insufficient_funds` => Insufficient Funds
- `card_declined` => Card Declined
- `card_expired` => Card Expired
- `payment_method_revoked` => Payment Method Revoked
- `subscription_paused` => Subscription Paused/Hold
- `other` => Other

---

## 9. Health / Alert Severity Logic

Severity thresholds come from `config/gateway_health.php`:
- `min_sample_size`
- `heartbeat_min_attempts`
- `warning_success_rate`
- `critical_success_rate`
- `warning_failure_rate`
- `critical_failure_rate`

Derived outcome per gateway:
- `critical` if severe degradation
- `warning` if moderate degradation
- `healthy` otherwise

These feed:
- banner (`gateway-alerts`)
- health cards (`warning`, `critical`, `recovery`, `healthy`)
- computed incidents table

## Formula and Logic (KPI Cards)

This section defines exactly how each card is computed in current implementation.

## Inputs
For each gateway (grouped by `gateway`) in selected filters (`app_id`, date range, gateway filter):

- `total_count = COUNT(*)`
- `success_count = COUNT(lifecycle_status IN ('resumed','info'))`
- `failed_count = COUNT(lifecycle_status IN ('failed','revoked','hold'))`
- `resumed_count = COUNT(lifecycle_status = 'resumed')`

Derived rates:
- `success_rate = (success_count / max(total_count,1)) * 100`
- `failure_rate = (failed_count / max(total_count,1)) * 100`

Thresholds from config:
- `min_sample_size`
- `heartbeat_min_attempts`
- `warning_success_rate`
- `critical_success_rate`
- `warning_failure_rate`
- `critical_failure_rate`

Per-gateway severity:
1. If `total_count < min_sample_size` => `healthy`
2. Else if `(success_count == 0 AND total_count >= heartbeat_min_attempts)`
   OR `success_rate <= critical_success_rate`
   OR `failure_rate >= critical_failure_rate`
   => `critical`
3. Else if `success_rate <= warning_success_rate`
   OR `failure_rate >= warning_failure_rate`
   => `warning`
4. Else => `healthy`

## Primary Lifecycle Cards

- `Total Events`:
  - `total = failed + resumed + revoked + hold + info`
  - From grouped `lifecycle_status` counts.

- `Failed`:
  - Count of rows where `lifecycle_status = 'failed'`.

- `Resumed`:
  - Count of rows where `lifecycle_status = 'resumed'`.

- `Revoked`:
  - Count of rows where `lifecycle_status = 'revoked'`.

- `Hold/Pause`:
  - Count of rows where `lifecycle_status = 'hold'`.

## Health Cards

- `Warning`:
  - Number of gateways whose computed severity is `warning`.

- `Critical`:
  - Number of gateways whose computed severity is `critical`.

- `Healthy`:
  - Number of gateways whose computed severity is `healthy`.

- `Recovery`:
  - Number of gateways where:
    - `resumed_count > 0` AND `failed_count > 0`
  - Interpreted as “had failure signals and also resumed events in same filtered window”.

## Notes

- These health cards are **gateway-level aggregates**, not raw event counts.
- `Revoked` and `Hold/Pause` are treated as failure-side events for severity computation.
- With very low sample (`total_count < min_sample_size`), severity is forced to `healthy` to avoid noisy alerts.

---

## 10. Frontend Rendering Blocks

1. Filter box
- date range, gateway, status, reason, search

2. Primary lifecycle KPI cards
- Total Events, Failed, Resumed, Revoked, Hold/Pause

3. Health KPI cards
- Warning, Critical, Recovery, Healthy

4. Main lifecycle DataTable
- gateway, status, reason, payment ref, order ref, amount, event time, action

5. Gateway incidents history DataTable
- gateway, severity, status, started, recovered, duration, fail rate, success rate, failed hits, total hits

---

## 11. Operational Commands

1. Clear caches after lifecycle code/config updates:
```bash
php artisan optimize:clear
```

2. Check routes:
```bash
php artisan route:list | rg "gateway-lifecycle|gateway-incidents|gateway-alerts"
```

3. Tail logs:
```bash
tail -f storage/logs/laravel.log
```

---

## 12. Troubleshooting Playbook

1. Empty page / 500 on summary
- Check `GatewayJourneyClickhouseService::runSelect failed` in logs
- Verify ClickHouse env vars (`services.clickhouse.*`)
- Validate table names exist in ClickHouse:
  - `user_journey_logs`
  - `user_journey_event_master`

2. Filters not working
- Confirm frontend sends `payment_gateway`
- Confirm service reads `payment_gateway` (not legacy key)

3. No rows but no error
- Verify data exists for selected `app_id` and date range
- Widen date range and clear gateway/status filters

4. Misclassified reasons/status
- Inspect raw payload columns (`details`, `others_info`) in `user_journey_logs`
- Adjust text match rules in `GatewayJourneyClickhouseService`

---

## 13. Extending to New Gateway Events

When adding a new gateway or lifecycle event:

1. Add gateway identification logic in `baseJourneyLifecycleSql`.
2. Extend status mapping rules for new `order_*` events.
3. Extend reason mapping pattern list.
4. Add/update UI badge mapping if needed.
5. Run smoke checks:
   - summary endpoint
   - listing endpoint
   - alerts endpoint
   - incidents endpoint

---

## 14. Current Contract Summary

Gateway Lifecycle page contract:
- Runtime reads lifecycle/alert/incident views from ClickHouse journey tables.
- Controller and UI are tenant-filtered (`app_id`) and date-filtered.
- Incident history shown on page is computed from ClickHouse event streams (not persisted in separate runtime table).
