// routes/configurePricing.js const express = require('express'); const axios = require('axios'); const { v4: uuid } = require('uuid'); const { getToken } = require('../tokenStore'); const { log } = require('../logger'); const router = express.Router(); const processes = {}; // POST /configure-pricing // body: { shop: string, priceType: "map" | "percentage", percentage: number } router.post('/', async (req, res) => { const { shop, priceType, percentage } = req.body; if (!shop) return res.status(400).json({ error: 'Missing `shop`' }); const procId = uuid(); processes[procId] = { status: 'started', detail: null }; log(shop, `๐Ÿ”” [${procId}] ConfigurePricing initiated (priceType=${priceType}, percentage=${percentage})`); res.json({ processId: procId, status: 'started' }); (async () => { try { processes[procId].status = 'preparing'; // 1) Get token const tokenRecord = getToken(shop); if (!tokenRecord) throw new Error('No token for shop'); const adminUrl = `https://${shop}/admin/api/2025-10/graphql.json`; const headers = { 'X-Shopify-Access-Token': tokenRecord.accessToken, 'Content-Type': 'application/json', }; log(shop, `๐Ÿ”‘ [${procId}] Using access token for shop ${shop}`); // 2) Fetch Shop GID processes[procId].status = 'fetching_shop'; const shopIdQuery = `{ shop { id name } }`; const shopIdResp = await axios.post(adminUrl, { query: shopIdQuery }, { headers }); const shopId = shopIdResp.data?.data?.shop?.id; if (!shopId) throw new Error(`Could not fetch shop id: ${JSON.stringify(shopIdResp.data)}`); log(shop, `๐Ÿท๏ธ [${procId}] Shop GID: ${shopId}`); // 3) Sanitize + build config JSON const normalizedType = (priceType || 'map').toString().toLowerCase(); const validType = ['map', 'percentage'].includes(normalizedType) ? normalizedType : 'map'; const rawPct = Number(percentage); const validPct = Number.isFinite(rawPct) ? rawPct : 0; const cfg = { priceType: validType, percentage: validPct }; const cfgValue = JSON.stringify(cfg); log(shop, `๐Ÿงพ [${procId}] Saving pricing_config: ${cfg.priceType}/${cfg.percentage}%`); // 4) Save metafield (inline GraphQL, double-stringify value to embed JSON safely) processes[procId].status = 'saving_metafield'; const resp = await axios.post(adminUrl, { query: ` mutation { metafieldsSet(metafields: [{ namespace: "turn14", key: "pricing_config", type: "json", ownerId: "${shopId}", value: ${JSON.stringify(cfgValue)} }]) { metafields { id key } userErrors { field message } } } ` }, { headers }); const errs = resp.data?.data?.metafieldsSet?.userErrors || []; if (errs.length) { log(shop, `โš ๏ธ [${procId}] metafieldsSet errors: ${JSON.stringify(errs)}`); processes[procId].status = 'error'; processes[procId].detail = JSON.stringify(errs); return; } log(shop, `โœ… [${procId}] pricing_config metafield saved`); processes[procId].status = 'done'; processes[procId].detail = null; } catch (err) { processes[procId].status = 'error'; processes[procId].detail = err.message; log(shop, `โŒ [${procId}] Error: ${err.message}`); } })(); }); // Optional: check status (same shape as manageBrands) router.get('/status/:processId', (req, res) => { const info = processes[req.params.processId]; if (!info) return res.status(404).json({ error: 'Not found' }); res.json(info); }); module.exports = router;