openapi: 3.1.0
info:
  title: Viva Wallet Merchant SDK — Référence PHP
  version: 1.3.5
  description: |
    Documentation de référence du SDK PHP `qrcommunication/viva-merchant-sdk`.

    Ce document décrit les **classes**, **méthodes**, **paramètres** et **types de retour**
    du SDK — pas les endpoints HTTP sous-jacents.

    ## Installation

    ```bash
    composer require qrcommunication/viva-merchant-sdk
    ```

    ## Initialisation

    ```php
    use QrCommunication\VivaMerchant\VivaClient;

    $viva = new VivaClient(
        merchantId: 'votre-uuid',
        apiKey: 'votre-api-key',
        clientId: 'xxx.apps.vivapayments.com',
        clientSecret: 'votre-secret',
        environment: 'demo', // ou 'production'
    );
    ```

    ## Ressources disponibles

    | Propriété | Classe | Description |
    |-----------|--------|-------------|
    | `$viva->orders` | `Orders` | Ordres de paiement Smart Checkout |
    | `$viva->transactions` | `Transactions` | Consultation, remboursement, capture, récurrent |
    | `$viva->sources` | `Sources` | Sources de paiement |
    | `$viva->wallets` | `Wallets` | Portefeuilles, soldes, transferts |
    | `$viva->bankAccounts` | `BankAccounts` | Comptes bancaires IBAN, virements SEPA |
    | `$viva->nativeCheckout` | `NativeCheckout` | Apple Pay / Google Pay |
    | `$viva->dataServices` | `DataServices` | Rapports MT940, souscriptions webhook |
    | `$viva->webhooks` | `Webhooks` | Vérification et parsing des webhooks |
    | `$viva->account` | `Account` | Informations du compte marchand |

    ## Montants

    Tous les montants sont en **centimes** (`int`). Exemple : `1500` = 15,00 EUR.

    ## Gestion d'erreurs

    Le SDK lève des exceptions typées :

    | Exception | Quand |
    |-----------|-------|
    | `AuthenticationException` | Échec OAuth2 (identifiants invalides) |
    | `ApiException` | Erreur retournée par l'API Viva (4xx, 5xx) |
    | `ValidationException` | Erreur de validation (422) |
    | `VivaException` | Classe de base pour toutes les exceptions SDK |

    ```php
    use QrCommunication\VivaMerchant\Exceptions\ApiException;
    use QrCommunication\VivaMerchant\Exceptions\AuthenticationException;

    try {
        $order = $viva->orders->create(amount: 1500);
    } catch (AuthenticationException $e) {
        // Identifiants invalides
    } catch (ApiException $e) {
        echo $e->httpStatus;    // 400, 404, etc.
        echo $e->responseBody;  // Réponse brute Viva
        echo $e->getErrorCode();
        echo $e->getErrorText();
    }
    ```
  contact:
    name: QrCommunication
    email: contact@qrcommunication.com
    url: https://qrcommunication.com
  license:
    name: MIT
    url: https://opensource.org/licenses/MIT

tags:
  - name: VivaClient
    description: |
      Point d'entrée du SDK — instanciation, test de connexion, gestion du token.

      ```php
      use QrCommunication\VivaMerchant\VivaClient;
      ```
  - name: Orders
    description: |
      `$viva->orders` — Ordres de paiement Smart Checkout.

      Crée des ordres de paiement, récupère leur statut, génère les URLs de checkout.

      ```php
      $order = $viva->orders->create(amount: 1500, customerDescription: 'Consultation');
      // Rediriger le client vers $order['checkout_url']
      ```
  - name: Transactions
    description: |
      `$viva->transactions` — Consultation, remboursement, capture et paiements récurrents.

      ```php
      $txn = $viva->transactions->get('uuid');
      $viva->transactions->cancel('uuid', amount: 500);
      $viva->transactions->capture('uuid', amount: 1500);
      $viva->transactions->recurring('initial-uuid', amount: 1500);
      ```
  - name: Sources
    description: |
      `$viva->sources` — Gestion des sources de paiement.

      Les sources définissent les domaines autorisés et les URLs de redirection après paiement.
  - name: Wallets
    description: |
      `$viva->wallets` — Portefeuilles, soldes et transferts.

      Gère les sous-comptes (wallets) du marchand, consulte les soldes et effectue des transferts entre wallets.
  - name: BankAccounts
    description: |
      `$viva->bankAccounts` — Comptes bancaires et virements SEPA.

      Lie des IBANs, vérifie les options de transfert, calcule les frais et exécute des virements.
  - name: NativeCheckout
    description: |
      `$viva->nativeCheckout` — Paiements Apple Pay et Google Pay.

      Génère des tokens de charge à partir des données Apple Pay / Google Pay, puis exécute les transactions.
  - name: DataServices
    description: |
      `$viva->dataServices` — Rapports MT940 et souscriptions webhook.

      Récupère les relevés bancaires au format MT940 et gère les souscriptions aux événements de fichiers.
  - name: Webhooks
    description: |
      `$viva->webhooks` — Vérification et parsing des webhooks Viva Wallet.

      Gère la vérification GET et le parsing des événements POST. 21 types d'événements supportés.
  - name: Account
    description: |
      `$viva->account` — Informations du compte marchand.

      Récupère les détails du compte et les soldes des portefeuilles.
  - name: Enums
    description: |
      Enums PHP 8.1+ fournis par le SDK pour le typage strict.

      - `Environment` — `demo` | `production`
      - `Currency` — Codes ISO 4217 numériques (978 = EUR, 826 = GBP, etc.)
      - `TransactionStatus` — Statuts de transaction (`F`, `A`, `C`, `E`, `M`, `X`, `R`)
  - name: Exceptions
    description: |
      Hiérarchie d'exceptions du SDK.

      ```
      RuntimeException
      └── VivaException
          ├── ApiException
          ├── AuthenticationException
          └── ValidationException
      ```

paths:
  # ============================================================
  # VivaClient
  # ============================================================
  /VivaClient/__construct:
    post:
      tags: [VivaClient]
      operationId: VivaClient__construct
      summary: new VivaClient(...)
      description: |
        Instancie le client SDK. L'authentification OAuth2 est automatique au premier appel API.

        ```php
        use QrCommunication\VivaMerchant\VivaClient;
        use QrCommunication\VivaMerchant\Enums\Environment;

        $viva = new VivaClient(
            merchantId: 'votre-merchant-uuid',
            apiKey: 'votre-api-key',
            clientId: 'xxx.apps.vivapayments.com',
            clientSecret: 'votre-client-secret',
            environment: 'demo', // string ou Environment enum
        );
        ```

        **Où trouver les identifiants ?**
        - `merchantId` + `apiKey` → Viva Dashboard > Settings > API Access
        - `clientId` + `clientSecret` → Viva Dashboard > Settings > API Access > OAuth Clients
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/VivaClientConstructorParams'
      responses:
        '200':
          description: Instance `VivaClient` créée
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VivaClientInstance'

  /VivaClient/testConnection:
    post:
      tags: [VivaClient]
      operationId: VivaClient_testConnection
      summary: "$viva->testConnection()"
      description: |
        Teste la connexion en s'authentifiant et en récupérant les infos du compte.

        ```php
        if ($viva->testConnection()) {
            echo 'Connexion OK';
        } else {
            echo 'Échec de connexion';
        }
        ```
      parameters: []
      responses:
        '200':
          description: "`bool` — `true` si la connexion est réussie, `false` sinon"
          content:
            application/json:
              schema:
                type: boolean

  /VivaClient/invalidateToken:
    post:
      tags: [VivaClient]
      operationId: VivaClient_invalidateToken
      summary: "$viva->invalidateToken()"
      description: |
        Force la ré-authentification au prochain appel API. Utile si le token a été révoqué.

        ```php
        $viva->invalidateToken();
        // Le prochain appel API demandera un nouveau token OAuth2
        ```
      responses:
        '200':
          description: "`void` — Le token est invalidé"

  /VivaClient/getConfig:
    get:
      tags: [VivaClient]
      operationId: VivaClient_getConfig
      summary: "$viva->getConfig()"
      description: |
        Retourne l'objet `Config` pour introspection ou débogage.

        ```php
        $config = $viva->getConfig();
        echo $config->merchantId;
        echo $config->environment->value; // 'demo' ou 'production'
        echo $config->isProduction();     // bool
        echo $config->apiUrl();           // 'https://demo-api.vivapayments.com'
        echo $config->legacyUrl();        // 'https://demo.vivapayments.com'
        echo $config->checkoutUrl();      // 'https://demo.vivapayments.com/web/checkout'
        echo $config->accountsUrl();      // 'https://demo-accounts.vivapayments.com'
        ```
      responses:
        '200':
          description: Objet `Config`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Config'

  # ============================================================
  # Orders
  # ============================================================
  /Orders/create:
    post:
      tags: [Orders]
      operationId: Orders_create
      summary: "$viva->orders->create()"
      description: |
        Crée un ordre de paiement Smart Checkout. Le client est redirigé vers l'URL de checkout Viva pour finaliser le paiement.

        ```php
        $order = $viva->orders->create(
            amount: 1500,                         // 15,00 EUR (centimes)
            customerDescription: 'Consultation',  // Affiché au client
            merchantReference: 'session_123',     // Référence interne
            allowRecurring: true,                 // Tokeniser la carte
            preauth: false,                       // Pré-autorisation ?
            maxInstallments: 3,                   // Paiement en 3x
        );

        echo $order['order_code'];   // 1234567890
        echo $order['checkout_url']; // https://demo.vivapayments.com/web/checkout?ref=1234567890

        // Rediriger le client :
        header('Location: ' . $order['checkout_url']);
        ```
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OrdersCreateParams'
      responses:
        '200':
          description: Ordre créé
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrdersCreateResult'
        '400':
          description: "`ApiException` — Erreur de création (montant invalide, source inconnue, etc.)"
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'

  /Orders/get:
    get:
      tags: [Orders]
      operationId: Orders_get
      summary: "$viva->orders->get()"
      description: |
        Récupère le statut d'un ordre de paiement.

        ```php
        $order = $viva->orders->get(orderCode: 1234567890);

        echo $order['StateId'];       // 0 = en attente, 2 = payé
        echo $order['Amount'];        // 1500
        echo $order['PaymentId'];     // UUID de la transaction
        ```
      parameters:
        - name: orderCode
          in: query
          required: true
          schema:
            type: integer
          description: "Code de l'ordre (retourné par `create()`)"
      responses:
        '200':
          description: Données brutes de l'ordre Viva
          content:
            application/json:
              schema:
                type: object
                description: "array<string, mixed>"
        '400':
          description: "`ApiException`"

  /Orders/cancel:
    delete:
      tags: [Orders]
      operationId: Orders_cancel
      summary: "$viva->orders->cancel()"
      description: |
        Annule un ordre de paiement non payé.

        ```php
        $result = $viva->orders->cancel(orderCode: 1234567890);
        ```
      parameters:
        - name: orderCode
          in: query
          required: true
          schema:
            type: integer
          description: Code de l'ordre à annuler
      responses:
        '200':
          description: Ordre annulé
          content:
            application/json:
              schema:
                type: object
        '400':
          description: "`ApiException` — Ordre déjà payé ou introuvable"

  /Orders/checkoutUrl:
    get:
      tags: [Orders]
      operationId: Orders_checkoutUrl
      summary: "$viva->orders->checkoutUrl()"
      description: |
        Génère l'URL Smart Checkout pour un code d'ordre existant, sans appel API.

        ```php
        $url = $viva->orders->checkoutUrl(orderCode: 1234567890);
        // => 'https://demo.vivapayments.com/web/checkout?ref=1234567890'
        ```
      parameters:
        - name: orderCode
          in: query
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: "`string` — URL de checkout"
          content:
            text/plain:
              schema:
                type: string
                example: "https://demo.vivapayments.com/web/checkout?ref=1234567890"

  # ============================================================
  # Transactions
  # ============================================================
  /Transactions/get:
    get:
      tags: [Transactions]
      operationId: Transactions_get
      summary: "$viva->transactions->get()"
      description: |
        Récupère les détails complets d'une transaction (Legacy API, réponse PascalCase).

        Inclut : Fee, Commission, Order, Payment, CreditCard, etc.

        ```php
        $txn = $viva->transactions->get('transaction-uuid');

        echo $txn['Transactions'][0]['Amount'];       // 1500
        echo $txn['Transactions'][0]['StatusId'];      // 'F'
        echo $txn['Transactions'][0]['CreditCard']['Number']; // '411111...1111'
        ```
      parameters:
        - name: transactionId
          in: query
          required: true
          schema:
            type: string
            format: uuid
          description: UUID de la transaction
      responses:
        '200':
          description: Données complètes de la transaction (PascalCase)
          content:
            application/json:
              schema:
                type: object
                description: "array<string, mixed>"
        '400':
          description: "`ApiException`"

  /Transactions/getV2:
    get:
      tags: [Transactions]
      operationId: Transactions_getV2
      summary: "$viva->transactions->getV2()"
      description: |
        Récupère les détails d'une transaction via la New API (Bearer, réponse camelCase).

        Réponse plus légère, recommandée pour vérifier les paiements Smart Checkout.

        ```php
        $txn = $viva->transactions->getV2('transaction-uuid');

        echo $txn['email'];             // 'client@example.com'
        echo $txn['amount'];            // 15.00 (attention : en EUR, pas en centimes)
        echo $txn['statusId'];          // 'F'
        echo $txn['orderCode'];         // 1234567890
        echo $txn['cardNumber'];        // '411111...1111'
        echo $txn['recurringSupport'];  // true/false
        ```
      parameters:
        - name: transactionId
          in: query
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Données de la transaction (camelCase)
          content:
            application/json:
              schema:
                type: object
                description: "array<string, mixed>"

  /Transactions/listByDate:
    get:
      tags: [Transactions]
      operationId: Transactions_listByDate
      summary: "$viva->transactions->listByDate()"
      description: |
        Liste toutes les transactions d'une date donnée.

        ```php
        $transactions = $viva->transactions->listByDate('2026-03-18');

        foreach ($transactions as $txn) {
            echo $txn['TransactionId'] . ' — ' . $txn['Amount'] . "\n";
        }
        ```
      parameters:
        - name: date
          in: query
          required: true
          schema:
            type: string
            format: date
          description: "Date au format `Y-m-d`"
      responses:
        '200':
          description: Liste de transactions
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                description: "array<int, array<string, mixed>>"

  /Transactions/cancel:
    delete:
      tags: [Transactions]
      operationId: Transactions_cancel
      summary: "$viva->transactions->cancel()"
      description: |
        Annule ou rembourse une transaction.

        - Même jour → annulation (void)
        - Jour passé → remboursement (refund)

        ```php
        // Remboursement total
        $result = $viva->transactions->cancel('transaction-uuid');

        // Remboursement partiel (5,00 EUR)
        $result = $viva->transactions->cancel('transaction-uuid', amount: 500);

        echo $result['TransactionId']; // UUID de la transaction de remboursement
        ```
      parameters:
        - name: transactionId
          in: query
          required: true
          schema:
            type: string
            format: uuid
          description: UUID de la transaction à annuler/rembourser
        - name: amount
          in: query
          required: false
          schema:
            type: integer
            nullable: true
          description: "Montant en centimes (`null` = remboursement total)"
        - name: sourceCode
          in: query
          required: false
          schema:
            type: string
            nullable: true
          description: Code de la source de paiement
      responses:
        '200':
          description: Transaction annulée/remboursée
          content:
            application/json:
              schema:
                type: object
                properties:
                  TransactionId:
                    type: string
                    description: UUID de la transaction de remboursement

  /Transactions/capture:
    post:
      tags: [Transactions]
      operationId: Transactions_capture
      summary: "$viva->transactions->capture()"
      description: |
        Capture une transaction pré-autorisée.

        ```php
        // Capture totale
        $result = $viva->transactions->capture('preauth-uuid', amount: 1500);

        // Capture partielle
        $result = $viva->transactions->capture('preauth-uuid', amount: 1000);
        ```
      parameters:
        - name: transactionId
          in: query
          required: true
          schema:
            type: string
            format: uuid
          description: UUID de la transaction pré-autorisée
        - name: amount
          in: query
          required: true
          schema:
            type: integer
          description: Montant en centimes à capturer
      responses:
        '200':
          description: Transaction capturée
          content:
            application/json:
              schema:
                type: object
        '400':
          description: "`ApiException` — Capture échouée"

  /Transactions/recurring:
    post:
      tags: [Transactions]
      operationId: Transactions_recurring
      summary: "$viva->transactions->recurring()"
      description: |
        Effectue un paiement récurrent en utilisant le token de la transaction initiale.

        Prérequis : l'ordre initial doit avoir été créé avec `allowRecurring: true`.

        ```php
        // Charge récurrente de 15,00 EUR
        $result = $viva->transactions->recurring(
            initialTransactionId: 'initial-txn-uuid',
            amount: 1500,
        );
        ```
      parameters:
        - name: initialTransactionId
          in: query
          required: true
          schema:
            type: string
            format: uuid
          description: UUID de la transaction initiale (avec carte tokenisée)
        - name: amount
          in: query
          required: true
          schema:
            type: integer
          description: Montant en centimes
        - name: sourceCode
          in: query
          required: false
          schema:
            type: string
            nullable: true
          description: Code de la source de paiement
      responses:
        '200':
          description: Paiement récurrent effectué
          content:
            application/json:
              schema:
                type: object
        '400':
          description: "`ApiException` — Charge récurrente échouée"

  # ============================================================
  # Sources
  # ============================================================
  /Sources/list:
    get:
      tags: [Sources]
      operationId: Sources_list
      summary: "$viva->sources->list()"
      description: |
        Liste toutes les sources de paiement du marchand.

        ```php
        $sources = $viva->sources->list();

        foreach ($sources as $source) {
            echo $source['Name'] . ' — ' . $source['SourceCode'] . "\n";
        }
        ```
      responses:
        '200':
          description: Liste des sources de paiement
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                description: "array<int, array<string, mixed>>"

  /Sources/create:
    post:
      tags: [Sources]
      operationId: Sources_create
      summary: "$viva->sources->create()"
      description: |
        Crée une nouvelle source de paiement.

        ```php
        $source = $viva->sources->create(
            name: 'Mon site web',
            sourceCode: '1234',
            domain: 'www.example.com',
            pathSuccess: '/paiement/succes',
            pathFail: '/paiement/echec',
        );
        ```
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SourcesCreateParams'
      responses:
        '200':
          description: Source créée
          content:
            application/json:
              schema:
                type: object

  # ============================================================
  # Wallets
  # ============================================================
  /Wallets/list:
    get:
      tags: [Wallets]
      operationId: Wallets_list
      summary: "$viva->wallets->list()"
      description: |
        Liste tous les portefeuilles du marchand (réponse basique).

        ```php
        $wallets = $viva->wallets->list();

        foreach ($wallets as $wallet) {
            echo $wallet['Available'] . ' ' . $wallet['CurrencyCode'] . "\n";
        }
        ```
      responses:
        '200':
          description: Liste des portefeuilles
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object

  /Wallets/balance:
    get:
      tags: [Wallets]
      operationId: Wallets_balance
      summary: "$viva->wallets->balance()"
      description: |
        Retourne le solde agrégé de tous les portefeuilles.

        ```php
        $balance = $viva->wallets->balance();

        echo $balance['available'];  // 150.50
        echo $balance['pending'];    // 25.00
        echo $balance['reserved'];   // 0.00
        echo $balance['currency'];   // 'EUR'
        ```
      responses:
        '200':
          description: Solde agrégé
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WalletBalance'

  /Wallets/transfer:
    post:
      tags: [Wallets]
      operationId: Wallets_transfer
      summary: "$viva->wallets->transfer()"
      description: |
        Transfère de l'argent entre deux portefeuilles Viva.

        Prérequis : « Allow transfers between accounts » doit être activé dans Settings > API Access.

        ```php
        $result = $viva->wallets->transfer(
            amount: 5000,                        // 50,00 EUR
            sourceWalletId: 'source-uuid',
            targetWalletId: 'target-uuid',
            description: 'Transfert mensuel',
        );
        ```
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WalletTransferParams'
      responses:
        '200':
          description: Transfert effectué
          content:
            application/json:
              schema:
                type: object

  /Wallets/listDetailed:
    get:
      tags: [Wallets]
      operationId: Wallets_listDetailed
      summary: "$viva->wallets->listDetailed()"
      description: |
        Liste les portefeuilles avec des détails enrichis (IBAN, SWIFT, etc.).

        ```php
        $wallets = $viva->wallets->listDetailed();

        foreach ($wallets as $wallet) {
            echo $wallet['iban'] . ' — ' . $wallet['amount'] . "\n";
            echo $wallet['isPrimary'] ? 'Principal' : 'Secondaire';
        }
        ```
      responses:
        '200':
          description: Liste détaillée des portefeuilles
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/WalletDetailed'

  /Wallets/create:
    post:
      tags: [Wallets]
      operationId: Wallets_create
      summary: "$viva->wallets->create()"
      description: |
        Crée un nouveau portefeuille (sous-compte).

        ```php
        $wallet = $viva->wallets->create(
            friendlyName: 'Compte secondaire',
            currencyCode: 'EUR',
        );
        ```
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [friendlyName]
              properties:
                friendlyName:
                  type: string
                  description: Nom d'affichage du portefeuille
                currencyCode:
                  type: string
                  default: EUR
                  description: "Code ISO 4217 alpha (ex: `EUR`)"
      responses:
        '200':
          description: Portefeuille créé
          content:
            application/json:
              schema:
                type: object

  /Wallets/update:
    put:
      tags: [Wallets]
      operationId: Wallets_update
      summary: "$viva->wallets->update()"
      description: |
        Met à jour le nom d'un portefeuille.

        ```php
        $viva->wallets->update(
            walletId: 12345,
            friendlyName: 'Nouveau nom',
        );
        ```
      parameters:
        - name: walletId
          in: query
          required: true
          schema:
            type: integer
          description: ID du portefeuille
        - name: friendlyName
          in: query
          required: true
          schema:
            type: string
          description: Nouveau nom d'affichage
      responses:
        '200':
          description: Portefeuille mis à jour
          content:
            application/json:
              schema:
                type: object

  /Wallets/searchTransactions:
    get:
      tags: [Wallets]
      operationId: Wallets_searchTransactions
      summary: "$viva->wallets->searchTransactions()"
      description: |
        Recherche les transactions de compte (tous les portefeuilles).

        ```php
        $transactions = $viva->wallets->searchTransactions([
            'date_from' => '2026-03-01',
            'date_to' => '2026-03-18',
            'walletId' => 12345,
        ]);

        foreach ($transactions as $txn) {
            echo $txn['amount'] . "\n";
        }
        ```
      parameters:
        - name: filters
          in: query
          required: false
          schema:
            type: object
            properties:
              date_from:
                type: string
                format: date
              date_to:
                type: string
                format: date
              walletId:
                type: integer
          description: "Filtres optionnels (`date_from`, `date_to`, `walletId`)"
      responses:
        '200':
          description: Liste de transactions de compte
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object

  /Wallets/getTransaction:
    get:
      tags: [Wallets]
      operationId: Wallets_getTransaction
      summary: "$viva->wallets->getTransaction()"
      description: |
        Récupère les détails d'une transaction de compte.

        ```php
        $txn = $viva->wallets->getTransaction('transaction-uuid');
        ```
      parameters:
        - name: transactionId
          in: query
          required: true
          schema:
            type: string
          description: UUID de la transaction de compte
      responses:
        '200':
          description: Détails de la transaction
          content:
            application/json:
              schema:
                type: object

  # ============================================================
  # BankAccounts
  # ============================================================
  /BankAccounts/link:
    post:
      tags: [BankAccounts]
      operationId: BankAccounts_link
      summary: "$viva->bankAccounts->link()"
      description: |
        Lie un compte bancaire (validation IBAN + enregistrement).

        ```php
        $result = $viva->bankAccounts->link(
            iban: 'FR7630006000011234567890189',
            beneficiaryName: 'Jean Dupont',
            friendlyName: 'Compte principal',
        );

        echo $result['bankAccountId']; // UUID
        echo $result['isVivaIban'];    // false (IBAN externe)
        ```
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BankAccountLinkParams'
      responses:
        '200':
          description: Compte bancaire lié
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BankAccountLinkResult'

  /BankAccounts/transferOptions:
    get:
      tags: [BankAccounts]
      operationId: BankAccounts_transferOptions
      summary: "$viva->bankAccounts->transferOptions()"
      description: |
        Récupère les options de transfert disponibles (SEPA standard, SEPA instantané, frais).

        Uniquement pour les IBANs externes — pas pour les wallets Viva internes.

        ```php
        $options = $viva->bankAccounts->transferOptions('bank-account-uuid');
        ```
      parameters:
        - name: bankAccountId
          in: query
          required: true
          schema:
            type: string
            format: uuid
          description: UUID du compte bancaire lié
      responses:
        '200':
          description: Options de transfert disponibles
          content:
            application/json:
              schema:
                type: object
                description: "Types d'instruction disponibles (SHA, OUR, instant)"

  /BankAccounts/feeCommand:
    post:
      tags: [BankAccounts]
      operationId: BankAccounts_feeCommand
      summary: "$viva->bankAccounts->feeCommand()"
      description: |
        Crée une commande de frais pour vérifier le coût d'un virement avant exécution.

        ```php
        $fees = $viva->bankAccounts->feeCommand(
            bankAccountId: 'bank-account-uuid',
            amount: 10000,                    // 100,00 EUR
            walletId: 'source-wallet-uuid',
            isInstant: true,                  // SEPA instantané
            instructionType: 'SHA',           // Frais partagés
        );

        echo $fees['bankCommandId']; // À passer à send()
        echo $fees['fee'];           // Frais en centimes
        ```
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BankAccountFeeCommandParams'
      responses:
        '200':
          description: Commande de frais créée
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BankAccountFeeCommandResult'

  /BankAccounts/send:
    post:
      tags: [BankAccounts]
      operationId: BankAccounts_send
      summary: "$viva->bankAccounts->send()"
      description: |
        Exécute un virement SEPA vers un compte bancaire lié.

        ```php
        $result = $viva->bankAccounts->send(
            bankAccountId: 'bank-account-uuid',
            amount: 10000,
            walletId: 'source-wallet-uuid',
            bankCommandId: 'fee-command-uuid',  // Optionnel
            description: 'Virement mensuel',
        );

        echo $result['commandId']; // UUID du virement
        echo $result['isInstant']; // true/false
        echo $result['fee'];       // Frais en centimes
        ```
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BankAccountSendParams'
      responses:
        '200':
          description: Virement exécuté
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BankAccountSendResult'

  /BankAccounts/list:
    get:
      tags: [BankAccounts]
      operationId: BankAccounts_list
      summary: "$viva->bankAccounts->list()"
      description: |
        Liste tous les comptes bancaires liés.

        ```php
        $accounts = $viva->bankAccounts->list();

        foreach ($accounts as $account) {
            echo $account['iban'] . ' — ' . $account['beneficiaryName'] . "\n";
        }
        ```
      responses:
        '200':
          description: Liste des comptes bancaires
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object

  /BankAccounts/get:
    get:
      tags: [BankAccounts]
      operationId: BankAccounts_get
      summary: "$viva->bankAccounts->get()"
      description: |
        Récupère les détails d'un compte bancaire lié.

        ```php
        $account = $viva->bankAccounts->get('bank-account-uuid');

        echo $account['iban'];
        echo $account['beneficiaryName'];
        ```
      parameters:
        - name: bankAccountId
          in: query
          required: true
          schema:
            type: string
            format: uuid
          description: UUID du compte bancaire
      responses:
        '200':
          description: Détails du compte bancaire
          content:
            application/json:
              schema:
                type: object

  # ============================================================
  # NativeCheckout
  # ============================================================
  /NativeCheckout/createChargeToken:
    post:
      tags: [NativeCheckout]
      operationId: NativeCheckout_createChargeToken
      summary: "$viva->nativeCheckout->createChargeToken()"
      description: |
        Génère un token de charge à usage unique à partir des données Apple Pay ou Google Pay.

        ```php
        $token = $viva->nativeCheckout->createChargeToken(
            amount: 1500,
            paymentData: $applePayPaymentDataString,
            paymentMethod: 'applepay',  // ou 'googlepay'
            sourceCode: '1234',
        );

        echo $token['chargeToken'];         // Token à passer à createTransaction()
        echo $token['redirectToACSForm'];    // Formulaire 3DS (si applicable)
        ```
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NativeCheckoutChargeTokenParams'
      responses:
        '200':
          description: Token de charge généré
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NativeCheckoutChargeTokenResult'

  /NativeCheckout/createTransaction:
    post:
      tags: [NativeCheckout]
      operationId: NativeCheckout_createTransaction
      summary: "$viva->nativeCheckout->createTransaction()"
      description: |
        Exécute une transaction à partir d'un token de charge obtenu via `createChargeToken()`.

        ```php
        $txn = $viva->nativeCheckout->createTransaction(
            chargeToken: $token['chargeToken'],
            amount: 1500,
            currencyCode: 978,             // EUR (ISO 4217 numérique)
            sourceCode: '1234',
            merchantTrns: 'ref_123',       // Référence interne
            customerTrns: 'Consultation',  // Affiché au client
            preauth: false,
            tipAmount: 0,
            installments: null,
        );

        echo $txn['transactionId']; // UUID
        echo $txn['statusId'];      // 'F' = finalisée
        echo $txn['amount'];        // 1500
        echo $txn['orderCode'];     // Code de l'ordre
        ```
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NativeCheckoutTransactionParams'
      responses:
        '200':
          description: Transaction créée
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NativeCheckoutTransactionResult'

  # ============================================================
  # DataServices
  # ============================================================
  /DataServices/mt940:
    get:
      tags: [DataServices]
      operationId: DataServices_mt940
      summary: "$viva->dataServices->mt940()"
      description: |
        Récupère le rapport MT940 (format standard bancaire) pour une date donnée.

        ```php
        $report = $viva->dataServices->mt940('2026-03-18');
        ```
      parameters:
        - name: date
          in: query
          required: true
          schema:
            type: string
            format: date
          description: "Date au format `Y-m-d`"
      responses:
        '200':
          description: Données du rapport MT940
          content:
            application/json:
              schema:
                type: object

  /DataServices/createSubscription:
    post:
      tags: [DataServices]
      operationId: DataServices_createSubscription
      summary: "$viva->dataServices->createSubscription()"
      description: |
        Crée une souscription webhook pour les événements de fichiers de données.

        ```php
        $sub = $viva->dataServices->createSubscription(
            url: 'https://example.com/webhooks/viva-files',
            eventType: 'SaleTransactionsFileGenerated',
        );

        echo $sub['subscriptionId']; // UUID
        ```
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [url]
              properties:
                url:
                  type: string
                  format: uri
                  description: URL de réception des webhooks
                eventType:
                  type: string
                  default: SaleTransactionsFileGenerated
                  description: Type d'événement
      responses:
        '200':
          description: Souscription créée
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DataServicesSubscription'

  /DataServices/updateSubscription:
    put:
      tags: [DataServices]
      operationId: DataServices_updateSubscription
      summary: "$viva->dataServices->updateSubscription()"
      description: |
        Met à jour une souscription webhook existante.

        ```php
        $viva->dataServices->updateSubscription(
            subscriptionId: 'sub-uuid',
            url: 'https://example.com/webhooks/new-url',
        );
        ```
      parameters:
        - name: subscriptionId
          in: query
          required: true
          schema:
            type: string
            format: uuid
        - name: url
          in: query
          required: true
          schema:
            type: string
            format: uri
        - name: eventType
          in: query
          required: false
          schema:
            type: string
            nullable: true
          description: "Nouveau type d'événement (`null` = conserver l'actuel)"
      responses:
        '200':
          description: Souscription mise à jour
          content:
            application/json:
              schema:
                type: object

  /DataServices/deleteSubscription:
    delete:
      tags: [DataServices]
      operationId: DataServices_deleteSubscription
      summary: "$viva->dataServices->deleteSubscription()"
      description: |
        Supprime une souscription webhook.

        ```php
        $viva->dataServices->deleteSubscription('sub-uuid');
        ```
      parameters:
        - name: subscriptionId
          in: query
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Souscription supprimée

  /DataServices/listSubscriptions:
    get:
      tags: [DataServices]
      operationId: DataServices_listSubscriptions
      summary: "$viva->dataServices->listSubscriptions()"
      description: |
        Liste toutes les souscriptions webhook actives.

        ```php
        $subs = $viva->dataServices->listSubscriptions();

        foreach ($subs as $sub) {
            echo $sub['subscriptionId'] . ' → ' . $sub['url'] . "\n";
        }
        ```
      responses:
        '200':
          description: Liste des souscriptions
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/DataServicesSubscription'

  /DataServices/requestFile:
    post:
      tags: [DataServices]
      operationId: DataServices_requestFile
      summary: "$viva->dataServices->requestFile()"
      description: |
        Demande la génération asynchrone d'un fichier de transactions pour une date.

        Utilisez une souscription webhook pour être notifié quand le fichier est prêt.

        ```php
        $viva->dataServices->requestFile('2026-03-18');
        ```
      parameters:
        - name: date
          in: query
          required: true
          schema:
            type: string
            format: date
          description: "Date au format `Y-m-d`"
      responses:
        '200':
          description: Demande de fichier enregistrée
          content:
            application/json:
              schema:
                type: object

  # ============================================================
  # Webhooks
  # ============================================================
  /Webhooks/verificationResponse:
    get:
      tags: [Webhooks]
      operationId: Webhooks_verificationResponse
      summary: "$viva->webhooks->verificationResponse()"
      description: |
        Génère la réponse de vérification pour le GET de confirmation envoyé par Viva.

        Quand vous configurez un webhook dans le Dashboard Viva, Viva envoie un GET pour vérifier que l'URL est accessible. Vous devez répondre avec votre clé de vérification.

        ```php
        // Dans votre contrôleur (Laravel) :
        public function handleVivaWebhookVerification(Request $request)
        {
            $response = $viva->webhooks->verificationResponse('votre-verification-key');

            return response()->json($response);
            // => {"StatusCode": 0, "Key": "votre-verification-key"}
        }
        ```
      parameters:
        - name: verificationKey
          in: query
          required: true
          schema:
            type: string
          description: Clé de vérification (Dashboard Viva > Webhooks)
      responses:
        '200':
          description: Réponse de vérification
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookVerificationResponse'

  /Webhooks/parse:
    post:
      tags: [Webhooks]
      operationId: Webhooks_parse
      summary: "$viva->webhooks->parse()"
      description: |
        Parse le payload JSON d'un webhook POST envoyé par Viva.

        ```php
        // Dans votre contrôleur (Laravel) :
        public function handleVivaWebhook(Request $request)
        {
            $event = $viva->webhooks->parse($request->getContent());

            echo $event['event_type'];     // 'transaction.payment.created'
            echo $event['event_type_id'];  // 1796
            echo $event['event_data'];     // Données de l'événement

            match ($event['event_type']) {
                'transaction.payment.created' => $this->handlePayment($event['event_data']),
                'transaction.refund.created'  => $this->handleRefund($event['event_data']),
                default => null,
            };
        }
        ```
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [rawBody]
              properties:
                rawBody:
                  type: string
                  description: Corps JSON brut du POST webhook
      responses:
        '200':
          description: Événement parsé
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookParsedEvent'
        '400':
          description: "`InvalidArgumentException` — JSON invalide"

  /Webhooks/isKnownEvent:
    get:
      tags: [Webhooks]
      operationId: Webhooks_isKnownEvent
      summary: "$viva->webhooks->isKnownEvent()"
      description: |
        Vérifie si un ID de type d'événement est reconnu par le SDK.

        ```php
        $viva->webhooks->isKnownEvent(1796); // true (transaction.payment.created)
        $viva->webhooks->isKnownEvent(9999); // false
        ```
      parameters:
        - name: eventTypeId
          in: query
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: "`bool`"
          content:
            application/json:
              schema:
                type: boolean

  /Webhooks/eventTypeIds:
    get:
      tags: [Webhooks]
      operationId: Webhooks_eventTypeIds
      summary: "$viva->webhooks->eventTypeIds()"
      description: |
        Retourne tous les IDs de types d'événements connus.

        ```php
        $ids = $viva->webhooks->eventTypeIds();
        // [1796, 1797, 1798, 1799, 1800, ..., 1828]
        ```
      responses:
        '200':
          description: Liste des IDs d'événements
          content:
            application/json:
              schema:
                type: array
                items:
                  type: integer

  # ============================================================
  # Account
  # ============================================================
  /Account/info:
    get:
      tags: [Account]
      operationId: Account_info
      summary: "$viva->account->info()"
      description: |
        Récupère les informations du compte marchand.

        ```php
        $info = $viva->account->info();

        echo $info['merchantId'];
        echo $info['businessName'];
        echo $info['email'];
        ```
      responses:
        '200':
          description: Informations du compte
          content:
            application/json:
              schema:
                type: object
                description: "array<string, mixed> — merchantId, businessName, email, etc."

  /Account/wallets:
    get:
      tags: [Account]
      operationId: Account_wallets
      summary: "$viva->account->wallets()"
      description: |
        Récupère les portefeuilles/soldes du marchand (via Account API).

        ```php
        $wallets = $viva->account->wallets();
        ```
      responses:
        '200':
          description: Portefeuilles du compte
          content:
            application/json:
              schema:
                type: object

  # ============================================================
  # Enums
  # ============================================================
  /Enums/Environment:
    get:
      tags: [Enums]
      operationId: Enum_Environment
      summary: "Environment::DEMO | Environment::PRODUCTION"
      description: |
        Enum PHP pour l'environnement Viva Wallet.

        ```php
        use QrCommunication\VivaMerchant\Enums\Environment;

        $env = Environment::DEMO;
        $env = Environment::PRODUCTION;
        $env = Environment::from('demo'); // Depuis une string

        echo $env->value;         // 'demo'
        echo $env->apiUrl();      // 'https://demo-api.vivapayments.com'
        echo $env->legacyUrl();   // 'https://demo.vivapayments.com'
        echo $env->checkoutUrl(); // 'https://demo.vivapayments.com/web/checkout'
        echo $env->accountsUrl(); // 'https://demo-accounts.vivapayments.com'
        ```

        | Valeur | apiUrl | legacyUrl |
        |--------|--------|-----------|
        | `demo` | `demo-api.vivapayments.com` | `demo.vivapayments.com` |
        | `production` | `api.vivapayments.com` | `www.vivapayments.com` |
      responses:
        '200':
          description: Enum Environment
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EnvironmentEnum'

  /Enums/Currency:
    get:
      tags: [Enums]
      operationId: Enum_Currency
      summary: "Currency::EUR | Currency::GBP | ..."
      description: |
        Enum PHP pour les devises (codes ISO 4217 numériques).

        ```php
        use QrCommunication\VivaMerchant\Enums\Currency;

        $eur = Currency::EUR;
        echo $eur->value;     // 978
        echo $eur->iso();     // 'EUR'

        $gbp = Currency::fromIso('GBP');
        echo $gbp->value;     // 826
        ```

        | Devise | Code numérique | Enum |
        |--------|---------------|------|
        | EUR | 978 | `Currency::EUR` |
        | GBP | 826 | `Currency::GBP` |
        | USD | 840 | `Currency::USD` |
        | PLN | 985 | `Currency::PLN` |
        | RON | 946 | `Currency::RON` |
        | BGN | 975 | `Currency::BGN` |
        | CZK | 203 | `Currency::CZK` |
        | HRK | 191 | `Currency::HRK` |
        | HUF | 348 | `Currency::HUF` |
        | DKK | 208 | `Currency::DKK` |
        | SEK | 752 | `Currency::SEK` |
        | NOK | 578 | `Currency::NOK` |
      responses:
        '200':
          description: Enum Currency
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CurrencyEnum'

  /Enums/TransactionStatus:
    get:
      tags: [Enums]
      operationId: Enum_TransactionStatus
      summary: "TransactionStatus::FINALIZED | ::PENDING | ..."
      description: |
        Enum PHP pour les statuts de transaction Viva Wallet.

        ```php
        use QrCommunication\VivaMerchant\Enums\TransactionStatus;

        $status = TransactionStatus::from('F');
        echo $status->label();        // 'Finalized'
        echo $status->isSuccessful();  // true
        echo $status->isPending();     // false
        echo $status->isFailed();      // false
        ```

        | Valeur | Constante | Label | Catégorie |
        |--------|-----------|-------|-----------|
        | `F` | `FINALIZED` | Finalized | Succès |
        | `A` | `PENDING` | Pending | En cours |
        | `C` | `CLEARING` | Clearing | En cours |
        | `E` | `ERROR` | Error | Échec |
        | `M` | `MANUALLY_REVERSED` | Reversed | Échec |
        | `X` | `REQUIRES_ACTION` | Requires Action | — |
        | `R` | `REFUNDED` | Refunded | — |

        **Méthodes utilitaires :**
        - `isSuccessful()` → `true` si `FINALIZED`
        - `isPending()` → `true` si `PENDING` ou `CLEARING`
        - `isFailed()` → `true` si `ERROR` ou `MANUALLY_REVERSED`
        - `label()` → label lisible en anglais
      responses:
        '200':
          description: Enum TransactionStatus
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionStatusEnum'

  # ============================================================
  # Exceptions
  # ============================================================
  /Exceptions/VivaException:
    get:
      tags: [Exceptions]
      operationId: Exception_VivaException
      summary: "VivaException — classe de base"
      description: |
        Classe de base pour toutes les exceptions du SDK. Étend `RuntimeException`.

        ```php
        use QrCommunication\VivaMerchant\Exceptions\VivaException;

        try {
            $viva->orders->create(amount: 0);
        } catch (VivaException $e) {
            echo $e->getMessage();     // Message d'erreur
            echo $e->httpStatus;       // Code HTTP (400, 401, 404, etc.)
            echo $e->responseBody;     // Réponse JSON brute (array|null)
            echo $e->getErrorCode();   // ErrorCode Viva (int|null)
            echo $e->getErrorText();   // ErrorText Viva (string|null)
        }
        ```

        **Propriétés :**
        - `httpStatus` (`int`) — Code HTTP de la réponse
        - `responseBody` (`?array`) — Corps JSON décodé de la réponse Viva

        **Méthodes :**
        - `getErrorCode(): ?int` — Code d'erreur Viva
        - `getErrorText(): ?string` — Message d'erreur Viva
      responses:
        '200':
          description: Classe VivaException

  /Exceptions/AuthenticationException:
    get:
      tags: [Exceptions]
      operationId: Exception_AuthenticationException
      summary: "AuthenticationException — échec OAuth2"
      description: |
        Levée quand l'authentification OAuth2 échoue (identifiants invalides, token expiré non renouvelable).

        ```php
        use QrCommunication\VivaMerchant\Exceptions\AuthenticationException;

        try {
            $viva->account->info();
        } catch (AuthenticationException $e) {
            echo $e->getMessage(); // 'OAuth2 authentication failed: ...'
            // httpStatus = 401
        }
        ```
      responses:
        '200':
          description: Classe AuthenticationException

  /Exceptions/ValidationException:
    get:
      tags: [Exceptions]
      operationId: Exception_ValidationException
      summary: "ValidationException — erreur de validation"
      description: |
        Levée quand l'API retourne une erreur de validation (422).

        ```php
        use QrCommunication\VivaMerchant\Exceptions\ValidationException;

        try {
            // ...
        } catch (ValidationException $e) {
            echo $e->errors;
            // ['amount' => ['Amount must be positive'], ...]
        }
        ```

        **Propriétés :**
        - `errors` (`array<string, string[]>`) — Erreurs par champ
      responses:
        '200':
          description: Classe ValidationException

components:
  schemas:
    # VivaClient
    VivaClientConstructorParams:
      type: object
      required: [merchantId, apiKey, clientId, clientSecret]
      properties:
        merchantId:
          type: string
          format: uuid
          description: "Merchant UUID — Dashboard Viva > Settings > API Access"
        apiKey:
          type: string
          description: "API Key — Dashboard Viva > Settings > API Access"
        clientId:
          type: string
          description: "OAuth Client ID — format `xxx.apps.vivapayments.com`"
        clientSecret:
          type: string
          description: OAuth Client Secret
        environment:
          type: string
          enum: [demo, production]
          default: demo
          description: "Environnement (`'demo'` ou `'production'`, ou `Environment` enum)"

    VivaClientInstance:
      type: object
      description: |
        Instance `VivaClient` avec les propriétés `readonly` suivantes :
      properties:
        orders:
          type: string
          description: "`Orders` — `$viva->orders`"
        transactions:
          type: string
          description: "`Transactions` — `$viva->transactions`"
        sources:
          type: string
          description: "`Sources` — `$viva->sources`"
        wallets:
          type: string
          description: "`Wallets` — `$viva->wallets`"
        bankAccounts:
          type: string
          description: "`BankAccounts` — `$viva->bankAccounts`"
        webhooks:
          type: string
          description: "`Webhooks` — `$viva->webhooks`"
        account:
          type: string
          description: "`Account` — `$viva->account`"
        nativeCheckout:
          type: string
          description: "`NativeCheckout` — `$viva->nativeCheckout`"
        dataServices:
          type: string
          description: "`DataServices` — `$viva->dataServices`"

    Config:
      type: object
      properties:
        merchantId:
          type: string
        apiKey:
          type: string
        clientId:
          type: string
        clientSecret:
          type: string
        environment:
          $ref: '#/components/schemas/EnvironmentEnum'
      description: |
        Objet de configuration interne.

        **Méthodes :**
        - `apiUrl(): string`
        - `legacyUrl(): string`
        - `checkoutUrl(): string`
        - `accountsUrl(): string`
        - `isProduction(): bool`

    # Orders
    OrdersCreateParams:
      type: object
      required: [amount]
      properties:
        amount:
          type: integer
          description: "Montant en centimes (`1500` = 15,00 EUR)"
          example: 1500
        customerDescription:
          type: string
          nullable: true
          description: Description affichée au client dans le checkout
          example: Consultation naturopathie
        merchantReference:
          type: string
          nullable: true
          description: Référence interne (apparaît dans les exports)
          example: session_123
        sourceCode:
          type: string
          nullable: true
          description: "Code source de paiement (`null` = source par défaut)"
        allowRecurring:
          type: boolean
          default: false
          description: Autoriser la tokenisation de la carte pour les charges récurrentes
        preauth:
          type: boolean
          default: false
          description: "Pré-autorisation uniquement (capturer plus tard avec `transactions->capture()`)"
        maxInstallments:
          type: integer
          default: 0
          description: "Nombre max de versements (`0` = désactivé)"

    OrdersCreateResult:
      type: object
      properties:
        order_code:
          type: integer
          description: Code unique de l'ordre Viva
          example: 1234567890
        checkout_url:
          type: string
          format: uri
          description: URL Smart Checkout pour rediriger le client
          example: "https://demo.vivapayments.com/web/checkout?ref=1234567890"

    # Sources
    SourcesCreateParams:
      type: object
      required: [name, sourceCode]
      properties:
        name:
          type: string
          description: Nom d'affichage de la source
          example: Mon site web
        sourceCode:
          type: string
          description: Code source à 4 chiffres
          example: "1234"
        domain:
          type: string
          nullable: true
          description: Domaine du site web
          example: www.example.com
        pathSuccess:
          type: string
          nullable: true
          description: Chemin de redirection après paiement réussi
          example: /paiement/succes
        pathFail:
          type: string
          nullable: true
          description: Chemin de redirection après paiement échoué
          example: /paiement/echec

    # Wallets
    WalletBalance:
      type: object
      properties:
        available:
          type: number
          format: float
          description: Solde disponible
          example: 150.50
        pending:
          type: number
          format: float
          description: Solde en attente
          example: 25.00
        reserved:
          type: number
          format: float
          description: Solde réservé
          example: 0.00
        currency:
          type: string
          description: Code devise ISO 4217
          example: EUR

    WalletTransferParams:
      type: object
      required: [amount, sourceWalletId, targetWalletId]
      properties:
        amount:
          type: integer
          description: Montant en centimes
          example: 5000
        sourceWalletId:
          type: string
          format: uuid
          description: UUID du portefeuille source
        targetWalletId:
          type: string
          format: uuid
          description: UUID du portefeuille cible
        description:
          type: string
          nullable: true
          description: Description du transfert
          example: Transfert mensuel

    WalletDetailed:
      type: object
      properties:
        iban:
          type: string
          description: IBAN du portefeuille
        walletId:
          type: integer
          description: ID numérique du portefeuille
        amount:
          type: number
          format: float
          description: Solde actuel
        isPrimary:
          type: boolean
          description: Portefeuille principal ?
        currencyCode:
          type: string
          description: Code devise ISO 4217
        friendlyName:
          type: string
          nullable: true
          description: Nom d'affichage

    # BankAccounts
    BankAccountLinkParams:
      type: object
      required: [iban, beneficiaryName]
      properties:
        iban:
          type: string
          description: IBAN du compte bancaire
          example: FR7630006000011234567890189
        beneficiaryName:
          type: string
          description: Nom du titulaire du compte
          example: Jean Dupont
        friendlyName:
          type: string
          nullable: true
          description: Nom d'affichage optionnel
          example: Compte principal

    BankAccountLinkResult:
      type: object
      properties:
        bankAccountId:
          type: string
          format: uuid
          description: UUID du compte bancaire lié
        isVivaIban:
          type: boolean
          description: "L'IBAN appartient-il à un compte Viva interne ?"

    BankAccountFeeCommandParams:
      type: object
      required: [bankAccountId, amount, walletId]
      properties:
        bankAccountId:
          type: string
          format: uuid
          description: UUID du compte bancaire lié
        amount:
          type: integer
          description: Montant en centimes
          example: 10000
        walletId:
          type: string
          description: UUID du portefeuille source
        isInstant:
          type: boolean
          default: false
          description: SEPA instantané ?
        instructionType:
          type: string
          enum: [SHA, OUR]
          default: SHA
          description: "Type d'instruction de frais (SHA = partagés, OUR = à notre charge)"

    BankAccountFeeCommandResult:
      type: object
      properties:
        bankCommandId:
          type: string
          description: "ID de la commande de frais (à passer à `send()`)"
        fee:
          type: integer
          description: Frais en centimes

    BankAccountSendParams:
      type: object
      required: [bankAccountId, amount, walletId]
      properties:
        bankAccountId:
          type: string
          format: uuid
          description: UUID du compte bancaire lié
        amount:
          type: integer
          description: Montant en centimes
          example: 10000
        walletId:
          type: string
          description: UUID du portefeuille source
        bankCommandId:
          type: string
          nullable: true
          description: "ID de la commande de frais (optionnel, obtenu via `feeCommand()`)"
        description:
          type: string
          nullable: true
          description: Description du virement

    BankAccountSendResult:
      type: object
      properties:
        commandId:
          type: string
          description: UUID du virement
        isInstant:
          type: boolean
          description: Virement instantané ?
        fee:
          type: integer
          description: Frais en centimes

    # NativeCheckout
    NativeCheckoutChargeTokenParams:
      type: object
      required: [amount, paymentData]
      properties:
        amount:
          type: integer
          description: Montant en centimes
          example: 1500
        paymentData:
          type: string
          description: Données de paiement Apple Pay / Google Pay (string encodée)
        paymentMethod:
          type: string
          enum: [applepay, googlepay]
          default: applepay
          description: "Méthode de paiement (`'applepay'` ou `'googlepay'`)"
        sourceCode:
          type: string
          nullable: true
          description: Code source de paiement
        dynamicDescriptor:
          type: string
          nullable: true
          description: Descripteur dynamique pour la charge

    NativeCheckoutChargeTokenResult:
      type: object
      properties:
        chargeToken:
          type: string
          description: "Token à usage unique pour `createTransaction()`"
        redirectToACSForm:
          type: string
          nullable: true
          description: Formulaire de redirection 3D Secure (si applicable)

    NativeCheckoutTransactionParams:
      type: object
      required: [chargeToken, amount]
      properties:
        chargeToken:
          type: string
          description: "Token obtenu via `createChargeToken()`"
        amount:
          type: integer
          description: Montant en centimes
          example: 1500
        currencyCode:
          type: integer
          default: 978
          description: "Code devise ISO 4217 numérique (`978` = EUR)"
        sourceCode:
          type: string
          nullable: true
          description: Code source de paiement
        merchantTrns:
          type: string
          nullable: true
          description: Référence interne
        customerTrns:
          type: string
          nullable: true
          description: Description affichée au client
        preauth:
          type: boolean
          default: false
          description: Pré-autorisation uniquement ?
        tipAmount:
          type: integer
          default: 0
          description: Pourboire en centimes
        installments:
          type: integer
          nullable: true
          description: "Nombre de versements (`null` = désactivé)"

    NativeCheckoutTransactionResult:
      type: object
      properties:
        transactionId:
          type: string
          format: uuid
          description: UUID de la transaction
        statusId:
          type: string
          description: "Statut (`'F'` = finalisée)"
        amount:
          type: integer
          description: Montant en centimes
        orderCode:
          type: integer
          description: Code de l'ordre

    # DataServices
    DataServicesSubscription:
      type: object
      properties:
        subscriptionId:
          type: string
          format: uuid
          description: UUID de la souscription
        url:
          type: string
          format: uri
          description: URL du webhook
        eventType:
          type: string
          description: Type d'événement

    # Webhooks
    WebhookVerificationResponse:
      type: object
      properties:
        StatusCode:
          type: integer
          example: 0
        Key:
          type: string
          description: Clé de vérification
          example: votre-verification-key

    WebhookParsedEvent:
      type: object
      properties:
        event_type:
          type: string
          description: "Type d'événement lisible (ex: `transaction.payment.created`)"
          example: transaction.payment.created
        event_type_id:
          type: integer
          description: ID numérique du type d'événement Viva
          example: 1796
        event_data:
          type: object
          description: Données de l'événement (structure variable selon le type)

    WebhookEventTypes:
      type: object
      description: |
        Les 21 types d'événements webhook supportés :

        | ID | Type |
        |----|------|
        | 1796 | `transaction.payment.created` |
        | 1797 | `transaction.refund.created` |
        | 1798 | `transaction.payment.cancelled` |
        | 1799 | `transaction.reversal.created` |
        | 1800 | `transaction.preauth.created` |
        | 1801 | `transaction.preauth.completed` |
        | 1802 | `transaction.preauth.cancelled` |
        | 1810 | `pos.session.created` |
        | 1811 | `pos.session.failed` |
        | 1812 | `transaction.price.calculated` |
        | 1813 | `transaction.failed` |
        | 1819 | `account.connected` |
        | 1820 | `account.verification.status.changed` |
        | 1821 | `account.transaction.created` |
        | 1822 | `command.bank.transfer.created` |
        | 1823 | `command.bank.transfer.executed` |
        | 1824 | `transfer.created` |
        | 1825 | `obligation.created` |
        | 1826 | `obligation.captured` |
        | 1827 | `order.updated` |
        | 1828 | `sale.transactions.file` |

    # Enums
    EnvironmentEnum:
      type: string
      enum: [demo, production]
      description: |
        `QrCommunication\VivaMerchant\Enums\Environment`

        Backed enum PHP 8.1+ avec valeurs string.

    CurrencyEnum:
      type: integer
      description: |
        `QrCommunication\VivaMerchant\Enums\Currency`

        Backed enum PHP 8.1+ avec valeurs int (codes ISO 4217 numériques).

    TransactionStatusEnum:
      type: string
      enum: ['F', 'A', 'C', 'E', 'M', 'X', 'R']
      description: |
        `QrCommunication\VivaMerchant\Enums\TransactionStatus`

        Backed enum PHP 8.1+ avec valeurs string.

    # Errors
    ApiError:
      type: object
      properties:
        message:
          type: string
          description: Message d'erreur
        httpStatus:
          type: integer
          description: Code HTTP
        responseBody:
          type: object
          nullable: true
          description: Réponse brute Viva
