← back to Sample Box
step 2: server.js — add Shopify catalog endpoints, Shopify-hosted order capture
931b363d2a4e0e0779a73dd2bd7c1f81b07c5ce6 · 2026-07-16 07:37:18 -0700 · Steve Abrams
New endpoints (read-only, no token):
- GET /api/catalog-meta → checkout_domain + sample_variant_id
- GET /api/shopify-products?sort=&q= → 200 real products from shopify-catalog.json
POST /api/orders now records payment_method=SHOPIFY_HOSTED,
payment_status=pending_shopify, and shopify_permalink=
https://www.designerwallcoverings.com/cart/<sample_variant_id>:<qty>
Stripe TEST_STUB references removed from order flow.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Files touched
Diff
commit 931b363d2a4e0e0779a73dd2bd7c1f81b07c5ce6
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Jul 16 07:37:18 2026 -0700
step 2: server.js — add Shopify catalog endpoints, Shopify-hosted order capture
New endpoints (read-only, no token):
- GET /api/catalog-meta → checkout_domain + sample_variant_id
- GET /api/shopify-products?sort=&q= → 200 real products from shopify-catalog.json
POST /api/orders now records payment_method=SHOPIFY_HOSTED,
payment_status=pending_shopify, and shopify_permalink=
https://www.designerwallcoverings.com/cart/<sample_variant_id>:<qty>
Stripe TEST_STUB references removed from order flow.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---
server.js | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 61 insertions(+), 5 deletions(-)
diff --git a/server.js b/server.js
index 5285ab5..a31e463 100644
--- a/server.js
+++ b/server.js
@@ -13,6 +13,16 @@ const DATA_DIR = path.join(DIR, 'data');
const ORDERS_FILE = path.join(DATA_DIR, 'orders.json');
const BOX_PRICE = 15.00; // $15 sample box, fully credited to first roll order
+// ── Shopify catalog (read-only static file — no token in server) ──────────────
+const SHOPIFY_CATALOG_FILE = path.join(DATA_DIR, 'shopify-catalog.json');
+let _shopifyCatalog = null;
+function loadShopifyCatalog() {
+ if (_shopifyCatalog) return _shopifyCatalog;
+ try { _shopifyCatalog = JSON.parse(fs.readFileSync(SHOPIFY_CATALOG_FILE, 'utf8')); }
+ catch { _shopifyCatalog = { products: [], checkout_domain: '', sample_variant_id: '', count: 0 }; }
+ return _shopifyCatalog;
+}
+
// ── Data helpers ──────────────────────────────────────────────────────────────
function loadProducts() {
try { return JSON.parse(fs.readFileSync(path.join(DATA_DIR, 'products.json'), 'utf8')); }
@@ -114,6 +124,44 @@ http.createServer(async (req, res) => {
return res.end(JSON.stringify({ vibes: ALL_VIBES.filter(v => vibeSet.has(v.id)) }));
}
+ // GET /api/catalog-meta — returns checkout_domain + sample_variant_id (public, no token)
+ if (u.pathname === '/api/catalog-meta' && mt === 'GET') {
+ const cat = loadShopifyCatalog();
+ res.writeHead(200, { 'Content-Type': 'application/json' });
+ return res.end(JSON.stringify({
+ checkout_domain: cat.checkout_domain || '',
+ sample_variant_id: cat.sample_variant_id || '',
+ pulled_at: cat.pulled_at || '',
+ shop_domain: cat.shop_domain || '',
+ }));
+ }
+
+ // GET /api/shopify-products?sort=newest&q= — real catalog from shopify-catalog.json
+ if (u.pathname === '/api/shopify-products' && mt === 'GET') {
+ const cat = loadShopifyCatalog();
+ let products = (cat.products || []).slice();
+ const q = (u.searchParams.get('q') || '').toLowerCase().trim();
+ if (q) {
+ products = products.filter(p =>
+ (p.title||'').toLowerCase().includes(q) ||
+ (p.vendor||'').toLowerCase().includes(q) ||
+ (p.sku||'').toLowerCase().includes(q) ||
+ (p.type||'').toLowerCase().includes(q)
+ );
+ }
+ const sortMode = u.searchParams.get('sort') || 'newest';
+ switch (sortMode) {
+ case 'title': products.sort((a,b) => (a.title||'').localeCompare(b.title||'')); break;
+ case 'sku': products.sort((a,b) => (a.sku||'').localeCompare(b.sku||'')); break;
+ case 'price-asc': products.sort((a,b) => (a.price||0) - (b.price||0)); break;
+ case 'price-desc': products.sort((a,b) => (b.price||0) - (a.price||0)); break;
+ case 'vendor': products.sort((a,b) => (a.vendor||'').localeCompare(b.vendor||'')); break;
+ default: break; // newest = natural catalog order
+ }
+ res.writeHead(200, { 'Content-Type': 'application/json' });
+ return res.end(JSON.stringify({ count: products.length, products }));
+ }
+
// POST /api/orders — place a TEST order (no real charge)
if (u.pathname === '/api/orders' && mt === 'POST') {
const body = await readBody(req);
@@ -125,25 +173,33 @@ http.createServer(async (req, res) => {
}
const orderId = 'SB-' + Date.now().toString(36).toUpperCase();
+ // Build Shopify cart permalink using sample_variant_id × swatch count
+ const cat = loadShopifyCatalog();
+ const checkoutDomain = cat.checkout_domain || 'www.designerwallcoverings.com';
+ const sampleVariantId = cat.sample_variant_id || '';
+ const swatchQty = swatches.length;
+ const shopifyPermalink = sampleVariantId
+ ? `https://${checkoutDomain}/cart/${sampleVariantId}:${swatchQty}`
+ : null;
const order = {
id: orderId,
vibe: vibe || 'all',
- swatches, // [{sku,title,image,price}]
+ swatches, // [{sku,title,image,price,variant_id}]
swatch_count: swatches.length,
box_price: BOX_PRICE,
credit_note: `$${BOX_PRICE.toFixed(2)} credited toward your first roll order (ref: ${orderId})`,
email,
shipping, // {name,address1,address2,city,state,zip,country}
- payment_method: payment_stub ? 'TEST_STUB' : 'STRIPE_TEST',
- payment_status: 'paid_test',
- stripe_session_id: payment_stub ? null : ('cs_test_' + crypto.randomBytes(12).toString('hex')),
+ payment_method: 'SHOPIFY_HOSTED',
+ payment_status: 'pending_shopify',
+ shopify_permalink: shopifyPermalink,
status: 'pending_fulfillment',
created_at: new Date().toISOString(),
};
saveOrder(order);
res.writeHead(201, { 'Content-Type': 'application/json' });
- return res.end(JSON.stringify({ success: true, order_id: orderId, order }));
+ return res.end(JSON.stringify({ success: true, order_id: orderId, order, shopify_permalink: shopifyPermalink }));
}
// ── Admin (gated) ───────────────────────────────────────────────────────────
← 5e7cf3b step 1: copy real Shopify catalog (200 products, checkout_do
·
back to Sample Box
·
step 3: index.html — real Shopify catalog swatch grid + Shop 936c9a8 →