const axios = require("axios"); const { log } = require("./logger"); const getLocationQuery = ` query { locations(first: 1, query: "name:'Shop location'") { nodes { id name address { address1 address2 city province provinceCode country countryCode zip phone } } } } `; async function getStoreAddress(client) { const response = await client.post("", { query: getLocationQuery }); const location = response.data?.data?.locations?.nodes?.[0]; return location?.address || null; } function createLocationMutation(address) { return ` mutation { locationAdd(input: { name: "(App) Race Nation Distribution" address: { address1: "${address?.address1 || ""}" address2: "${address?.address2 || ""}" city: "${address?.city || ""}" provinceCode: "${address?.provinceCode || "ON"}" countryCode: ${JSON.stringify(address?.countryCode || "US")} zip: "${address?.zip || ""}" phone: "${address?.phone || ""}" } fulfillsOnlineOrders: true }) { location { id name } userErrors { code field message } } } `; } async function createCustomLocation(address, client) { const response = await client.post("", { query: createLocationMutation(address) }); return response.data?.data?.locationAdd || { location: null, userErrors: [] }; } 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", }, }); const mutation = ` mutation { fulfillmentServiceCreate( name: "Race Nation Distribution" callbackUrl: "https://backend.race-nation.com/fulfillment" ) { fulfillmentService { id serviceName callbackUrl handle location { id } } userErrors { field message } } } `; try { log(shop, "Creating fulfillment service..."); const response = await client.post("", { query: mutation }); const data = response.data?.data?.fulfillmentServiceCreate; let fulfillmentService = data?.fulfillmentService || null; let locationId = fulfillmentService?.location?.id || null; if (data?.userErrors?.length) { // Service already exists — query it to get its locationId log(shop, `Fulfillment service already exists, querying existing...`); try { const existingResp = await client.post("", { query: `{ fulfillmentServices { id serviceName location { id name } } }`, }); const existing = existingResp.data?.data?.fulfillmentServices || []; const ours = existing.find((s) => s.serviceName === "Race Nation Distribution") || existing[0]; if (ours?.location?.id) { locationId = ours.location.id; fulfillmentService = ours; log(shop, `Found existing fulfillment service location: ${ours.location.name} (${locationId})`); } } catch (queryErr) { log(shop, `Could not query existing fulfillment services: ${queryErr.message}`); } } if (!locationId) { // Last resort: try to get shop's primary location try { const locResp = await client.post("", { query: `{ locations(first: 1) { nodes { id name } } }`, }); locationId = locResp.data?.data?.locations?.nodes?.[0]?.id || null; if (locationId) log(shop, `Using primary store location: ${locationId}`); } catch {} } if (!locationId && data?.userErrors?.length) { return { success: false, errors: data.userErrors }; } const address = await getStoreAddress(client).catch(() => null); if (address && !locationId) { const customLocation = await createCustomLocation(address, client); if (!customLocation?.userErrors?.length && customLocation?.location?.id) { locationId = customLocation.location.id; } } return { success: true, fulfillmentService, locationId, }; } catch (error) { log(shop, `Fulfillment service request failed: ${error.response ? JSON.stringify(error.response.data) : error.message}`); return { success: false, error: error.response ? error.response.data : error.message, }; } } module.exports = { createFulfillmentService, };