# Scripts de Référence - Passe Marché

## Vue d'ensemble

Cette section regroupe tous les scripts bash, exemples curl et utilitaires pratiques pour l'intégration avec l'API Passe Marché. Ces scripts sont des outils complémentaires à la documentation technique principale.

## Configuration des environnements

Tous les scripts utilisent la variable `BASE_URL`. Définissez-la selon votre environnement :

```bash
# Staging (recommandé pour le développement)
export BASE_URL="https://staging.passemarche.data.gouv.fr"

# Production
export BASE_URL="https://passemarche.data.gouv.fr"
```

Consultez la [documentation des environnements](https://guides.data.gouv.fr/passe-marche/docs/08_environnements) pour plus de détails.

***

## Authentification OAuth2

### Script d'authentification de base

```bash
#!/bin/bash
# Authentification OAuth2 simple

CLIENT_ID="${CLIENT_ID:?CLIENT_ID requis}"
CLIENT_SECRET="${CLIENT_SECRET:?CLIENT_SECRET requis}"
BASE_URL="${BASE_URL:-https://staging.passemarche.data.gouv.fr}"

get_access_token() {
  curl -s -X POST "$BASE_URL/oauth/token" \
    -H "Content-Type: application/x-www-form-urlencoded" \
    -d "grant_type=client_credentials&client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET&scope=api_access" \
    | jq -r '.access_token'
}

# Utilisation
TOKEN=$(get_access_token)
echo "Token obtenu: ${TOKEN:0:20}..."
```

### Script d'authentification avec retry

```bash
#!/bin/bash
# Script de retry pour l'authentification OAuth

authenticate_with_retry() {
  local max_attempts=3
  local attempt=1

  while [ $attempt -le $max_attempts ]; do
    echo "Tentative d'authentification $attempt/$max_attempts"

    response=$(curl -s -w "HTTPSTATUS:%{http_code}" -X POST \
      ${BASE_URL}/oauth/token \
      -H "Content-Type: application/x-www-form-urlencoded" \
      -d "grant_type=client_credentials&client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET&scope=api_access")

    http_code=$(echo $response | tr -d '\n' | sed -e 's/.*HTTPSTATUS://')
    body=$(echo $response | sed -e 's/HTTPSTATUS:.*//g')

    if [ $http_code -eq 200 ]; then
      echo "Authentification réussie"
      echo $body | jq '.access_token' -r
      return 0
    elif [ $http_code -eq 401 ]; then
      echo "Erreur d'authentification (401): Vérifiez vos identifiants"
      return 1
    elif [ $http_code -ge 500 ]; then
      echo "Erreur serveur ($http_code), retry dans 5s..."
      sleep 5
      ((attempt++))
    else
      echo "Erreur HTTP $http_code: $body"
      return 1
    fi
  done

  echo "Échec après $max_attempts tentatives"
  return 1
}
```

### Validation de token

```bash
#!/bin/bash
# Validation de l'expiration du token

validate_token() {
  local token_file="$1"
  local buffer_minutes=5

  if [ ! -f "$token_file" ]; then
    echo "false"
    return 1
  fi

  local issued_at=$(jq -r '.issued_at' "$token_file")
  local expires_in=$(jq -r '.expires_in' "$token_file")
  local current_time=$(date +%s)
  local expiration=$((issued_at + expires_in))
  local buffer_time=$((buffer_minutes * 60))

  if [ $current_time -lt $((expiration - buffer_time)) ]; then
    echo "true"
    return 0
  else
    echo "false"
    return 1
  fi
}

# Utilisation
if [ "$(validate_token ./token.json)" = "true" ]; then
  echo "Token encore valide"
else
  echo "Token expiré, renouvellement nécessaire"
fi
```

### Gestionnaire de token complet

```bash
#!/bin/bash
# Script complet de gestion OAuth avec curl

# Configuration
CLIENT_ID="${CLIENT_ID:-your_client_id}"
CLIENT_SECRET="${CLIENT_SECRET:-your_client_secret}"
BASE_URL="${BASE_URL:-https://staging.passemarche.data.gouv.fr}"
TOKEN_FILE="./oauth_token.json"

# Fonction d'obtention du token d'accès
get_access_token() {
  echo "Demande d'un nouveau token OAuth..."

  local response=$(curl -s -w "HTTPSTATUS:%{http_code}" -X POST \
    "$BASE_URL/oauth/token" \
    -H "Content-Type: application/x-www-form-urlencoded" \
    -d "grant_type=client_credentials&client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET&scope=api_access")

  local http_code=$(echo $response | tr -d '\n' | sed -e 's/.*HTTPSTATUS://')
  local body=$(echo $response | sed -e 's/HTTPSTATUS:.*//g')

  if [ $http_code -eq 200 ]; then
    # Ajouter timestamp d'émission
    echo $body | jq ". + {\"issued_at\": $(date +%s)}" > "$TOKEN_FILE"
    echo "Token obtenu et sauvegardé"
    echo $body | jq -r '.access_token'
  else
    echo "Erreur lors de l'obtention du token: HTTP $http_code"
    echo $body
    return 1
  fi
}

# Fonction de validation du token
is_token_valid() {
  if [ ! -f "$TOKEN_FILE" ]; then
    return 1
  fi

  local issued_at=$(jq -r '.issued_at // 0' "$TOKEN_FILE")
  local expires_in=$(jq -r '.expires_in // 0' "$TOKEN_FILE")
  local current_time=$(date +%s)
  local buffer_time=300  # 5 minutes

  if [ $issued_at -eq 0 ] || [ $expires_in -eq 0 ]; then
    return 1
  fi

  local expiration=$((issued_at + expires_in - buffer_time))

  [ $current_time -lt $expiration ]
}

# Fonction de requête authentifiée
make_authenticated_request() {
  local endpoint="$1"
  local method="${2:-GET}"
  local data="$3"

  # Obtenir un token valide
  local token
  if is_token_valid; then
    token=$(jq -r '.access_token' "$TOKEN_FILE")
    echo "Utilisation du token existant"
  else
    echo "Token invalide ou expiré, renouvellement..."
    token=$(get_access_token)
    if [ $? -ne 0 ]; then
      echo "Impossible d'obtenir un token"
      return 1
    fi
  fi

  # Effectuer la requête
  local curl_args=(
    -H "Authorization: Bearer $token"
    -H "Content-Type: application/json"
    -H "Accept: application/json"
  )

  if [ "$method" = "POST" ] && [ -n "$data" ]; then
    curl_args+=(-X POST -d "$data")
  elif [ "$method" = "PUT" ] && [ -n "$data" ]; then
    curl_args+=(-X PUT -d "$data")
  elif [ "$method" = "DELETE" ]; then
    curl_args+=(-X DELETE)
  fi

  curl -s "${curl_args[@]}" "$BASE_URL$endpoint"
}

# Exemples d'utilisation
# make_authenticated_request "/api/v1/public_markets"
# make_authenticated_request "/api/v1/public_markets" "POST" '{"public_market":{"name":"Test"}}'
```

***

## Gestion des Marchés Publics

### Création de marché - Script complet

```bash
#!/bin/bash
# market-creation.sh
create_market() {
  # Configuration
  local CLIENT_ID="${CLIENT_ID:?CLIENT_ID requis}"
  local CLIENT_SECRET="${CLIENT_SECRET:?CLIENT_SECRET requis}"
  local BASE_URL="${BASE_URL:-https://staging.passemarche.data.gouv.fr}"

  echo "🚀 Création d'un marché de test..."

  # 1. Authentification OAuth
  echo "🔐 Authentification..."
  local auth_response=$(curl -s -X POST "$BASE_URL/oauth/token" \
    -H "Content-Type: application/x-www-form-urlencoded" \
    -d "grant_type=client_credentials&client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET&scope=api_access")

  local token=$(echo "$auth_response" | jq -r '.access_token')

  if [ "$token" = "null" ] || [ -z "$token" ]; then
    echo "❌ Échec authentification"
    echo "$auth_response" | jq .
    return 1
  fi

  echo "✅ Token obtenu: ${token:0:20}..."

  # 2. Création du marché
  echo "📝 Création du marché..."
  local market_data='{
    "public_market": {
      "name": "Test - Fourniture équipements informatiques",
      "lot_name": "Lot unique - Ordinateurs",
      "deadline": "2024-12-31T23:59:59Z",
      "siret": "13002526500013",
      "market_type_codes": ["supplies"]
    }
  }'

  local api_response=$(curl -s -w "HTTPSTATUS:%{http_code}" \
    -X POST "$BASE_URL/api/v1/public_markets" \
    -H "Authorization: Bearer $token" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -d "$market_data")

  local http_code=$(echo "$api_response" | tr -d '\n' | sed -e 's/.*HTTPSTATUS://')
  local response_body=$(echo "$api_response" | sed -e 's/HTTPSTATUS:.*//g')

  if [ "$http_code" -eq 201 ]; then
    local identifier=$(echo "$response_body" | jq -r '.identifier')
    local config_url=$(echo "$response_body" | jq -r '.configuration_url')

    echo "✅ Marché créé: $identifier"
    echo "🔗 Configuration URL: $config_url"

    # Sauvegarde pour utilisation ultérieure
    echo "$response_body" > "./market_$identifier.json"
    echo "📄 Détails sauvegardés dans: market_$identifier.json"

    return 0
  else
    echo "❌ Erreur création marché: HTTP $http_code"
    echo "$response_body" | jq .
    return 1
  fi
}

# Test d'exécution
if [ "${BASH_SOURCE[0]}" = "${0}" ]; then
  create_market
fi
```

### Création de candidature

```bash
#!/bin/bash
# Création de candidature via API

create_application() {
  local market_identifier="$1"
  local siret="$2"

  # Obtenir token (réutilise la fonction précédente)
  local token=$(get_access_token)

  local application_data='{
    "market_application": {}'

  if [ -n "$siret" ]; then
    application_data=$(echo "$application_data" | jq --arg siret "$siret" '.market_application.siret = $siret')
  fi

  curl -s -X POST "$BASE_URL/api/v1/public_markets/$market_identifier/market_applications" \
    -H "Authorization: Bearer $token" \
    -H "Content-Type: application/json" \
    -d "$application_data"
}

# Utilisation
# create_application "VR-2024-A1B2C3D4E5F6" "12345678901234"
```

***

## Webhooks

### Serveur webhook minimal (Node.js)

```javascript
// webhook-server.js
const express = require('express');
const crypto = require('crypto');

const app = express();
const WEBHOOK_SECRET = 'VOTRE_WEBHOOK_SECRET';

// Middleware pour raw body (requis pour signature)
app.use('/webhooks', express.raw({type: 'application/json'}));

function verifySignature(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload, 'utf8')
    .digest('hex');

  const cleanSignature = signature.replace('sha256=', '');

  return crypto.timingSafeEqual(
    Buffer.from(expectedSignature, 'hex'),
    Buffer.from(cleanSignature, 'hex')
  );
}

app.post('/webhooks/passemarche', (req, res) => {
  const payload = req.body.toString('utf8');
  const signature = req.headers['x-webhook-signature-sha256'];

  // 1. Vérification signature
  if (!signature || !verifySignature(payload, signature, WEBHOOK_SECRET)) {
    console.error('⚠️ Signature invalide');
    return res.status(401).send('Unauthorized');
  }

  try {
    // 2. Parsing JSON
    const event = JSON.parse(payload);

    console.log(`📨 Webhook reçu: ${event.event}`);
    console.log(`⏰ Timestamp: ${event.timestamp}`);

    // 3. Traitement selon le type
    switch (event.event) {
      case 'market.completed':
        handleMarketCompleted(event);
        break;
      case 'market_application.completed':
        handleApplicationCompleted(event);
        break;
      default:
        console.log(`⚠️ Type d'événement inconnu: ${event.event}`);
    }

    res.status(200).send('OK');
  } catch (error) {
    console.error('❌ Erreur traitement webhook:', error);
    res.status(500).send('Internal Server Error');
  }
});

function handleMarketCompleted(event) {
  const { market } = event;
  console.log('🎯 Marché configuré:');
  console.log(`   ID: ${market.identifier}`);
  console.log(`   Nom: ${market.name}`);
  console.log(`   Champs: ${market.field_keys.length} configurés`);
  console.log(`   Types: ${market.market_type_codes.join(', ')}`);
}

function handleApplicationCompleted(event) {
  const { market_application, market_identifier } = event;
  console.log('🎯 Candidature finalisée:');
  console.log(`   ID: ${market_application.identifier}`);
  console.log(`   Marché: ${market_identifier}`);
  console.log(`   Entreprise: ${market_application.company_name}`);
  console.log(`   SIRET: ${market_application.siret}`);
  console.log(`   Attestation: ${market_application.attestation_url}`);
  console.log(`   Dossier: ${market_application.documents_package_url}`);
}

app.listen(3001, () => {
  console.log('🚀 Serveur webhook démarré sur le port 3001');
  console.log('🔗 URL webhook: http://localhost:3001/webhooks/passemarche');
});
```

### Vérification signature HMAC (Bash)

```bash
#!/bin/bash
# Vérification signature webhook avec bash

verify_webhook_signature() {
  local payload="$1"
  local signature_header="$2"
  local secret="$3"

  # Extraire la signature (retirer sha256=)
  local signature=$(echo "$signature_header" | sed 's/^sha256=//')

  # Calculer la signature attendue
  local expected_signature=$(echo -n "$payload" | openssl dgst -sha256 -hmac "$secret" -hex | cut -d' ' -f2)

  # Comparaison sécurisée
  if [ "$signature" = "$expected_signature" ]; then
    echo "✅ Signature valide"
    return 0
  else
    echo "❌ Signature invalide"
    return 1
  fi
}

# Test
test_webhook_signature() {
  local secret="test_secret_123"
  local payload='{"event":"test","timestamp":"2024-01-01T00:00:00Z"}'

  # Générer signature (comme Passe Marché)
  local signature=$(echo -n "$payload" | openssl dgst -sha256 -hmac "$secret" -hex | cut -d' ' -f2)
  local header_value="sha256=$signature"

  echo "Signature générée: $header_value"

  # Vérifier signature
  verify_webhook_signature "$payload" "$header_value" "$secret"
}

# test_webhook_signature
```

***

## Tests et Monitoring

### Test d'intégration complet

```bash
#!/bin/bash
# Test d'intégration OAuth complète

set -e  # Arrêt en cas d'erreur

# Configuration
CLIENT_ID="${CLIENT_ID:?CLIENT_ID requis}"
CLIENT_SECRET="${CLIENT_SECRET:?CLIENT_SECRET requis}"
BASE_URL="${BASE_URL:-https://staging.passemarche.data.gouv.fr}"

echo "=== Test d'authentification OAuth ==="
echo "Client ID: $CLIENT_ID"
echo "Base URL: $BASE_URL"
echo

# Étape 1: Obtenir le token
echo "1. Obtention du token..."
auth_response=$(curl -s -w "HTTPSTATUS:%{http_code}" -X POST \
  "$BASE_URL/oauth/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials&client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET&scope=api_access")

http_code=$(echo $auth_response | tr -d '\n' | sed -e 's/.*HTTPSTATUS://')
body=$(echo $auth_response | sed -e 's/HTTPSTATUS:.*//g')

if [ $http_code -ne 200 ]; then
  echo "❌ Échec de l'authentification: HTTP $http_code"
  echo $body | jq .
  exit 1
fi

token=$(echo $body | jq -r '.access_token')
expires_in=$(echo $body | jq -r '.expires_in')

echo "✅ Token obtenu (expire dans ${expires_in}s)"
echo

# Étape 2: Test d'une requête API
echo "2. Test d'appel API authentifié..."
api_response=$(curl -s -w "HTTPSTATUS:%{http_code}" \
  -H "Authorization: Bearer $token" \
  -H "Accept: application/json" \
  "$BASE_URL/api/v1/status")

api_http_code=$(echo $api_response | tr -d '\n' | sed -e 's/.*HTTPSTATUS://')
api_body=$(echo $api_response | sed -e 's/HTTPSTATUS:.*//g')

if [ $api_http_code -eq 200 ]; then
  echo "✅ Appel API réussi"
  echo $api_body | jq .
else
  echo "❌ Échec de l'appel API: HTTP $api_http_code"
  echo $api_body
fi

echo
echo "=== Test terminé ==="
```

### Logging et monitoring

```bash
#!/bin/bash
# Fonctions de logging pour OAuth

# Configuration du logging
LOG_FILE="/var/log/passemarche-oauth.log"

log_message() {
  local level="$1"
  local message="$2"
  local timestamp=$(date -Iseconds)
  echo "[$timestamp] [$level] $message" | tee -a "$LOG_FILE"
}

log_token_request() {
  log_message "INFO" "Demande de token OAuth"
}

log_token_received() {
  local expires_in="$1"
  local expiration=$(date -Iseconds -d "+${expires_in} seconds")
  log_message "INFO" "Token reçu, expire le: $expiration"
}

log_auth_error() {
  local error="$1"
  log_message "ERROR" "Erreur authentification: $error"
}

# Exemple d'obtention de token avec logging
get_token_with_logging() {
  log_token_request

  local response=$(curl -s -w "HTTPSTATUS:%{http_code}" -X POST \
    "$BASE_URL/oauth/token" \
    -H "Content-Type: application/x-www-form-urlencoded" \
    -d "grant_type=client_credentials&client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET&scope=api_access")

  local http_code=$(echo $response | tr -d '\n' | sed -e 's/.*HTTPSTATUS://')
  local body=$(echo $response | sed -e 's/HTTPSTATUS:.*//g')

  if [ $http_code -eq 200 ]; then
    local expires_in=$(echo $body | jq -r '.expires_in')
    log_token_received "$expires_in"
    echo $body | jq -r '.access_token'
  else
    local error_desc=$(echo $body | jq -r '.error_description // "Erreur inconnue"')
    log_auth_error "HTTP $http_code: $error_desc"
    return 1
  fi
}
```

***

## Utilitaires

### Configuration d'environnement

```bash
#!/bin/bash
# setup-env.sh - Configuration d'environnement

create_env_file() {
  cat > .env << EOF
# Configuration Passe Marché
CLIENT_ID=votre_client_id
CLIENT_SECRET=votre_client_secret
BASE_URL=https://staging.passemarche.data.gouv.fr

# Configuration webhook
WEBHOOK_SECRET=votre_webhook_secret
WEBHOOK_PORT=3001

# Environnement
RACK_ENV=development
EOF

  echo "✅ Fichier .env créé"
  echo "⚠️  Éditez .env avec vos vraies valeurs"
}

load_env() {
  if [ -f .env ]; then
    export $(cat .env | grep -v '^#' | xargs)
    echo "✅ Variables d'environnement chargées"
  else
    echo "❌ Fichier .env non trouvé"
    return 1
  fi
}

# create_env_file
# load_env
```

### Oneshot quick test

```bash
#!/bin/bash
# Test rapide en une ligne

quick_auth_test() {
  curl -s -X POST ${BASE_URL}/oauth/token \
    -H "Content-Type: application/x-www-form-urlencoded" \
    -d "grant_type=client_credentials&client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET&scope=api_access" \
    | jq -r '.access_token // "ERREUR"'
}

quick_api_test() {
  local token="$1"
  curl -s -H "Authorization: Bearer $token" \
    -H "Accept: application/json" \
    ${BASE_URL}/api/v1/status \
    | jq .
}

# Usage: TOKEN=$(quick_auth_test) && quick_api_test "$TOKEN"
```

***

## Support et Debugging

### Diagnostics réseau

```bash
#!/bin/bash
# Tests de connectivité et diagnostics

# Configuration
BASE_URL="${BASE_URL:-https://staging.passemarche.data.gouv.fr}"
# Extraire le hostname de BASE_URL
HOSTNAME=$(echo "$BASE_URL" | sed -e 's|https://||' -e 's|/.*||')

test_connectivity() {
  echo "=== Tests de connectivité pour $HOSTNAME ==="

  # Test DNS
  echo "1. Test DNS..."
  if nslookup "$HOSTNAME" > /dev/null 2>&1; then
    echo "✅ DNS OK"
  else
    echo "❌ Problème DNS"
  fi

  # Test HTTPS
  echo "2. Test HTTPS..."
  if curl -s -I "$BASE_URL" | head -n 1 | grep -q "200\|301\|302"; then
    echo "✅ HTTPS accessible"
  else
    echo "❌ HTTPS inaccessible"
  fi

  # Test certificat SSL
  echo "3. Test certificat SSL..."
  if echo | openssl s_client -connect "$HOSTNAME:443" -servername "$HOSTNAME" 2>/dev/null | grep -q "Verify return code: 0"; then
    echo "✅ Certificat SSL valide"
  else
    echo "⚠️ Problème certificat SSL"
  fi
}

# test_connectivity
```

***

Ces scripts sont fournis comme utilitaires complémentaires. Adaptez-les selon vos besoins spécifiques et votre environnement de production.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://guides.data.gouv.fr/passe-marche/docs/99_scripts_reference.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
