# Callback + subscriber status comparison

World Cup `ProcessDcbCallbackJob` vs Quick-Fun `ProcessCallbackJob`.

**Status: implemented (Option A — 3 subscriber states).** Quick-Fun subscriber writes now follow World Cup `dcb_subscribers`. Callback row success stays `completed` (this project's existing value). Already-done rows (`completed` or `processed`) are skipped.

World Cup has two subscriber tables. The DCB equivalent of Quick-Fun `subscribers` is World Cup `dcb_subscribers`. Quiz `subscribers` (points/tokens) was not copied.

---

## 1. Callback record status

| Event | World Cup | Quick-Fun after change |
|---|---|---|
| HTTP insert | `pending` | `pending` |
| Job start | stays `pending` | `processing` (kept for visibility) |
| Known action handled | `processed` | `completed` |
| Exception | `failed` + rethrow (`$tries = 3`) | `failed`, swallowed (no retry) |
| Already finished | skip if `processed` | skip if `completed` or `processed` |
| Service missing | still `processed` | `failed` (`service_not_found`) |
| Unknown `action_type` | still `processed` | `failed` (`unknown_action`) |

Intentionally not copied: silent success on missing service / unknown action, and job retries.

---

## 2. Subscriber statuses (aligned)

| Status | Meaning | Written by |
|---|---|---|
| `inactive` | Opted in, not billed / charge failed | SUB, OOB |
| `active` | Successful charge | RENEWAL |
| `unsubscribed` | Left the service | UNSUB |

---

## 3. Per-action writes (now the same)

| Action | Missing row | Existing row |
|---|---|---|
| SUB (`1`) | create `inactive` | set `inactive`, reset `subscribed_at`, clear `unsubscribed_at` |
| RENEWAL (`2`) | create `active` + `last_billing_date` | force `active` + `last_billing_date` + `last_active_at`, keep original `subscribed_at` |
| OOB (`3`) | no create | set `inactive` (do not set `unsubscribed_at`) |
| UNSUB (`0`) | no create | set `unsubscribed` + `unsubscribed_at` |

---

## 4. Left different on purpose

- Send/verify and the HE success page do **not** write `subscribers`. Callbacks own the table (same as World Cup).
- PostHog `subscription_success` still fires on OTP verify. `CampaignTracker` subscription counts fire on verify unless the operator said `user already exist`.
- Smart-service portal SMS on SUB fires only for a new row or a re-sub after `unsubscribed` (not on duplicate SUB / OOB).
- Quick-Fun has no quiz subscriber, points, or token revoke on OOB/UNSUB.
