{
 "id": "RhR4Slip00000001",
 "name": "N6 ตรวจสลิป + ใบเสร็จ PDF",
 "active": false,
 "nodes": [
  {
   "id": "r4n7",
   "name": "Webhook",
   "type": "n8n-nodes-base.webhook",
   "typeVersion": 2,
   "position": [
    0,
    300
   ],
   "parameters": {
    "httpMethod": "POST",
    "path": "r4-slip",
    "responseMode": "responseNode",
    "options": {}
   },
   "webhookId": "5df0ca7b-e414-da7b-37a0-c0fead7f7ed6"
  },
  {
   "id": "r4n8",
   "name": "Slip verifier (mock)",
   "type": "n8n-nodes-base.httpRequest",
   "typeVersion": 4.2,
   "position": [
    220,
    300
   ],
   "parameters": {
    "method": "POST",
    "url": "={{ $env.BUYER_API_BASE }}/slip/verify",
    "sendBody": true,
    "specifyBody": "json",
    "jsonBody": "={{ JSON.stringify({ image: $json.body.slip_image_ref }) }}",
    "options": {
     "timeout": 10000
    }
   },
   "onError": "continueErrorOutput",
   "retryOnFail": true,
   "maxTries": 3
  },
  {
   "id": "r4n9",
   "name": "Judge slip",
   "type": "n8n-nodes-base.code",
   "typeVersion": 2,
   "position": [
    440,
    300
   ],
   "parameters": {
    "jsCode": "// N6 ออเดอร์ + สลิป → ตรวจกับผู้ให้บริการตรวจสลิป (จำลอง) → ใบเสร็จ HTML → PDF\n// ตัดสินจากคำตอบของผู้ตรวจ ไม่ใช่จากรูปสลิป · ยอดต้องตรงทุกสตางค์ · บัญชีผู้รับต้องเป็นของร้าน · เลขอ้างอิงใช้ได้ครั้งเดียว\nfunction orderTotal(items) {\n  if (!Array.isArray(items) || !items.length) throw new Error(\"ไม่มีรายการสินค้า\");\n  let s = 0;\n  for (const it of items) {\n    if (!(Number.isInteger(it.qty) && it.qty > 0) || !(it.price >= 0)) throw new Error(\"จำนวน/ราคาไม่ถูกต้อง\");\n    s += Math.round(it.qty * it.price * 100);\n  }\n  return s / 100;\n}\n\nfunction judgeSlip(order, verify, ctx) {\n  // verify = คำตอบจากผู้ตรวจสลิป {success, data:{transRef, amount, receiverAccountTail}}\n  if (!verify || verify.success !== true || !verify.data) return { status: \"ตรวจไม่ได้\", paid: false };\n  const d = verify.data;\n  const total = orderTotal(order.items);\n  if (ctx.usedRefs.has(d.transRef)) return { status: \"สลิปซ้ำ\", paid: false, transRef: d.transRef };\n  if (d.receiverAccountTail !== ctx.shopAccountTail) return { status: \"ผู้รับไม่ใช่ร้าน\", paid: false };\n  if (Math.round(d.amount * 100) !== Math.round(total * 100)) return { status: Math.round(d.amount * 100) < Math.round(total * 100) ? \"ยอดขาด\" : \"ยอดเกิน\", paid: false, diff: Math.round((d.amount - total) * 100) / 100 };\n  ctx.usedRefs.add(d.transRef);\n  return { status: \"ชำระแล้ว\", paid: true, transRef: d.transRef, total };\n}\n\nfunction esc(s) { return String(s).replace(/[&<>\"]/g, (c) => ({ \"&\": \"&amp;\", \"<\": \"&lt;\", \">\": \"&gt;\", '\"': \"&quot;\" }[c])); }\n\nfunction receiptHtml(order, j) {\n  if (!j.paid) throw new Error(\"ยังไม่ชำระ ไม่ออกใบเสร็จ\");\n  const rows = order.items.map((it) => `<tr><td>${esc(it.name)}</td><td style=\"text-align:right\">${it.qty}</td><td style=\"text-align:right\">${(it.qty * it.price).toFixed(2)}</td></tr>`).join(\"\");\n  return `<h2>ใบเสร็จรับเงิน (ตัวอย่าง)</h2><p>เลขที่ ${esc(order.order_id)} · อ้างอิงการโอน ${esc(j.transRef)}</p>` +\n    `<table style=\"width:100%\">${rows}<tr><td><b>รวม</b></td><td></td><td style=\"text-align:right\"><b>${j.total.toFixed(2)}</b></td></tr></table>`;\n}\n\n\n\nconst order = $(\"Webhook\").first().json.body;\nconst st = $getWorkflowStaticData(\"global\"); st.refs = st.refs || [];\nconst ctx = { usedRefs: new Set(st.refs), shopAccountTail: $env.SHOP_ACCOUNT_TAIL };\nconst j = judgeSlip(order, $input.first().json, ctx);\nst.refs = [...ctx.usedRefs];\nreturn [{ json: { ...j, html: j.paid ? receiptHtml(order, j) : null, code: 200, response: { status: j.status } } }];"
   }
  },
  {
   "id": "r4n10",
   "name": "Paid?",
   "type": "n8n-nodes-base.if",
   "typeVersion": 2,
   "position": [
    660,
    300
   ],
   "parameters": {
    "conditions": {
     "options": {
      "caseSensitive": true,
      "leftValue": "",
      "typeValidation": "loose"
     },
     "conditions": [
      {
       "id": "c1",
       "leftValue": "={{ $json.paid }}",
       "rightValue": "",
       "operator": {
        "type": "boolean",
        "operation": "true",
        "singleValue": true
       }
      }
     ],
     "combinator": "and"
    },
    "options": {}
   }
  },
  {
   "id": "r4n11",
   "name": "HTML to PDF (buyer renderer)",
   "type": "n8n-nodes-base.httpRequest",
   "typeVersion": 4.2,
   "position": [
    880,
    200
   ],
   "parameters": {
    "method": "POST",
    "url": "={{ $env.BUYER_API_BASE }}/pdf",
    "sendBody": true,
    "specifyBody": "json",
    "jsonBody": "={{ JSON.stringify({ html: $json.html }) }}",
    "options": {
     "timeout": 10000
    }
   },
   "onError": "continueErrorOutput",
   "retryOnFail": true,
   "maxTries": 3
  },
  {
   "id": "r4n12",
   "name": "Respond",
   "type": "n8n-nodes-base.respondToWebhook",
   "typeVersion": 1.1,
   "position": [
    1100,
    300
   ],
   "parameters": {
    "respondWith": "json",
    "responseBody": "={{ JSON.stringify($json.response !== undefined ? $json.response : $(\"Judge slip\").first().json.response) }}",
    "options": {
     "responseCode": "={{ $json.code !== undefined ? $json.code : $(\"Judge slip\").first().json.code }}"
    }
   }
  },
  {
   "id": "r4n13",
   "name": "Verifier error",
   "type": "n8n-nodes-base.code",
   "typeVersion": 2,
   "position": [
    440,
    500
   ],
   "parameters": {
    "jsCode": "return [{ json: { code: 502, response: { status: 'ตรวจไม่ได้ ลองใหม่หรือให้คนตรวจ' } } }];"
   }
  }
 ],
 "connections": {
  "Webhook": {
   "main": [
    [
     {
      "node": "Slip verifier (mock)",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "Slip verifier (mock)": {
   "main": [
    [
     {
      "node": "Judge slip",
      "type": "main",
      "index": 0
     }
    ],
    [
     {
      "node": "Verifier error",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "Judge slip": {
   "main": [
    [
     {
      "node": "Paid?",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "Paid?": {
   "main": [
    [
     {
      "node": "HTML to PDF (buyer renderer)",
      "type": "main",
      "index": 0
     }
    ],
    [
     {
      "node": "Respond",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "HTML to PDF (buyer renderer)": {
   "main": [
    [
     {
      "node": "Respond",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "Verifier error": {
   "main": [
    [
     {
      "node": "Respond",
      "type": "main",
      "index": 0
     }
    ]
   ]
  }
 },
 "settings": {
  "executionOrder": "v1",
  "saveDataErrorExecution": "all"
 },
 "pinData": {}
}