← back to Dw Signup Fulfillment
retail: FINAL = gift card (Steve, memo §2) — re-wire webhook+admin+selftest to giftcard, shared-code demoted to alternate; preserves trade.js customer-id hardening
f385a171ec7c944eff746c25277722b5ccac0ead · 2026-07-28 10:34:18 -0700 · Steve
Files touched
M scripts/selftest.jsM server.js
Diff
commit f385a171ec7c944eff746c25277722b5ccac0ead
Author: Steve <steve@designerwallcoverings.com>
Date: Tue Jul 28 10:34:18 2026 -0700
retail: FINAL = gift card (Steve, memo §2) — re-wire webhook+admin+selftest to giftcard, shared-code demoted to alternate; preserves trade.js customer-id hardening
---
scripts/selftest.js | 51 +++++++++++++++++++++------------------------------
server.js | 27 ++++++++++++++-------------
2 files changed, 35 insertions(+), 43 deletions(-)
diff --git a/scripts/selftest.js b/scripts/selftest.js
index 78eea4e..d4a714b 100644
--- a/scripts/selftest.js
+++ b/scripts/selftest.js
@@ -53,8 +53,8 @@ function restore() {
const config = require('../lib/config');
const webhook = require('../lib/webhook');
-const retailCode = require('../lib/retail-code'); // WIRED retail path (DTD path B)
-const giftcard = require('../lib/giftcard'); // alternate (not wired)
+const giftcard = require('../lib/giftcard'); // WIRED retail path (FINAL — gift card, memo §2)
+const retailCode = require('../lib/retail-code'); // alternate (shared function code — needs admin discount)
const trade = require('../lib/trade');
const reps = require('../lib/reps');
@@ -66,49 +66,40 @@ let failures = 0;
async function main() {
console.log('DW signup fulfillment — SELFTEST (DRY_RUN=' + config.DRY_RUN + ')');
console.log('Store: ' + config.SHOP_DOMAIN + ' API ' + config.SHOPIFY_API_VERSION);
- console.log('Retail path (WIRED): sample-locked SHARED code (first ' + config.FREE_SAMPLE_COUNT + ' sample units free, function-enforced) · RETAIL_SHARED_CODE=' + (config.RETAIL_SHARED_CODE || '(unset — will WARN+skip send)'));
+ console.log('Retail path (WIRED): unique GIFT CARD (' + config.FREE_SAMPLE_COUNT + ' × $' + config.SAMPLE_PRICE + ' = $' + config.SAMPLE_GIFT_VALUE + ') emailed per signup');
if (!config.DRY_RUN) { fail('DRY_RUN is OFF — refusing to run selftest that would make live writes'); return; }
// ---------------------------------------------------------------------------
- hr('(a) customers/create webhook — VALID HMAC → WIRED sample-locked shared-code path');
+ hr('(a) customers/create webhook — VALID HMAC → WIRED gift-card path');
const fakeCustomer = { id: 8675309, email: 'newshopper@example.com', first_name: 'Dana', created_at: new Date().toISOString() };
const raw = Buffer.from(JSON.stringify(fakeCustomer), 'utf8');
const goodHmac = webhook.sign(raw, TEST_SECRET);
console.log(' computed X-Shopify-Hmac-Sha256 = ' + goodHmac);
if (webhook.verify(raw, goodHmac)) ok('HMAC verify ACCEPTED the valid signature'); else fail('valid HMAC was rejected');
- console.log(' --- WIRED retail issuance (shared sample-locked code, what it WOULD do) ---');
- const rcResult = await retailCode.issueRetailCode(fakeCustomer);
- console.log(' result: ' + JSON.stringify(rcResult, null, 2));
- if (rcResult.path === 'shared_code') ok('retail path is shared_code (sample-locked function discount — never touches a roll)'); else fail('retail path is not shared_code: ' + rcResult.path);
- if (rcResult.code === TEST_SHARED_CODE) ok('emits the configured RETAIL_SHARED_CODE (' + rcResult.code + ')'); else fail('unexpected code: ' + rcResult.code);
- if (rcResult.warn === null) ok('no WARN when RETAIL_SHARED_CODE is set'); else fail('unexpected warn: ' + rcResult.warn);
- if (rcResult.email && rcResult.email.dryRun && !rcResult.email.skipped) ok('WOULD email the code to the customer (dry-run, not skipped)'); else fail('code email not dry-run / was skipped');
-
- console.log(' --- fail-loud guard: unset RETAIL_SHARED_CODE must WARN + skip send ---');
- const savedCode = process.env.RETAIL_SHARED_CODE; delete process.env.RETAIL_SHARED_CODE;
- delete require.cache[require.resolve('../lib/config')]; delete require.cache[require.resolve('../lib/retail-code')];
- const retailCodeUnset = require('../lib/retail-code');
- const rcUnset = await retailCodeUnset.issueRetailCode(fakeCustomer);
- if (rcUnset.warn && rcUnset.email.skipped) ok('unset code → WARN + email SKIPPED (never emails a wrong/blank code)'); else fail('unset code did not warn+skip');
- if (savedCode != null) process.env.RETAIL_SHARED_CODE = savedCode;
- delete require.cache[require.resolve('../lib/config')]; delete require.cache[require.resolve('../lib/retail-code')];
-
- console.log(' --- alternate (NOT wired) gift-card path still functions for reference ---');
+ console.log(' --- WIRED retail issuance (gift card, what it WOULD do) ---');
const gcResult = await giftcard.issueRetailGiftCode(fakeCustomer);
- if (gcResult.path === 'gift_card' && gcResult.value === 12.75) ok('alternate gift_card path OK ($12.75) — kept for reference only'); else fail('alternate gift-card path broken');
+ console.log(' result: ' + JSON.stringify(gcResult, null, 2));
+ if (gcResult.path === 'gift_card') ok('retail path is gift_card (unique code emailed per signup)'); else fail('retail path is not gift_card: ' + gcResult.path);
+ if (gcResult.value === 12.75) ok('gift value = 3 × $4.25 = $12.75'); else fail('unexpected gift value: ' + gcResult.value);
+ const would = gcResult.shopifyCall && gcResult.shopifyCall.WOULD;
+ if (would && /gift_cards/.test(would)) ok('WOULD POST gift_cards (' + would + ')'); else fail('did not record a WOULD gift_cards call');
+ if (gcResult.email && gcResult.email.dryRun) ok('WOULD email the gift code to the customer (dry-run, no real send)'); else fail('gift email not dry-run');
+
+ console.log(' --- gift email clearly offers the 3 free samples ---');
+ const gtpl = require('../lib/email').retailGiftEmail({ firstName: 'Dana', code: 'DEMO-CODE-1234', value: 12.75, count: 3 });
+ const blob = (gtpl.subject || '') + ' ' + (gtpl.html || '');
+ if (/\b3\b/.test(blob) && /sample/i.test(blob)) ok('email references "3" and "sample"'); else fail('email does not clearly offer 3 free samples');
+ if (/DEMO-CODE-1234/.test(blob)) ok('email includes the gift code'); else fail('email missing the code');
+
+ console.log(' --- alternate (NOT wired) shared-code path still shape-checks ---');
+ const rcResult = await retailCode.issueRetailCode(fakeCustomer);
+ if (rcResult.path === 'shared_code') ok('alternate shared_code path present — kept for reference only'); else fail('alternate shared-code path broken: ' + rcResult.path);
// ---------------------------------------------------------------------------
hr('(b) customers/create webhook — INVALID HMAC → rejected');
if (!webhook.verify(raw, 'this-is-not-the-right-signature')) ok('HMAC verify REJECTED a bad signature'); else fail('bad HMAC was accepted');
- // ---------------------------------------------------------------------------
- hr('(a2) WIRED code email clearly offers the 3 free samples + includes the code');
- const gtpl = require('../lib/email').retailCodeEmail({ firstName: 'Dana', code: 'DEMO-CODE-1234', count: 3 });
- const blob = (gtpl.subject || '') + ' ' + (gtpl.html || '');
- if (/\b3\b/.test(blob) && /sample/i.test(blob)) ok('email references "3" and "sample"'); else fail('email does not clearly offer 3 free samples');
- if (/DEMO-CODE-1234/.test(blob)) ok('email includes the sample code'); else fail('email missing the code');
-
// ---------------------------------------------------------------------------
hr('(c) trade application → moderated approve');
const created = trade.apply({ email: 'Studio@BigDesignCo.com', business_name: 'Big Design Co', resale_cert: 'CA-RESALE-99887', phone: '310-555-0142', shopify_customer_id: 5551234 });
diff --git a/server.js b/server.js
index 16fd759..e141bc5 100644
--- a/server.js
+++ b/server.js
@@ -11,8 +11,8 @@
const express = require('express');
const config = require('./lib/config');
const webhook = require('./lib/webhook');
-const retailCode = require('./lib/retail-code'); // WIRED default: sample-locked shared function code (memo §2, DTD path B)
-const giftcard = require('./lib/giftcard'); // alternate (not wired — spendable on rolls)
+const giftcard = require('./lib/giftcard'); // WIRED default (FINAL — Steve, memo §2): unique gift-card code per signup
+const retailCode = require('./lib/retail-code'); // alternate (shared function code — needs a DW Free Samples admin discount)
const giftcodeDiscount = require('./lib/giftcode-discount'); // alternate (not wired)
const trade = require('./lib/trade');
const reps = require('./lib/reps');
@@ -60,11 +60,13 @@ app.post('/webhooks/customers/create',
res.status(200).json({ ok: true, received: true });
try {
console.log(`[webhook] customers/create id=${customer.id} email=${customer.email}`);
- // WIRED retail path (DTD 2026-07-28, 5/5 → path B): email the new customer the
- // SAMPLE-LOCKED shared "DW Free Samples" code (function-backed, never discounts a
- // roll). Matches approved memo §2. Gift-card path retained as alternate only.
- const result = await retailCode.issueRetailCode(customer);
- console.log('[webhook] retail shared-code result:', JSON.stringify(result));
+ // WIRED retail path (FINAL — Steve 2026-07-28, memo §2): email the new customer a
+ // unique ~$12.75 gift-card code for their 3 free samples. Works with our token, no
+ // admin discount needed, risk bounded to the balance. The sample-locked shared-code
+ // path is retained as a labeled alternate (it needs a DW Free Samples admin discount
+ // that was never created — only a stray "3FREE" basic discount existed).
+ const result = await giftcard.issueRetailGiftCode(customer);
+ console.log('[webhook] retail gift-card result:', JSON.stringify(result));
} catch (e) {
console.error('[webhook] fulfillment error:', e.message);
}
@@ -131,17 +133,16 @@ app.post('/admin/trade/:id/reject', adminAuth, async (req, res) => {
app.get('/reps/next', adminAuth, (_req, res) => res.json({ assigned: reps.houseAccount() }));
// Manual retail trigger (admin) — handy for go-live smoke test without a real
-// webhook. Default mirrors the WIRED sample-locked shared-code path (DTD path B).
-// ALTERNATES for comparison only: ?mode=giftcard (stored value) and ?mode=discount
-// (collection code).
+// webhook. Default mirrors the WIRED gift-card path (FINAL, memo §2). ALTERNATES for
+// comparison only: ?mode=sharedcode (function code) and ?mode=discount (collection code).
app.post('/admin/retail/issue', adminAuth, async (req, res) => {
const customer = req.body || {};
if (!customer.email) return res.status(400).json({ ok: false, error: 'email required' });
- let mode = req.query.mode || 'sharedcode';
+ let mode = req.query.mode || 'giftcard';
let result;
- if (mode === 'giftcard') result = await giftcard.issueRetailGiftCode(customer); // alternate (spendable on rolls)
+ if (mode === 'sharedcode') result = await retailCode.issueRetailCode(customer); // alternate (needs admin discount)
else if (mode === 'discount') result = await giftcodeDiscount.issueRetailDiscountCode(customer); // alternate
- else { mode = 'sharedcode'; result = await retailCode.issueRetailCode(customer); } // WIRED default
+ else { mode = 'giftcard'; result = await giftcard.issueRetailGiftCode(customer); } // WIRED default
res.json({ ok: true, mode, result });
});
← 379738e harden per contrarian gate: resolve trade customer-id by ema
·
back to Dw Signup Fulfillment
·
hosting: live on Kamatera dwsignup.agentabrams.com (pm2 :986 766b6b6 →