Aller au contenu principal

Contrat API V1

Nous recommandons l’API server-to-server. JavaScript est uniquement une solution analytique de secours facultative et ne fonctionne qu’après l’obtention du consentement analytique.

Contrat API V1 Lien vers la section Contrat API V1

Nous recommandons l’API server-to-server. JavaScript est uniquement une solution analytique de secours facultative et ne fonctionne qu’après l’obtention du consentement analytique.

Les commandes, le chiffre d’affaires et les indicateurs dérivés ne sont affichés que lorsque le suivi des conversions est actif. Ils servent uniquement à l’analyse et ne modifient pas la facturation CPC.

schema_version

1.0

payload_contract

order_v1

Content-Type

application/json

request_limit

64 KiB

Comment connecter la mesure Lien vers la section Comment connecter la mesure

Nous recommandons l’API server-to-server. JavaScript est uniquement une solution analytique de secours facultative et ne fonctionne qu’après l’obtention du consentement analytique.

  1. 1 Enregistrez le paramètre zclid de l’URL de destination avec le panier ou la commande pendant 30 jours.
  2. 2 Sur le serveur, créez une empreinte HMAC-SHA-256 stable de l’ID interne de la commande à l’aide d’une clé distincte. N’envoyez ni l’ID brut ni de données personnelles.
  3. 3 Après la création de la commande, envoyez le JSON à l’API et signez le corps exact de la requête avec la clé secrète d’intégration.
  4. 4 Pour le paiement, l’annulation et les remboursements cumulés, réutilisez les mêmes zclid et order_id_hash. Conservez les totaux et lignes finaux.

La clé secrète d’intégration ne s’affiche qu’une seule fois. Enregistrez-la dans le gestionnaire de secrets du serveur du marchand.

Recommandé : API server-to-server Lien vers la section Recommandé : API server-to-server

Le serveur du marchand envoie les commandes validées, les changements de statut et les remboursements directement à Zoneo. N’insérez jamais la clé secrète dans le navigateur.

POST https://izoneo.fr/api/v1/conversions
Sandbox https://izoneo.fr/api/v1/conversions/sandbox

Sur le serveur, créez une empreinte HMAC-SHA-256 stable de l’ID interne de la commande à l’aide d’une clé distincte. N’envoyez ni l’ID brut ni de données personnelles.

order_id_hash · PHP

$orderIdHash = hash_hmac(
    'sha256',
    "zoneo-order-v1\n".$internalOrderId,
    $_ENV['ZONEO_ORDER_HASH_KEY'],
);

Exemple de requête Lien vers la section Exemple de requête

Après la création de la commande, envoyez le JSON à l’API et signez le corps exact de la requête avec la clé secrète d’intégration.

order_v1 · JSON

{
    "schema_version": "1.0",
    "zclid": "018fb72a-7d8e-7c3c-a4da-f37ce07ad739",
    "order_id_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
    "currency": "EUR",
    "occurred_at": "2026-08-31T12:34:56Z",
    "status": "placed",
    "refund_amount_minor": 0,
    "totals": {
        "items_gross_minor": 14000,
        "discount_minor": 1500,
        "shipping_gross_minor": 390,
        "fees_gross_minor": 100,
        "tax_minor": 2165,
        "order_total_gross_minor": 12990
    },
    "items": [
        {
            "merchant_item_id": "ITEM_ID_FROM_FEED",
            "item_group_id": "MODEL-10",
            "variant_id": "size:42",
            "name": "PRODUCT_NAME",
            "gtin": "8581234567890",
            "quantity": 2,
            "unit_price_gross_minor": 7000,
            "line_total_gross_minor": 14000
        }
    ],
    "order_locale": "fr",
    "expected_delivery_date": "2026-09-03"
}
order_v1 · JSON
JSON Champs obligatoires V1
schema_version = "1.0"
zclid UUID
order_id_hash HMAC-SHA-256 · [a-f0-9]{64}
currency ISO 4217 · EUR
occurred_at ISO 8601 · UTC
status placed | paid | cancelled | partially_refunded | refunded
refund_amount_minor integer ≥ 0 · Σ · monotonic
totals object · integer · gross
items array[1..100]
order_locale BCP 47
expected_delivery_date YYYY-MM-DD
order_v1 · items[]
items[] Champs obligatoires V1
merchant_item_id feed.ITEM_ID · stable
quantity integer · 1..1000
unit_price_gross_minor integer ≥ 0
line_total_gross_minor unit_price_gross_minor × quantity
item_group_id string
variant_id string
name string · PRODUCT_NAME · PII = 0
gtin [0-9]{8,14}

totals · EUR · integer

totals.items_gross_minor = sum(items[].line_total_gross_minor)

totals.order_total_gross_minor = totals.items_gross_minor - totals.discount_minor + totals.shipping_gross_minor + totals.fees_gross_minor

line_total_gross_minor = unit_price_gross_minor × quantity

Signature canonique Lien vers la section Signature canonique

Si vous n’avez pas enregistré la clé secrète d’origine, utilisez Restaurer la clé secrète et enregistrez immédiatement la nouvelle clé en lieu sûr.

HTTP · HMAC-SHA-256
HTTP V1
Content-Type application/json
X-Zoneo-Integration-ID zci_...
X-Zoneo-Timestamp Unix · UTC
X-Zoneo-Nonce CSPRNG · unique · len ≥ 16
Idempotency-Key order:{hash}:{status}
X-Zoneo-Signature v1=HMAC_SHA256_HEX

HMAC-SHA-256 · canonical request

UPPERCASE_HTTP_METHOD
/exact/request/path
unix_timestamp
nonce
idempotency_key
sha256_hex_of_exact_raw_body

body_hash = SHA256(raw_body)
signature = HMAC_SHA256(api_secret, canonical_request)
X-Zoneo-Signature = "v1=" + lowercase_hex(signature)

S2S · PHP

<?php

$path = '/api/v1/conversions';
$body = json_encode($payload, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES);
$timestamp = time();
$nonce = bin2hex(random_bytes(16));
$idempotencyKey = 'order:'.$orderIdHash.':'.$payload['status'];
$canonical = implode("\n", [
    'POST',
    $path,
    (string) $timestamp,
    $nonce,
    $idempotencyKey,
    hash('sha256', $body),
]);
$signature = hash_hmac('sha256', $canonical, $_ENV['ZONEO_API_SECRET']);

$headers = [
    'Content-Type: application/json',
    'X-Zoneo-Integration-ID: '.$_ENV['ZONEO_INTEGRATION_ID'],
    'X-Zoneo-Timestamp: '.$timestamp,
    'X-Zoneo-Nonce: '.$nonce,
    'Idempotency-Key: '.$idempotencyKey,
    'X-Zoneo-Signature: v1='.$signature,
];

$curl = curl_init('https://izoneo.fr/api/v1/conversions');
curl_setopt_array($curl, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => $headers,
    CURLOPT_POSTFIELDS => $body,
    CURLOPT_TIMEOUT => 10,
]);
$response = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
curl_close($curl);

Créée → Remboursée Lien vers la section Créée → Remboursée

Pour le paiement, l’annulation et les remboursements cumulés, réutilisez les mêmes zclid et order_id_hash. Conservez les totaux et lignes finaux.

Créée · placed Payée · paid Annulée · cancelled Partiellement remboursée · partially_refunded Remboursée · refunded

order_v1 · lifecycle

placed -> paid | cancelled | partially_refunded | refunded
paid -> partially_refunded | refunded
partially_refunded -> refunded
cancelled, refunded -> terminal

0 <= refund_amount_minor <= totals.order_total_gross_minor
new_refund_amount_minor >= previous_refund_amount_minor

Idempotency-Key · retry

nonce₁ != nonce₂
retry = nonce₂ + Idempotency-Key₁ + SHA256(JSON₁)
Idempotency-Key₁ + SHA256(JSON₁) -> HTTP 200
Idempotency-Key₁ + SHA256(JSON₂) -> HTTP 409 idempotency_conflict

Sandbox V1 Lien vers la section Sandbox V1

Collez un JSON V1 pour vérifier les champs, les totaux et l’association au flux sans créer de commande ni modifier la facturation.

POST https://izoneo.fr/api/v1/conversions/sandbox
persisted = false billing_impact = false

Mesure facultative via JavaScript Lien vers la section Mesure facultative via JavaScript

La bibliothèque conserve zclid après consentement et envoie uniquement l’événement placed initial depuis la page de remerciement. Envoyez les états suivants par S2S sécurisé.

Le consentement est désactivé par défaut. La fonction consent ne doit renvoyer true qu’après l’obtention d’un consentement analytique valide de l’utilisateur.

Chargement et initialisation

<script src="https://izoneo.fr/integrations/zoneo-conversion-v1.js"></script>
<script>
const zoneo = window.ZoneoConversions.init({
  integrationId: 'zci_...',
  apiBase: 'https://izoneo.fr/api/v1/conversions',
  consent: () => analyticsConsent === true
})

zoneo.track({
  order_id_hash: 'SERVER_HMAC_SHA256',
  currency: 'EUR',
  occurred_at: new Date().toISOString(),
  status: 'placed',
  totals: {
    items_gross_minor: 12990,
    discount_minor: 0,
    shipping_gross_minor: 0,
    fees_gross_minor: 0,
    tax_minor: 2165,
    order_total_gross_minor: 12990
  },
  items: [{
    merchant_item_id: 'ITEM_ID_FROM_FEED',
    quantity: 1,
    unit_price_gross_minor: 12990,
    line_total_gross_minor: 12990
  }]
})
</script>

État de l’intégration Lien vers la section État de l’intégration

Événements acceptés et rejetés au cours des 7 derniers jours.

201 · created = true
200 · idempotent = true | deduplicated = true
4xx · error.code

HTTP 201 · JSON

{
    "data": {
        "conversion_reference": "6bfca33e-3ac7-48dc-a733-c1f313853269",
        "status": "placed",
        "source": "s2s",
        "verification": "hmac_current",
        "schema_version": "1.0",
        "payload_contract": "order_v1",
        "totals": {
            "items_gross_minor": 14000,
            "discount_minor": 1500,
            "shipping_gross_minor": 390,
            "fees_gross_minor": 100,
            "tax_minor": 2165,
            "order_total_gross_minor": 12990
        },
        "refund_amount_minor": 0,
        "net_revenue_minor": 12990,
        "items": {
            "count": 1,
            "quantity_total": 2,
            "matched_count": 1,
            "match_status": "complete"
        },
        "totals_reconciled": true,
        "warnings": [],
        "currency": "EUR",
        "created": true,
        "idempotent": false,
        "deduplicated": false,
        "provisional": false,
        "billing_impact": false
    }
}

HTTP 4xx · JSON

{
    "error": {
        "code": "order_total_mismatch",
        "field": "totals.order_total_gross_minor",
        "details": {
            "expected_minor": 12990,
            "received_minor": 13000
        }
    }
}
invalid_signature stale_timestamp replayed_nonce pii_not_allowed items_total_mismatch order_total_mismatch currency_mismatch click_not_eligible store_or_market_mismatch not_last_zoneo_click attribution_window_expired invalid_state_transition order_definition_conflict refund_amount_decreased order_attribution_conflict

Protection des données personnelles Lien vers la section Protection des données personnelles

Dernières commandes reçues par Zoneo à des fins analytiques uniquement. Aucun identifiant brut ni donnée personnelle ne sont affichés.

Sur le serveur, créez une empreinte HMAC-SHA-256 stable de l’ID interne de la commande à l’aide d’une clé distincte. N’envoyez ni l’ID brut ni de données personnelles.

Les commandes, le chiffre d’affaires et les indicateurs dérivés ne sont affichés que lorsque le suivi des conversions est actif. Ils servent uniquement à l’analyse et ne modifient pas la facturation CPC.

Comment connecter la mesure

Nous recommandons l’API server-to-server. JavaScript est uniquement une solution analytique de secours facultative et ne fonctionne qu’après l’obtention du consentement analytique.