Passer au contenu principal

Environnement & .env

Comment stocker en sécurité les credentials de l'API DZBuild dans votre application — des fichiers .env aux KMS jusqu'aux GitHub Secrets.

Écrit par Support

Ce guide couvre comment porter vos credentials DZBuild API en sécurité dans votre application — dev local, staging, production. Que vous construisiez une vitrine Node.js, un back-office Laravel/Symfony, un pipeline Python, un microservice Go ou une fonction edge serverless, les règles sont les mêmes.

Avant de commencer

  • Votre boutique doit être sur un plan Enterprise actif, et votre clé doit être inscrite au pilote. L'API v1 est réservée au plan Enterprise et au pilote en production. Sur tout autre plan, ou dès que l'abonnement Enterprise expire, chaque appel renvoie 403 forbidden "API access requires an active Enterprise plan". Les clés générées dans Paramètres → API sont inscrites automatiquement ; une clé non inscrite renvoie 403 forbidden "API is in pilot mode; key not enrolled" à chaque appel, aussi correcte que soit votre configuration.

  • Pointez toujours DZBUILD_API_BASE sur https://api.dzbuild.app/v1. dzbuild.com/api/v1/... n'est qu'un alias de la même API : certains chemins peuvent y être bloqués. Le rate limiting par boutique (partagé par toutes les clés de la boutique) et le rejeu d'idempotence s'appliquent sur les deux hôtes.

Ce que vous devez stocker

Pour une intégration vitrine / back-office typique :

Variable

Exemple

Notes

DZBUILD_API_KEY

dzpk_live_0123456789abcd.<48 hex>

Le bearer token complet. Traitez-le comme un mot de passe.

DZBUILD_API_BASE

https://api.dzbuild.app/v1

URL de base. En prod, utilisez cet hôte, jamais dzbuild.com/api/v1 (juste un alias).

DZBUILD_WEBHOOK_SECRET

64 caractères hex minuscules

Secret par webhook retourné à l'enregistrement. Sans préfixe. Chaque livraison vers ce webhook est signée avec lui ; voir Secrets de webhooks.

Anatomie du bearer token

Les marchands le tronquent en permanence, autant l'expliciter. Le bearer token d'une clé plateforme est :

dzpk_live_<14 hex>.<48 hex>

73 caractères, tous en minuscules après le préfixe, avec un seul point au milieu. La partie dzpk_live_… avant le point est le key id — un identifiant, pas un credential. Authorization: Bearer a besoin de la chaîne entière de 73 caractères.

Se tromper là-dessus fait diverger les deux couches, ce qui est un diagnostic utile :

  • Un token sans aucun point → 401 unauthorized "Invalid bearer format".

  • Un token bien formé mais inconnu / révoqué → 401 unauthorized "Invalid or revoked API key".

Pour le flow clé publique (signups / events depuis un client public) :

Variable

Exemple

Notes

DZBUILD_PUBLIC_KEY_ID

dzpub_live_...

Safe à shipper dans le code client (l'id est public, le secret non)

DZBUILD_SIGNING_SECRET

64 caractères hex minuscules, sans préfixe

Server-side seulement — sert à HMAC-signer chaque body de requête

Fichiers .env

Le pattern le plus simple :

# .env (à la racine, gitignored)
DZBUILD_API_KEY=dzpk_live_0123456789abcd.0123456789abcdef0123456789abcdef0123456789abcdef
DZBUILD_API_BASE=https://api.dzbuild.app/v1
DZBUILD_WEBHOOK_SECRET=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef

# .gitignore
.env
.env.local
.env.*.local

Committez un .env.example avec des placeholders pour que les autres devs sachent quelles vars définir :

# .env.example (committé)
DZBUILD_API_KEY=dzpk_live_REPLACE_ME
DZBUILD_API_BASE=https://api.dzbuild.app/v1
DZBUILD_WEBHOOK_SECRET=REPLACE_ME_64_HEX

Charger .env par langage

Node.js / Next.js / Express

// next.config.js — Next.js charge .env, .env.local, .env.production automatiquement
// Pour Node simple :
import 'dotenv/config';
const key = process.env.DZBUILD_API_KEY;

Python

# pip install python-dotenv
from dotenv import load_dotenv
import os
load_dotenv()
key = os.environ['DZBUILD_API_KEY']

PHP / Laravel

// Laravel charge .env auto
$key = env('DZBUILD_API_KEY');// PHP simple :
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();
$key = $_ENV['DZBUILD_API_KEY'];

Go

// go get github.com/joho/godotenv
godotenv.Load()
key := os.Getenv("DZBUILD_API_KEY")

Flutter / mobile

Ne shippez pas la clé plateforme dans votre app mobile. À la place :

  1. Votre app mobile appelle votre back-end.

  2. Votre back-end détient la clé et proxie vers DZBuild.

Si votre app mobile doit enregistrer des signups, envoyez-les aussi à votre back-end. Chaque requête clé publique porte une signature HMAC calculée avec le secret de signature, et ce secret ne doit jamais être shippé dans une app : votre back-end signe et relaie l'appel, comme dans la section clé publique ci-dessous. Seul l'id de la clé publique peut être exposé sans risque. Voir Endpoints clé publique.

Environnements de production

En prod, n'utilisez pas un fichier .env. Utilisez le secret store natif de la plateforme :

Vercel / Netlify / Cloudflare Pages

Projet → Settings → Environment Variables
   DZBUILD_API_KEY = dzpk_live_...
   DZBUILD_API_BASE = https://api.dzbuild.app/v1
   DZBUILD_WEBHOOK_SECRET = <64-char lowercase hex>

Marquez les vars prod-only en scope « Production ». Les vars staging en « Preview » ou env séparé.

Cloudflare Workers

wrangler secret put DZBUILD_API_KEY
# (collez la valeur quand demandé)

Disponible dans le worker comme env.DZBUILD_API_KEY (avec [vars] déclaré dans wrangler.toml).

AWS Lambda / API Gateway

Utilisez AWS Secrets Manager ou Parameter Store :

import boto3, json
secret = json.loads(
    boto3.client('secretsmanager').get_secret_value(SecretId='dzbuild/prod')['SecretString']
)
key = secret['DZBUILD_API_KEY']

Ne stockez pas la clé dans Lambda env vars en clair (ils apparaissent dans CloudTrail logs). Référencez le secret manager.

Docker / Docker Compose

# docker-compose.yml
services:
  app:
    image: yourapp:latest
    env_file:
      - .env.production    # NON committé
    environment:
      - NODE_ENV=production

Pour Kubernetes, utilisez un Secret :

apiVersion: v1
kind: Secret
metadata:
  name: dzbuild-creds
type: Opaque
stringData:
  DZBUILD_API_KEY: dzpk_live_...
  DZBUILD_WEBHOOK_SECRET: <64-char lowercase hex>

Référence dans le deployment :

envFrom:
  - secretRef:
      name: dzbuild-creds

GitHub Actions

# .github/workflows/deploy.yml
env:
  DZBUILD_API_KEY: ${{ secrets.DZBUILD_API_KEY }}

Définissez le secret dans Repo → Settings → Secrets and variables → Actions.

Clés dev vs production

Créez toujours deux clés séparées :

  • Une nommée dev ou staging — utilisée dans .env.local / dev

  • Une nommée production — utilisée seulement en prod réelle

Si la clé dev fuit, vous ne brûlez que dev. Les données prod restent intactes.

Comment obtenir une clé, concrètement

Le propriétaire de la boutique génère les clés depuis le dashboard marchand, dans Paramètres → API (/dashboard/api). La boutique doit être sur un plan Enterprise actif, et elle détient au plus 3 clés actives.

  1. Votre première clé vient de cette page : cliquez sur Générer une clé API, nommez-la d'après l'environnement où elle vivra (myapp-dev, myapp-prod) et copiez le bearer token, affiché une seule fois. Les clés créées là sont déjà inscrites au pilote.

  2. Les clés suivantes, vous pouvez les créer vous-même à partir d'une clé existante :

bash curl -X POST 'https://api.dzbuild.app/v1/keys' \ -H "Authorization: Bearer $DZBUILD_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{"type":"platform","name":"myapp-dev"}' # HTTP 200 → { "data": { "key_id", "bearer_token", "signing_secret", "note" } }

La nouvelle clé hérite tel quel du tier de rate limit et du flag pilote de la clé appelante.

Les scopes ne sont pas sélectionnables. POST /v1/keys n'accepte que type et name. Une clé plateforme créée dans Paramètres → API reçoit l'ensemble par défaut complet : store:read, store:write, products:read, products:write, orders:read, orders:write, customers:read, landing_pages:read, landing_pages:write, promos:read, promos:write, pixels:read, pixels:write, shipping:read, shipping:write, webhooks:read, webhooks:write, usage:read, analytics:read, whatsapp:read, whatsapp:send. Une clé plateforme créée avec POST /v1/keys reçoit cet ensemble moins chaque scope que la clé appelante n'a pas, jamais davantage. Une clé publique reçoit toujours signups:write et events:write. La séparation dev / prod vient de l'usage de deux clés différentes, pas d'une restriction de permissions.

Secrets de webhooks

Quand vous enregistrez un webhook, la réponse inclut un secret :

curl -X POST 'https://api.dzbuild.app/v1/webhooks' \
  -H "Authorization: Bearer $DZBUILD_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "url":    "https://yourapp.com/webhook",
    "events": ["order.created", "order.confirmed"]
  }'

Réponse — HTTP 200, et seulement trois champs :

{
  "data": {
    "id":     42,
    "secret": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
    "note":   "Save the secret now — it is not retrievable after this response."
  },
  "meta": { "request_id": "...", "api_version": "v1" }
}

Le secret est une chaîne nue de 64 caractères hex minuscules — il n'y a pas de préfixe dzwh_sec_. Il est affiché une fois ; sauvez-le immédiatement dans votre coffre. Si vous le perdez, supprimez le webhook et enregistrez-en un autre.

ℹ️ Info — Vérifiez chaque livraison API v1 avec ce secret

Chaque livraison vers le webhook porte X-DZ-Signature: t=<unix seconds>,v1=<hex>, le HMAC-SHA256 de t + "." + raw_body calculé avec DZBUILD_WEBHOOK_SECRET. Recalculez-le, comparez à temps constant et rejetez un t à plus de 300 secondes de votre horloge. Un webhook enregistré avant l'arrivée de la signature par webhook garde une ancienne signature que ce secret ne peut pas vérifier : enregistrez ce webhook de nouveau.

La recette et le code en 4 langages se trouvent dans Vérifier les signatures.

Flow clé publique (signups / events)

Le flow clé publique existe pour des endpoints à scope étroit (signup tracking, event tracking) où vous ne voulez pas de clé plateforme dans la boucle. Seul l'id de la clé publique n'est pas secret — le secret de signature reste sur votre back-end.

⚠️ Attention — N'appelez pas /v1/signups directement depuis un navigateur

Trois choses cassent un appel navigateur direct aujourd'hui :

  • Pas de CORS. L'API ne répond à aucun preflight : une requête OPTIONS sans clé reçoit le 401 habituel, sans en-tête Access-Control-*, et le navigateur bloque l'appel. L'API sert uniquement aux appels de serveur à serveur.

  • Idempotency-Key est obligatoire sur chaque POST, et c'est facile à oublier côté client.

  • crypto.randomUUID() n'est pas un nonce valide. Le nonce doit faire exactement 32 caractères hex minuscules ; un UUID de 36 caractères avec tirets renvoie 401 unauthorized "Invalid nonce format".

Passez par votre propre back-end à la place — le pattern ci-dessous.

// Navigateur : parlez à VOTRE endpoint, jamais à api.dzbuild.app
await fetch('/api/track-signup', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ email, external_user_id: externalId })
});

// Votre back-end (Node) : détient le secret de signature, signe et relaie.
import crypto from 'node:crypto';const KEY_ID = process.env.DZBUILD_PUBLIC_KEY_ID;
const SECRET = process.env.DZBUILD_SIGNING_SECRET;export async function trackSignup({ email, external_user_id }) {
  const nonce = crypto.randomBytes(16).toString('hex');   // 32 hex minuscules
  const ts    = Math.floor(Date.now() / 1000).toString();
  const body  = JSON.stringify({ email, external_user_id, source: 'web', nonce });
  const hash  = crypto.createHash('sha256').update(body).digest('hex');
  const sig   = crypto.createHmac('sha256', SECRET)
    .update(`${KEY_ID}\n${nonce}\n${ts}\n${hash}`).digest('hex');  await fetch('https://api.dzbuild.app/v1/signups', {
    method: 'POST',
    headers: {
      'Authorization':   `DZ-Public ${KEY_ID}`,
      'X-DZ-Timestamp':  ts,
      'X-DZ-Nonce':      nonce,
      'X-DZ-Signature':  sig,
      'Idempotency-Key': nonce,
      'Content-Type':    'application/json'
    },
    body
  });
}

Le secret de signature ne quitte jamais votre back-end. L'id de clé publique peut être inspecté par n'importe qui — c'est voulu. Voir Inscriptions pour le contrat complet.

Tunneling local pour les webhooks

Les webhooks DZBuild ont besoin d'une URL HTTPS publique. Pour tester localement :

# ngrok
ngrok http 3000# Cloudflare Tunnel
cloudflared tunnel --url http://localhost:3000

Enregistrez l'URL du tunnel comme cible webhook. L'URL d'un webhook enregistré ne peut pas être modifiée : quand l'URL du tunnel change, supprimez le webhook, enregistrez la nouvelle URL et conservez le nouveau secret renvoyé par l'enregistrement (ngrok free change d'URL ; payez 8$/mois pour un sous-domaine stable).

Politique de rotation

Faites tourner les clés :

  • Trimestriellement pour les clés prod (mettez un rappel calendrier)

  • Immédiatement si une clé peut avoir fuité (committée à git, screenshot, envoyée en chat)

  • Au départ d'un membre d'équipe s'il avait accès au coffre

Procédure :

  1. Créez une nouvelle clé avec POST /v1/keys (voir Comment obtenir une clé, concrètement). Elle hérite du tier et du flag pilote de l'ancienne. Une boutique détient au plus 3 clés actives : à la limite, l'appel répond 400 bad_request "Key limit reached for this store (3 active keys). Revoke unused keys first.", révoquez donc une clé inutilisée avant la rotation.

  2. Mettez à jour votre coffre / env vars vers la nouvelle clé.

  3. Déployez. Vérifiez que la nouvelle clé est utilisée : GET /v1/keys liste chaque clé avec son last_used_at (GET /v1/usage compte toute la boutique, pas une seule clé).

  4. Après 24 h de trafic propre sur la nouvelle, révoquez l'ancienne : DELETE /v1/keys/{old_key_id}.

Erreurs courantes

Erreur

Conséquence

Fix

.env committé à git

La clé est dans l'historique git pour toujours ; rotation immédiate

git filter-repo ne corrige pas les forks/clones ; considérez compromis

Clé plateforme dans le code navigateur

Les clients voient et volent la clé dans network tab

Move au back-end / serverless ; rotation

dzpk_live_... hardcodé en source

Pareil

env vars ; rotation

Même clé pour dev et prod

Une boulette dev touche la prod

Deux clés ; ne jamais partager

Secret webhook perdu

Plus moyen de vérifier les livraisons

Supprimez le webhook, enregistrez-en un autre

Logguer la clé API dans les logs app

Auditeurs / log shippers / quiconque a accès aux logs voit

Redactez les secrets dans le logger ; rotation

Checklist avant prod

  • [ ] .env est dans .gitignore (et n'a jamais été committé par accident)

  • [ ] Clé prod nommée prod et utilisée seulement en prod

  • [ ] Clé dev nommée dev et seulement en dev/staging

  • [ ] Secrets webhooks dans secret manager, pas dans le code

  • [ ] En-tête Authorization sur chaque appel API depuis le back-end

  • [ ] Le code navigateur ne voit jamais dzpk_live_* (seulement dzpub_live_* si flow clé publique)

  • [ ] Votre endpoint webhook vérifie X-DZ-Signature avec le secret du webhook et déduplique sur le delivery_id du body

  • [ ] Rappel rotation de clés sur votre calendrier

  • [ ] Logging qui ne capture PAS les bodies complets (peuvent contenir des clés API en client headers)

Avez-vous trouvé la réponse à votre question ?