# Troubleshooting – Ordio Loop Affiliate

**Last Updated:** 2026-02-04

## Troubleshooting: 403 when assigning admin

If you see **"Keine Admin-Rechte"** (403) when clicking **Admin zuweisen** on the Admin page:

1. **Same origin in local dev**  
   The admin page and admin APIs must be served from the **same origin** (same host and port). The Admin page uses `window.location.origin + "/v2/api/..."` for all admin API requests so requests go to the **same host and port** as the page (e.g. if the page is `http://localhost:8003/partner/admin`, requests go to `http://localhost:8003/v2/api/...`). If you still see a request to a different port (e.g. `localhost:8081`) in DevTools → Network, a proxy or dev-server config may be overriding the URL; fix it so one server serves both, or remove the proxy (see [LOCAL_DEV.md](LOCAL_DEV.md)).

2. **Log out and log in again**  
   The session stores your email for the config-admin fallback. If you logged in before this was added, your session may not have `affiliate_email` set. **Log out** and **log in again** so the session is refreshed; then try assigning an admin again.

3. **Check server logs**  
   When the API returns 403, it logs one line with prefix `[Affiliate Admin 403]`. Grep your PHP/web server error log for that string. The line includes:

   - `partner_id` (from session)
   - `session_email=yes|no` (whether session has affiliate_email)
   - `partner_found=yes|no` (whether the partner was found in the data file)
   - `email`, `is_admin`, `config_check=yes|no`, `record_is_admin=yes|no`

   Use this to see whether the failure was "partner not found", "no session email", "email not in config", or "record has no is_admin".

4. **Config admin email**  
   Bootstrap admins are defined in [v2/config/affiliate-config.php](v2/config/affiliate-config.php) as `AFFILIATE_ADMIN_EMAILS` (e.g. `['hady@ordio.com']`). Ensure the account you use to log in is listed there, or that your partner record has `is_admin: true` in the partner data file.

5. **Verify admin check (CLI)**  
   Run `php v2/scripts/affiliate/verify-admin-check.php --partner-id=AP-YYYYMMDD-XXXXXX` (optional: `--file=/path/to/affiliate_partners.json`). Expected output when admin: `result: ADMIN` and `reason: email is in AFFILIATE_ADMIN_EMAILS` or `reason: partner record has is_admin=true`. If you see `result: NOT ADMIN`, fix config or run `set-partner-admin.php` for your email.

6. **Setting admin on pre-existing partners (production)**  
   If you need to set `is_admin` for partners that were created before this field was set, use the Admin UI (“Admin zuweisen”) when you can log in as admin, or run on the server:  
   `php v2/scripts/affiliate/set-partner-admin.php --email=partner@example.com --is-admin=1`  
   (or `--partner-id=...`). Use `--dry-run` first. See [ADMIN_DELETE_PARTNERS.md](ADMIN_DELETE_PARTNERS.md#updating-partner-records-in-production-admin-etc) for all options (UI, script, manual edit).

## 403 even though I'm listed in Admins

If the Admin page shows you in the "Admins" section but **assign admin** or **deactivate** still returns 403:

1. **Log out and log in again** so the session has `affiliate_email` set (needed for config-admin check).
2. **Run verify-admin-check.php** for your partner ID: `php v2/scripts/affiliate/verify-admin-check.php --partner-id=AP-YYYYMMDD-XXXXXX`. Expect `result: ADMIN`.
3. **Run set-partner-admin.php** for your email so your partner record has `is_admin: true`: `php v2/scripts/affiliate/set-partner-admin.php --email=your@email.com --is-admin=1` (use `--dry-run` first).
4. **Confirm same origin**: Admin page and admin APIs must be on the same host and port (see [LOCAL_DEV.md](LOCAL_DEV.md)).
5. **Check server logs** for `[Affiliate Admin 403]` to see `config_check`, `record_is_admin`, and `session_email`; the 403 response also includes a `hint` field with next steps.

## Debug: identifying 403 cause

When a 403 occurs on **Admin zuweisen**, **Deaktivieren**, or **Sync**, the error modal now shows debug lines so you can see exactly why:

- **Anfrage an:** The URL the browser used for the request. It should be the same host and port as the page (e.g. `http://localhost:8003/v2/api/...`). If it shows a different port (e.g. `localhost:8081`), the request is going to another origin and the session cookie is not sent → that is the cause.
- **Seite (Client):** The page’s origin (e.g. `http://localhost:8003`).
- **Request erreichte Server (Host):** The `Host` header the API server received. If this differs from the page’s host/port (e.g. `localhost:8081` vs page on `8003`), you have a port/origin mismatch; use one server for both page and API.

**Network:** Admin requests are sent to the same origin as the page. In DevTools → Network, confirm the failed request URL matches the page origin and path (same host/port). If the failed request’s URL does not match, a proxy or tool is rewriting the URL.

**Server log:** The `[Affiliate Admin 403]` log line now includes `request_host=...` and `page_origin_header=...` so you can see which host received the request and what origin the client claimed.

## Partner shows Pending after I reactivated

This is **expected** when the partner **never verified their email** (no `email_verified_at` on their record). Reactivating (Aktivieren) does not bypass verification: unverified partners are restored to **Pending** (pending_verification) so they must still verify before they can log in. Verified partners (with `email_verified_at`) are set to **Active** when you reactivate. To let an unverified partner log in without verifying (e.g. support override), use the API with `force_active: true` in the request body; this is logged in the admin audit.

## Manual test: Reactivate and verification

To verify that reactivate respects email verification:

1. **Create a pending partner** (or use an existing partner who has never verified): register at `/partner/register` and do **not** open the verification link.
2. **As admin:** Open Verwaltung, find the partner (Status: Ausstehend). Click **Deaktivieren** and confirm.
3. **Reactivate:** Click **Aktivieren** for that partner.
4. **Confirm:** Partner status should show **Ausstehend** (pending_verification), not Aktiv. Attempting to log in as that partner should show "Account is not active. Please verify your email."
5. **Verify email:** Open the verification link from the registration email (or use partner-verify-email.php?token=...&partner=...). Then partner status becomes Aktiv and they can log in.
6. **Verified partner:** For a partner who already has `email_verified_at`, deactivate then reactivate → status should become **Aktiv** and they can log in immediately.

**Resend verification:** When a partner is Pending (e.g. after reactivate), their original verification link may have expired (tokens typically expire after 24 hours). To allow them to verify again, you can: (a) add an admin action or script that generates a new verification token and sends a new verification email (see `v2/helpers/affiliate-email.php` `sendVerificationEmail()` and the token/expiry logic in `v2/api/partner-register.php`), or (b) add a "Verifizierungs-E-Mail erneut senden" link on the login page for pending partners. Document the chosen approach in the runbook.
