Aller au contenu

Ingénierie

Concevoir un journal d'audit pour les validations de prix : qui a approuvé quoi, selon quelle règle

Par Jahangir Alam · 14 octobre 2026 · 16 min de lecture

Dernière vérification
API Shopify
2026-10
Public
Développeurs et agences qui construisent ou auditent des validations de remises et de prix dans une couche de devis sur Shopify
Périmètre
L'enregistrement d'audit des validations de prix à côté de Shopify en API 2026-10 : ce que le journal d'activité, les chronologies et les objets Event de Shopify ne contiennent pas, l'enregistrement champ par champ (version concernée et facteur de la remise sur l'ensemble du devis, révision de la règle et entrées, acteur, horodatage, issue y compris étapes ignorées et dérogations), l'écriture des décisions dans une seule transaction conditionnée à l'état, l'append-only imposé par la base, ce que prouve une chaîne de hachage avec vérification et ancrage, la séparation entre journal d'audit, outbox et fil d'activité, l'event sourcing, la conservation et les données personnelles

Une validation ne constitue une preuve que si, des mois plus tard, vous pouvez montrer qui a décidé, sur quels chiffres exacts, selon quelle règle telle qu'elle était à ce moment-là, et ce qui est arrivé à chaque étape - y compris celles qui ont été ignorées. Les journaux de Shopify ne contiennent pas cet enregistrement : son journal d'activité, ses chronologies et ses événements d'API ne portent ni règle, ni seuil, ni version. Une couche de devis qui soumet des remises à validation doit donc tenir son propre journal d'audit, et les questions de conception sont concrètes : ce que contient l'enregistrement, comment il est écrit, comment il reste append-only (on ne fait qu'y ajouter des lignes), et ce qu'une chaîne de hachage prouve ou ne prouve pas.

Cette page présente cette conception, champ par champ, avec du code simplifié. Les flux de validation expliquent comment construire une politique de validation, et la matrice de validation ce qu'elle doit exiger ; cette page porte sur l'enregistrement que ces décisions laissent derrière elles. Quand QuotWay sert d'exemple, il est décrit tel qu'il fonctionne aujourd'hui, et ce qu'il ne fait pas encore est dit clairement - il s'agit alors de recommandations, signalées comme telles.

Les faits Shopify ci-dessous ont été vérifiés sur les pages de Shopify le 14 octobre 2026, en version d'API 2026-10 ; les sources sont en fin de page.

La question à laquelle le journal doit répondre

« Qui a approuvé cette remise, et selon quelle règle ? » se décompose en cinq questions, et l'enregistrement a besoin d'une partie pour chacune :

  1. Sur quoi ? La version exacte de la proposition et ses chiffres - y compris l'effet d'une remise sur l'ensemble du devis sur chaque ligne.
  2. Selon quelle règle ? La politique qui s'est appliquée, la branche qui a correspondu, les entrées qui l'ont fait correspondre, et la politique telle qu'elle était alors.
  3. Qui ? La personne ou le système qui a décidé, dans quel rôle, pour le compte de qui, et qui d'autre aurait pu décider.
  4. Quand, et dans quel ordre ? L'heure du serveur et l'ordre dans lequel les décisions ont été validées en base.
  5. Avec quelle issue ? Approbation, rejet - et les issues qu'on oublie : une étape ignorée et pourquoi, une dérogation avec sa justification, une chaîne remplacée par une nouvelle version, un délai expiré.

Ce que Shopify enregistre, et ce qu'il n'enregistre pas

Shopify a trois journaux, et aucun n'est un enregistrement de validation :

  • Le journal d'activité de la boutique affiche « recent actions taken by store owners and users » (les actions récentes des propriétaires et des utilisateurs de la boutique) - supprimer un produit, modifier un paramètre, accorder un accès à une application. Il est en lecture seule, il « can't be exported or downloaded » (ne peut être ni exporté ni téléchargé), et il « displays a maximum of 250 results » (affiche 250 résultats au maximum).
  • Les chronologies de chaque commande, commande provisoire, client et transfert de stock, plus le journal d'activité propre à une remise, sur sa page.
  • Le journal d'activité de la gestion des utilisateurs, qui enregistre « when a user or user role is created, edited or deleted » (la création, la modification ou la suppression d'un utilisateur ou d'un rôle), pour les utilisateurs éligibles.

Via l'Admin API, les events d'une commande provisoire renvoient des objets Event avec une action, un message, l'auteur, l'indication qu'un utilisateur ou une application en est à l'origine, et un horodatage. Aucun champ ne porte une politique, un seuil, une justification ou la version sur laquelle une décision a été prise. Et l'auditTrail arrivé sur la tarification contextuelle en API 2026-10 explique comment un prix de catalogue a été obtenu - ce n'est pas non plus un enregistrement de validation. Savoir quel membre du personnel Shopify a agi exige aussi un accès restreint (build vs buy donne les règles de scopes) : une couche de devis tient donc ses propres identités d'approbateurs.

L'enregistrement, champ par champ

Partie Champs La question à laquelle elle répond plus tard Ce qui se passe sans elle
Objet Devis, identifiant et numéro de la version de proposition ; les totaux de la version, la remise sur l'ensemble du devis et son facteur ; le prix effectif de chaque ligne « Qu'est-ce qui a été approuvé, exactement ? » Une version ultérieure est prise pour celle qui a été approuvée
Règle Identifiant et révision de la politique ; la branche qui a correspondu ; les entrées calculées (montant et devise, remise moyenne pondérée et remise de ligne la plus forte, % d'acompte, tags client) « Sous quel seuil cela est-il passé ? » Une politique modifiée rend le passé inexplicable
Acteur Identifiant et type d'acteur (personnel, acheteur, système) ; l'ensemble d'approbateurs résolu au démarrage de la chaîne ; délégation de qui à qui ; e-mail pour un approbateur externe ; rôle à ce moment-là « Cette personne avait-elle le droit de décider ? » Un changement de rôle réécrit qui aurait pu approuver
Horodatage Heure du serveur ; ordre de validation en base « Qu'est-ce qui s'est passé en premier ? » Des décisions simultanées ne peuvent pas être ordonnées
Issue Approbation, rejet, étape ignorée avec sa raison, dérogation avec justification, remplacée, annulée, délai expiré « Pourquoi l'étape 2 n'a-t-elle jamais tourné ? » Une étape ignorée ressemble à une étape manquante

Sur quoi porte la décision - et le piège de la remise sur le devis

Faites pointer l'enregistrement vers une version de proposition qui ne change plus, jamais vers le devis : une proposition envoyée ne change pas, et une modification produit une nouvelle version qui remplace toute chaîne de validation encore en cours sur l'ancienne (propositions et versions). Mais l'identifiant de version ne suffit pas, parce que les chiffres qu'une règle a vus étaient calculés.

Voici le cas qui montre pourquoi - pour la même raison que le total d'un acheteur n'est jamais la somme des lignes. Jusqu'au 30 septembre 2026, QuotWay évaluait les conditions de remise sur les seuls prix des lignes : une proposition dont toutes les lignes étaient au plein prix, avec une forte remise sur l'ensemble du devis, ne déclenchait aucune validation de remise. Depuis, chaque ligne est évaluée à ce que l'acheteur paie réellement - son prix après sa part de la remise sur l'ensemble du devis :

// Factor applied to every line: the buyer pays (subtotal - orderDiscount) / subtotal of each price.
function orderDiscountFactor(subtotal: Decimal, orderDiscount: Decimal): Decimal {
  if (subtotal.lte(0) || orderDiscount.lte(0)) return new Decimal(1);
  if (orderDiscount.gte(subtotal)) return new Decimal(0);
  return subtotal.minus(orderDiscount).div(subtotal);
}

function approvalInputs(version: VersionSnapshot) {
  const factor = orderDiscountFactor(version.subtotal, version.orderDiscount);
  let catalogTotal = new Decimal(0), paidTotal = new Decimal(0);
  let deepestLinePct: number | undefined;
  for (const line of version.lines) {
    const effective = line.finalPrice.times(factor);          // what the buyer actually pays
    catalogTotal = catalogTotal.plus(line.catalogPrice.times(line.quantity));
    paidTotal = paidTotal.plus(line.finalPrice.times(line.quantity));
    const base = line.baselinePrice ?? line.catalogPrice;
    if (base.gt(0)) {
      const pct = base.minus(effective).div(base).times(100).toNumber();
      deepestLinePct = Math.max(deepestLinePct ?? -Infinity, pct);
    }
  }
  const blendedPct = catalogTotal.isZero()
    ? undefined
    : catalogTotal.minus(paidTotal.times(factor)).div(catalogTotal).times(100).toNumber();
  return { factor, blendedPct, deepestLinePct };
}

Un enregistrement qui ne stockerait que les prix des lignes montrerait une chaîne qui « n'aurait pas dû » tourner - ou, avant le changement, une remise de 25 pour cent sur le devis passée sans validation et sans raison visible. Stockez les entrées calculées - le facteur, la remise moyenne pondérée et la remise de ligne la plus forte - sur la chaîne, à côté de l'identifiant de version. C'est une recommandation ; QuotWay stocke aujourd'hui la version et une courte raison, pas les entrées.

Quelle règle a correspondu, et dans quelle version

Une politique modifiée sur place efface la réponse à « sous quel seuil ? ». La solution est une révision : incrémentez-la à chaque modification, conservez les anciennes révisions, et écrivez la révision - ou un instantané de la règle qui a correspondu - sur la chaîne à son démarrage. Une forme recommandée, pas le schéma actuel de QuotWay :

-- On the approval chain, written once when it starts:
policy_id         text   not null,
policy_revision   int    not null,   -- bumped on every policy edit; old revisions kept
matched_rule      jsonb  not null,   -- the scope and conditions as they were
inputs            jsonb  not null,   -- amount, currency, rate, blended %, deepest line %,
                                     -- order-discount factor, deposit %, customer tags
step_skips        jsonb  not null    -- [{ step, reason: "rule did not match" | "missing: customerTags" }]

QuotWay modifie aujourd'hui les politiques sur place et stocke l'identifiant de la politique ainsi qu'une courte raison qui mentionne le montant et la devise pour les règles de valeur ; enregistrer la révision, les entrées et la raison de chaque étape ignorée est sa prochaine étape.

Qui a décidé

Résolvez les approbateurs en personnes concrètes au démarrage de la chaîne, pour qu'un changement de rôle ultérieur ne puisse pas changer qui aurait pu décider - QuotWay le fait. Enregistrez chaque décision avec l'identifiant et le type de l'acteur : personnel, approbateur côté acheteur (pour un approbateur externe, l'e-mail sous lequel il a agi), ou system pour une décision automatique à l'expiration d'un délai. Enregistrez la délégation comme une ligne à part, avec de qui à qui elle est passée. Et stockez le rôle de l'approbateur au moment de la décision, parce que la fiche du membre du personnel changera (personnel et rôles).

L'issue, y compris celles qu'on oublie

Approuver et rejeter, c'est facile. L'enregistrement a aussi besoin de : une étape ignorée parce que sa condition ne correspondait pas, avec la raison ; une dérogation, avec la personne et sa justification ; une chaîne remplacée parce qu'une nouvelle version a été envoyée ; une chaîne annulée ; et une décision prise à l'expiration d'un délai, avec le système comme acteur et la raison. Une étape ignorée sans raison ressemble exactement à une étape qui n'a jamais tourné.

L'écrire : une transaction, conditionnée à l'état

Deux approbateurs peuvent cliquer sur « approuver » au même instant. Écrivez chaque décision comme une nouvelle ligne, puis ne faites avancer l'étape que si elle est encore active - ainsi exactement une décision aboutit et l'autre reçoit un conflit - et faites l'étape suivante de la chaîne et l'événement final dans la même transaction. Voici comment QuotWay écrit une décision, en simplifié :

await db.$transaction(async (tx) => {
  const step = await tx.approvalSteps.findUnique({ where: { id: stepId } });
  if (step.state !== "ACTIVE") throw new StepNotActive();
  if (actor.type !== "SYSTEM" && !step.approvers.includes(actor.id)
      && !(actor.email && step.approvers.includes(actor.email))
      && step.delegateId !== actor.id) throw new NotAnApprover();

  // 1. The decision is a new row, never an update.
  await tx.approvalDecisions.create({ data: {
    chainId: step.chainId, stepId, decision, rationale,
    actorId: actor.id, actorType: actor.type, actorEmail: actor.email,
  }});

  // 2. Move the step only if nobody beat us to it - 0 rows means a concurrent decision.
  await tx.approvalSteps.update({
    where: { id: stepId, state: "ACTIVE" },
    data: { state: next(decision), resolvedAt: new Date() },
  });

  // 3. Advance the chain; on a final verdict, append the quote event - same transaction.
  const outcome = advance(await tx.approvalSteps.findMany({ where: { chainId: step.chainId } }));
  if (outcome.terminal) await appendEvent(tx, { quoteId, type: verdictEvent(outcome), chainId: step.chainId });
});
// After commit: notify the next approvers, send the released proposal, dispatch webhooks.

Les effets de bord - e-mails, proposition libérée, webhooks - ont lieu après le commit, via une outbox écrite dans la même transaction. L'article sur la conversion idempotente détaille ce schéma.

L'append-only est une propriété de la base, pas une convention

« L'application ne met jamais à jour une décision » est une convention. Faites-en une propriété :

  • Révoquez UPDATE et DELETE sur les tables d'audit pour le rôle avec lequel l'application se connecte. Dans PostgreSQL, le propriétaire d'une table « is always treated as holding all grant options » (est toujours considéré comme détenant toutes les options de privilèges) : l'application ne doit donc pas se connecter en tant que propriétaire. Ou ajoutez un trigger qui lève une erreur en cas de mise à jour ou de suppression.
  • Aucune cascade vers les tables d'audit. Une clé étrangère ON DELETE CASCADE d'une politique ou d'une étape vers les décisions, c'est une suppression que l'application n'a jamais écrite. Supprimer une politique, ou en retirer une étape, ne doit pas emporter les décisions passées - archivez plutôt la politique.
  • Les corrections sont de nouvelles lignes. Une décision erronée se corrige par une décision compensatoire, jamais par une modification.

QuotWay écrit les décisions comme de nouvelles lignes et ne les modifie jamais, mais aujourd'hui ses enregistrements de décision peuvent encore être supprimés avec la politique ou l'étape à laquelle ils appartiennent. Empêcher ces suppressions est la correction dont il a besoin ; d'ici là, il ne qualifie pas cet historique d'append-only.

Chaînes de hachage : ce qu'elles prouvent et ce qu'elles ne prouvent pas

Une chaîne de hachage stocke dans chaque ligne le hachage de la ligne précédente, si bien que modifier ou supprimer une ligne casse tous les hachages suivants. QuotWay écrit ainsi ses événements de devis, avec la ligne d'outbox d'intégration dans la même transaction :

async function appendEvent(tx: Tx, e: EventInput) {
  const prev = await tx.auditEvents.findFirst({
    where: { quoteId: e.quoteId }, orderBy: { createdAt: "desc" }, select: { hash: true },
  });
  const createdAt = new Date();
  const hash = sha256((prev?.hash ?? "") + "|" + canonicalJson({ ...e, createdAt: createdAt.toISOString() }));
  const row = await tx.auditEvents.create({ data: { ...e, createdAt, prevHash: prev?.hash ?? null, hash } });
  await tx.outbox.create({ data: toPublicEvent(row) });   // commits or rolls back with the event
  return row;
}
// canonicalJson = JSON with object keys sorted recursively, so the hash is reproducible.

Ce qu'elle prouve dépend de trois autres choses, et aucune n'est gratuite. Quelqu'un doit vérifier la chaîne - recalculer chaque hachage dans l'ordre. Le dernier hachage doit être ancré quelque part où celui qui écrit ne peut pas le réécrire, car quiconque peut réécrire toute la table peut recalculer toute la chaîne ; le guide de gestion des journaux du NIST recommande de stocker les empreintes de façon sécurisée, par exemple « on read-only media » (sur un support en lecture seule). Et les ajouts doivent être sérialisés, sinon deux processus d'écriture peuvent faire bifurquer la chaîne. Une forme recommandée - pas du code QuotWay :

// Serialise appends per quote so two writers can't fork the chain:
await tx.$executeRaw`SELECT pg_advisory_xact_lock(hashtext(${quoteId}))`;

// Verify: recompute every hash in order; the first mismatch is where the record changed.
function verify(rows: AuditRow[]): { ok: true } | { ok: false; at: string } {
  let prev = "";
  for (const r of rows) {                 // ordered by a per-quote sequence, not by timestamp
    if ((r.prevHash ?? "") !== prev) return { ok: false, at: r.id };
    if (sha256(prev + "|" + canonicalJson(fieldsOf(r))) !== r.hash) return { ok: false, at: r.id };
    prev = r.hash;
  }
  return { ok: true };
}
// Anchor: copy the latest hash per quote to storage the app can't rewrite (a write-once bucket).

Les événements de QuotWay portent le hachage de l'événement précédent ; la vérification et l'ancrage de la chaîne ne sont pas encore construits, donc il ne présente pas cet enregistrement comme protégé contre toute altération. Les recommandations de journalisation de l'OWASP disent la même chose en une ligne : « Build in tamper detection so you know if a record has been modified or deleted » (intégrez une détection d'altération pour savoir si un enregistrement a été modifié ou supprimé) - et la détection exige un vérificateur.

Un second sceau, plus étroit, vaut la peine : QuotWay stocke un hachage d'acceptation calculé sur les lignes acceptées - l'identifiant de version et, pour chaque ligne, son identifiant, sa quantité, son prix unitaire et son total - et l'affiche sur l'écran de conversion, sur la carte de la commande Shopify et sur le PDF de confirmation, pour que la commande puisse être rapprochée exactement de ce que l'acheteur a accepté.

Trois journaux, trois rôles

Journal d'audit Outbox d'intégration Fil d'activité
Lecteur Un auditeur, un litige, vous dans un an D'autres systèmes (ERP, CRM, webhooks) Des personnes sur un tableau de bord
Contenu Chaque décision, au complet Les issues, pour livraison Une sélection éditée
Données personnelles Uniquement ce dont la décision a besoin Aucune Noms affichés
Conservation Aussi longtemps que le devis qu'il autorise Jusqu'à la livraison, puis courte Courte
Mutabilité Append-only Écrite une fois, livrée, expirée Une projection - reconstruite librement

L'OWASP conseille de séparer les traces d'audit et de transactions des autres journaux, et le tableau montre pourquoi : un seul stockage ne peut pas servir trois lecteurs avec trois durées de conservation. L'architecture de devis place le journal d'audit dans la couche de devis pour la même raison. Dans QuotWay, l'outbox ne transporte que les issues des validations, sans aucune donnée personnelle, et le fil d'activité du tableau de bord est une liste éditée qui ne comprend aucun événement de validation.

Event sourcing ou journal d'audit ?

L'event sourcing stocke « the full series of actions taken on an object in an append-only store » (la série complète des actions effectuées sur un objet, dans un stockage append-only) et reconstruit l'état à partir de là. Les recommandations de Microsoft le qualifient de « a complex pattern that introduces significant trade-offs » (un modèle complexe aux compromis importants) et notent que « for most systems... traditional data management is sufficient » (pour la plupart des systèmes, une gestion de données classique suffit) ; la description d'origine de Martin Fowler dit qu'un journal d'audit est l'un des sous-produits que l'on « could also do... with more regular logging mechanisms » (pourrait aussi obtenir avec des mécanismes de journalisation plus ordinaires). Pour « qui a approuvé ceci, selon quelle règle », une table de décisions append-only à côté de tables ordinaires suffit. Une couche de devis à côté de Shopify se prête aussi mal à un rejeu complet : rejouer les événements toucherait de nouveau Shopify, et les systèmes externes « don't know the difference between real processing and replays » (ne font pas la différence entre un traitement réel et un rejeu).

Conservation et données personnelles

Minimisez au moment de l'écriture : une ligne append-only ne peut pas être nettoyée plus tard. L'OWASP range les identifiants de session, les jetons d'accès et les données personnelles sensibles parmi ce qu'il ne faut pas journaliser, et le NIST demande d'« avoid recording unneeded sensitive data » (éviter d'enregistrer des données sensibles inutiles). Référencez les personnes par identifiant et gardez leurs coordonnées ailleurs - les recommandations de Microsoft citent cette approche, ou le chiffrement des données personnelles avec une clé propre à chaque personne que l'on peut détruire, comme réponse aux demandes d'effacement dans un stockage append-only. Conservez les décisions aussi longtemps que le devis qu'elles autorisent ; la durée dépend de vos propres obligations, et nous n'en citons aucune. La checklist de revue de sécurité donne les questions à poser à un éditeur sur les journaux et les données personnelles.

Répondre à la question des mois plus tard

« Qui a approuvé les 18 pour cent sur le devis QW-1042, version 3, et selon quelle règle ? » Avec l'enregistrement ci-dessus :

  1. Trouvez la chaîne de la version 3 - pas celle du devis, qui peut avoir des versions ultérieures.
  2. Lisez la règle de la chaîne : la politique et sa révision, et les entrées qu'elle a fait correspondre - une remise moyenne pondérée de 18,4 pour cent incluant un facteur de remise sur l'ensemble du devis de 0,95, face à un seuil de 15 pour cent.
  3. Lisez les décisions dans l'ordre de validation en base : étape 1 approuvée par un responsable commercial à 10 h 02 ; étape 2 ignorée parce que sa condition (acompte inférieur à 20 pour cent) ne correspondait pas ; étape 3 approuvée par l'approbateur financier à qui elle avait été déléguée.
  4. Vérifiez que le hachage d'acceptation de la commande correspond aux lignes acceptées de la version 3.

Changez maintenant une seule chose : le seuil de la politique a été relevé à 20 pour cent le mois dernier. Sans la révision sur la chaîne, l'étape 2 se lit comme « ne se déclencherait pas aujourd'hui », et personne ne peut dire pourquoi elle a tourné à l'époque. Avec elle, la réponse tient en une ligne.

Une checklist pour votre propre développement

  • Chaque décision est une nouvelle ligne ; rien ne la met à jour ni ne la supprime.
  • Le rôle de base de données de l'application ne peut ni UPDATE ni DELETE les tables d'audit, et rien n'y cascade.
  • Chaque chaîne pointe vers une version de proposition qui ne change plus, pas vers le devis.
  • Chaque chaîne stocke la révision de la politique (ou un instantané de la règle) et les entrées calculées - y compris le facteur de la remise sur l'ensemble du devis.
  • Les approbateurs sont résolus au démarrage de la chaîne ; le rôle au moment de la décision est stocké.
  • La délégation est une ligne avec un « de » et un « à » ; les approbateurs externes sont enregistrés sous l'identité avec laquelle ils ont agi.
  • Les étapes ignorées portent leur raison ; les dérogations portent la personne et la justification ; les délais expirés sont enregistrés au nom du système.
  • Deux décisions simultanées : exactement une aboutit.
  • Les effets de bord s'exécutent après le commit, via une outbox dans la même transaction.
  • Si vous chaînez par hachage : les ajouts sont sérialisés, un vérificateur tourne, et le dernier hachage est ancré hors de portée de celui qui écrit.
  • Le journal d'audit, l'outbox et le fil d'activité sont des stockages distincts, avec des durées de conservation distinctes.
  • Aucun jeton, identifiant de session ou donnée personnelle dont la décision n'a pas besoin.

Vous évaluez une application plutôt que de développer ? La même liste fonctionne sous forme de questions ; la checklist d'évaluation contient le test du journal d'audit, et build vs buy chiffre ce que coûte cette partie à construire.

Où cela se trouve dans QuotWay

Les flux de validation côté marchand et les chaînes de validation côté acheteur sont disponibles à partir du forfait Professional, et s'appliquent aux devis ; les étapes parallèles et conditionnelles, les délais, l'escalade et la délégation sont sur Enterprise (flux de validation, forfaits). Une chaîne appartient à une version de proposition, et une nouvelle version la remplace. Les approbateurs sont résolus au démarrage de la chaîne ; chaque décision est écrite comme une nouvelle ligne avec l'acteur, son type et la justification, et de deux décisions simultanées, une seule aboutit. Les événements de devis portent le hachage de l'événement précédent, et le hachage d'acceptation est affiché sur la carte de la commande Shopify. Les enregistrements de devis, y compris leurs événements et leurs hachages d'acceptation, sont conservés 7 ans par défaut. Le fonctionnement de l'application des validations décrit ce qu'une validation bloque. Depuis le 30 septembre 2026, les conditions de remise évaluent chaque ligne à son prix après sa part de la remise sur l'ensemble du devis. Les webhooks de validation de l'API Enterprise ne transportent que les issues des chaînes, sans aucune donnée personnelle (la fonctionnalité de validation).

Questions fréquentes

Que doit enregistrer un journal d'audit des validations de prix ?

La version de la proposition et ses chiffres, la règle et sa révision avec les entrées qui l'ont fait correspondre, qui a décidé et pour le compte de qui, quand, et l'issue - y compris les étapes ignorées avec leur raison et les dérogations avec leur justification.

Shopify a-t-il un journal d'audit pour les validations de remise ?

Non. Shopify a un journal d'activité de la boutique, des chronologies sur les commandes et les commandes provisoires, un journal d'activité propre à chaque remise et un journal de gestion des utilisateurs. Aucun ne stocke une règle, un seuil ou la version sur laquelle une décision a été prise.

Un journal d'audit doit-il être immuable ?

Append-only : les corrections sont de nouvelles lignes. Imposez-le dans la base de données - révoquez la mise à jour et la suppression pour le rôle de l'application, ou utilisez un trigger - et tenez les suppressions en cascade à l'écart des tables d'audit.

Une chaîne de hachage suffit-elle à protéger un journal d'audit contre la falsification ?

Pas à elle seule. Elle ne révèle une altération que si quelqu'un vérifie la chaîne et que le dernier hachage est stocké là où celui qui écrit ne peut pas le modifier ; sinon, quiconque peut réécrire la table peut recalculer chaque hachage.

Event sourcing ou journal d'audit ?

Un journal d'audit, sauf si vous devez reconstruire l'état à partir de l'historique. L'event sourcing ajoute des compromis importants, et rejouer des événements ne fait pas bon ménage avec des systèmes externes comme Shopify.

Une remise sur l'ensemble du devis compte-t-elle pour la validation des remises ?

Elle le devrait : évaluez chaque ligne à son prix après sa part de la remise sur l'ensemble du devis, et stockez le facteur utilisé. QuotWay le fait depuis le 30 septembre 2026.

Qu'advient-il de l'enregistrement si la politique de validation change plus tard ?

Si la chaîne n'a stocké que l'identifiant de la politique, vous ne pouvez plus dire quel seuil s'appliquait. Stockez la révision de la politique, ou un instantané de la règle qui a correspondu, au démarrage de la chaîne.

Combien de temps conserver les enregistrements de validation ?

Aussi longtemps que le devis qu'ils autorisent, selon vos propres obligations. Nous ne citons aucune durée légale.

Quelle différence entre un journal d'audit et un fil d'activité ?

Le fil d'activité est un résumé édité et lisible pour un tableau de bord. Le journal d'audit est l'enregistrement complet conservé comme preuve. Ils ont des lecteurs, des contenus et des durées de conservation différents : gardez-les dans des stockages séparés.

Sources

Lues le 14 octobre 2026 :

Le comportement de QuotWay est décrit d'après le code source de l'application, lu le 14 octobre 2026 ; le code est simplifié, sans les noms internes.

Articles liés

Découvrez comment QuotWay gère cela sur votre boutique.

Nous souhaitons déposer des cookies de mesure d'audience pour comprendre comment le site est utilisé. Ils ne sont pas nécessaires : refuser ne change rien au fonctionnement du site, et vous pouvez revenir sur votre choix à tout moment depuis notre page de confidentialité.