# QRCODE — Flux complet d'onboarding Android via QR code

> **Destinataire :** Agent Android (Android Studio / VS Code)
> Ce fichier décrit tout ce que l'app Android doit implémenter pour que le flux QR code fonctionne de bout en bout.

---

## Vue d'ensemble du flux

```
Admin (panel web)
    ↓ génère QR code → choisit société + code 4 chiffres + durée
    ↓ INSERT invite_tokens (token aléatoire 64 hex + code_4digits)
    ↓ QR code encode : https://decube.fleetapp.be/join.php?token=XXXX

Employé scanne le QR avec son téléphone Android
    ↓ navigateur Android → https://decube.fleetapp.be/join.php?token=XXXX
    ↓ serveur détecte User-Agent Android
    ↓ lookup token → récupère tenant_slug → lookup vps_url dans fleetapp_central
    ↓ construit referrer : "token=XXXX&vps=https%3A%2F%2Fdecube.fleetapp.be"
    ↓ redirect vers Play Store :
      https://play.google.com/store/apps/details?id=be.fleetapp
        &referrer=token%3DXXXX%26vps%3Dhttps%253A%252F%252Fdecube.fleetapp.be

Employé installe l'app depuis le Play Store
    ↓ après installation, app lit le referrer via Play Install Referrer API
    ↓ parse : token + vps_url
    ↓ stocke vps_url dans EncryptedSharedPreferences (URL du bon VPS)
    ↓ affiche écran "Saisir votre code à 4 chiffres"
    ↓ employé saisit le code reçu séparément de son admin

Activation :
    ↓ app crée un compte → POST {vps_url}/api/login.php → obtient JWT
    ↓ app appelle POST {vps_url}/api/join.php avec :
        { "token": "XXXX", "code": "1234" }   + Authorization: Bearer <JWT>
    ↓ serveur vérifie token + code_4digits + marque token used_at = NOW()
    ↓ UPDATE connexion SET company_id = ? WHERE ID = user_id
    ↓ réponse : { "success": true, "company": "Monnaie SA", "company_id": 2 }

Admin assigne des groupes à l'utilisateur (admin panel)
    ↓ GET /api/vehicles.php retourne uniquement les véhicules des groupes assignés
```

---

## Détail de chaque étape Android

### 1. Lire le referrer Play Install Referrer API

**Dépendance Gradle :**
```gradle
implementation 'com.android.installreferrer:installreferrer:2.2'
```

**Code Java :**
```java
InstallReferrerClient referrerClient = InstallReferrerClient.newBuilder(context).build();
referrerClient.startConnection(new InstallReferrerStateListener() {
    @Override
    public void onInstallReferrerSetupFinished(int responseCode) {
        if (responseCode == InstallReferrerClient.InstallReferrerResponse.OK) {
            try {
                String referrer = referrerClient.getInstallReferrer()
                                               .getInstallReferrer();
                // referrer = "token=XXXX&vps=https%3A%2F%2Fdecube.fleetapp.be"
                parseReferrer(referrer);
            } catch (RemoteException e) { /* ... */ }
            referrerClient.endConnection();
        }
    }
    @Override
    public void onInstallReferrerServiceDisconnected() {}
});
```

**Parser le referrer :**
```java
private void parseReferrer(String referrer) {
    // referrer est URL-encodé une fois par Play Store
    // les valeurs internes peuvent être double-encodées
    Uri uri = Uri.parse("https://x.x?" + referrer);
    String token  = uri.getQueryParameter("token");   // "XXXX"
    String vpsRaw = uri.getQueryParameter("vps");     // "https://decube.fleetapp.be"
    // si vps est encore encodé : Uri.decode(vpsRaw)

    if (token != null && vpsRaw != null) {
        String vpsUrl = Uri.decode(vpsRaw); // au cas où double-encodé
        saveToPrefs(token, vpsUrl);
        showCodeInputScreen(token, vpsUrl);
    }
}
```

### 2. Stocker le vps_url dans EncryptedSharedPreferences

```java
// Stockage sécurisé
SharedPreferences prefs = EncryptedSharedPreferences.create(
    context,
    "fleet_prefs",
    MasterKey,
    EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
    EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);

prefs.edit()
    .putString("vps_url", vpsUrl)       // ex: "https://decube.fleetapp.be"
    .putString("onboarding_token", token)
    .apply();
```

**Toutes les requêtes API utilisent ce vps_url comme base URL.**

### 3. Écran de saisie du code 4 chiffres

- Afficher : `"Saisissez le code à 4 chiffres fourni par votre administrateur"`
- Input numérique uniquement, exactement 4 chiffres
- Le code n'est PAS dans le QR — l'admin le communique séparément (SMS, verbal, etc.)

### 4. Appel POST /api/join.php après login

L'utilisateur doit d'abord avoir un compte et être connecté (JWT valide).

**Endpoint :** `POST {vps_url}/api/join.php`

**Headers :**
```
Authorization: Bearer <JWT>
Content-Type: application/json
```

**Body :**
```json
{
  "token": "abc123...",
  "code": "1234"
}
```

> ⚠️ **Note backend :** Le champ `code` n'est pas encore vérifié côté serveur dans `api/join.php` (la colonne `code_4digits` existe en BDD mais la vérification n'est pas implémentée). Android doit quand même l'envoyer — la vérification sera ajoutée.

**Réponse 200 :**
```json
{
  "success": true,
  "company": "Monnaie SA",
  "company_id": 2
}
```

**Erreurs possibles :**
| HTTP | Signification |
|------|---------------|
| 401  | JWT manquant ou invalide |
| 400  | token manquant |
| 404  | token introuvable |
| 410  | token déjà utilisé ou expiré |
| 500  | erreur serveur |

---

## Fallback : scan QR depuis l'app (sans Install Referrer)

Si l'utilisateur installe l'app sans passer par le QR (téléchargement direct), ou si le Install Referrer n'est pas disponible :

1. App affiche écran verrouillé : `"Contactez votre administrateur pour obtenir un QR code"`
2. Bouton `"Scanner mon QR code"` → ouvre le scanner
3. QR code encode : `https://decube.fleetapp.be/join.php?token=XXXX`
4. App extrait le token de l'URL
5. App vérifie le token : `GET {vps_url}/api/join.php?token=XXXX`
   - Réponse : `{ "valid": true, "company": "Monnaie SA", "company_id": 2, "expires_at": "..." }`
6. App demande le code 4 chiffres
7. App appelle `POST {vps_url}/api/join.php` (même flow qu'au-dessus)

> Dans ce cas, le vps_url n'est pas dans le referrer. L'app doit soit :
> - Utiliser une URL par défaut hardcodée (ex: `https://decube.fleetapp.be`)
> - Ou lire le vps_url depuis `api/central/resolve.php`

**Résolution du vps via le serveur central :**
```
GET https://decube.fleetapp.be/api/central/resolve.php?token=XXXX
Réponse : { "vps_url": "https://decube.fleetapp.be" }
```

---

## Structure BDD — table invite_tokens

```sql
invite_tokens (
    id           INT AUTO_INCREMENT PRIMARY KEY,
    tenant_id    INT NOT NULL,
    company_id   BIGINT UNSIGNED NOT NULL,
    token        VARCHAR(64) UNIQUE NOT NULL,   -- 32 bytes hex aléatoires
    code_4digits CHAR(4),                       -- ex: "7382" — transmis séparément
    platform_hint VARCHAR(10),                  -- "android" | "ios" | "other"
    expires_at   DATETIME NOT NULL,             -- max 7 jours
    used_at      DATETIME,                      -- NULL = pas encore utilisé
    created_at   DATETIME DEFAULT CURRENT_TIMESTAMP
)
```

- `token` → contenu du QR code (partie `?token=` de l'URL)
- `code_4digits` → jamais dans le QR, transmis séparément par l'admin
- `used_at` → mis à `NOW()` quand `POST /api/join.php` réussit (token à usage unique)

---

## Endpoints backend impliqués

| Méthode | URL | Rôle |
|---------|-----|------|
| `GET` | `{vps}/join.php?token=XXXX` | Page publique — détecte Android → redirect Play Store avec referrer |
| `GET` | `{vps}/api/join.php?token=XXXX` | Vérification token (valide / expiré / déjà utilisé) |
| `POST` | `{vps}/api/join.php` | Activation — lie l'utilisateur à la société, marque token utilisé |
| `GET` | `{vps}/api/central/resolve.php?token=XXXX` | Résoudre le vps_url depuis le token (fallback) |

---

## Construction du referrer Play Store (côté serveur — join.php)

```php
$referrerData = 'token=' . $token;
if ($vpsUrl) {
    $referrerData .= '&vps=' . rawurlencode($vpsUrl);
}
$playUrl = 'https://play.google.com/store/apps/details'
         . '?id=be.fleetapp'
         . '&referrer=' . urlencode($referrerData);
header('Location: ' . $playUrl);
```

**Ce que l'app reçoit via Install Referrer API :**
```
token=XXXX&vps=https%3A%2F%2Fdecube.fleetapp.be
```

Le `vps` est encodé une fois (`rawurlencode`), puis le tout est encodé une seconde fois (`urlencode`) dans le paramètre `referrer` de l'URL Play Store. Après que Play Store délivre le referrer à l'app, **un niveau d'encodage est retiré** — l'app reçoit la chaîne avec le `vps` encodé une fois. Utiliser `Uri.decode()` ou `URLDecoder.decode()` pour obtenir l'URL propre.

---

## Sécurité

| Risque | Contre-mesure |
|--------|---------------|
| QR code forgé | Token = 32 bytes aléatoires (64 hex) — impossible à deviner |
| QR intercepté | Code 4 chiffres obligatoire, transmis séparément |
| Brute force code | ⏳ Rate limiting à implémenter côté backend sur `POST /api/join.php` |
| Token volé | Token à usage unique (`used_at`) + expiration configurable (6h → 7 jours) |
| Replay attack | `used_at` mis à NOW() à la première utilisation |
| IP VPS exposée | Le QR ne contient jamais l'IP — seulement le sous-domaine public |

---

## ⚠️ Ce qui reste à implémenter

### Backend — vérification du code 4 chiffres (non fait)

Dans `api/join.php` POST, le champ `code` est reçu mais **non vérifié** contre `code_4digits` en BDD. À ajouter :

```php
$code = trim($body['code'] ?? '');
// ...après avoir récupéré $invite :
if ($invite['code_4digits'] && $invite['code_4digits'] !== $code) {
    http_response_code(403);
    echo json_encode(['error' => 'Code incorrect']);
    exit;
}
```

### Android — Play Install Referrer API (à implémenter)

- Lire le referrer au premier lancement (une seule fois)
- Stocker `vps_url` dans EncryptedSharedPreferences
- Si pas de referrer → afficher bouton "Scanner QR code"
- Après onboarding réussi → supprimer `onboarding_token` des prefs

---

## Package Android et URLs de production

| Constante | Valeur |
|-----------|--------|
| Package ID | `be.fleetapp` |
| VPS Decube | `https://decube.fleetapp.be` (aussi accessible via `https://admin.fleetapp.be`) |
| URL base API | `https://admin.fleetapp.be` (APP_URL officielle) |
| Page QR publique | `https://decube.fleetapp.be/join.php?token=XXXX` |
| Play Store | `https://play.google.com/store/apps/details?id=be.fleetapp` |
