From 752845302a28b49bc8e90f9b88fb85a2605149cf Mon Sep 17 00:00:00 2001 From: metatroncubeswdev Date: Fri, 30 Oct 2026 22:35:24 -0400 Subject: [PATCH] Check DATA_ENCRYPTION_KEY at startup; launch skill: bad key broke order analytics (review 2.1.1) Co-Authored-By: Claude Opus 5.5 --- .claude/skills/shopify-app-launch/SKILL.md | 1 + app/entry.server.tsx | 2 ++ app/lib/crypto.server.ts | 5 +++++ 3 files changed, 8 insertions(+) diff --git a/.claude/skills/shopify-app-launch/SKILL.md b/.claude/skills/shopify-app-launch/SKILL.md index c6fd96a..d9fd247 100644 --- a/.claude/skills/shopify-app-launch/SKILL.md +++ b/.claude/skills/shopify-app-launch/SKILL.md @@ -299,3 +299,4 @@ Go to Partner Dashboard › Distribution › **App Store review**. | Feature "doesn't work" in test | Test products out of stock or not in the collection | Use in-stock products; check the data before recording | | Chrome tab "hidden" or screenshot timeouts | Window minimised or not visible | Bring the window forward or open a new tab group; read state with JS | | Review 1.2.2 "fails to register the plan" | App Pricing (enrolled after April 2026) does not populate `currentAppInstallation.activeSubscriptions` or send billing webhooks | Read the plan from the Partner API `activeSubscription(appId, shopId)` (client with "Manage apps"); in the `/app` root loader redirect shops with no subscription **or `cancelAtEndOfCycle: true`** (an uninstall keeps a prepaid period running, so a reinstall still returns a subscription) to `admin.shopify.com/store//charges//pricing_plans` (`target: "_top"`); show `pendingUpdate` (yearly downgrades apply at cycle end); never redirect on API errors | +| Review 2.1.1 "orders not updated in analytics" | `DATA_ENCRYPTION_KEY` was 48 hex chars (`openssl rand -hex 24`), so every `orders/create` webhook threw and returned 500 | Generate with `openssl rand -hex 32` (64 chars); validate the key at startup so the container fails loudly; after any deploy, place one test order and confirm the webhook returns 200 | diff --git a/app/entry.server.tsx b/app/entry.server.tsx index bfd8489..07da256 100644 --- a/app/entry.server.tsx +++ b/app/entry.server.tsx @@ -6,8 +6,10 @@ import { type EntryContext } from "react-router"; import { isbot } from "isbot"; import { addDocumentResponseHeaders } from "./shopify.server"; import { startScheduler } from "./lib/scheduler.server"; +import { assertEncryptionKey } from "./lib/crypto.server"; // Re-publishes offer configurations at every start/end boundary (brief §11.5). +if (process.env.NODE_ENV === "production") assertEncryptionKey(); startScheduler(); export const streamTimeout = 5000; diff --git a/app/lib/crypto.server.ts b/app/lib/crypto.server.ts index 7d593dd..dcbdcf8 100644 --- a/app/lib/crypto.server.ts +++ b/app/lib/crypto.server.ts @@ -8,6 +8,11 @@ import { createCipheriv, createDecipheriv, createHash, createHmac, randomBytes } let keys: { enc: Buffer; mac: Buffer } | null = null; +/** Fails fast at startup: a bad key would otherwise only surface as 500s on every orders/create webhook. */ +export function assertEncryptionKey() { + getKeys(); +} + function getKeys() { if (keys) return keys; const hex = process.env.DATA_ENCRYPTION_KEY || "";