"Read only-for-dev/MASTER_PLATFORM_INTEGRATION_ROADMAP.md and start Sprint 1"

# Master Platform Integration Roadmap

**Project:** `apnar-business-licence-platform`
**Parent Platform:** `apnarbusiness.com`
**Last Updated:** 2025-08-10

---

## Quick Context

This license server connects bidirectionally with the master platform (ApnarBusiness):

- **Inbound (platform → license server):** Webhook events + Admin API calls
- **Outbound (license server → platform):** Status change notifications via webhook outbox

---

## ✅ Already Implemented (Phase A Complete)

### Inbound
| Endpoint | Auth | Purpose |
|----------|------|---------|
| `POST /api/v1/webhooks/platform` | HMAC-SHA256 (`X-Webhook-Signature`), timestamp ±5min, idempotent `event_id` | `payment.succeeded`, `subscription.cancelled`, `subscription.updated`, `refunded`, `license.created/updated` |
| `POST /api/v1/admin/licenses` | Sanctum user token + `Idempotency-Key` header | Create license by `app_key` or `product_id`, returns raw key once |
| `POST /api/v1/admin/payments` | Sanctum token + `Idempotency-Key` | Record payment, extend license |
| License CRUD + block/unblock/revoke/extend/domains | Sanctum token + RBAC | Full license lifecycle |
| Dashboard, events, webhooks, products, customers | Sanctum token | Admin visibility |

### Outbound
| Mechanism | Events | Delivery |
|-----------|--------|----------|
| `webhook_outbox` + `SendWebhook` job | `license.grace`, `license.expired`, `license.blocked`, `payment_received` | POST to `WEBHOOK_PLATFORM_URL` with HMAC-SHA256 |

### Supporting Infra
- Idempotency middleware (`EnsureIdempotency`) on license/payment create
- Inbound webhook retry command: `license:inbound-webhooks:process {--limit=50}`
- Domain normalizer (PRD §9) wired into activate/validate/admin
- RBAC: `super_admin` / `support` / `readonly` (Filament + API parity)
- Key hashing (`key_hash`, `key_last4`) + backfill command

---

## ❌ Gaps vs PRD / Production Requirements

### P0 — Critical Fixes (Do First)

#### 1. Outbound Webhook Signature Mismatch
**File:** `app/Jobs/SendWebhook.php:44-48`
**Problem:** Signature computed over `json_encode($payload)`, but `Http::post($url, $payload)` sends form-encoded body. Receiver verifying HMAC over raw body will fail.
**Fix:** Send JSON body with `Content-Type: application/json`.
```php
$response = Http::timeout(10)
    ->withHeaders([
        'X-Webhook-Signature' => $signature,
        'Content-Type' => 'application/json',
    ])
    ->post($url, json_encode($payload));
```

#### 2. Inbound Webhook Rate Limiting
**File:** `routes/api.php:13`
**Problem:** `POST /api/v1/webhooks/platform` has no throttle. PRD §22 requires rate limiting.
**Fix:** Add throttle middleware or named limiter.

#### 3. Dedicated Internal API Credentials (B8 — Phase B)
**Files:** New migration, models, middleware
**Problem:** Master platform currently uses a Sanctum *user* token (`license:create-token`). PRD §22 requires dedicated `api_clients`/`api_keys` with:
- `key_hash` (unique, SHA-256)
- Scopes (e.g., `licenses:write`, `webhooks:read`)
- IP allow-list
- Rotation / expiry
- Request signing (optional but recommended)

**Implementation:**
- Migration: `api_clients`, `api_keys` (per PRD §33)
- Middleware: `AuthenticateApiClient` → validates `X-Api-Key` + signature + timestamp + replay protection
- Replace `auth:sanctum` on internal routes with `auth:api-client`

---

### P1 — Platform Integration Completeness

#### 4. Enrich `subscription.updated` for Plan/Entitlement Reconcile
**File:** `app/Services/License/PlatformWebhookService.php:197-212`
**Current:** Only updates `valid_until`
**Required:** When master platform changes plan/entitlements, reconcile on license side.
**Depends on:** Phase B1 (entitlements) + B2 (plans) tables + `EntitlementService`.

#### 5. Request ID Middleware (Observability C2)
**Missing:** No `X-Request-ID` propagation inbound/outbound. PRD §22 + C2 require it.

#### 6. Stronger Replay Protection for Inbound Webhooks
**Current:** Only timestamp skew (±5min). Same payload within window is accepted.
**Improve:** Store `X-Request-ID` (or `event_id` + hash of payload) with short TTL to reject replays.

---

### P2 — Admin/Observability (Phase B/C)

| Task | Description | PRD Ref |
|------|-------------|---------|
| Validation Logs | `validation_logs` table (key_hash, status, ip, domain, instance_id, response_code), 90-180 day retention, prune command | B5 |
| Audit Logs | Immutable `audit_logs` for all admin state changes (Filament, API, CLI) | B6 |
| Entitlements | `entitlements`, `plan_entitlements`, `license_entitlements` + snapshot on create/plan-change | B1 |
| Plans | `plans` table, `licenses.plan_id` FK, CRUD admin | B2 |
| Product Versions | `product_versions` table, deprecation/blocking, optional min-version enforcement | B3 |
| Deactivate/Heartbeat | Public endpoints: `POST /license/deactivate`, `POST /license/heartbeat`, `GET /license/entitlements` | B7 |
| API Clients/Keys Admin | Filament resource + API for managing api_clients/api_keys | B8 |

---

## Suggested Execution Order

### Sprint 1 (Week 1) — Critical Fixes
1. Fix `SendWebhook` JSON body + Content-Type
2. Add rate limiter on `POST /api/v1/webhooks/platform`
3. Add `X-Request-ID` middleware (inbound + outbound)

### Sprint 2 (Week 2) — Internal Auth (B8)
4. Create `api_clients` / `api_keys` migration + models
5. Build `AuthenticateApiClient` middleware (key lookup, HMAC verify, timestamp, replay)
6. Migrate internal admin routes from `auth:sanctum` → `auth:api-client`
7. Add Filament resource for API client/key management

### Sprint 3 (Week 3-4) — Platform Feature Completeness
8. Implement Entitlements (B1) + Plans (B2) — core schema + services
9. Enrich `subscription.updated` handler to reconcile plans/entitlements
10. Implement Validation Logs (B5) + Audit Logs (B6)

### Sprint 4 (Week 5+) — Polish & Scale
11. Product Versions (B3)
12. Deactivate/Heartbeat/Entitlements public endpoints (B7)
13. Validation cache + sweepers (C1)
14. Offline tokens + SDK foundation (C3)
15. Reporting + backup ops (C5)

---

## How to Use This File with Opencode

```bash
# In opencode session:
> read only-for-dev/MASTER_PLATFORM_INTEGRATION_ROADMAP.md
> "Start Sprint 1: fix outbound webhook signature and add inbound rate limit"
# or
> "Implement B8 api_clients/api_keys per the roadmap"
```

The file is self-contained — it has context, current state, gaps prioritized, and concrete file references.

---

## Key Files to Touch

| Task | Files |
|------|-------|
| Outbound signature fix | `app/Jobs/SendWebhook.php` |
| Inbound rate limit | `routes/api.php`, `app/Providers/AppServiceProvider.php` |
| Request ID middleware | New: `app/Http/Middleware/RequestId.php`, register in Kernel |
| B8 api_clients/api_keys | Migration, `app/Models/ApiClient.php`, `app/Models/ApiKey.php`, `app/Http/Middleware/AuthenticateApiClient.php`, Filament resource |
| Plan/Entitlement reconcile | `app/Services/License/PlatformWebhookService.php`, new EntitlementService |
| Validation/Audit logs | Migrations, models, commands, Filament resources |

---

## Environment Variables Needed

```env
# Already used
WEBHOOK_PLATFORM_URL=https://apnarbusiness.com/api/license-webhooks
WEBHOOK_PLATFORM_SECRET=shared-secret-for-hmac

# New for B8
API_CLIENT_RATE_LIMIT=120,1  # per client
WEBHOOK_INBOUND_RATE_LIMIT=60,1  # per IP
```

---

## Test Coverage Targets

| Area | Tests |
|------|-------|
| Outbound webhook | Signature matches raw JSON body; retry/backoff works |
| Inbound rate limit | 429 after burst; independent per IP |
| B8 auth | Valid key+signature → 200; bad signature → 401; replay → 409; expired timestamp → 401 |
| subscription.updated | Plan change → license entitlements snapshot updated; valid_until extended |
| Validation logs | Log entry per validate; prune command removes >180 days |
| Audit logs | Every admin mutation writes row; no update/delete endpoints |

---

**Status:** Phase A complete. Ready for Sprint 1 (P0 fixes) → Sprint 2 (B8 auth).