{
  "id": "KHvxUBua7hi5e1x1",
  "name": "VenueDesk - Financial Operations (UUID Fix)",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "pay-balance",
        "responseMode": "responseNode",
        "options": {},
        "authentication": "none"
      },
      "id": "e1f835fd-4c70-475e-acf4-0a378de5e5cc",
      "name": "Webhook: Pay Balance",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        0,
        0
      ],
      "credentials": {}
    },
    {
      "parameters": {
        "jsCode": "// UNWRAP\nconst input = ($input.first().json.body) ? $input.first().json.body : $input.first().json;\n\n// VALIDATE\nconst amount = parseFloat(input.amount);\nif (isNaN(amount) || amount <= 0) throw new Error('Payment amount must be positive');\nif (amount > 100000) throw new Error('Amount exceeds maximum');\nif (!input.booking_id)  throw new Error('Missing booking_id');\nif (!input.customer_id) throw new Error('Missing customer_id');\n\n// DETERMINE TYPE \u2014 preserve 'deposit' if caller sends it, otherwise 'balance'\nconst sentType = (input.payment_type || '').toLowerCase();\nconst payment_type = (sentType === 'deposit') ? 'deposit' : 'balance';\n\n// is_partial: only relevant for balance payments\nconst balance = parseFloat(input.balance_due || 999999);\nconst is_partial = payment_type === 'balance' && amount < (balance - 0.005);\n\n// GENERATE REFERENCE NUMBER\nconst ts = new Date();\nconst pad = n => String(n).padStart(2,'0');\nconst stamp = ts.getFullYear() + pad(ts.getMonth()+1) + pad(ts.getDate()) +\n              pad(ts.getHours()) + pad(ts.getMinutes()) + pad(ts.getSeconds());\nconst rand = Math.random().toString(36).slice(2,6).toUpperCase();\nconst prefix = payment_type === 'deposit' ? 'DEP' : 'REF';\nconst reference_number = prefix + '-' + stamp + '-' + rand;\n\nreturn {\n  json: {\n    ...input,\n    tenant_id: parseInt(input.tenant_id || $input.first().json.headers?.['x-tenant-id'] || $input.first().json.query?.tenant_id || '0'),\n    amount,\n    payment_type,\n    is_partial,\n    reference_number,\n    payment_method:    input.payment_method || 'cash',\n    deposit_reference: input.deposit_reference || null,\n    // Explicit trim prevents whitespace-corrupted JWTs reaching the auth header\n    jwt: String(input.jwt || '').trim()\n  }\n};\n"
      },
      "id": "6c1b0790-6c06-419b-bf26-956a04753044",
      "name": "Code: Sanitize",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        224,
        0
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.venuedesk.co.uk/payments/record",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "<redacted-credential>"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "options": {
          "response": {
            "response": {
              "fullResponse": false,
              "neverError": true
            }
          },
          "timeout": 15000
        },
        "sendBody": true,
        "contentType": "json",
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ booking_id: $json.booking_id, amount: $json.amount, payment_method: $json.payment_method, payment_type: $json.payment_type || 'balance', reference: $json.reference || '', jwt: $json.jwt || '' }) }}"
      },
      "id": "http-record-payment-001",
      "name": "DB: Record Payment",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "continueOnFail": true,
      "position": [
        448,
        0
      ]
    },
    {
      "parameters": {
        "jsCode": "return $input.all();"
      },
      "id": "code-passthrough-001",
      "name": "DB: Update Balance",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        672,
        0
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({  status: 'success',  message: 'Payment processed',  reference_number: $('DB: Record Payment').first().json.reference_number || $('DB: Record Payment').first().json.id,  payment_type: $('Code: Sanitize').first().json.is_partial ? 'partial' : 'full',  amount: $('Code: Sanitize').first().json.amount}) }}",
        "options": {
          "responseHeaders": {
            "entries": [
              {
                "name": "Access-Control-Allow-Origin",
                "value": "*"
              }
            ]
          }
        }
      },
      "id": "ee61c79c-62a2-42d4-8df2-45466f044461",
      "name": "Respond: Success",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        880,
        0
      ]
    },
    {
      "parameters": {
        "method": "GET",
        "url": "=https://api.venuedesk.co.uk/bookings/{{ $('Code: Sanitize').first().json.booking_id }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "={{ 'Bearer ' + $('Code: Sanitize').first().json.jwt }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "options": {
          "response": {
            "response": {
              "fullResponse": false,
              "neverError": true
            }
          },
          "timeout": 15000
        }
      },
      "id": "http-get-customer-det-001",
      "name": "DB: Get Customer Details",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "continueOnFail": true,
      "position": [
        880,
        208
      ]
    },
    {
      "id": "code-build-email-001",
      "name": "Code: Build Email Content",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1120,
        208
      ],
      "parameters": {
        "jsCode": "const sanitize   = $('Code: Sanitize').first().json;\nconst paymentNode = $('DB: Record Payment').first().json;\n\n// paymentData: use API response when successful; fall back to {} so we never\n// crash if DB: Record Payment itself failed (network error, 5xx etc.)\nconst paymentData = paymentNode?.success ? (paymentNode.data ?? paymentNode) : {};\n// detailsRaw: DB: Get Customer Details enrichment (best-effort, may 401)\nconst detailsRaw  = ($json.success !== false) ? ($json.data ?? $json) : {};\n// webhookData: original sanitised webhook body as the final safety net\nconst webhookData = sanitize;\n\n// Merge order: webhookData (base) \u2192 paymentData (richer) \u2192 detailsRaw (freshest)\nconst raw = { ...webhookData, ...paymentData, ...detailsRaw };\n\nconst customer = {\n  ...raw,\n  email:      raw.customer_email || raw.email      || sanitize.email      || '',\n  full_name:  raw.customer_name  || raw.full_name  || 'Valued Customer',\n  event_date: raw.event_date     || raw.date_from  || raw.booking_date    || null,\n};\n\nconst payment_type = sanitize.payment_type;\nconst is_partial   = sanitize.is_partial;\nconst amount       = parseFloat(sanitize.amount || 0);\nconst ref          = sanitize.reference_number || '\\u2014';\nconst remaining    = parseFloat(raw.balance_due ?? raw.new_balance ?? 0);\nconst total        = parseFloat(customer.total_amount || 0);\n\nconst fmt = n => '\\u00a3' + parseFloat(n || 0).toFixed(2);\nconst fmtDate = d => {\n  if (!d) return '\\u2014';\n  const dt = new Date(String(d).slice(0,10) + 'T00:00:00');\n  return dt.toLocaleDateString('en-GB', { weekday:'long', day:'numeric', month:'long', year:'numeric' });\n};\nconst fmtTime = t => t ? String(t).slice(0,5) : '\\u2014';\n\nlet subject, headingColour, headingText, statusBadge, bodyText;\n\nif (payment_type === 'deposit') {\n  subject       = `Deposit Payment Confirmed \\u2014 ${fmtDate(customer.event_date)} | Ref: ${ref}`;\n  headingColour = '#6366f1';\n  headingText   = 'Deposit Payment Confirmed';\n  statusBadge   = { text:'DEPOSIT RECEIVED', bg:'#eef2ff', color:'#4f46e5' };\n  bodyText      = `Thank you for your deposit payment of <strong>${fmt(amount)}</strong>. Your booking for <strong>${customer.room_name || 'your venue'}</strong> is now secured.`;\n} else if (is_partial) {\n  subject       = `Payment Received \\u2014 Remaining Balance: ${fmt(remaining)} | Ref: ${ref}`;\n  headingColour = '#f59e0b';\n  headingText   = 'Partial Balance Payment Received';\n  statusBadge   = { text:'PAYMENT RECEIVED', bg:'#fffbeb', color:'#b45309' };\n  bodyText      = `Thank you for your payment of <strong>${fmt(amount)}</strong>. Your remaining balance of <strong>${fmt(remaining)}</strong> is due before your event date.`;\n} else {\n  subject       = `Balance Fully Settled \\u2014 Booking Confirmed \\u2713 | Ref: ${ref}`;\n  headingColour = '#10b981';\n  headingText   = 'Balance Fully Settled \\u2713';\n  statusBadge   = { text:'FULLY PAID', bg:'#ecfdf5', color:'#065f46' };\n  bodyText      = `Thank you for your final payment of <strong>${fmt(amount)}</strong>. Your booking balance is now <strong>fully settled</strong> \\u2014 we look forward to hosting your event!`;\n}\n\nconst remainingRow = remaining > 0\n  ? `<tr style=\"border-top:1px solid #e2e8f0;\"><td style=\"padding:8px 0 5px;color:#64748b;\">Balance Remaining</td><td style=\"padding:8px 0 5px;color:#f59e0b;font-weight:700;text-align:right;\">${fmt(remaining)}</td></tr>`\n  : `<tr style=\"border-top:1px solid #e2e8f0;\"><td style=\"padding:8px 0 5px;color:#64748b;\">Balance Remaining</td><td style=\"padding:8px 0 5px;color:#10b981;font-weight:700;text-align:right;\">\\u00a30.00 \\u2014 Fully Paid \\u2713</td></tr>`;\n\nconst html = `<!DOCTYPE html>\n<html><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\"></head>\n<body style=\"margin:0;padding:0;background:#f8fafc;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;\">\n  <div style=\"max-width:600px;margin:0 auto;padding:32px 16px;\">\n    <div style=\"background:${headingColour};border-radius:12px 12px 0 0;padding:32px;text-align:center;\">\n      <p style=\"margin:0 0 6px;color:rgba(255,255,255,0.8);font-size:12px;letter-spacing:1.5px;text-transform:uppercase;\">VenueDesk Booking System</p>\n      <h1 style=\"margin:0;color:#ffffff;font-size:22px;font-weight:700;line-height:1.3;\">${headingText}</h1>\n    </div>\n    <div style=\"background:#ffffff;border:1px solid #e2e8f0;border-top:none;border-radius:0 0 12px 12px;padding:32px;\">\n      <div style=\"text-align:center;margin-bottom:24px;\">\n        <span style=\"display:inline-block;background:${statusBadge.bg};color:${statusBadge.color};font-size:11px;font-weight:700;letter-spacing:1.5px;padding:6px 18px;border-radius:20px;\">${statusBadge.text}</span>\n      </div>\n      <p style=\"margin:0 0 16px;color:#1e293b;font-size:15px;\">Dear <strong>${customer.full_name || 'Valued Customer'}</strong>,</p>\n      <p style=\"margin:0 0 24px;color:#374151;font-size:15px;line-height:1.7;\">${bodyText}</p>\n      <div style=\"background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px;padding:20px;margin-bottom:16px;\">\n        <p style=\"margin:0 0 14px;color:#0f172a;font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:0.8px;\">Booking Details</p>\n        <table style=\"width:100%;border-collapse:collapse;font-size:14px;\">\n          <tr><td style=\"padding:5px 0;color:#64748b;width:45%;\">Room / Venue</td><td style=\"padding:5px 0;color:#1e293b;font-weight:600;\">${customer.room_name || '\\u2014'}</td></tr>\n          <tr><td style=\"padding:5px 0;color:#64748b;\">Event Date</td><td style=\"padding:5px 0;color:#1e293b;font-weight:600;\">${fmtDate(customer.event_date)}</td></tr>\n          <tr><td style=\"padding:5px 0;color:#64748b;\">Time</td><td style=\"padding:5px 0;color:#1e293b;font-weight:600;\">${fmtTime(customer.start_time)} \\u2013 ${fmtTime(customer.end_time)}</td></tr>\n        </table>\n      </div>\n      <div style=\"background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px;padding:20px;margin-bottom:24px;\">\n        <p style=\"margin:0 0 14px;color:#0f172a;font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:0.8px;\">Payment Summary</p>\n        <table style=\"width:100%;border-collapse:collapse;font-size:14px;\">\n          <tr><td style=\"padding:5px 0;color:#64748b;\">Total Booking Value</td><td style=\"padding:5px 0;color:#1e293b;text-align:right;\">${fmt(total)}</td></tr>\n          <tr><td style=\"padding:5px 0;color:#64748b;\">Amount Paid (This Transaction)</td><td style=\"padding:5px 0;color:${headingColour};font-weight:700;text-align:right;\">${fmt(amount)}</td></tr>\n          ${remainingRow}\n        </table>\n      </div>\n      <div style=\"background:#eef2ff;border-radius:8px;padding:14px 20px;margin-bottom:28px;text-align:center;\">\n        <p style=\"margin:0;color:#64748b;font-size:12px;\">Payment Reference</p>\n        <p style=\"margin:4px 0 0;color:#4f46e5;font-size:17px;font-weight:700;letter-spacing:0.5px;\">${ref}</p>\n      </div>\n      <p style=\"margin:0;color:#64748b;font-size:13px;line-height:1.6;\">Please keep this email as your payment receipt. If you have any questions, reply quoting your reference number or contact us at <a href=\"mailto:bookings@venuedesk.co.uk\" style=\"color:#6366f1;text-decoration:none;\">bookings@venuedesk.co.uk</a>.</p>\n    </div>\n    <div style=\"text-align:center;padding:20px 0 0;\">\n      <p style=\"margin:0;color:#94a3b8;font-size:11px;\">This is an automated message from VenueDesk Booking System. Please do not reply to this email directly.</p>\n    </div>\n  </div>\n</body></html>`;\n\nreturn { json: { ...customer, subject, html, email: customer.email } };"
      }
    },
    {
      "id": "email-pay-conf-001",
      "name": "Email: Payment Confirmation",
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2.1,
      "position": [
        1344,
        208
      ],
      "credentials": {
        "smtp": {
          "name": "<your credential>"
        }
      },
      "parameters": {
        "fromEmail": "bookings@venuedesk.co.uk",
        "toEmail": "={{ $json.email }}",
        "subject": "={{ $json.subject }}",
        "html": "={{ $json.html }}",
        "options": {
          "replyTo": "bookings@venuedesk.co.uk"
        }
      }
    },
    {
      "id": "if-fully-paid-001",
      "name": "If Fully Paid?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1568,
        208
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "check_bal",
              "leftValue": "={{ parseFloat($('DB: Update Balance').first().json.balance_due) }}",
              "rightValue": 0,
              "operator": {
                "type": "number",
                "operation": "lte"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      }
    }
  ],
  "connections": {
    "Webhook: Pay Balance": {
      "main": [
        [
          {
            "node": "Code: Sanitize",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code: Sanitize": {
      "main": [
        [
          {
            "node": "DB: Record Payment",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "DB: Record Payment": {
      "main": [
        [
          {
            "node": "DB: Update Balance",
            "type": "main",
            "index": 0
          },
          {
            "node": "Respond: Success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "DB: Update Balance": {
      "main": [
        [
          {
            "node": "DB: Get Customer Details",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "DB: Get Customer Details": {
      "main": [
        [
          {
            "node": "Code: Build Email Content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code: Build Email Content": {
      "main": [
        [
          {
            "node": "Email: Payment Confirmation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Email: Payment Confirmation": {
      "main": [
        [
          {
            "node": "If Fully Paid?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Fully Paid?": {
      "main": [
        []
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "binaryMode": "separate",
    "availableInMCP": true,
    "callerPolicy": "workflowsFromSameOwner"
  },
  "staticData": null,
  "meta": null,
  "versionId": "a54f2490-8b93-4527-861f-d6c5ba715c28"
}