Thèmes & vitrines personnalisés
DZBuild livre 5 thèmes (Brico, Digital, Prestige, Showcase, Starter) et un customizer no-code. Mais si vous voulez un contrôle total — votre propre front-end React/Vue/Next.js/Flutter, votre design pixel-perfect, votre routing — l'API est faite pour vous.
Ce guide construit une vitrine headless de bout en bout :
- Rendu du catalogue (produits, catégories, variantes)
- Construction du panier (côté client ou serveur, à votre choix)
- Soumission de la commande via l'API
- Réception des webhooks au changement de statut
À la fin, votre vitrine custom sera 100% interopérable avec le tableau de bord marchand DZBuild — les commandes apparaissent, le stock décrémente, la livraison s'intègre avec les transporteurs du marchand, sans compromis.
Architecture
┌──────────────────────┐ ┌──────────────────────────┐
│ Vitrine custom │ HTTPS │ api.dzbuild.app/v1/* │
│ (React, Vue, Flutter)│ ──────► │ Authorization: Bearer … │
│ │ │ │
│ - Lit les produits │ ◄────── │ Réponses JSON │
│ - Affiche le panier │ │ │
│ - Soumet les commandes│ └──────────────────────────┘
└──────────────────────┘ │
▼
Tableau de bord marchand DZBuild
- Confirme les commandes
- Gère le stock
- S'intègre aux transporteurs
Vous possédez l'UI. DZBuild possède la donnée et l'opérationnel. Le marchand se connecte à dzbuild.com/dashboard pour gérer les commandes confirmées dans votre UI custom.
Prérequis
- Compte marchand DZBuild en plan Pro ou supérieur (Free n'inclut pas l'API — voir Plans).
- Une clé API avec les bons scopes :
products:read— pour rendre le catalogueorders:read— pour consulter les commandes (pages statut/tracking)orders:write— pour créer les commandes depuis la vitrine
- Un back-end (ou serverless / edge worker) qui détient la clé API. Ne livrez jamais le secret au navigateur — voir Sécurité.
Étape 0 — obtenir une clé
Dans le tableau de bord marchand :
Tableau de bord → Developer → API Keys → "+ Créer une clé plateforme"
• Nom : "Custom storefront — production"
• Scopes : products:read, orders:read, orders:write
• Sauvegarder
Copiez le secret (dzpk_live_…) — affiché une seule fois. Perdu ? Révoquez et créez-en un.
Test :
curl https://api.dzbuild.app/v1/whoami \
-H "Authorization: Bearer dzpk_live_..."
# attendu : { "data": { "key_id": "...", "store_id": 13, "type": "platform", "scopes": [...] } }
Étape 1 — rendre le catalogue
// pages/index.js (Next.js)
export async function getServerSideProps() {
const res = await fetch('https://api.dzbuild.app/v1/products?limit=50&status=active', {
headers: { 'Authorization': `Bearer ${process.env.DZ_KEY}` },
});
const { data } = await res.json();
return { props: { products: data.items } };
}
export default function Home({ products }) {
return (
<ul>
{products.map(p => (
<li key={p.id}>
<img src={`https://cdn.dzbuild.app/${p.store_id}/products/${p.primary_image}`} />
<h2>{p.name}</h2>
<p>{p.price} DZD</p>
<a href={`/product/${p.slug}`}>Voir</a>
</li>
))}
</ul>
);
}
Cachez la réponse — la liste des produits est mise en cache en périphérie pendant 30 s, donc les lectures répétées à grande échelle sont peu coûteuses.
Étape 2 — page produit avec variantes
const res = await fetch(`https://api.dzbuild.app/v1/products/${id}`, {
headers: { 'Authorization': `Bearer ${process.env.DZ_KEY}` },
});
const { data: product } = await res.json();
La réponse inclut variants[] — chaque entrée est un groupe de variantes avec ses options :
"variants": [
{ "id": 11, "name": "Color", "type": "color",
"options": [
{ "id": 14, "value": "Red", "color_code": "#ff0000", "image_id": 28, "stock": 12 },
{ "id": 15, "value": "Blue", "color_code": "#0000ff", "image_id": 29, "stock": 5 }
]
},
{ "id": 12, "name": "Size", "type": "text",
"options": [
{ "id": 16, "value": "S", "stock": 10 },
{ "id": 17, "value": "M", "stock": 10 },
{ "id": 18, "value": "L", "stock": 5 }
]
}
]
Affichez un sélecteur par groupe. Pour type: color, des swatches avec color_code ; type: text, des labels ; type: image_text, des thumbnails (le filename est sur options[].image_id — récupérez images[].url correspondant).
Gestion stock épuisé : options[].stock vaut null si le marchand ne suit pas le stock par variante. S'il est ≤ 0, grisez l'option. Le check définitif se fait côté serveur au confirm-time.
Étape 3 — construire un panier
Le panier est côté client (React state, Vuex, Pinia, localStorage…). Pas besoin d'appel API pour ajouter au panier. Chaque ligne :
{
product_id: 26,
product_name: "T-shirt",
base_price: 1500,
quantity: 1,
selected_variants: [
{ group_name: "Color", option_name: "Red", color_code: "#ff0000", price_adjustment: 0 },
{ group_name: "Size", option_name: "L", color_code: null, price_adjustment: 200 }
]
}
Calculez le total ligne côté client : (base_price + sum(price_adjustment)) × quantity. Affichez le total panier au client. (Le serveur recalcule à la soumission — ne faites jamais confiance au calcul client pour la facturation finale.)
Étape 4 — collecte des infos client + calcul livraison
Formulaire checkout standard : nom, téléphone, wilaya, commune, adresse. Utilisez la liste wilayas depuis GET /v1/store (à venir) ou hardcodez les 58 wilayas — liste stable.
Pour le coût de livraison, deux options :
- Hardcodez les tarifs par wilaya + type de livraison dans la vitrine (le plus simple).
- Lisez depuis la config marchand via
GET /v1/store(prévu v1.1).
Passez le shipping calculé en shipping_cost. Le serveur ne recalcule pas le shipping pour vous actuellement.
Étape 5 — soumettre la commande
// Sur votre back-end (Next.js API route, Edge function, server…)
async function placeOrder(req, res) {
const cart = req.body;
const order = {
customer: {
name: cart.name,
phone: cart.phone,
email: cart.email || null,
wilaya_id: cart.wilaya_id,
commune: cart.commune,
address: cart.address || ''
},
delivery: {
type: cart.delivery_type,
desk_id: cart.desk_id || null,
desk_name: cart.desk_name || null
},
items: cart.items.map(line => ({
product_id: line.product_id,
quantity: line.quantity,
variants: line.selected_variants
})),
shipping_cost: cart.shipping_cost,
discount: 0,
payment_method: 'cod',
notes: cart.notes || null
};
const idempKey = req.headers['x-checkout-id'] || crypto.randomUUID();
const apiRes = await fetch('https://api.dzbuild.app/v1/orders', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.DZ_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': idempKey
},
body: JSON.stringify(order)
});
if (!apiRes.ok) {
const err = await apiRes.json();
return res.status(apiRes.status).json(err);
}
const { data: createdOrder } = await apiRes.json();
return res.status(201).json({
order_number: createdOrder.order_number,
total: createdOrder.amounts.total
});
}
Le client voit son order_number sur la page de succès. Le dashboard marchand affiche maintenant la nouvelle commande dans la liste pending, prête à confirmer.
Étape 6 — recevoir les webhooks (optionnel mais puissant)
Enregistrez un webhook pour réagir aux events de commande :
curl -X POST 'https://api.dzbuild.app/v1/webhooks' \
-H "Authorization: Bearer $DZ_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"url": "https://yourstorefront.com/api/dz-webhook",
"events": ["order.created", "order.confirmed", "order.shipped", "order.delivered"]
}'
Vous recevrez un secret dans la réponse — stockez-le. Chaque livraison webhook inclut un en-tête X-DZ-Signature à vérifier. Voir Vérifier les signatures.
Utilisez les webhooks pour :
- Envoyer un SMS au client à la confirmation.
- Mettre à jour CRM / Google Sheet / analytics.
- Invalider une page « merci » après confirmation marchand.
- Déclencher la livraison de produit téléchargeable après
order.delivered.
Patterns courants
Vitrine multilingue
Stockez vos traductions en front. L'API renvoie les noms et descriptions tels que saisis par le marchand. Si le marchand utilise l'add-on Multi-language, GET /v1/products/{id} retournera (en v1.1) name_ar, name_fr, description_ar, description_fr à côté des champs canoniques. En attendant, seul le canonique est exposé — choisissez côté client ce que veut votre client.
Sélecteur stop-desk
Pour delivery.type = "desk" :
// 1. Lisez les stop desks du marchand pour la wilaya choisie
// (prévu v1.1 : GET /v1/store/desks?wilaya=16)
// En attendant, requêtez directement le transporteur (Yalidine, ZR, etc.)
// 2. Affichez la liste, le client choisit :
selectedDesk = { id: 7842, name: "Yalidine Bab Ezzouar" };
// 3. Passez à la commande :
order.delivery = {
type: "desk",
desk_id: selectedDesk.id,
desk_name: selectedDesk.name
};
Paiement en ligne (SlickPay / Edahabia)
Pour payment_method = "digital_payment" :
- Soumettez la commande via l'API normalement ; créée en
pendingavecpayment_status = pending. - Redirigez le client vers SlickPay / Edahabia checkout.
- Au succès, votre back-end reçoit un webhook SlickPay → appelez
PATCH /v1/orders/{id}pour passerpayment_statusàpaid(prévu v1.1 ; en attendant le paiement est mis à jour par le receveur webhook SlickPay existant du dashboard).
Retours & remboursements
Gérés actuellement dans le dashboard. Le support API pour POST /v1/orders/{id}/refund est sur la roadmap.
Sécurité
- Ne mettez jamais la clé API dans du code exposé au navigateur. Les clés appartiennent à votre serveur / serverless / edge. Le navigateur appelle votre endpoint, votre endpoint appelle DZBuild.
- HTTPS only entre vitrine et
api.dzbuild.app. Le Worker rejette HTTP. - Idempotency-Key requis sur toutes les écritures. Sans =
400 idempotency_key_required. - Limites de taux par clé —
pro1500 req/min,pro_plus5000,enterpriseillimité. Voir Limites. - Une clé par environnement. Pas de partage dev/prod. Créez une clé staging séparée ; révoquez-la après staging.
Dépannage
| Problème | Cause probable | Fix |
|---|---|---|
401 unauthorized "Missing Authorization header" | En-tête Authorization: Bearer … oublié | Ajoutez |
401 unauthorized "Invalid API key" | Typo, clé révoquée ou mauvais env var | Re-créez via dashboard |
403 forbidden "Missing scope: orders:write" | La clé n'a pas le scope | Re-créez avec le scope |
400 bad_request "Product N does not belong to this store" | ID cross-store (clé store A, produit store B) | Bonne clé |
400 bad_request "items must be a non-empty array" | Panier vide | Ne soumettez pas vide |
429 rate_limited | Polling trop agressif | Passez aux webhooks ; ou upgradez |
Apps de référence
Templates clonables :
dzbuild-storefront-nextjs— Next.js 14 + App Router + Tailwind. Liste produits, page produit avec variantes, panier, checkout.dzbuild-storefront-flutter— App mobile Flutter sur la même API.
(Les deux sont des starter — adaptez librement.)
Mise en production
- Testez à fond avec une clé
pilotsur un store de test. - Créez une clé séparée prod (mêmes scopes, autre nom).
- Déployez avec la clé prod en env vars.
- Passez une vraie commande test ; confirmez sur dashboard.
- Testez un remboursement si vous en offrez.
- Enregistrez vos webhooks pointant sur l'URL prod.
- Surveillez
GET /v1/usagechaque jour la première semaine.
Roadmap
| Feature | ETA |
|---|---|
GET /v1/store avec rates wilaya + stop desks | v1.1 |
GET /v1/products/{id}/combinations pour stock par combo | v1.1 |
POST /v1/orders/{id}/refund via API | v1.1 |
| Upload image produit via R2 presigned URL | v1.1 |
| Champs multilingues sur réponses produits | v1.1 |
Besoin d'une de ces features plus tôt ? Contactez le support.