// Shared authentication and GID utilities for shopify-new (GraphQL Admin API v2025-10) export async function getShopifyNewAccessToken(os: any): Promise<{ accessToken: string, host: string, graphqlUrl: string }> { // Normalize shop URL let host = (os.marketplace_url || '').trim() if (!host) { throw new Error('No marketplace_url configured for this Order Source') } if (!/^https?:\/\//i.test(host)) host = `https://${host}` host = host.replace(/\/$/, '') if (!host.includes('.myshopify.com') && !host.includes('/admin')) { const shopName = host.replace(/^https?:\/\//i, '').split('.')[0] host = `https://${shopName}.myshopify.com` } const graphqlUrl = `${host}/admin/api/2025-10/graphql.json` let accessToken = '' // Try Client Credentials Grant first (preferred for shopify-new) const key = os.marketplace_key const secret = os.marketplace_secret if (key && secret) { try { const tokenRes: any = await $fetch(`${host}/admin/oauth/access_token`, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: `grant_type=client_credentials&client_id=${encodeURIComponent(key)}&client_secret=${encodeURIComponent(secret)}` }) if (tokenRes?.access_token) { accessToken = tokenRes.access_token console.log('shopify-new: Authenticated via Client Credentials Grant') } } catch (err: any) { console.log('shopify-new: Client Credentials Grant failed, falling back to token:', err?.message) } } // Fallback to stored token if (!accessToken) { accessToken = os.marketplace_token || '' } if (!accessToken) { throw new Error('No access token available for shopify-new. Configure marketplace_key + marketplace_secret, or marketplace_token.') } return { accessToken, host, graphqlUrl } } // Execute a GraphQL query against the Shopify Admin API export async function shopifyNewGraphQL(graphqlUrl: string, accessToken: string, query: string, variables: Record = {}): Promise { const res: any = await $fetch(graphqlUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Shopify-Access-Token': accessToken }, body: { query, variables } }) // Check for GraphQL-level errors if (res?.errors && res.errors.length > 0) { const errMsg = res.errors.map((e: any) => e.message).join('; ') throw new Error(`Shopify GraphQL error: ${errMsg}`) } return res } // Extract numeric ID from Shopify GID: "gid://shopify/Product/12345" -> "12345" export function parseShopifyGid(gid: string): string { if (!gid) return '' const parts = String(gid).split('/') return parts[parts.length - 1] } // Build a GID from type and numeric ID: ("Location", "12345") -> "gid://shopify/Location/12345" export function buildShopifyGid(type: string, id: string | number): string { if (String(id).startsWith('gid://')) return String(id) return `gid://shopify/${type}/${id}` }