# Partenaires — fonctionnement & onboarding

> Document de référence (versionné) décrivant **comment** un tiers (galerie,
> plateforme culturelle, marketplace NFT — p.ex. `montmartre-nft.fr`,
> `antenne-culture.fr`) intègre ArtAI et **qui gère quoi**. Également résumé sur
> la page **`/docs#partners`**.

## 1. Positionnement

ArtAI est une **infrastructure B2B**. Elle ne veut pas gérer les utilisateurs
finaux : ce sont les **partenaires** qui possèdent la relation avec leurs propres
utilisateurs.

**L'API est entièrement gratuite** : aucun crédit, aucun quota, aucun abonnement,
aucune mesure d'usage facturée. Le jeton `sk_` sert uniquement à **identifier**
le partenaire (droits d'écriture, traçabilité), jamais à le facturer.

## 2. Qui gère quoi

| Responsabilité | Partenaire (ex. antenne-culture.fr) | ArtAI |
|---|:---:|:---:|
| Comptes des utilisateurs finaux | ✅ | — |
| Authentification / login des utilisateurs finaux | ✅ | — |
| Appels à l'API ArtAI (serveur-à-serveur, jeton `sk_`) | ✅ | — |
| Émission du jeton partenaire (1×) | — | ✅ |
| Preuve de propriété du domaine | publie le TXT | vérifie |
| Curation / vérification des signaux de confiance | — | ✅ (jamais délégué) |

Le partenaire n'obtient **jamais** le scope `signal:verify` : la confiance
(validation des ventes, reconnaissances, avis) reste chez ArtAI.

## 3. Cycle de vie d'un partenaire

```
   apply            verify-domain          approve (admin)          suspend (admin)
 (candidature) ──▶  (preuve DNS TXT) ──▶  provisioning ──▶ ACTIVE ◀──▶ SUSPENDED
      │                                        │              │
   PENDING ───────────────────────────────────┘         (jeton rejeté)
      │
      └──▶ REJECTED
```

- **`pending`** — candidature déposée, en attente de preuve de domaine + validation humaine.
- **`approved` → `active`** — validé par un admin ; le provisioning crée le compte de service et le jeton.
- **`suspended`** — désactivé (abus…) ; le jeton d'API est **immédiatement rejeté** (401). Réversible : une nouvelle validation réactive **le même** jeton.
- **`rejected`** — candidature refusée.

## 4. Étape par étape

### 4.1 Candidature — `POST /api/partners/apply` (public)

Le partenaire déclare son `name`, son `domain`, un `contactEmail` et un volume
estimé. ArtAI crée un `Partner` en `pending` et renvoie un **challenge DNS**.

```json
POST /api/partners/apply
{ "name": "Antenne Culture", "domain": "antenne-culture.fr", "contactEmail": "tech@antenne-culture.fr", "expectedVolume": 300 }
→ 201
{ "partner": { "uuid": "…", "status": "pending" },
  "domainVerification": { "record": { "type": "TXT", "value": "artai-verify=<token>" } } }
```

### 4.2 Preuve de propriété du domaine — `POST /api/partners/{ref}/verify-domain` (public)

Le partenaire publie le TXT `artai-verify=<token>` sur son domaine (sur l'apex ou
sur `_artai.<domaine>`), puis appelle l'endpoint. ArtAI relit le DNS
(`DomainVerificationService`) et marque `domainVerified: true`. Cela **ne
valide pas** encore le partenaire : c'est une condition préalable à la validation.

### 4.3 Validation — `POST /api/partners/{ref}/approve` (ROLE_ADMIN) ou CLI

C'est **toi** (ArtAI) qui valides. Deux voies équivalentes :

```bash
php bin/console app:partner:approve antenne-culture.fr
php bin/console app:partner:approve antenne-culture.fr --force        # ignore la preuve DNS
```

```json
POST /api/partners/{ref}/approve      (Bearer <jeton admin>)
{ "force": false }
→ 201
{ "apiToken": "sk_…",                 // affiché UNE seule fois
  "scopes": ["artwork:write", "artwork:read", "artist:read", "cote:read", "edition:read"] }
```

Le **provisioning** (`PartnerProvisioner`) est **idempotent** : il crée un compte
de service `User` et **un** jeton `sk_` scopé. Ré-approuver un partenaire (p.ex.
après suspension) **réutilise** le même jeton — aucun doublon.

### 4.4 Self-service partenaire (Bearer `sk_`)

- `GET /api/partners/me` — profil du partenaire et réseau blockchain.
- `POST /api/partners/me/blockchain` — choisir son propre réseau pour l'ancrage NFT
  (vide = réseau ArtAI).

## 5. Gratuité

- Aucun endpoint ne décompte de crédit ni ne renvoie `402 Payment Required`.
- Aucune mesure d'usage n'est reportée à un prestataire de paiement.
- Toutes les étapes du pipeline (empreinte, stéganographie, C2PA, IPFS, NFT,
  certificat PDF, ArtScore) sont disponibles pour tout compte authentifié.

Pour soutenir le projet : [montmartre-nft.fr](http://www.montmartre-nft.fr).

## 6. Sécurité

- Le jeton partenaire est **scopé serré** (lecture + `artwork:write`), **sans**
  `signal:verify`.
- Un partenaire **suspendu** voit son jeton rejeté à l'authentification
  (`ApiTokenAuthenticator`, 401 « Partner account is not active »).
- La preuve de domaine (DNS TXT) empêche qu'un tiers réclame un domaine qu'il ne
  possède pas.
- L'inscription publique (`/api/auth/register`) reste distincte : les partenaires
  sont créés par candidature + validation, jamais en libre-service total.

## 7. Endpoints (référence rapide)

| Méthode | Chemin | Accès | Rôle |
|---|---|---|---|
| `POST` | `/api/partners/apply` | public | candidater |
| `POST` | `/api/partners/{ref}/verify-domain` | public | prouver le domaine |
| `GET` | `/api/partners/{ref}` | public | statut de la candidature |
| `POST` | `/api/partners/{ref}/approve` | ROLE_ADMIN | valider + provisionner |
| `POST` | `/api/partners/{ref}/suspend` | ROLE_ADMIN | suspendre |
| `GET` | `/api/partners/me` | jeton partenaire | profil |
| `POST` | `/api/partners/me/blockchain` | jeton partenaire | réseau d'ancrage |

`{ref}` = id, uuid ou domaine du partenaire.

## 8. Fichiers concernés

- `src/Entity/Partner.php`, `src/Enum/PartnerStatus.php`, `src/Repository/PartnerRepository.php`
- `src/Service/DomainVerificationService.php`, `src/Service/PartnerProvisioner.php`
- `src/Controller/Api/PartnerController.php`, `src/Command/PartnerApproveCommand.php`
- `src/Security/ApiTokenAuthenticator.php` (rejet des suspendus + entry-point 401)
