EXEMPLE : une entreprise fictive. Pas un client d'Adwaizer.
Version française de l'exemple de rapport en anglais. Même contenu et mêmes chiffres que la version anglaise ; en cas d'écart, la version anglaise fait foi. Les noms de fichiers, les commandes, le code et les sorties de journal restent en anglais.
Correctif d'application Shopify : rapport de préparation à l'examen et résumé du correctif pour Tidewell Restock Alerts (exemple)
Préparé par : Adwaizer AI Consulting Inc. · Mandat : correction de bogue ou petite fonctionnalité dans une application Shopify, niveau Starter (un correctif dans une application, 6 jours ouvrables) · Version du rapport : exemple, octobre 2026
Ce qu'est cette page. Voici le rapport de remise que le client conserve à la fin du correctif d'application Shopify. Tidewell Restock Alerts, son développeur Tidewell Apps et chaque personne, boutique, dépôt, commit et chiffre ci-dessous sont inventés pour montrer la forme et le niveau de détail ; rien de tout cela n'existe. Dans un vrai mandat, chaque ligne renvoie au dépôt du client, à sa pull request et à sa boutique de développement.
Comment lire les preuves. Une « preuve » est ce qu'un réviseur peut ouvrir et vérifier : un fichier et une ligne dans le dépôt, le nom d'un test, une commande et sa sortie, ou un écran de la boutique de développement. Rien ici n'a été exécuté sur une vraie application.
1. Sommaire
Tidewell Restock Alerts est une application Shopify publique fictive qui envoie un courriel aux clients lorsqu'une variante de produit en rupture de stock est de nouveau disponible. Son développeur nous a demandé de corriger un seul bogue : certaines livraisons du webhook de confidentialité obligatoire customers/redact étaient rejetées avec un code HTTP 401, de sorte que la demande d'effacement d'un client pouvait rester sans réponse.
- Le bogue : le gestionnaire du webhook vérifiait la signature
X-Shopify-Hmac-SHA256sur le corps JSON après l'avoir analysé puis resérialisé, et non sur le corps brut de la requête. Toute charge utile dont les octets changeaient à la resérialisation (noms non ASCII, caractères échappés, espacement différent des clés) échouait à la vérification. - Le correctif : les routes de webhook lisent maintenant les octets bruts du corps, vérifient la signature sur ces octets avec une comparaison à temps constant, et n'analysent le JSON qu'une fois la vérification réussie. Une fonction utilitaire partagée remplace trois copies de la vérification.
- Le test : un nouveau test signe, sur ses octets bruts, une charge utile de référence contenant un nom de client non ASCII. Il échouait avec l'ancien code (401) et réussit avec la modification (200). La suite existante de 212 tests réussit toujours.
- Préparation à l'examen : sur les 14 points de notre liste de vérification pour l'examen des applications Shopify, 10 sont conformes, 1 a été corrigé par cette modification, 2 exigent un correctif hors de cette portée et 1 ne s'applique pas (section 4).
- Incidence côté Shopify : aucune étendue d'accès (access scope), aucun abonnement webhook, aucune facturation et aucune configuration d'extension n'a changé, donc aucun nouveau consentement des marchands n'est attendu. Le client publie une nouvelle version de l'application ; un nouvel examen par l'App Store n'est pas attendu pour un correctif côté serveur, mais c'est Shopify qui en décide, pas nous.
2. Portée écrite (jour 1, convenue par écrit avant le travail)
| Élément | Convenu |
|---|---|
| Le bogue | Livraisons de customers/redact rejetées avec 401 pour certains clients ; observé dans les journaux de l'application pour 3 livraisons de test sur 20 dans la boutique de développement |
| Comportement attendu | Chaque livraison de webhook authentique de Shopify réussit la vérification HMAC et reçoit un code de la série 200 ; une livraison avec une mauvaise signature est rejetée avec 401 |
| Fichiers modifiés | app/routes/webhooks.*.tsx (3 routes), un nouveau app/lib/verify-webhook.server.ts, des tests dans test/webhooks/ |
| Changements d'étendues d'accès, de webhooks, de facturation ou de configuration d'extension | Aucun. shopify.app.toml n'est pas modifié |
| Hors portée | Le changement de chargement d'App Bridge et les captures d'écran de la fiche trouvés lors de la vérification d'examen (section 4) ; les deux appels à l'API REST Admin ; toute modification de la boutique en production |
| Vérifications d'acceptation | Les quatre vérifications de la section 6 |
3. Le correctif
3.1 Bogue reproduit (jour 2)
- Test en échec :
test/webhooks/customers-redact.test.ts› "accepts a genuine delivery whose payload has non-ASCII characters". La référence est une charge utilecustomers/redactpour un client de test nommé « Zoë Ménard » (fictif), signée avec le secret de test de l'application de développement sur ses octets bruts. - Résultat avec l'ancien code : statut attendu 200, reçu 401.
- Dans la boutique de développement
tidewell-dev.myshopify.com(fictive, données de test seulement) : un client de test au nom accentué a été créé et une demande d'effacement a été déclenchée depuis l'interface administrateur de la boutique ; le journal de l'application affichaitwebhook hmac mismatch topic=customers/redact. Aucune boutique en production n'a été utilisée. - Cause racine :
app/routes/webhooks.customers-redact.tsxappelaitawait request.json(), puis calculait le HMAC surJSON.stringify(body). La resérialisation modifiait les caractères échappés, si bien que l'empreinte différait de celle de Shopify.
3.2 Modification (jour 3-4)
| Fichier | Modification |
|---|---|
app/lib/verify-webhook.server.ts (nouveau) | Lit request.arrayBuffer(), calcule le HMAC-SHA256 avec le secret de l'application, le compare à l'en-tête avec crypto.timingSafeEqual et ne renvoie la charge utile analysée qu'en cas de correspondance |
app/routes/webhooks.customers-redact.tsx, webhooks.customers-data-request.tsx, webhooks.shop-redact.tsx | Utilisent la fonction utilitaire ; répondent 401 en cas de non-correspondance, 200 une fois la tâche mise en file d'attente |
test/webhooks/*.test.ts | Nouveau test de la section 3.1, plus un test par route vérifiant qu'une mauvaise signature est rejetée avec 401 |
Pull request : tidewell-apps/restock-alerts#58 (fictive). Diff : +74 / -41 lignes dans 6 fichiers.
3.3 Vérifications effectuées sur la modification
| Vérification | Résultat |
|---|---|
| Nouveau test avec l'ancien code | Échoue : 401, 200 attendu |
| Nouveau test avec la modification | Réussit |
Suite complète (npm test) | 215 réussis, 0 en échec (212 existants + 3 nouveaux) |
Lint et vérification des types (npm run lint, npx tsc --noEmit) | 0 erreur |
| Boutique de développement : demande d'effacement pour le client de test au nom accentué | Le journal de l'application affiche customers/redact accepted, réponse 200 |
| Boutique de développement : livraison avec un corps altéré (envoyée depuis un script local) | Rejetée avec 401 |
| Session de révision distincte, par rapport à la portée écrite | Approuvée ; aucun fichier hors de la portée n'a été modifié |
Analyse des commits à la recherche de secrets (gitleaks) | Aucune fuite trouvée |
4. Tableau de préparation à l'examen
Chaque point de notre liste de vérification gratuite (adwaizer.com/shopify-app-review-checklist), vérifié dans la boutique de développement et dans le dépôt au commit corrigé. Le statut est l'un de Conforme, Corrigé (par cette modification), À corriger (trouvé, hors de cette portée) ou Sans objet.
| # | Point de la liste | Statut | Preuve qu'un réviseur verrait |
|---|---|---|---|
| 1 | OAuth s'exécute avant toute autre chose à l'installation et à la réinstallation | Conforme | Une nouvelle installation et une désinstallation suivie d'une réinstallation dans la boutique de développement ouvrent toutes deux d'abord l'écran d'autorisation et mènent à l'accueil de l'application après acceptation ; app/shopify.server.ts utilise l'authentification du cadre d'application pour chaque route d'administration |
| 2 | L'application intégrée s'authentifie avec des jetons de session | Conforme | authenticate.admin(request) sur chaque loader et action de app/routes/app.* ; aucun témoin (cookie) d'authentification ni jeton en stockage local dans grep -rn "localStorage|document.cookie" app/ |
| 3 | La dernière version d'App Bridge se charge depuis la balise script du CDN de Shopify | À corriger | app/root.tsx charge App Bridge depuis une ancienne version du paquet npm, et non depuis la balise script du CDN comme premier script. Hors de cette portée ; chiffré comme élément distinct |
| 4 | Les trois webhooks de confidentialité obligatoires sont abonnés et reçoivent une réponse | Corrigé | shopify.app.toml déclare customers/data_request, customers/redact et shop/redact ; avec la modification, les trois répondent 200 aux livraisons authentiques (tests dans test/webhooks/, journal de la boutique de développement en 3.3) |
| 5 | Chaque webhook est vérifié avec sa signature HMAC | Corrigé | La fonction utilitaire partagée vérifie X-Shopify-Hmac-SHA256 sur le corps brut pour chaque route de webhook ; une livraison altérée est rejetée avec 401 (test "rejects a wrong signature" dans le fichier de test de chaque route) |
| 6 | Tous les frais passent par la facturation de Shopify | Sans objet | L'application est gratuite et ne facture rien : aucun code ni forfait de facturation dans le dépôt, aucun frais dans l'historique de l'application de la boutique de développement |
| 7 | L'application ne demande que les étendues d'accès qu'elle utilise | Conforme | shopify.app.toml demande read_products, read_inventory, read_customers ; chacune correspond à une fonctionnalité (liste des variantes, webhook de niveau de stock, courriel du client à l'inscription) ; inchangé par ce correctif |
| 8 | L'accès aux données client protégées est demandé avant qu'elles soient lues | Conforme | L'application conserve les courriels des clients pour les alertes ; le Partner Dashboard affiche la demande d'accès aux données client protégées approuvée pour le courriel (capture d'écran fournie par le client, puisque nous n'avions pas accès au Partner Dashboard) |
| 9 | Les nouvelles applications publiques appellent l'API GraphQL Admin, pas REST | Conforme | Toutes les lectures de produits et de stocks utilisent l'API GraphQL Admin ; deux appels REST subsistent dans app/jobs/legacy-sync.server.ts, qui ne s'exécute que pour les boutiques installées avant le passage à GraphQL. Signalé au client ; hors de cette portée |
| 10 | Les fonctionnalités de la vitrine utilisent des extensions d'application de thème | Conforme | Le bouton « Notify me » est un bloc d'application dans extensions/notify-button/ ; aucune écriture par l'Asset API et aucun ScriptTag dans le code |
| 11 | L'application s'installe et fonctionne sans erreur | Conforme | Installation, inscription à une alerte, réapprovisionnement et courriel d'alerte, désinstallation : aucun écran en échec et aucune erreur dans la console du navigateur dans la boutique de développement |
| 12 | L'application ne ralentit pas la vitrine | Conforme | Performance Lighthouse sur la page produit de la boutique de développement (thème par défaut gratuit, mobile) : 81 sans l'application, 78 avec le bloc d'application, une baisse de 3 points |
| 13 | L'application est servie avec un certificat TLS valide | Conforme | L'URL de l'application et le point de terminaison du webhook présentent tous deux un certificat valide, sans erreur dans le navigateur ni avec curl -v |
| 14 | La fiche ne décrit que ce que fait l'application | À corriger | Deux captures d'écran de la fiche montrent une page de paramètres retirée dans une version antérieure. Hors de cette portée ; le client les remplace avant la prochaine mise à jour de la fiche |
Shopify met ses exigences à jour ; chaque point de la page de la liste de vérification renvoie à la page Shopify dont il provient. Ce tableau ne garantit pas l'approbation.
5. Vérification Shopify de la modification (jour 5)
| Question | Réponse |
|---|---|
| Étendue d'accès ajoutée ou retirée? | Non |
| Les webhooks de confidentialité obligatoires reçoivent-ils toujours une réponse? | Oui, les trois, y compris maintenant les charges utiles qui échouaient auparavant |
| Vérifications HMAC du webhook et de session intactes ou toujours testées? | Vérification HMAC réécrite et testée pour chaque route ; vérifications de session intactes |
| Appel à l'API Admin retiré d'une version d'API prise en charge? | Non |
| Facturation touchée? | Non (l'application n'en a pas) |
| La configuration des extensions se déploie-t-elle toujours? | Oui : shopify app info et la validation de la configuration sur l'application de développement n'ont montré aucun changement d'extension ni de configuration |
| Publication d'une version de l'application ou nouvel examen par l'App Store? | Le client publie une nouvelle version de l'application avec sa commande habituelle shopify app deploy. La modification est uniquement côté serveur, donc un nouvel examen par l'App Store n'est pas attendu ; c'est Shopify qui en décide |
6. Liste d'acceptation (pour le client)
- Le nouveau test échoue avec votre ancien code et réussit avec la modification (
npm test -- test/webhooks). - Votre suite de tests existante réussit toujours (
npm test). - Dans votre boutique de développement, une demande d'effacement pour un client au nom accentué reçoit une réponse 200.
- La vérification Shopify de la section 5 ne montre aucun changement d'étendue d'accès, de webhook, de facturation ou d'extension que la portée écrite ne nommait pas.
Une vérification en échec est corrigée dans un délai de 5 jours ouvrables, sans frais.
7. Étapes de publication que vous exécutez
- Révisez et fusionnez la pull request #58 dans votre dépôt.
- Déployez le serveur de l'application comme vous le faites d'habitude.
- Exécutez
shopify app deploypour publier la version de l'application (elle ne contient aucun changement de configuration ni d'extension). - Surveillez le journal des webhooks de l'application pendant une journée : les livraisons de
customers/redactne devraient afficher que 200.
Nous ne fusionnons, ne déployons, ne publions et ne soumettons jamais rien à Shopify à votre place, et nous ne touchons jamais à votre boutique en production.
8. Trouvé mais non corrigé (pour une portée distincte)
| Élément | Pourquoi c'est important | Prochaine étape suggérée |
|---|---|---|
| App Bridge depuis un ancien paquet npm (ligne 3) | Shopify demande aux applications de l'App Store de charger la dernière version d'App Bridge depuis son CDN | Un petit correctif : passer à la balise script du CDN et retester les écrans intégrés |
| Captures d'écran de la fiche périmées (ligne 14) | L'examen compare la fiche à l'application soumise | Tâche du client : nouvelles captures d'écran de la page de paramètres actuelle |
| Deux appels à l'API REST Admin (ligne 9) | Ce n'est pas un obstacle à l'examen pour cette application aujourd'hui ; REST pourrait perdre des fonctionnalités avec le temps | Migrer legacy-sync vers GraphQL au moment opportun |