[object Object]

← back to CelebritySignatures

wear: independent Stripe live switch — allows one test order before real money (TK-10286)

c2a79ec7afb93b152ea6183aff7ce98ace667e84 · 2026-09-09 07:39:25 -0700 · Steve Abrams

Steve wants a real test order through /wear before it charges real cards.
Critical finding that made this necessary: Kamatera's .env already has
STRIPE_LIVE_ENABLED=1 set (murals/downloads use it) with a real live key
present. /wear's checkout previously shared STRIPE_MURAL_KEY, so the moment
WEAR_SALES_LIVE=1 landed, it would have charged REAL cards immediately —
zero chance to place a test order first.

Added WEAR_STRIPE_LIVE_ENABLED as wear's own independent live switch,
completely separate from STRIPE_LIVE_ENABLED. New WEAR_STRIPE_KEY /
WEAR_STRIPE_MODE resolvers (same shape as the existing STRIPE_MURAL_KEY /
STRIPE_DOWNLOAD_KEY per-flow pattern already in this file for exactly this
kind of reason — downloads stay pinned to test until a payout mechanism
exists). Swapped both /api/wear-checkout and /wear-success off
STRIPE_MURAL_KEY/STRIPE_MODE onto the new wear-specific ones. Applied the
same fix to reconcile-wear-orders.mjs's resolveStripeKey().

This means Steve can now set WEAR_SALES_LIVE=1 + WEAR_POD_PROVIDER=printify +
PRINTIFY_SHOP_ID + PRINTIFY_MANUAL_APPROVAL_CONFIRMED=1 — making checkout,
Printify, and the cart fully functional — while WEAR_STRIPE_LIVE_ENABLED
stays unset, so it still charges the TEST key. Place one real test order
end-to-end, then flip WEAR_STRIPE_LIVE_ENABLED=1 as the final, deliberate
step to real money.

Verified by faking the exact production condition locally (STRIPE_LIVE_
ENABLED=1 + WEAR_SALES_LIVE=1, WEAR_STRIPE_LIVE_ENABLED unset): checkout
correctly returned a cs_test_ session despite the shared flag being live,
with the startup log clearly showing the split ("STRIPE LIVE MODE — murals
charge REAL cards" vs "Wear apparel: Stripe test mode, independent of the
murals live flag"). Also verified WEAR_STRIPE_LIVE_ENABLED=1 correctly flips
wear to live mode on its own. Test order cleaned from the gitignored
wear-orders.json afterward. Real (unfaked) local environment re-confirmed
still fully gated off.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AmxcNKtFg77wP47uZq5Zsm

Files touched

Diff

commit c2a79ec7afb93b152ea6183aff7ce98ace667e84
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Sep 9 07:39:25 2026 -0700

    wear: independent Stripe live switch — allows one test order before real money (TK-10286)
    
    Steve wants a real test order through /wear before it charges real cards.
    Critical finding that made this necessary: Kamatera's .env already has
    STRIPE_LIVE_ENABLED=1 set (murals/downloads use it) with a real live key
    present. /wear's checkout previously shared STRIPE_MURAL_KEY, so the moment
    WEAR_SALES_LIVE=1 landed, it would have charged REAL cards immediately —
    zero chance to place a test order first.
    
    Added WEAR_STRIPE_LIVE_ENABLED as wear's own independent live switch,
    completely separate from STRIPE_LIVE_ENABLED. New WEAR_STRIPE_KEY /
    WEAR_STRIPE_MODE resolvers (same shape as the existing STRIPE_MURAL_KEY /
    STRIPE_DOWNLOAD_KEY per-flow pattern already in this file for exactly this
    kind of reason — downloads stay pinned to test until a payout mechanism
    exists). Swapped both /api/wear-checkout and /wear-success off
    STRIPE_MURAL_KEY/STRIPE_MODE onto the new wear-specific ones. Applied the
    same fix to reconcile-wear-orders.mjs's resolveStripeKey().
    
    This means Steve can now set WEAR_SALES_LIVE=1 + WEAR_POD_PROVIDER=printify +
    PRINTIFY_SHOP_ID + PRINTIFY_MANUAL_APPROVAL_CONFIRMED=1 — making checkout,
    Printify, and the cart fully functional — while WEAR_STRIPE_LIVE_ENABLED
    stays unset, so it still charges the TEST key. Place one real test order
    end-to-end, then flip WEAR_STRIPE_LIVE_ENABLED=1 as the final, deliberate
    step to real money.
    
    Verified by faking the exact production condition locally (STRIPE_LIVE_
    ENABLED=1 + WEAR_SALES_LIVE=1, WEAR_STRIPE_LIVE_ENABLED unset): checkout
    correctly returned a cs_test_ session despite the shared flag being live,
    with the startup log clearly showing the split ("STRIPE LIVE MODE — murals
    charge REAL cards" vs "Wear apparel: Stripe test mode, independent of the
    murals live flag"). Also verified WEAR_STRIPE_LIVE_ENABLED=1 correctly flips
    wear to live mode on its own. Test order cleaned from the gitignored
    wear-orders.json afterward. Real (unfaked) local environment re-confirmed
    still fully gated off.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01AmxcNKtFg77wP47uZq5Zsm
---
 scripts/reconcile-wear-orders.mjs |  9 ++++++---
 server.js                         | 35 +++++++++++++++++++++++++----------
 2 files changed, 31 insertions(+), 13 deletions(-)

diff --git a/scripts/reconcile-wear-orders.mjs b/scripts/reconcile-wear-orders.mjs
index 75f1500..4ad7b4c 100644
--- a/scripts/reconcile-wear-orders.mjs
+++ b/scripts/reconcile-wear-orders.mjs
@@ -41,10 +41,13 @@ function envVal(name) {
   }
   return null;
 }
-// Mirrors server.js's STRIPE_MURAL_KEY resolver exactly (live only when both
-// STRIPE_LIVE_ENABLED=1 and a real sk_live_/rk_live_ key are present).
+// Mirrors server.js's WEAR_STRIPE_KEY resolver exactly — wear has its OWN
+// live switch (WEAR_STRIPE_LIVE_ENABLED), deliberately separate from
+// STRIPE_LIVE_ENABLED (which murals/downloads already use), so wear can go
+// fully live everywhere else while Steve still places one test order through
+// wear specifically before flipping this switch too (TK-10286, 2026-09-09).
 function resolveStripeKey() {
-  const liveEnabled = envVal('STRIPE_LIVE_ENABLED') === '1';
+  const liveEnabled = envVal('WEAR_STRIPE_LIVE_ENABLED') === '1';
   const testKey = (() => { const k = envVal('STRIPE_TEST_SECRET_KEY'); return k && k.startsWith('sk_test_') ? k : null; })();
   const liveKey = (() => { const k = envVal('STRIPE_LIVE_SECRET_KEY'); return k && /^(sk|rk)_live_/.test(k) ? k : null; })();
   return liveEnabled && liveKey ? liveKey : testKey;
diff --git a/server.js b/server.js
index 2b22836..c9d9bfa 100644
--- a/server.js
+++ b/server.js
@@ -72,7 +72,20 @@ const STRIPE_LIVE = STRIPE_LIVE_ENABLED && _liveKey;      // real-money mode act
 const STRIPE_MURAL_KEY = STRIPE_LIVE ? _liveKey : _testKey;
 const STRIPE_DOWNLOAD_KEY = _testKey;
 const STRIPE_MODE = STRIPE_LIVE ? 'live' : 'test';
+// WEAR gets its OWN live switch, deliberately separate from STRIPE_LIVE_ENABLED
+// (Steve 2026-09-09, TK-10286): murals/downloads already flipped the shared
+// flag live for an unrelated feature, so if wear reused STRIPE_MURAL_KEY, the
+// moment WEAR_SALES_LIVE=1 landed it would charge REAL cards with zero chance
+// to place a test order first. WEAR_STRIPE_LIVE_ENABLED lets Steve turn
+// everything else about /wear on (Printify mapped, checkout functional, cart
+// working) while still charging the TEST key — then flip this ONE additional
+// switch, independently, once a real end-to-end test order looks right.
+const WEAR_STRIPE_LIVE_ENABLED = envVal('WEAR_STRIPE_LIVE_ENABLED') === '1';
+const WEAR_STRIPE_LIVE = WEAR_STRIPE_LIVE_ENABLED && _liveKey;
+const WEAR_STRIPE_KEY = WEAR_STRIPE_LIVE ? _liveKey : _testKey;
+const WEAR_STRIPE_MODE = WEAR_STRIPE_LIVE ? 'live' : 'test';
 if (STRIPE_LIVE) console.log('⚠️  STRIPE LIVE MODE — murals charge REAL cards; downloads stay TEST'); else if (_testKey) console.log('Stripe test mode active (murals + downloads)');
+if (WEAR_STRIPE_LIVE) console.log('⚠️  WEAR STRIPE LIVE MODE — apparel charges REAL cards'); else if (_testKey) console.log('Wear apparel: Stripe test mode (independent of the murals live flag)');
 // ---- Google AdSense (Auto ads) ---------------------------------------------
 // Driven by ONE env var, ADSENSE_PUB_ID (stored as `pub-XXXXXXXXXXXXXXXX`; a
 // leading `ca-` is tolerated). When set, the loader is injected into every HTML
@@ -881,11 +894,13 @@ ${paid ? `<div class="ok">✓</div><h1>Order confirmed</h1>
       const podProvider = wearPodProvider();
       const podTemplates = await wearTemplates();
       if (!wearPodReady(podTemplates, podProvider)) return sendJSON(res, 503, { ok: false, error: 'apparel fulfillment is not configured' });
-      // Apparel is a physical POD sale — charge through the live-capable resolver
-      // (STRIPE_MURAL_KEY: live only when STRIPE_LIVE_ENABLED=1 AND a sk_live_ key
-      // is present, else the test key). NOT STRIPE_DOWNLOAD_KEY, which is test-only,
-      // so wear would never have charged a real card. One consistent live switch.
-      if (!STRIPE_MURAL_KEY) return sendJSON(res, 503, { ok: false, error: 'apparel not purchasable yet (awaiting Stripe key)' });
+      // Apparel is a physical POD sale — charge through wear's OWN live-capable
+      // resolver (WEAR_STRIPE_KEY: live only when WEAR_STRIPE_LIVE_ENABLED=1 AND
+      // a sk_live_ key is present, else the test key). Deliberately NOT
+      // STRIPE_MURAL_KEY — that flag is already live for an unrelated feature,
+      // so reusing it would let real charges start before Steve ever placed a
+      // test order through wear specifically.
+      if (!WEAR_STRIPE_KEY) return sendJSON(res, 503, { ok: false, error: 'apparel not purchasable yet (awaiting Stripe key)' });
       const b = await readBody(req);
       const email = String(b.email || '').trim();
       if (!/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email)) return sendJSON(res, 400, { ok: false, error: 'valid email required' });
@@ -921,7 +936,7 @@ ${paid ? `<div class="ok">✓</div><h1>Order confirmed</h1>
         // WEAR_POD_PROVIDER (or its readiness) changes before the customer lands
         // on /wear-success, the draft must still route to the provider that was
         // actually configured — and eligible — when the charge happened.
-        status: 'pending_payment', mode: STRIPE_MODE, provider: podProvider, pod_submitted: false };
+        status: 'pending_payment', mode: WEAR_STRIPE_MODE, provider: podProvider, pod_submitted: false };
       list.push(order); await store('wear-orders.json', list);
       const params = new URLSearchParams();
       params.set('mode', 'payment');
@@ -936,13 +951,13 @@ ${paid ? `<div class="ok">✓</div><h1>Order confirmed</h1>
         params.set(`line_items[${i}][price_data][currency]`, 'usd');
         params.set(`line_items[${i}][price_data][unit_amount]`, String(it.amountCents));
         params.set(`line_items[${i}][price_data][product_data][name]`, `${it.signature_name} signature — left-chest ${it.garment_label} (${it.color_label}, ${it.size})`);
-        params.set(`line_items[${i}][price_data][product_data][description]`, `Signature printed at the left chest; made to order.${STRIPE_MODE === 'test' ? ' TEST checkout.' : ''}`);
+        params.set(`line_items[${i}][price_data][product_data][description]`, `Signature printed at the left chest; made to order.${WEAR_STRIPE_MODE === 'test' ? ' TEST checkout.' : ''}`);
       });
       params.set('metadata[order_id]', String(id));
       try {
         const sres = await fetch('https://api.stripe.com/v1/checkout/sessions', {
           method: 'POST',
-          headers: { Authorization: `Bearer ${STRIPE_MURAL_KEY}`, 'Content-Type': 'application/x-www-form-urlencoded' },
+          headers: { Authorization: `Bearer ${WEAR_STRIPE_KEY}`, 'Content-Type': 'application/x-www-form-urlencoded' },
           body: params.toString(),
         });
         const sj = await sres.json();
@@ -954,9 +969,9 @@ ${paid ? `<div class="ok">✓</div><h1>Order confirmed</h1>
     if (path === '/wear-success' && M === 'GET') {
       const sid = url.searchParams.get('sid') || '';
       let paid = false, order = null;
-      if (STRIPE_MURAL_KEY && /^cs_(test|live)_[A-Za-z0-9]+$/.test(sid)) {
+      if (WEAR_STRIPE_KEY && /^cs_(test|live)_[A-Za-z0-9]+$/.test(sid)) {
         try {
-          const s = await (await fetch(`https://api.stripe.com/v1/checkout/sessions/${sid}`, { headers: { Authorization: `Bearer ${STRIPE_MURAL_KEY}` } })).json();
+          const s = await (await fetch(`https://api.stripe.com/v1/checkout/sessions/${sid}`, { headers: { Authorization: `Bearer ${WEAR_STRIPE_KEY}` } })).json();
           if (s.payment_status === 'paid') {
             paid = true;
             const list = await load('wear-orders.json', []);

← f2976c0 wear: use 'convert' not 'magick' — prod box only has ImageMa  ·  back to CelebritySignatures  ·  wear: fix recolored signature URL being relative, not absolu c9d72f3 →