const axios = require('axios'); const { log } = require('./logger'); /** * Create (or retrieve existing) fulfillment service + location for a shop. * Returns { fulfillmentService, locationId } — locationId is NEVER null if a * fulfillment service exists, because we fall back to the service's own location. */ async function createFulfillmentService(shop, accessToken) { const client = axios.create({ baseURL: `https://${shop}/admin/api/2025-10/graphql.json`, headers: { 'X-Shopify-Access-Token': accessToken, 'Content-Type': 'application/json', }, }); try { log(shop, `🚀 [FulfillmentService] Creating fulfillment service...`); // ── Step 1: Create fulfillment service (or retrieve existing) ──────────── const createResp = await client.post('', { query: ` mutation { fulfillmentServiceCreate( name: "Data4Autos Distribution", callbackUrl: "https://backend.data4autos.com/fulfillment" ) { fulfillmentService { id serviceName callbackUrl handle location { id name } } userErrors { field message } } } `, }); let fulfillmentService = createResp.data?.data?.fulfillmentServiceCreate?.fulfillmentService; const createErrors = createResp.data?.data?.fulfillmentServiceCreate?.userErrors || []; if (createErrors.length > 0) { log(shop, `⚠️ [FulfillmentService] Create errors: ${JSON.stringify(createErrors)}`); // Service likely already exists — query it instead log(shop, `🔍 [FulfillmentService] Querying existing fulfillment service...`); const existingResp = await client.post('', { query: ` query { fulfillmentServices(type: THIRD_PARTY) { id serviceName callbackUrl handle location { id name } } } `, }); const services = existingResp.data?.data?.fulfillmentServices || []; fulfillmentService = services.find(s => s.handle === 'data4autos-distribution') || services[0] || null; if (!fulfillmentService) { log(shop, `❌ [FulfillmentService] Could not create or find existing service`); return { fulfillmentService: null, locationId: null }; } log(shop, `✅ [FulfillmentService] Found existing: ${fulfillmentService.id}`); } else { log(shop, `✅ [FulfillmentService] Created: ${fulfillmentService.id}`); } // ── Step 2: Get the shop's primary location ID ─────────────────────────── // Shopify never returns a location from fulfillmentServiceCreate — use the // shop's first active location (the physical/online store location) instead. let locationId = null; try { const locResp = await client.post('', { query: `query { locations(first: 1, includeLegacy: true) { nodes { id name isActive } } }`, }); const locNode = locResp.data?.data?.locations?.nodes?.[0]; if (locNode?.id) { locationId = locNode.id; log(shop, `📍 [FulfillmentService] Primary shop location: "${locNode.name}" → ${locationId}`); } else { log(shop, `⚠️ [FulfillmentService] No locations returned from Shopify`); } } catch (locErr) { log(shop, `⚠️ [FulfillmentService] Could not fetch shop location: ${locErr.message}`); } log(shop, `📍 [FulfillmentService] Final locationId: ${locationId}`); return { fulfillmentService, locationId }; } catch (error) { const errDetail = error.response ? JSON.stringify(error.response.data) : error.message; log(shop, `💥 [FulfillmentService] Request failed: ${errDetail}`); return { fulfillmentService: null, locationId: null }; } } module.exports = { createFulfillmentService };