← back to Dw Vendor Microsites
sample flow: wire Shopify Storefront checkout (buybutton.js + config route + submit branch); falls back to no-payment request until token+sample-variant set
3da6af6f35bcd8cd81da04ff3497bc0e789216af · 2026-06-30 14:20:52 -0700 · Steve
Files touched
A template/public/buybutton.jsM template/public/index.htmlM template/server.js
Diff
commit 3da6af6f35bcd8cd81da04ff3497bc0e789216af
Author: Steve <steve@designerwallcoverings.com>
Date: Tue Jun 30 14:20:52 2026 -0700
sample flow: wire Shopify Storefront checkout (buybutton.js + config route + submit branch); falls back to no-payment request until token+sample-variant set
---
template/public/buybutton.js | 70 ++++++++++++++++++++++++++++++++++++++++++++
template/public/index.html | 9 ++++++
template/server.js | 7 ++++-
3 files changed, 85 insertions(+), 1 deletion(-)
diff --git a/template/public/buybutton.js b/template/public/buybutton.js
new file mode 100644
index 0000000..f479e9a
--- /dev/null
+++ b/template/public/buybutton.js
@@ -0,0 +1,70 @@
+/* Shopify sample checkout for the DW vendor microsites.
+ *
+ * These vendor lines are OFF Shopify (no per-product Shopify variant), so "Request
+ * Sample" cannot add a per-product variant. Instead it uses ONE generic "Memo Sample"
+ * Shopify product/variant and adds it to a real Shopify cart once per requested swatch,
+ * carrying each swatch's pattern/color/SKU/vendor as line-item custom properties, then
+ * opens Shopify's secure hosted checkout. The order line-items tell fulfillment which
+ * swatches to pull.
+ *
+ * Config is injected by the server at /config.js as window.DWV_CFG = {
+ * domain: '<store>.myshopify.com', storefrontToken: '<token>', sampleVariantId: '<gid or numeric>'
+ * }. Until storefrontToken + sampleVariantId are set, this falls back to the no-payment
+ * JSONL sample-request endpoint (POST /api/sample-request) so the button always works.
+ */
+(function () {
+ var CFG = window.DWV_CFG || {};
+ var ready = CFG.storefrontToken && CFG.sampleVariantId && CFG.domain;
+ var client = null, checkoutId = null;
+
+ function loadSDK(cb) {
+ if (window.ShopifyBuy && window.ShopifyBuy.buildClient) return cb();
+ var s = document.createElement('script');
+ s.src = 'https://sdks.shopifycdn.com/js-buy-sdk/latest/index.umd.min.js';
+ s.onload = cb; s.onerror = function () { ready = false; cb(); };
+ document.head.appendChild(s);
+ }
+
+ function ensureClient(cb) {
+ if (client) return cb();
+ loadSDK(function () {
+ if (!window.ShopifyBuy || !ready) { ready = false; return cb(); }
+ client = window.ShopifyBuy.buildClient({ domain: CFG.domain, storefrontAccessToken: CFG.storefrontToken });
+ client.checkout.create().then(function (co) { checkoutId = co.id; cb(); }).catch(function () { ready = false; cb(); });
+ });
+ }
+
+ // items: [{sku, mfr_sku, pattern_name, color_name, line}] -> Shopify checkout
+ function shopifyCheckout(items, contact) {
+ return new Promise(function (resolve, reject) {
+ ensureClient(function () {
+ if (!ready || !client || !checkoutId) return reject(new Error('shopify-unavailable'));
+ var lines = items.slice(0, 250).map(function (it) {
+ return {
+ variantId: CFG.sampleVariantId,
+ quantity: 1,
+ customAttributes: [
+ { key: 'Pattern', value: String(it.pattern_name || it.sku || '') },
+ { key: 'Color', value: String(it.color_name || '') },
+ { key: 'SKU', value: String(it.mfr_sku || it.sku || '') },
+ { key: 'Line', value: String(it.line || '') }
+ ]
+ };
+ });
+ var attrs = [];
+ if (contact && contact.name) attrs.push({ key: 'Requested by', value: contact.name });
+ var p = client.checkout.addLineItems(checkoutId, lines);
+ p.then(function (co) {
+ if (attrs.length) return client.checkout.updateAttributes(checkoutId, { customAttributes: attrs }).then(function () { return co; });
+ return co;
+ }).then(function (co) { window.location.href = co.webUrl; resolve(co); }).catch(reject);
+ });
+ });
+ }
+
+ // Expose for the viewer's sample modal. Returns true if Shopify path is wired.
+ window.DWV_SAMPLE = {
+ available: function () { return !!ready; },
+ checkout: shopifyCheckout
+ };
+})();
diff --git a/template/public/index.html b/template/public/index.html
index 00101ee..ce0d82b 100644
--- a/template/public/index.html
+++ b/template/public/index.html
@@ -365,6 +365,11 @@ $('#reqform').addEventListener('submit',async e=>{
const payload={items:cart};
for(const [k,v] of fd.entries()) payload[k]=v;
const btn=$('#submitbtn'); btn.disabled=true; btn.textContent='Submitting…';
+ // Shopify checkout path (real sample order) when wired; else no-payment request below.
+ if(window.DWV_SAMPLE && window.DWV_SAMPLE.available()){
+ try{ btn.textContent='Opening secure checkout…'; await window.DWV_SAMPLE.checkout(cart,{name:payload.name,email:payload.email}); return; }
+ catch(err){ /* fall through to the no-payment request flow */ }
+ }
try{
const r=await fetch('/api/sample-request',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(payload)});
const d=await r.json();
@@ -387,6 +392,10 @@ $('#reqform').addEventListener('submit',async e=>{
$('#brand').innerHTML = esc(cfg.title)+'<small>Designer Wallcoverings</small>';
NS = 'dwvm.'+cfg.title.replace(/[^a-z0-9]+/gi,'_').toLowerCase();
}
+ window.DWV_CFG = cfg.shopify || {};
+ if(window.DWV_CFG.storefrontToken && window.DWV_CFG.sampleVariantId){
+ await new Promise(res=>{const s=document.createElement('script');s.src='/buybutton.js';s.onload=res;s.onerror=res;document.head.appendChild(s);});
+ }
}catch{}
sort = LS.get('sort','newest');
density = LS.get('density',5);
diff --git a/template/server.js b/template/server.js
index 51f28e7..9904dbf 100644
--- a/template/server.js
+++ b/template/server.js
@@ -319,7 +319,12 @@ const server = http.createServer((req, res) => {
const u = new URL(req.url, 'http://localhost');
if (u.pathname === '/api/config') {
- return json(res, 200, { title: TITLE, samples_only: SAMPLES_ONLY, total: ROWS.length });
+ return json(res, 200, { title: TITLE, samples_only: SAMPLES_ONLY, total: ROWS.length,
+ shopify: {
+ domain: process.env.SHOPIFY_STORE_DOMAIN || 'designer-laboratory-sandbox.myshopify.com',
+ storefrontToken: process.env.SHOPIFY_STOREFRONT_TOKEN || '',
+ sampleVariantId: process.env.SHOPIFY_SAMPLE_VARIANT_ID || ''
+ } });
}
if (u.pathname === '/api/facets') {
← 4946bd5 exclude armani_casa — Steve: DO NOT SHOW; took down live sit
·
back to Dw Vendor Microsites
·
exclude spoonflower — marketplace scrape, not Steve's own de bade609 →