From ab145327677d3dd368c8e55416a31048b67dc1ca Mon Sep 17 00:00:00 2001 From: New1Direction <285551516+New1Direction@users.noreply.github.com> Date: Wed, 10 Jun 2026 15:42:34 -0600 Subject: [PATCH] feat(impulse): Calculator Pro ($4.99 portfolio tool) + guide/Pro CTAs on /tools /drops /guides --- worker/src/articles.ts | 2 + worker/src/digital.ts | 129 +++++++++++++++++++++++++++++++++--- worker/src/drops_seo.ts | 4 ++ worker/src/index.ts | 17 +++++ worker/src/tools.ts | 9 +++ worker/test/digital.test.ts | 29 ++++++++ 6 files changed, 181 insertions(+), 9 deletions(-) diff --git a/worker/src/articles.ts b/worker/src/articles.ts index 80b1e42..8f0bdbd 100644 --- a/worker/src/articles.ts +++ b/worker/src/articles.ts @@ -3754,6 +3754,8 @@ ${uiNav(origin)} ${relatedHtml(a)} +
📘 The Drop-Day Playbook — the restock-day checklist ($2.99)
+ diff --git a/worker/src/digital.ts b/worker/src/digital.ts index adf6439..6b2ee1b 100644 --- a/worker/src/digital.ts +++ b/worker/src/digital.ts @@ -18,14 +18,17 @@ type Env = { KEYS: KVNamespace; STRIPE_SECRET_KEY?: string; STRIPE_PRICE_GUIDE?: string; + STRIPE_PRICE_CALCPRO?: string; }; -type ProductId = "guide"; +type ProductId = "guide" | "calcpro"; interface ProductDef { priceEnv: keyof Env; price: string; // display only title: string; + tagline: string; // sales-page sub-headline + bullets: string[]; // "what's inside" render: () => string; // the gated content (HTML body) } @@ -34,8 +37,30 @@ const PRODUCTS: RecordAdd every sealed box and card you're tracking. It keeps a running tally of what you'd spend at retail, what you save versus resale, and your profit if you flip — saved right on this device.
+ +No items yet — add your first above. Savings/profit = (resale − retail) × qty. Resale prices move, so update them as you go.
+ + +`; + // ------------------------------- handlers ------------------------------- // POST /api/v1/That access link isn't valid. Get the playbook
`), 403); + if (!ok) return c.html(shell(def.title, `That access link isn't valid. Get ${esc(def.title)}
`), 403); return c.html(renderUnlocked(def, product, t)); } @@ -194,16 +309,12 @@ export function digitalSalesHtml(origin: string, product: ProductId, canceled: b if (!def) return shell("Not found", "Stop paying resellers. The exact restock-day checklist to land Pokémon sealed product at retail — on your phone, no app, no subscription.
-${canceled ? `` : ""} +${esc(def.tagline)}
+${canceled ? `` : ""}The free tool above does one item. Pro tallies every sealed box and card you own — total spend, savings, and resale profit at a glance, saved on your device. One-time, no app.
+ +${esc(c.ctaP)}
diff --git a/worker/test/digital.test.ts b/worker/test/digital.test.ts index 645c4db..0bc3242 100644 --- a/worker/test/digital.test.ts +++ b/worker/test/digital.test.ts @@ -52,4 +52,33 @@ describe("digital impulse products", () => { expect(html).toContain("Get instant access"); expect(html).toContain("no signup"); }); + + it("calcpro checkout is fail-closed without its price (503)", async () => { + const c = makeCtx({ env: envMock({ STRIPE_SECRET_KEY: "sk_test" }), method: "POST", body: {}, url: "https://wmcp.sh/api/v1/calcpro/checkout" }); + const r: any = await createDigitalCheckout(c, "calcpro"); + expect(r.status).toBe(503); + expect(r.body.error).toBe("calcpro_not_configured"); + }); + + it("calcpro sales page renders at \$4.99 with its own copy", () => { + const html = digitalSalesHtml("https://wmcp.sh", "calcpro", false); + expect(html).toContain("$4.99"); + expect(html).toContain('data-product="calcpro"'); + expect(html).toContain("Portfolio"); + }); + + it("calcpro unlock content serves the portfolio tool for a valid token", async () => { + const env = envMock(); + await env.KEYS.put("dlg:cptoken", JSON.stringify({ product: "calcpro", ts: 1 })); + const c = makeCtx({ env, method: "GET", query: { t: "cptoken" }, url: "https://wmcp.sh/calcpro/read?t=cptoken" }); + const r: any = await digitalRead(c, "calcpro"); + expect(r.status).toBe(200); + expect(String(r.body)).toContain("Pro Portfolio Calculator"); + // a guide token must not open calcpro + const env2 = envMock(); + await env2.KEYS.put("dlg:gtok", JSON.stringify({ product: "guide", ts: 1 })); + const c2 = makeCtx({ env: env2, method: "GET", query: { t: "gtok" }, url: "https://wmcp.sh/calcpro/read?t=gtok" }); + const r2: any = await digitalRead(c2, "calcpro"); + expect(r2.status).toBe(403); + }); });