{
  "updatedAt": "2026-07-01T00:04:25.494Z",
  "createdAt": "2026-06-30T08:01:32.058Z",
  "id": "Vf2zXp84NiBfbXmz",
  "name": "\ucfe0\ud321 \uc140\ub7ec \uc790\ub3d9\ud654 - \ucfe0\ud321 \uc1a1\uc7a5\uc5c5\ub85c\ub4dc (\uc0c1\ud488\uc900\ube44\uc911\u2192\ubc30\uc1a1\uc9c0\uc2dc)",
  "description": null,
  "active": true,
  "isArchived": false,
  "nodes": [
    {
      "parameters": {},
      "id": "866e2a22-4b84-4e00-931f-fc875d2bd23a",
      "name": "Manual Trigger",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        -208,
        208
      ]
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "50 * * * *"
            }
          ]
        }
      },
      "id": "7aa8440b-a121-4428-aace-822a2dffbd63",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.3,
      "position": [
        -208,
        432
      ]
    },
    {
      "parameters": {
        "endpoint": "={{ $env.GRAPHQL_URL }}",
        "query": "query Plugins { seller: plugin(id: \"plugin.seller_api\") { configuration { name value } } }",
        "headerParametersUi": {
          "parameter": [
            {
              "name": "Authorization",
              "value": "={{ $env.AUTH_TOKEN }}"
            }
          ]
        }
      },
      "id": "5b7599e1-c108-47ad-a459-71f188418d73",
      "name": "\ud50c\ub7ec\uadf8\uc778 \ud0a4 \uc870\ud68c",
      "type": "n8n-nodes-base.graphql",
      "typeVersion": 1.1,
      "position": [
        16,
        320
      ]
    },
    {
      "parameters": {
        "jsCode": "const OVERRIDE = { accessKey: $env.COUPANG_ACCESS_KEY || '', secretKey: $env.COUPANG_SECRET_KEY || '', vendorId: $env.COUPANG_VENDOR_ID || '' };\nconst root = $input.first().json.data || {};\nconst toMap = (p) => Object.fromEntries(((p && p.configuration) || []).map(c => [c.name, c.value]));\nconst seller = toMap(root.seller);\nconst coupang = {\n  accessKey: OVERRIDE.accessKey || seller.coupang_access_key,\n  secretKey: OVERRIDE.secretKey || seller.coupang_secret_key,\n  vendorId: OVERRIDE.vendorId || seller.coupang_vendor_id,\n};\nif (!coupang.accessKey || !coupang.secretKey || !coupang.vendorId) {\n  throw new Error('\ucfe0\ud321 \ud0a4 \uc5c6\uc74c: COUPANG_* env \ub610\ub294 \uc140\ub7ec \ud50c\ub7ec\uadf8\uc778 \ud655\uc778');\n}\nreturn [{ json: { coupang } }];"
      },
      "id": "c3c48359-cf9a-449f-8e57-dc2a28330dab",
      "name": "Config",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        240,
        320
      ]
    },
    {
      "parameters": {
        "jsCode": "const crypto = require('crypto');\nconst cfg = $('Config').first().json.coupang;\nconst accessKey = cfg.accessKey, secretKey = cfg.secretKey, vendorId = cfg.vendorId;\nfunction sd() {\n  const d = new Date();\n  const p = n => String(n).padStart(2, '0');\n  return p(d.getUTCFullYear() % 100) + p(d.getUTCMonth() + 1) + p(d.getUTCDate()) + 'T' + p(d.getUTCHours()) + p(d.getUTCMinutes()) + p(d.getUTCSeconds()) + 'Z';\n}\nfunction kstDate(d) { return new Intl.DateTimeFormat('sv-SE', { timeZone: 'Asia/Seoul', year: 'numeric', month: '2-digit', day: '2-digit' }).format(d); }\nconst path = '/v2/providers/openapi/apis/api/v4/vendors/' + vendorId + '/ordersheets';\nconst from = kstDate(new Date(Date.now() - 7 * 24 * 3600 * 1000));\nconst to = kstDate(new Date());\nconst query = 'createdAtFrom=' + from + '&createdAtTo=' + to + '&status=INSTRUCT&maxPerPage=50';\nconst d = sd();\nconst signature = crypto.createHmac('sha256', secretKey).update(d + 'GET' + path + query).digest('hex');\nconst authorization = 'CEA algorithm=HmacSHA256, access-key=' + accessKey + ', signed-date=' + d + ', signature=' + signature;\nconst url = 'https://api-gateway.coupang.com' + path + '?' + query;\nreturn [{ json: { url, authorization } }];"
      },
      "id": "596e9d2e-4638-4d79-8dd1-35419aca7e8b",
      "name": "\ucfe0\ud321 INSTRUCT \uc11c\uba85 \uc0dd\uc131",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        464,
        320
      ]
    },
    {
      "parameters": {
        "url": "={{ $json.url }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "={{ $json.authorization }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json;charset=UTF-8"
            },
            {
              "name": "X-EXTENDED-TIMEOUT",
              "value": "90000"
            }
          ]
        },
        "options": {
          "response": {
            "response": {
              "responseFormat": "text"
            }
          }
        }
      },
      "id": "52949b5c-d817-4e4f-8691-dff521607616",
      "name": "\ucfe0\ud321 \ubc1c\uc8fc\uc11c \uc870\ud68c",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        672,
        320
      ]
    },
    {
      "parameters": {
        "jsCode": "const raw = $input.first().json;\nconst str = typeof raw === 'string' ? raw : (raw.data != null ? raw.data : JSON.stringify(raw));\n// shipmentBoxId/orderId \ub4f1 16\uc790\ub9ac+ \uc815\uc218\ub294 JS \uc548\uc804\uc815\uc218 \ucd08\uacfc \u2192 \ubb38\uc790\uc5f4\ub85c \uac10\uc2fc \ub4a4 \ud30c\uc2f1 (\uc815\ubc00\ub3c4 \ubcf4\uc874)\nconst safe = String(str).replace(/:(\\s*)(\\d{16,})/g, ':$1\"$2\"');\nconst body = JSON.parse(safe);\nif (body.nextToken) { console.log('[\uc1a1\uc7a5\uc5c5\ub85c\ub4dc] \ucd94\uac00 \ud398\uc774\uc9c0 \uc788\uc74c(nextToken) - 50\uac74 \ucd08\uacfc\ubd84 \ub204\ub77d, \ud398\uc774\uc9d5 \ubcf4\uc644 \ud544\uc694'); }\nconst data = body.data || [];\n// \ubc1c\uc8fc\uc11c(=shipmentBox) 1\uac74\ub2f9 1 item\nreturn data.map(o => ({ json: o }));"
      },
      "id": "9ce5752a-2fe8-4643-8666-aed70fd0f960",
      "name": "\uc8fc\ubb38 \ubaa9\ub85d \ubd84\ub9ac",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        896,
        320
      ]
    },
    {
      "parameters": {
        "jsCode": "const axios = require('axios');\nconst url = $env.GRAPHQL_URL;\nconst token = $env.AUTH_TOKEN;\n\n// \ucfe0\ud321 \uc0c1\ud488\uc900\ube44\uc911 \uc8fc\ubb38 (\uc815\ubc00\ub3c4 \ubcf4\uc874)\nconst coupangOrders = $('\uc8fc\ubb38 \ubaa9\ub85d \ubd84\ub9ac').all().map(i => i.json);\nif (coupangOrders.length === 0) return [];\nconst targetSet = new Set(coupangOrders.map(o => String(o.orderId)));\n\n// Saleor \ucd9c\uace0+\uc1a1\uc7a5 \uc8fc\ubb38 \uc870\ud68c (deliveryStatus=TRACKING_DELIVERY) \u2014 \ud544\uc694\ud55c \ud544\ub4dc\ub9cc\nconst QUERY = 'query($first:Int,$after:String,$filter:OrderFilterInput,$sort:OrderSortingInput){ orders(first:$first,after:$after,filter:$filter,sortBy:$sort){ edges{ node{ etcInfo fulfillments{ trackingNumber courier{ codeGoodsflow } lines{ orderLine{ variant{ sku } } } } } } pageInfo{ endCursor hasNextPage } } }';\nconst filter = { deliveryStatus: ['TRACKING_DELIVERY'], type: ['BY_STAFF_FOR_BAD_STOCK'] };\nconst sort = { direction: 'DESC', field: 'NUMBER' };\n\n// \ucfe0\ud321\uc5c5\ub85c\ub4dc \ubd80\uc801\ud569 \ud0dd\ubc30\uc0ac \uc81c\uc678 (\uc790\uccb4\ubc30\uc1a1/\ub85c\ucf13/\ubbf8\uc0c1)\nconst EXCLUDED = new Set(['POJANGBOSS', 'COUPANG', '']);\n// (open_mall_order_number + '||' + sku) -> { trackingNumber, courierCode }\nconst trackMap = {};\nlet after = null, pages = 0;\nwhile (pages < 20) {\n  pages++;\n  const resp = await axios.post(url, { query: QUERY, variables: { first: 100, after, filter, sort } }, { headers: { Authorization: token, 'Content-Type': 'application/json' } });\n  const conn = (((resp.data || {}).data || {}).orders) || {};\n  for (const e of (conn.edges || [])) {\n    const n = e.node;\n    const et = n.etcInfo || {};\n    const omn = et.open_mall_order_number != null ? String(et.open_mall_order_number) : '';\n    if (!omn || !targetSet.has(omn)) continue;\n    for (const f of (n.fulfillments || [])) {\n      if (!f.trackingNumber) continue;\n      const code = (f.courier && f.courier.codeGoodsflow) || '';\n      if (EXCLUDED.has(code)) continue;\n      const tn = String(f.trackingNumber).replace(/[^0-9]/g, '');\n      if (tn.length < 10) continue;\n      for (const ln of (f.lines || [])) {\n        const sku = ln.orderLine && ln.orderLine.variant && ln.orderLine.variant.sku;\n        if (!sku) continue;\n        const key = omn + '||' + sku;\n        if (!trackMap[key]) trackMap[key] = { trackingNumber: tn, courierCode: code };  // \uc720\ud6a8\ud55c \uccab \uc1a1\uc7a5\n      }\n    }\n  }\n  if (!conn.pageInfo || !conn.pageInfo.hasNextPage) break;\n  after = conn.pageInfo.endCursor;\n}\n\n// \ucfe0\ud321 \uc8fc\ubb38 \u00d7 \uc1a1\uc7a5 \u2192 \uc5c5\ub85c\ub4dc entry (vendorItem \ub2e8\uc704)\nconst entries = [];\nconst missing = [];\nfor (const o of coupangOrders) {\n  const omn = String(o.orderId);\n  for (const it of (o.orderItems || [])) {\n    const sku = it.externalVendorSkuCode;\n    const m = trackMap[omn + '||' + sku];\n    if (!m) { missing.push({ orderId: omn, sku, vendorItemId: String(it.vendorItemId) }); continue; }\n    entries.push({ shipmentBoxId: o.shipmentBoxId, orderId: o.orderId, vendorItemId: it.vendorItemId, deliveryCompanyCode: m.courierCode, invoiceNumber: m.trackingNumber });\n  }\n}\nif (missing.length) console.log('[\uc1a1\uc7a5\uc5c5\ub85c\ub4dc] \ubbf8\ucd9c\uace0/\uc1a1\uc7a5\uc5c6\uc74c ' + missing.length + '\uac74: ' + JSON.stringify(missing.slice(0, 30)));\nif (entries.length === 0) return [];\nreturn [{ json: { entries } }];"
      },
      "id": "de7747ab-ace9-4613-8156-66d1b8290500",
      "name": "Saleor \uc1a1\uc7a5 \uc870\ud68c+\ub9e4\ud551",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1120,
        320
      ]
    },
    {
      "parameters": {
        "jsCode": "const crypto = require('crypto');\nconst cfg = $('Config').first().json.coupang;\nconst accessKey = cfg.accessKey, secretKey = cfg.secretKey, vendorId = cfg.vendorId;\nfunction sd() {\n  const d = new Date();\n  const p = n => String(n).padStart(2, '0');\n  return p(d.getUTCFullYear() % 100) + p(d.getUTCMonth() + 1) + p(d.getUTCDate()) + 'T' + p(d.getUTCHours()) + p(d.getUTCMinutes()) + p(d.getUTCSeconds()) + 'Z';\n}\nconst entries = (($input.first().json) || {}).entries || [];\nif (entries.length === 0) return [];\nconst chunks = [];\nfor (let i = 0; i < entries.length; i += 50) chunks.push(entries.slice(i, i + 50));\nconst path = '/v2/providers/openapi/apis/api/v4/vendors/' + vendorId + '/orders/invoices';\nconst url = 'https://api-gateway.coupang.com' + path;\nfunction entryJson(e) {\n  return '{\"shipmentBoxId\":' + e.shipmentBoxId + ',\"orderId\":' + e.orderId + ',\"vendorItemId\":' + e.vendorItemId + ',\"deliveryCompanyCode\":\"' + e.deliveryCompanyCode + '\",\"invoiceNumber\":\"' + e.invoiceNumber + '\",\"splitShipping\":false,\"preSplitShipped\":false}';\n}\nreturn chunks.map(ch => {\n  const d = sd();\n  const signature = crypto.createHmac('sha256', secretKey).update(d + 'POST' + path).digest('hex');\n  const authorization = 'CEA algorithm=HmacSHA256, access-key=' + accessKey + ', signed-date=' + d + ', signature=' + signature;\n  const bodyStr = '{\"vendorId\":\"' + vendorId + '\",\"orderSheetInvoiceApplyDtos\":[' + ch.map(entryJson).join(',') + ']}';\n  return { json: { url, authorization, bodyStr, count: ch.length } };\n});"
      },
      "id": "4b4c5eec-e9fe-48d4-a893-54554756de40",
      "name": "\ucfe0\ud321 \uc1a1\uc7a5\uc5c5\ub85c\ub4dc \uc11c\uba85 \uc0dd\uc131",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1344,
        320
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $json.url }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "={{ $json.authorization }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json;charset=UTF-8"
            },
            {
              "name": "X-EXTENDED-TIMEOUT",
              "value": "90000"
            }
          ]
        },
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "application/json",
        "body": "={{ $json.bodyStr }}",
        "options": {}
      },
      "id": "1edafaff-a0ca-43bc-b59b-329e730809de",
      "name": "\ucfe0\ud321 \uc1a1\uc7a5\uc5c5\ub85c\ub4dc",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1552,
        320
      ]
    },
    {
      "parameters": {
        "jsCode": "const res = $input.first().json || {};\nconst data = res.data || {};\nconst list = data.responseList || [];\nconst failed = list.filter(r => !r.succeed);\nif (failed.length) {\n  console.log('[\uc1a1\uc7a5\uc5c5\ub85c\ub4dc] \uc2e4\ud328 ' + failed.length + '/' + list.length + ': ' + JSON.stringify(failed.map(f => ({ id: String(f.shipmentBoxId), code: f.resultCode, msg: f.resultMessage, retry: f.retryRequired }))));\n}\nreturn [{ json: { responseCode: data.responseCode, responseMessage: data.responseMessage, total: list.length, failed: failed.length } }];"
      },
      "id": "d05864e8-a3ff-4410-b4df-e18da4ced10c",
      "name": "\uc1a1\uc7a5\uc5c5\ub85c\ub4dc \uacb0\uacfc \ud655\uc778",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1776,
        320
      ]
    }
  ],
  "connections": {
    "Manual Trigger": {
      "main": [
        [
          {
            "node": "\ud50c\ub7ec\uadf8\uc778 \ud0a4 \uc870\ud68c",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "\ud50c\ub7ec\uadf8\uc778 \ud0a4 \uc870\ud68c",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud50c\ub7ec\uadf8\uc778 \ud0a4 \uc870\ud68c": {
      "main": [
        [
          {
            "node": "Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Config": {
      "main": [
        [
          {
            "node": "\ucfe0\ud321 INSTRUCT \uc11c\uba85 \uc0dd\uc131",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ucfe0\ud321 INSTRUCT \uc11c\uba85 \uc0dd\uc131": {
      "main": [
        [
          {
            "node": "\ucfe0\ud321 \ubc1c\uc8fc\uc11c \uc870\ud68c",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ucfe0\ud321 \ubc1c\uc8fc\uc11c \uc870\ud68c": {
      "main": [
        [
          {
            "node": "\uc8fc\ubb38 \ubaa9\ub85d \ubd84\ub9ac",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\uc8fc\ubb38 \ubaa9\ub85d \ubd84\ub9ac": {
      "main": [
        [
          {
            "node": "Saleor \uc1a1\uc7a5 \uc870\ud68c+\ub9e4\ud551",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Saleor \uc1a1\uc7a5 \uc870\ud68c+\ub9e4\ud551": {
      "main": [
        [
          {
            "node": "\ucfe0\ud321 \uc1a1\uc7a5\uc5c5\ub85c\ub4dc \uc11c\uba85 \uc0dd\uc131",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ucfe0\ud321 \uc1a1\uc7a5\uc5c5\ub85c\ub4dc \uc11c\uba85 \uc0dd\uc131": {
      "main": [
        [
          {
            "node": "\ucfe0\ud321 \uc1a1\uc7a5\uc5c5\ub85c\ub4dc",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ucfe0\ud321 \uc1a1\uc7a5\uc5c5\ub85c\ub4dc": {
      "main": [
        [
          {
            "node": "\uc1a1\uc7a5\uc5c5\ub85c\ub4dc \uacb0\uacfc \ud655\uc778",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "staticData": {
    "node:Schedule Trigger": {
      "recurrenceRules": []
    }
  },
  "meta": null,
  "versionId": "2cfb3de5-d5f1-49fd-8c9f-2990285ac808",
  "activeVersionId": "2cfb3de5-d5f1-49fd-8c9f-2990285ac808",
  "versionCounter": 24,
  "triggerCount": 1,
  "tags": [],
  "shared": [
    {
      "updatedAt": "2026-06-30T08:01:32.068Z",
      "createdAt": "2026-06-30T08:01:32.068Z",
      "role": "workflow:owner",
      "workflowId": "Vf2zXp84NiBfbXmz",
      "projectId": "PyrRNJYltDksNq22",
      "project": {
        "updatedAt": "2025-12-17T00:26:00.367Z",
        "createdAt": "2025-12-17T00:18:19.940Z",
        "id": "PyrRNJYltDksNq22",
        "name": "\ub3c4\ud604 \uae40 <dev-do@naver.com>",
        "type": "personal",
        "icon": null,
        "description": null
      }
    }
  ]
}