{
 "name": "Cobranza escalonada · Sheets → Mercado Pago → WhatsApp",
 "nodes": [
  {
   "id": "fd0fe194-1fb4-4d48-bc26-35dc51b1271b",
   "name": "Nota 3b39",
   "type": "n8n-nodes-base.stickyNote",
   "typeVersion": 1,
   "position": [
    -80,
    -400
   ],
   "parameters": {
    "content": "## Cobranza escalonada\nTodos los días a las 10 lee la hoja **Clientes** (cliente | telefono | monto | vencimiento AAAA-MM-DD | estado | ultimo_aviso | link_pago) y le manda a cada uno el aviso que le toca: 3 días antes, el día del vencimiento, a los 3 y a los 7 días. A los 15 te avisa por Telegram para que sigas vos. Cada aviso lleva su link de pago de Mercado Pago.\nCuando Mercado Pago avisa un pago, el flujo lo lee de la API, controla que esté aprobado y que el monto cubra lo adeudado, y marca la fila como **pagado**.\n\n**Credenciales:** Google Sheets (OAuth2), una «Header Auth» `Authorization: Bearer <access token>` de Mercado Pago, otra para WhatsApp y Telegram. **Plantilla de WhatsApp** `aviso_pago` (utilidad) con 4 parámetros: cliente, monto, cuándo y link.",
    "width": 900,
    "height": 300,
    "color": 6
   }
  },
  {
   "id": "b0cb4a94-f6e9-43fb-8767-e55936a5e107",
   "name": "Todos los días a las 10",
   "type": "n8n-nodes-base.scheduleTrigger",
   "typeVersion": 1.2,
   "position": [
    0,
    0
   ],
   "parameters": {
    "rule": {
     "interval": [
      {
       "field": "cronExpression",
       "expression": "0 10 * * *"
      }
     ]
    }
   }
  },
  {
   "id": "6b02f537-5400-4bed-9077-765ba168b9e9",
   "name": "Probar ahora",
   "type": "n8n-nodes-base.manualTrigger",
   "typeVersion": 1,
   "position": [
    0,
    160
   ],
   "parameters": {}
  },
  {
   "id": "b460d95a-7761-4d4e-932b-776686358e51",
   "name": "Configuración",
   "type": "n8n-nodes-base.set",
   "typeVersion": 3.4,
   "position": [
    240,
    80
   ],
   "parameters": {
    "assignments": {
     "assignments": [
      {
       "id": "fdcf37ea-12b6-40a0-920e-5f3a41a78000",
       "name": "hojaId",
       "value": "ID_DE_TU_PLANILLA",
       "type": "string"
      },
      {
       "id": "be837429-700a-4c75-bf94-388e1fe1fe50",
       "name": "rango",
       "value": "Clientes!A:G",
       "type": "string"
      },
      {
       "id": "14aeb2ea-1298-45e3-a077-eb940d6b31cd",
       "name": "negocio",
       "value": "Tu Negocio",
       "type": "string"
      },
      {
       "id": "11f4de7e-b020-445f-b497-1ef48d86ac29",
       "name": "moneda",
       "value": "ARS",
       "type": "string"
      },
      {
       "id": "466f327a-34fa-46c0-ace5-c1d8604bdead",
       "name": "zonaHoraria",
       "value": -3,
       "type": "number"
      },
      {
       "id": "e5f9dd8d-5769-48f2-878a-1f92b8a21655",
       "name": "waApiBase",
       "value": "https://graph.facebook.com/v23.0",
       "type": "string"
      },
      {
       "id": "22b89f4e-b2d5-4336-afc6-57b4bbf1fa45",
       "name": "phoneNumberId",
       "value": "TU_PHONE_NUMBER_ID",
       "type": "string"
      },
      {
       "id": "c92db308-6455-43d6-8872-ba164f0ce4f5",
       "name": "plantilla",
       "value": "aviso_pago",
       "type": "string"
      },
      {
       "id": "ce6c9be4-b4ba-4791-aff3-09a3199b23a1",
       "name": "idioma",
       "value": "es",
       "type": "string"
      },
      {
       "id": "4362ff3e-ca95-46d0-9333-b0450cb289ce",
       "name": "avisoPagoUrl",
       "value": "https://TU-N8N/webhook/mp-pagos",
       "type": "string"
      },
      {
       "id": "23d0bf5b-c1d0-4b5e-a7fb-c467a7d597d7",
       "name": "telegramChatId",
       "value": "TU_CHAT_ID",
       "type": "string"
      }
     ]
    },
    "includeOtherFields": true,
    "include": "all",
    "options": {}
   }
  },
  {
   "id": "21ad9408-cc54-4d95-bc7d-2393996ebbec",
   "name": "Leer la hoja Clientes",
   "type": "n8n-nodes-base.httpRequest",
   "typeVersion": 4.2,
   "position": [
    480,
    80
   ],
   "parameters": {
    "method": "GET",
    "url": "={{ 'https://sheets.googleapis.com/v4/spreadsheets/' + $json.hojaId + '/values/' + encodeURIComponent($json.rango) }}",
    "authentication": "predefinedCredentialType",
    "nodeCredentialType": "googleSheetsOAuth2Api",
    "options": {}
   }
  },
  {
   "id": "db14a371-ecd4-4e0a-ae75-64b9f179bffd",
   "name": "¿A quién le toca hoy?",
   "type": "n8n-nodes-base.code",
   "typeVersion": 2,
   "position": [
    720,
    80
   ],
   "parameters": {
    "jsCode": "// ── Nodo «¿A quién le toca hoy?» (Code) ──\n// Entrada: la hoja «Clientes» (Google Sheets, values.get) con las columnas: cliente | telefono | monto | vencimiento\n// (AAAA-MM-DD) | estado (pendiente/pagado) | ultimo_aviso | link_pago. Salida: un ítem por cliente que hoy tiene que\n// recibir un aviso, con su escalón. Cada escalón se manda una sola vez (queda anotado en «ultimo_aviso»).\nconst cfg = $('Configuración').first().json;\nconst filas = $input.first().json.values || [];\nconst ESCALONES = [[-3, 'antes'], [0, 'hoy'], [3, 'recordatorio'], [7, 'firme'], [15, 'persona']];\nconst hoy = new Date(Date.now() + cfg.zonaHoraria * 3600e3); hoy.setUTCHours(0, 0, 0, 0);\nconst salida = [];\nfilas.slice(1).forEach((f, i) => {\n  const [cliente, telefono, monto, vencimiento, estado, ultimoAviso] = f;\n  if (!cliente || !/^\\d{4}-\\d{2}-\\d{2}$/.test(vencimiento || '') || /pagad/i.test(estado || '')) return;\n  const dias = Math.round((hoy - Date.parse(vencimiento + 'T00:00:00Z')) / 86400e3);   // negativo = todavía no vence\n  const escalon = ESCALONES.filter(([d]) => dias >= d).pop();\n  if (!escalon || String(ultimoAviso) === escalon[1]) return;\n  const importe = Number(String(monto).replace(/[^\\d,.-]/g, '').replace(/\\.(?=\\d{3}\\b)/g, '').replace(',', '.')) || 0;\n  salida.push({ json: { fila: i + 2, cliente, telefono: String(telefono).replace(/\\D/g, ''), importe, vencimiento, dias, escalon: escalon[1],\n    montoTexto: new Intl.NumberFormat('es-AR', { style: 'currency', currency: cfg.moneda, minimumFractionDigits: Number.isInteger(importe) ? 0 : 2 }).format(importe),\n    cuando: dias < 0 ? `vence en ${-dias} días` : dias === 0 ? 'vence hoy' : `venció hace ${dias} días` } });\n});\nreturn salida;\n"
   }
  },
  {
   "id": "0b210bc1-6c88-4f48-9213-c174d31be50c",
   "name": "Crear el link de pago",
   "type": "n8n-nodes-base.httpRequest",
   "typeVersion": 4.2,
   "position": [
    960,
    80
   ],
   "parameters": {
    "method": "POST",
    "url": "https://api.mercadopago.com/checkout/preferences",
    "authentication": "genericCredentialType",
    "genericAuthType": "httpHeaderAuth",
    "sendBody": true,
    "specifyBody": "json",
    "jsonBody": "={{ JSON.stringify({ items: [{ title: $('Configuración').first().json.negocio + ' · cuota', quantity: 1, unit_price: $json.importe, currency_id: $('Configuración').first().json.moneda }], external_reference: String($json.fila), notification_url: $('Configuración').first().json.avisoPagoUrl }) }}",
    "options": {}
   }
  },
  {
   "id": "9246eab5-9800-4a14-94f1-b2b8ce16e33e",
   "name": "Enviar el aviso por WhatsApp",
   "type": "n8n-nodes-base.httpRequest",
   "typeVersion": 4.2,
   "position": [
    1200,
    80
   ],
   "parameters": {
    "method": "POST",
    "url": "={{ $('Configuración').first().json.waApiBase }}/{{ $('Configuración').first().json.phoneNumberId }}/messages",
    "authentication": "genericCredentialType",
    "genericAuthType": "httpHeaderAuth",
    "sendBody": true,
    "specifyBody": "json",
    "jsonBody": "={{ JSON.stringify({ messaging_product: 'whatsapp', to: $('¿A quién le toca hoy?').item.json.telefono, type: 'template', template: { name: $('Configuración').first().json.plantilla, language: { code: $('Configuración').first().json.idioma }, components: [{ type: 'body', parameters: [$('¿A quién le toca hoy?').item.json.cliente, $('¿A quién le toca hoy?').item.json.montoTexto, $('¿A quién le toca hoy?').item.json.cuando, $json.init_point].map((t) => ({ type: 'text', text: String(t) })) }] } }) }}",
    "options": {}
   }
  },
  {
   "id": "899c30bd-fc6c-491c-a867-50b0167543fd",
   "name": "Anotar el aviso en la hoja",
   "type": "n8n-nodes-base.httpRequest",
   "typeVersion": 4.2,
   "position": [
    1440,
    80
   ],
   "parameters": {
    "method": "PUT",
    "url": "={{ 'https://sheets.googleapis.com/v4/spreadsheets/' + $('Configuración').first().json.hojaId + '/values/' + encodeURIComponent('Clientes!F' + $('¿A quién le toca hoy?').item.json.fila + ':G' + $('¿A quién le toca hoy?').item.json.fila) + '?valueInputOption=RAW' }}",
    "authentication": "predefinedCredentialType",
    "nodeCredentialType": "googleSheetsOAuth2Api",
    "sendBody": true,
    "specifyBody": "json",
    "jsonBody": "={{ JSON.stringify({ values: [[$('¿A quién le toca hoy?').item.json.escalon, $('Crear el link de pago').item.json.init_point]] }) }}",
    "options": {}
   }
  },
  {
   "id": "6db5a47e-35d5-4485-906f-27e0c8e6a35b",
   "name": "¿Pasa a una persona?",
   "type": "n8n-nodes-base.if",
   "typeVersion": 2.2,
   "position": [
    1680,
    80
   ],
   "parameters": {
    "conditions": {
     "options": {
      "caseSensitive": true,
      "leftValue": "",
      "typeValidation": "loose",
      "version": 2
     },
     "conditions": [
      {
       "id": "e783cde6-1299-4de3-bd65-e24bbe1aea29",
       "leftValue": "={{ $('¿A quién le toca hoy?').item.json.escalon }}",
       "rightValue": "persona",
       "operator": {
        "type": "string",
        "operation": "equals"
       }
      }
     ],
     "combinator": "and"
    },
    "options": {}
   }
  },
  {
   "id": "61195cb5-60e5-4376-905f-96348560ba4f",
   "name": "Avisarme: cobranza manual",
   "type": "n8n-nodes-base.telegram",
   "typeVersion": 1.2,
   "position": [
    1920,
    60
   ],
   "parameters": {
    "chatId": "={{ $('Configuración').first().json.telegramChatId }}",
    "text": "=<b>Cobranza para seguir a mano</b>: {{ $('¿A quién le toca hoy?').item.json.cliente }} · {{ $('¿A quién le toca hoy?').item.json.montoTexto }} · {{ $('¿A quién le toca hoy?').item.json.cuando }}",
    "additionalFields": {
     "parse_mode": "HTML",
     "appendAttribution": false
    }
   }
  },
  {
   "id": "c9761f7c-9a7b-4420-8894-14895311eacd",
   "name": "Aviso de Mercado Pago",
   "type": "n8n-nodes-base.webhook",
   "typeVersion": 2.1,
   "position": [
    0,
    480
   ],
   "parameters": {
    "httpMethod": "POST",
    "path": "mp-pagos",
    "options": {}
   },
   "webhookId": "fab2555c-ba9b-4117-8440-de83cf72a295"
  },
  {
   "id": "165a9b58-ee43-4203-99e2-370a1397f9c8",
   "name": "Configuración (pagos)",
   "type": "n8n-nodes-base.set",
   "typeVersion": 3.4,
   "position": [
    240,
    480
   ],
   "parameters": {
    "assignments": {
     "assignments": [
      {
       "id": "50fa0b4e-a41d-45a3-9874-0b3f3402955b",
       "name": "hojaId",
       "value": "ID_DE_TU_PLANILLA",
       "type": "string"
      },
      {
       "id": "e039d9df-598c-48c2-9ce9-ba211b3e4ebe",
       "name": "rango",
       "value": "Clientes!A:G",
       "type": "string"
      },
      {
       "id": "3a00de2d-bd76-49ba-b585-96c3fdd7ff80",
       "name": "negocio",
       "value": "Tu Negocio",
       "type": "string"
      },
      {
       "id": "73fc72f8-c088-4338-a352-a9ba51f8ca3c",
       "name": "moneda",
       "value": "ARS",
       "type": "string"
      },
      {
       "id": "79d47b1d-d1d5-4c31-8e74-a249afd964d7",
       "name": "zonaHoraria",
       "value": -3,
       "type": "number"
      },
      {
       "id": "fad4d806-2669-4868-96f7-40558ee444e1",
       "name": "waApiBase",
       "value": "https://graph.facebook.com/v23.0",
       "type": "string"
      },
      {
       "id": "fe173aa9-096f-4744-b6f2-9832591a96e9",
       "name": "phoneNumberId",
       "value": "TU_PHONE_NUMBER_ID",
       "type": "string"
      },
      {
       "id": "9c421f6e-71a7-4f19-aca5-6f6bb2c2a27d",
       "name": "plantilla",
       "value": "aviso_pago",
       "type": "string"
      },
      {
       "id": "13e0c4b4-1a42-4f56-81e4-85fc593bb191",
       "name": "idioma",
       "value": "es",
       "type": "string"
      },
      {
       "id": "aaa48c69-468b-47ed-bef2-58730da7acd5",
       "name": "avisoPagoUrl",
       "value": "https://TU-N8N/webhook/mp-pagos",
       "type": "string"
      },
      {
       "id": "bf47a686-1423-4c0e-a650-8b24be2bea8f",
       "name": "telegramChatId",
       "value": "TU_CHAT_ID",
       "type": "string"
      }
     ]
    },
    "includeOtherFields": true,
    "include": "all",
    "options": {}
   }
  },
  {
   "id": "073a9c6f-1f14-46f9-ba35-d3d4aeb50727",
   "name": "¿Es un pago?",
   "type": "n8n-nodes-base.if",
   "typeVersion": 2.2,
   "position": [
    480,
    480
   ],
   "parameters": {
    "conditions": {
     "options": {
      "caseSensitive": true,
      "leftValue": "",
      "typeValidation": "loose",
      "version": 2
     },
     "conditions": [
      {
       "id": "97568a6e-7ecf-4e0b-a832-8f93204ee2e8",
       "leftValue": "={{ ($json.body?.type || $json.body?.topic) === 'payment' && !!$json.body?.data?.id }}",
       "rightValue": "",
       "operator": {
        "type": "boolean",
        "operation": "true",
        "singleValue": true
       }
      }
     ],
     "combinator": "and"
    },
    "options": {}
   }
  },
  {
   "id": "8c963a39-5039-4321-b5f9-091d4793a938",
   "name": "Leer el pago",
   "type": "n8n-nodes-base.httpRequest",
   "typeVersion": 4.2,
   "position": [
    720,
    460
   ],
   "parameters": {
    "method": "GET",
    "url": "=https://api.mercadopago.com/v1/payments/{{ $json.body.data.id }}",
    "authentication": "genericCredentialType",
    "genericAuthType": "httpHeaderAuth",
    "options": {}
   }
  },
  {
   "id": "2b81d9b7-cfec-4c7e-94f5-be861b151ae6",
   "name": "Leer la hoja (pago)",
   "type": "n8n-nodes-base.httpRequest",
   "typeVersion": 4.2,
   "position": [
    960,
    460
   ],
   "parameters": {
    "method": "GET",
    "url": "={{ 'https://sheets.googleapis.com/v4/spreadsheets/' + $('Configuración (pagos)').first().json.hojaId + '/values/' + encodeURIComponent($('Configuración (pagos)').first().json.rango) }}",
    "authentication": "predefinedCredentialType",
    "nodeCredentialType": "googleSheetsOAuth2Api",
    "options": {}
   }
  },
  {
   "id": "b99ec7b8-49c7-4d60-9f0c-ef6f6beefc65",
   "name": "Conciliar el pago",
   "type": "n8n-nodes-base.code",
   "typeVersion": 2,
   "position": [
    1200,
    460
   ],
   "parameters": {
    "jsCode": "// ── Nodo «Conciliar el pago» (Code) ──\n// Entrada: el pago leído de la API de Mercado Pago (nunca el aviso: el aviso sólo trae el id) y la hoja «Clientes».\n// Salida: la fila a marcar como pagada, sólo si el pago está aprobado, es de esa fila (external_reference) y el monto\n// cubre lo adeudado (tolerancia del 0,5 %). Lo que no cierra va a revisar.\nconst pago = $('Leer el pago').first().json;\nconst filas = $input.first().json.values || [];\nconst fila = Number(pago.external_reference);\nconst f = filas[fila - 1] || [];\nconst adeudado = Number(String(f[2] || '').replace(/[^\\d,.-]/g, '').replace(/\\.(?=\\d{3}\\b)/g, '').replace(',', '.')) || 0;\nconst recibido = Number(pago.transaction_amount) || 0;\nconst cierra = pago.status === 'approved' && fila >= 2 && !!f[0] && recibido + Math.max(adeudado * 0.005, 0.5) >= adeudado;\nreturn [{ json: { fila, cliente: f[0] || '?', adeudado, recibido, pagoId: pago.id, estadoPago: pago.status, cierra,\n  motivo: cierra ? '' : pago.status !== 'approved' ? `El pago está ${pago.status}` : !f[0] ? 'El pago no corresponde a ninguna fila' : `Pagó ${recibido} de ${adeudado}` } }];\n"
   }
  },
  {
   "id": "89383fdf-819f-4a25-9eb6-b57057fb29fd",
   "name": "¿Cierra?",
   "type": "n8n-nodes-base.if",
   "typeVersion": 2.2,
   "position": [
    1440,
    460
   ],
   "parameters": {
    "conditions": {
     "options": {
      "caseSensitive": true,
      "leftValue": "",
      "typeValidation": "loose",
      "version": 2
     },
     "conditions": [
      {
       "id": "4c29ebac-2194-44f8-9346-5a3f3599637e",
       "leftValue": "={{ $json.cierra }}",
       "rightValue": "",
       "operator": {
        "type": "boolean",
        "operation": "true",
        "singleValue": true
       }
      }
     ],
     "combinator": "and"
    },
    "options": {}
   }
  },
  {
   "id": "084ef4e6-2ec7-4da0-9e1b-9298764999f3",
   "name": "Marcar como pagado",
   "type": "n8n-nodes-base.httpRequest",
   "typeVersion": 4.2,
   "position": [
    1680,
    400
   ],
   "parameters": {
    "method": "PUT",
    "url": "={{ 'https://sheets.googleapis.com/v4/spreadsheets/' + $('Configuración (pagos)').first().json.hojaId + '/values/' + encodeURIComponent('Clientes!E' + $json.fila) + '?valueInputOption=RAW' }}",
    "authentication": "predefinedCredentialType",
    "nodeCredentialType": "googleSheetsOAuth2Api",
    "sendBody": true,
    "specifyBody": "json",
    "jsonBody": "={{ JSON.stringify({ values: [[\"pagado\"]] }) }}",
    "options": {}
   }
  },
  {
   "id": "aa4b5e1d-6de4-4045-990e-19458a293b4e",
   "name": "Avisarme: cobrado",
   "type": "n8n-nodes-base.telegram",
   "typeVersion": 1.2,
   "position": [
    1920,
    400
   ],
   "parameters": {
    "chatId": "={{ $('Configuración (pagos)').first().json.telegramChatId }}",
    "text": "=<b>Cobrado</b>: {{ $('Conciliar el pago').item.json.cliente }} pagó {{ $('Conciliar el pago').item.json.recibido }} por Mercado Pago",
    "additionalFields": {
     "parse_mode": "HTML",
     "appendAttribution": false
    }
   }
  },
  {
   "id": "9c40e99a-11b1-4fde-a671-617a7742c073",
   "name": "Avisarme: revisar el pago",
   "type": "n8n-nodes-base.telegram",
   "typeVersion": 1.2,
   "position": [
    1680,
    560
   ],
   "parameters": {
    "chatId": "={{ $('Configuración (pagos)').first().json.telegramChatId }}",
    "text": "=<b>Pago para revisar</b> (fila {{ $json.fila }}): {{ $json.motivo }}",
    "additionalFields": {
     "parse_mode": "HTML",
     "appendAttribution": false
    }
   }
  }
 ],
 "settings": {
  "executionOrder": "v1",
  "timezone": "America/Argentina/Buenos_Aires"
 },
 "pinData": {},
 "connections": {
  "Todos los días a las 10": {
   "main": [
    [
     {
      "node": "Configuración",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "Probar ahora": {
   "main": [
    [
     {
      "node": "Configuración",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "Configuración": {
   "main": [
    [
     {
      "node": "Leer la hoja Clientes",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "Leer la hoja Clientes": {
   "main": [
    [
     {
      "node": "¿A quién le toca hoy?",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "¿A quién le toca hoy?": {
   "main": [
    [
     {
      "node": "Crear el link de pago",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "Crear el link de pago": {
   "main": [
    [
     {
      "node": "Enviar el aviso por WhatsApp",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "Enviar el aviso por WhatsApp": {
   "main": [
    [
     {
      "node": "Anotar el aviso en la hoja",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "Anotar el aviso en la hoja": {
   "main": [
    [
     {
      "node": "¿Pasa a una persona?",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "¿Pasa a una persona?": {
   "main": [
    [
     {
      "node": "Avisarme: cobranza manual",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "Aviso de Mercado Pago": {
   "main": [
    [
     {
      "node": "Configuración (pagos)",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "Configuración (pagos)": {
   "main": [
    [
     {
      "node": "¿Es un pago?",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "¿Es un pago?": {
   "main": [
    [
     {
      "node": "Leer el pago",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "Leer el pago": {
   "main": [
    [
     {
      "node": "Leer la hoja (pago)",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "Leer la hoja (pago)": {
   "main": [
    [
     {
      "node": "Conciliar el pago",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "Conciliar el pago": {
   "main": [
    [
     {
      "node": "¿Cierra?",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "¿Cierra?": {
   "main": [
    [
     {
      "node": "Marcar como pagado",
      "type": "main",
      "index": 0
     }
    ],
    [
     {
      "node": "Avisarme: revisar el pago",
      "type": "main",
      "index": 0
     }
    ]
   ]
  },
  "Marcar como pagado": {
   "main": [
    [
     {
      "node": "Avisarme: cobrado",
      "type": "main",
      "index": 0
     }
    ]
   ]
  }
 }
}