AutomationFlowsFinance › Multi-platform Revenue Reconciliation Across Stripe, Paypal & Bank with Tax…

Multi-platform Revenue Reconciliation Across Stripe, Paypal & Bank with Tax…

Original n8n title: Multi-platform Revenue Reconciliation Across Stripe, Paypal & Bank with Tax Archive

ByCheng Siong Chin @cschin on n8n.io

This workflow automates monthly revenue reconciliation across Stripe, PayPal, and bank statements by standardizing data formats, detecting discrepancies, and producing audit-ready reports. It concurrently retrieves revenue data from multiple sources, normalizes datasets into…

Cron / scheduled trigger★★★★☆ complexity22 nodesHTTP RequestGoogle DriveGmail
Finance Trigger: Cron / scheduled Nodes: 22 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #12033 — we link there as the canonical source.

This workflow follows the Gmail → Google Drive recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "id": "hnrLA7lWf3bSl7Dw",
  "name": "Automated Revenue Reconciliation & Tax Evidence Archive",
  "tags": [],
  "nodes": [
    {
      "id": "bbb0bbe0-fd8f-46cd-bd2f-c4826efcca19",
      "name": "Monthly Schedule",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -448,
        192
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "months",
              "triggerAtHour": 2
            }
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "6d08da53-983a-438c-b164-ff86ce7c1b47",
      "name": "Workflow Configuration",
      "type": "n8n-nodes-base.set",
      "position": [
        -224,
        192
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "stripeApiUrl",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__Stripe API endpoint URL__>"
            },
            {
              "id": "id-2",
              "name": "paypalApiUrl",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__PayPal API endpoint URL__>"
            },
            {
              "id": "id-3",
              "name": "bankApiUrl",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__Bank API endpoint URL__>"
            },
            {
              "id": "id-4",
              "name": "archiveFolderId",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__Google Drive folder ID for archive__>"
            },
            {
              "id": "id-5",
              "name": "taxAgentEmail",
              "type": "string",
              "value": "<__PLACEHOLDER_VALUE__Tax agent email address__>"
            },
            {
              "id": "id-6",
              "name": "reconciliationThreshold",
              "type": "number",
              "value": 0.01
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "ead117d5-1bee-4c3f-bd93-1810f7f9600a",
      "name": "Get Stripe Revenue",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        0,
        0
      ],
      "parameters": {
        "url": "={{ $('Workflow Configuration').first().json.stripeApiUrl }}",
        "options": {},
        "sendQuery": true,
        "authentication": "predefinedCredentialType",
        "queryParameters": {
          "parameters": [
            {
              "name": "created[gte]",
              "value": "={{ $now.minus({ months: 1 }).startOf('month').toUnixInteger() }}"
            },
            {
              "name": "created[lt]",
              "value": "={{ $now.startOf('month').toUnixInteger() }}"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "43052c70-6f3f-49b7-aa1a-22f8db32790d",
      "name": "Get PayPal Revenue",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        0,
        192
      ],
      "parameters": {
        "url": "={{ $('Workflow Configuration').first().json.paypalApiUrl }}",
        "options": {},
        "sendQuery": true,
        "authentication": "predefinedCredentialType",
        "queryParameters": {
          "parameters": [
            {
              "name": "start_date",
              "value": "={{ $now.minus({ months: 1 }).startOf('month').toISO() }}"
            },
            {
              "name": "end_date",
              "value": "={{ $now.startOf('month').toISO() }}"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "8efb034f-82d7-43db-aa89-96ca9bb14f63",
      "name": "Get Bank Statements",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        224,
        384
      ],
      "parameters": {
        "url": "={{ $('Workflow Configuration').first().json.bankApiUrl }}",
        "options": {},
        "sendQuery": true,
        "authentication": "predefinedCredentialType",
        "queryParameters": {
          "parameters": [
            {
              "name": "from_date",
              "value": "={{ $now.minus({ months: 1 }).startOf('month').toISODate() }}"
            },
            {
              "name": "to_date",
              "value": "={{ $now.startOf('month').toISODate() }}"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "ba8740a8-2dcb-4513-94e0-047e96cc0f26",
      "name": "Normalize Stripe Data",
      "type": "n8n-nodes-base.set",
      "position": [
        224,
        0
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "transactionId",
              "type": "string",
              "value": "={{ $json.id }}"
            },
            {
              "id": "id-2",
              "name": "amount",
              "type": "number",
              "value": "={{ $json.amount / 100 }}"
            },
            {
              "id": "id-3",
              "name": "currency",
              "type": "string",
              "value": "={{ $json.currency }}"
            },
            {
              "id": "id-4",
              "name": "date",
              "type": "string",
              "value": "={{ $json.created }}"
            },
            {
              "id": "id-5",
              "name": "source",
              "type": "string",
              "value": "Stripe"
            },
            {
              "id": "id-6",
              "name": "description",
              "type": "string",
              "value": "={{ $json.description }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "131da027-2258-4957-a297-c232325e4ea5",
      "name": "Normalize PayPal Data",
      "type": "n8n-nodes-base.set",
      "position": [
        224,
        192
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "transactionId",
              "type": "string",
              "value": "={{ $json.transaction_info.transaction_id }}"
            },
            {
              "id": "id-2",
              "name": "amount",
              "type": "number",
              "value": "={{ $json.transaction_info.transaction_amount.value }}"
            },
            {
              "id": "id-3",
              "name": "currency",
              "type": "string",
              "value": "={{ $json.transaction_info.transaction_amount.currency_code }}"
            },
            {
              "id": "id-4",
              "name": "date",
              "type": "string",
              "value": "={{ $json.transaction_info.transaction_initiation_date }}"
            },
            {
              "id": "id-5",
              "name": "source",
              "type": "string",
              "value": "PayPal"
            },
            {
              "id": "id-6",
              "name": "description",
              "type": "string",
              "value": "={{ $json.transaction_info.transaction_subject }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "ac495591-815b-4443-a661-be7aa94af167",
      "name": "Normalize Bank Data",
      "type": "n8n-nodes-base.set",
      "position": [
        448,
        384
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "transactionId",
              "type": "string",
              "value": "={{ $json.transaction_id }}"
            },
            {
              "id": "id-2",
              "name": "amount",
              "type": "number",
              "value": "={{ $json.amount }}"
            },
            {
              "id": "id-3",
              "name": "currency",
              "type": "string",
              "value": "={{ $json.currency }}"
            },
            {
              "id": "id-4",
              "name": "date",
              "type": "string",
              "value": "={{ $json.booking_date }}"
            },
            {
              "id": "id-5",
              "name": "source",
              "type": "string",
              "value": "Bank"
            },
            {
              "id": "id-6",
              "name": "description",
              "type": "string",
              "value": "={{ $json.remittance_information }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "6e01a2e2-2224-441c-983e-91ed044382ee",
      "name": "Combine Revenue Sources",
      "type": "n8n-nodes-base.merge",
      "position": [
        448,
        96
      ],
      "parameters": {},
      "typeVersion": 3.2
    },
    {
      "id": "a2d97894-1d5d-48b0-a05d-26a2bf3db021",
      "name": "Reconcile Revenue vs Bank",
      "type": "n8n-nodes-base.code",
      "position": [
        672,
        192
      ],
      "parameters": {
        "jsCode": "// Reconcile revenue sources against bank statements\nconst items = $input.all();\n\n// Separate revenue sources from bank data\nconst revenueItems = [];\nconst bankItems = [];\n\nfor (const item of items) {\n  if (item.json.source === 'stripe' || item.json.source === 'paypal') {\n    revenueItems.push(item.json);\n  } else if (item.json.source === 'bank') {\n    bankItems.push(item.json);\n  }\n}\n\n// Calculate total revenue from payment processors\nconst totalRevenue = revenueItems.reduce((sum, item) => {\n  return sum + (parseFloat(item.amount) || 0);\n}, 0);\n\n// Calculate total deposits from bank\nconst totalBankDeposits = bankItems.reduce((sum, item) => {\n  return sum + (parseFloat(item.amount) || 0);\n}, 0);\n\n// Calculate difference\nconst difference = totalRevenue - totalBankDeposits;\nconst differencePercentage = totalBankDeposits > 0 \n  ? ((difference / totalBankDeposits) * 100).toFixed(2)\n  : 0;\n\n// Create reconciliation report\nconst reconciliation = {\n  totalRevenue: totalRevenue.toFixed(2),\n  totalBankDeposits: totalBankDeposits.toFixed(2),\n  difference: difference.toFixed(2),\n  differencePercentage: differencePercentage,\n  hasMismatch: Math.abs(difference) > 0.01, // Allow for small rounding differences\n  revenueCount: revenueItems.length,\n  bankTransactionCount: bankItems.length,\n  reconciliationDate: new Date().toISOString(),\n  revenueBreakdown: {\n    stripe: revenueItems.filter(i => i.source === 'stripe').reduce((sum, i) => sum + parseFloat(i.amount || 0), 0).toFixed(2),\n    paypal: revenueItems.filter(i => i.source === 'paypal').reduce((sum, i) => sum + parseFloat(i.amount || 0), 0).toFixed(2)\n  },\n  allTransactions: [...revenueItems, ...bankItems]\n};\n\nreturn [{ json: reconciliation }];"
      },
      "typeVersion": 2
    },
    {
      "id": "dfdd2e11-c8f1-4504-b13e-d7137c59b2a1",
      "name": "Check for Mismatches",
      "type": "n8n-nodes-base.if",
      "position": [
        896,
        192
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "id-1",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $('Reconcile Revenue vs Bank').item.json.hasMismatches }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "858def70-5a67-4b79-8c2a-e3528082f850",
      "name": "Flag Mismatches",
      "type": "n8n-nodes-base.set",
      "position": [
        1120,
        128
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "id-1",
              "name": "mismatchFlag",
              "type": "string",
              "value": "ATTENTION REQUIRED"
            },
            {
              "id": "id-2",
              "name": "reviewRequired",
              "type": "boolean",
              "value": true
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "c907f41d-c860-4d1a-8708-d26a62dc4786",
      "name": "Aggregate Monthly Data",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        1344,
        192
      ],
      "parameters": {
        "options": {},
        "aggregate": "aggregateAllItemData"
      },
      "typeVersion": 1
    },
    {
      "id": "c64b6ad1-cc8e-4dc7-bc93-7598b0379e8e",
      "name": "Generate Audit Report",
      "type": "n8n-nodes-base.code",
      "position": [
        1568,
        192
      ],
      "parameters": {
        "jsCode": "// Generate comprehensive audit-proof monthly revenue report\nconst items = $input.all();\n\n// Extract date range from workflow configuration\nconst currentDate = new Date();\nconst reportMonth = currentDate.toLocaleString('default', { month: 'long', year: 'numeric' });\n\n// Aggregate data from all items\nlet totalStripeRevenue = 0;\nlet totalPayPalRevenue = 0;\nlet totalBankDeposits = 0;\nlet transactions = [];\nlet mismatches = [];\n\nfor (const item of items) {\n  const data = item.json;\n  \n  // Collect transaction details\n  if (data.source) {\n    transactions.push({\n      date: data.date || data.transaction_date,\n      source: data.source,\n      amount: data.amount,\n      description: data.description || data.memo,\n      transactionId: data.transaction_id || data.id\n    });\n  }\n  \n  // Aggregate revenue by source\n  if (data.source === 'Stripe') {\n    totalStripeRevenue += parseFloat(data.amount || 0);\n  } else if (data.source === 'PayPal') {\n    totalPayPalRevenue += parseFloat(data.amount || 0);\n  } else if (data.source === 'Bank') {\n    totalBankDeposits += parseFloat(data.amount || 0);\n  }\n  \n  // Collect mismatches\n  if (data.mismatch_flag || data.reconciliation_status === 'mismatch') {\n    mismatches.push({\n      date: data.date,\n      expectedAmount: data.expected_amount,\n      actualAmount: data.actual_amount,\n      difference: data.difference,\n      notes: data.notes\n    });\n  }\n}\n\n// Calculate totals and variance\nconst totalRevenue = totalStripeRevenue + totalPayPalRevenue;\nconst variance = totalBankDeposits - totalRevenue;\nconst variancePercentage = totalRevenue > 0 ? ((variance / totalRevenue) * 100).toFixed(2) : 0;\n\n// Generate report structure\nconst report = {\n  reportMetadata: {\n    title: `Monthly Revenue Reconciliation Report - ${reportMonth}`,\n    generatedDate: currentDate.toISOString(),\n    reportPeriod: reportMonth,\n    reportType: 'Tax Audit Evidence'\n  },\n  executiveSummary: {\n    totalStripeRevenue: totalStripeRevenue.toFixed(2),\n    totalPayPalRevenue: totalPayPalRevenue.toFixed(2),\n    totalRevenue: totalRevenue.toFixed(2),\n    totalBankDeposits: totalBankDeposits.toFixed(2),\n    variance: variance.toFixed(2),\n    variancePercentage: `${variancePercentage}%`,\n    reconciliationStatus: Math.abs(variance) < 1 ? 'RECONCILED' : 'VARIANCE DETECTED',\n    totalTransactions: transactions.length,\n    mismatchCount: mismatches.length\n  },\n  revenueBreakdown: {\n    stripe: {\n      total: totalStripeRevenue.toFixed(2),\n      percentage: totalRevenue > 0 ? ((totalStripeRevenue / totalRevenue) * 100).toFixed(2) + '%' : '0%'\n    },\n    paypal: {\n      total: totalPayPalRevenue.toFixed(2),\n      percentage: totalRevenue > 0 ? ((totalPayPalRevenue / totalRevenue) * 100).toFixed(2) + '%' : '0%'\n    }\n  },\n  transactions: transactions.sort((a, b) => new Date(a.date) - new Date(b.date)),\n  mismatches: mismatches,\n  reconciliationNotes: {\n    methodology: 'Automated reconciliation comparing payment processor revenue against bank deposits',\n    toleranceThreshold: '$1.00',\n    dataSource: 'Stripe API, PayPal API, Bank Statements',\n    verificationStatus: mismatches.length === 0 ? 'All transactions verified' : `${mismatches.length} discrepancies require review`\n  },\n  auditTrail: {\n    workflowExecutionId: $execution.id,\n    executionDate: currentDate.toISOString(),\n    dataIntegrity: 'Verified',\n    complianceStandard: 'Tax Authority Requirements'\n  }\n};\n\n// Generate PDF-ready HTML content\nconst htmlReport = `\n<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"UTF-8\">\n  <title>Revenue Reconciliation Report - ${reportMonth}</title>\n  <style>\n    body { font-family: Arial, sans-serif; margin: 40px; color: #333; }\n    h1 { color: #2c3e50; border-bottom: 3px solid #3498db; padding-bottom: 10px; }\n    h2 { color: #34495e; margin-top: 30px; border-bottom: 1px solid #bdc3c7; padding-bottom: 5px; }\n    .summary-box { background: #ecf0f1; padding: 20px; border-radius: 5px; margin: 20px 0; }\n    .status-ok { color: #27ae60; font-weight: bold; }\n    .status-warning { color: #e74c3c; font-weight: bold; }\n    table { width: 100%; border-collapse: collapse; margin: 20px 0; }\n    th { background: #34495e; color: white; padding: 12px; text-align: left; }\n    td { padding: 10px; border-bottom: 1px solid #ddd; }\n    tr:hover { background: #f5f5f5; }\n    .amount { text-align: right; font-family: monospace; }\n    .footer { margin-top: 50px; font-size: 12px; color: #7f8c8d; border-top: 1px solid #bdc3c7; padding-top: 20px; }\n  </style>\n</head>\n<body>\n  <h1>${report.reportMetadata.title}</h1>\n  <p><strong>Generated:</strong> ${new Date(report.reportMetadata.generatedDate).toLocaleString()}</p>\n  <p><strong>Report Type:</strong> ${report.reportMetadata.reportType}</p>\n  \n  <div class=\"summary-box\">\n    <h2>Executive Summary</h2>\n    <table>\n      <tr><td><strong>Total Stripe Revenue:</strong></td><td class=\"amount\">$${report.executiveSummary.totalStripeRevenue}</td></tr>\n      <tr><td><strong>Total PayPal Revenue:</strong></td><td class=\"amount\">$${report.executiveSummary.totalPayPalRevenue}</td></tr>\n      <tr><td><strong>Total Revenue:</strong></td><td class=\"amount\">$${report.executiveSummary.totalRevenue}</td></tr>\n      <tr><td><strong>Total Bank Deposits:</strong></td><td class=\"amount\">$${report.executiveSummary.totalBankDeposits}</td></tr>\n      <tr><td><strong>Variance:</strong></td><td class=\"amount ${Math.abs(variance) < 1 ? 'status-ok' : 'status-warning'}\">$${report.executiveSummary.variance} (${report.executiveSummary.variancePercentage})</td></tr>\n      <tr><td><strong>Status:</strong></td><td class=\"${Math.abs(variance) < 1 ? 'status-ok' : 'status-warning'}\">${report.executiveSummary.reconciliationStatus}</td></tr>\n      <tr><td><strong>Total Transactions:</strong></td><td>${report.executiveSummary.totalTransactions}</td></tr>\n      <tr><td><strong>Mismatches:</strong></td><td class=\"${mismatches.length === 0 ? 'status-ok' : 'status-warning'}\">${report.executiveSummary.mismatchCount}</td></tr>\n    </table>\n  </div>\n  \n  <h2>Revenue Breakdown by Source</h2>\n  <table>\n    <tr><th>Source</th><th>Amount</th><th>Percentage</th></tr>\n    <tr><td>Stripe</td><td class=\"amount\">$${report.revenueBreakdown.stripe.total}</td><td class=\"amount\">${report.revenueBreakdown.stripe.percentage}</td></tr>\n    <tr><td>PayPal</td><td class=\"amount\">$${report.revenueBreakdown.paypal.total}</td><td class=\"amount\">${report.revenueBreakdown.paypal.percentage}</td></tr>\n  </table>\n  \n  <h2>Transaction Details</h2>\n  <table>\n    <tr><th>Date</th><th>Source</th><th>Transaction ID</th><th>Description</th><th>Amount</th></tr>\n    ${transactions.map(t => `\n      <tr>\n        <td>${new Date(t.date).toLocaleDateString()}</td>\n        <td>${t.source}</td>\n        <td>${t.transactionId}</td>\n        <td>${t.description}</td>\n        <td class=\"amount\">$${parseFloat(t.amount).toFixed(2)}</td>\n      </tr>\n    `).join('')}\n  </table>\n  \n  ${mismatches.length > 0 ? `\n    <h2>Discrepancies Requiring Review</h2>\n    <table>\n      <tr><th>Date</th><th>Expected</th><th>Actual</th><th>Difference</th><th>Notes</th></tr>\n      ${mismatches.map(m => `\n        <tr>\n          <td>${new Date(m.date).toLocaleDateString()}</td>\n          <td class=\"amount\">$${parseFloat(m.expectedAmount).toFixed(2)}</td>\n          <td class=\"amount\">$${parseFloat(m.actualAmount).toFixed(2)}</td>\n          <td class=\"amount status-warning\">$${parseFloat(m.difference).toFixed(2)}</td>\n          <td>${m.notes}</td>\n        </tr>\n      `).join('')}\n    </table>\n  ` : '<p class=\"status-ok\">No discrepancies detected. All transactions reconciled successfully.</p>'}\n  \n  <h2>Reconciliation Methodology</h2>\n  <p>${report.reconciliationNotes.methodology}</p>\n  <p><strong>Tolerance Threshold:</strong> ${report.reconciliationNotes.toleranceThreshold}</p>\n  <p><strong>Data Sources:</strong> ${report.reconciliationNotes.dataSource}</p>\n  <p><strong>Verification Status:</strong> ${report.reconciliationNotes.verificationStatus}</p>\n  \n  <div class=\"footer\">\n    <h2>Audit Trail</h2>\n    <p><strong>Workflow Execution ID:</strong> ${report.auditTrail.workflowExecutionId}</p>\n    <p><strong>Execution Date:</strong> ${new Date(report.auditTrail.executionDate).toLocaleString()}</p>\n    <p><strong>Data Integrity:</strong> ${report.auditTrail.dataIntegrity}</p>\n    <p><strong>Compliance Standard:</strong> ${report.auditTrail.complianceStandard}</p>\n    <p><em>This report was automatically generated by an audited workflow process and contains verified financial data suitable for tax authority submission.</em></p>\n  </div>\n</body>\n</html>\n`;\n\n// Return both JSON and HTML formats\nreturn [\n  {\n    json: {\n      report: report,\n      htmlReport: htmlReport,\n      fileName: `Revenue_Reconciliation_${reportMonth.replace(' ', '_')}_${currentDate.toISOString().split('T')[0]}.html`,\n      pdfReady: true,\n      summary: {\n        totalRevenue: totalRevenue,\n        totalBankDeposits: totalBankDeposits,\n        variance: variance,\n        status: Math.abs(variance) < 1 ? 'RECONCILED' : 'VARIANCE_DETECTED',\n        mismatchCount: mismatches.length\n      }\n    }\n  }\n];"
      },
      "typeVersion": 2
    },
    {
      "id": "511a0aa7-79b0-4a62-a21c-daee9e7796f3",
      "name": "Upload to Archive",
      "type": "n8n-nodes-base.googleDrive",
      "position": [
        1792,
        192
      ],
      "parameters": {
        "name": "={{ 'Revenue_Report_' + $now.minus({ months: 1 }).toFormat('yyyy-MM') + '.pdf' }}",
        "driveId": {
          "__rl": true,
          "mode": "list",
          "value": "My Drive"
        },
        "options": {},
        "folderId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $('Workflow Configuration').first().json.archiveFolderId }}"
        }
      },
      "credentials": {
        "googleDriveOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 3
    },
    {
      "id": "f11ee402-4b02-49d6-869f-fbcd9a151fd0",
      "name": "Notify Tax Agent",
      "type": "n8n-nodes-base.gmail",
      "position": [
        2016,
        192
      ],
      "parameters": {
        "sendTo": "={{ $('Workflow Configuration').first().json.taxAgentEmail }}",
        "message": "=<h2>Monthly Revenue Report</h2>\n\n<p>Dear Tax Agent,</p>\n\n<p>The automated revenue reconciliation for {{ $now.minus({ months: 1 }).toFormat('MMMM yyyy') }} has been completed.</p>\n\n<h3>Report Summary:</h3>\n<ul>\n  <li><strong>Total Revenue:</strong> {{ $('Aggregate Monthly Data').first().json.totalRevenue }}</li>\n  <li><strong>Stripe Revenue:</strong> {{ $('Aggregate Monthly Data').first().json.stripeRevenue }}</li>\n  <li><strong>PayPal Revenue:</strong> {{ $('Aggregate Monthly Data').first().json.paypalRevenue }}</li>\n  <li><strong>Bank Deposits:</strong> {{ $('Aggregate Monthly Data').first().json.bankDeposits }}</li>\n</ul>\n\n{{ $('Flag Mismatches').all().length > 0 ? '<h3 style=\"color: #ff6b6b;\">\u26a0\ufe0f Mismatches Detected:</h3><p>There are ' + $('Flag Mismatches').all().length + ' discrepancies that require review. Please check the detailed report.</p>' : '<p style=\"color: #51cf66;\">\u2713 All revenue sources reconciled successfully with no discrepancies.</p>' }}\n\n<h3>Archived Report:</h3>\n<p>The complete audit report has been uploaded to Google Drive:</p>\n<p><a href=\"{{ $('Upload to Archive').first().json.webViewLink }}\">View Report</a></p>\n\n<p>Best regards,<br>Automated Revenue Reconciliation System</p>",
        "options": {},
        "subject": "={{ 'Monthly Revenue Report Ready - ' + $now.minus({ months: 1 }).toFormat('MMMM yyyy') }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "22bc4a46-41f2-409a-8376-1f7b8081f26f",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        896,
        -416
      ],
      "parameters": {
        "color": 5,
        "width": 720,
        "height": 288,
        "content": "## Prerequisites\nStripe, PayPal, and bank statement accounts; API credentials for each source \n## Use Cases\nAccounting firms automating client revenue verification; multi-channel e-commerce businesses  \n## Customization\nAdd additional payment sources (Square, Shopify), adjust normalization rules for regional formats \n## Benefits\nEliminates manual reconciliation, detects discrepancies automatically "
      },
      "typeVersion": 1
    },
    {
      "id": "ebd4913f-814c-4fdd-bc36-41f3b187f965",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        384,
        -352
      ],
      "parameters": {
        "width": 464,
        "height": 208,
        "content": "## Setup Steps\n1. Configure Stripe, PayPal.\n2. Set up normalization rules for date, currency, and transaction ID mappings.\n3. Connect Google Drive for report archiving and Gmail for agent notifications.\n4. Define mismatch thresholds and reconciliation tolerance parameters."
      },
      "typeVersion": 1
    },
    {
      "id": "f04bccea-bc33-465f-a9cf-c83c55420662",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -448,
        -352
      ],
      "parameters": {
        "width": 800,
        "height": 224,
        "content": "## How It Works\nThis workflow automates monthly revenue reconciliation across Stripe, PayPal, and bank statements by standardizing data formats, detecting discrepancies, and producing audit-ready reports. It concurrently retrieves revenue data from multiple sources, normalizes datasets into consistent structures, consolidates records, and reconciles transactions against bank statements with intelligent mismatch detection. The system aggregates monthly totals, generates detailed audit reports with clearly flagged discrepancies, archives finalized outputs to Google Drive, and notifies tax agents. Designed for accounting firms, finance teams, and businesses, it enables automated revenue verification, multi-channel reconciliation, discrepancy identification, and compliance audit documentation without manual record matching or error-prone spreadsheet workflows."
      },
      "typeVersion": 1
    },
    {
      "id": "71f605f8-f4e9-4bc5-bd86-13f1e375cff0",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1504,
        -96
      ],
      "parameters": {
        "color": 7,
        "width": 656,
        "height": 688,
        "content": "## Generate Audit Report & Notification\nWhat: Compiles flagged mismatches, monthly aggregates into audit documentation.\nWhy: Provides compliance evidence, supports tax filing \n"
      },
      "typeVersion": 1
    },
    {
      "id": "99a328fb-27cb-4781-a415-bc349e543a1b",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        368,
        -96
      ],
      "parameters": {
        "color": 7,
        "width": 1120,
        "height": 704,
        "content": "## Reconcile & Flag Mismatches\nWhat: Compares aggregated revenue against bank statement totals and identifies discrepancies.\nWhy: Detects timing differences, missing transactions, and potential fraud  "
      },
      "typeVersion": 1
    },
    {
      "id": "1595a83a-4df7-46b6-91bc-7bde3fe23798",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -496,
        -96
      ],
      "parameters": {
        "color": 7,
        "width": 848,
        "height": 672,
        "content": "## Fetch Multi-Source Data\nWhat: Retrieves revenue records from Stripe, PayPal, and bank statements in parallel.\nWhy: Captures complete revenue picture across all payment and settlement channels."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "df95ae9d-7021-4834-b49d-efc9c1ca3243",
  "connections": {
    "Flag Mismatches": {
      "main": [
        [
          {
            "node": "Aggregate Monthly Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Monthly Schedule": {
      "main": [
        [
          {
            "node": "Workflow Configuration",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload to Archive": {
      "main": [
        [
          {
            "node": "Notify Tax Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get PayPal Revenue": {
      "main": [
        [
          {
            "node": "Normalize PayPal Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Stripe Revenue": {
      "main": [
        [
          {
            "node": "Normalize Stripe Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Bank Statements": {
      "main": [
        [
          {
            "node": "Normalize Bank Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Bank Data": {
      "main": [
        [
          {
            "node": "Reconcile Revenue vs Bank",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check for Mismatches": {
      "main": [
        [
          {
            "node": "Flag Mismatches",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Aggregate Monthly Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Audit Report": {
      "main": [
        [
          {
            "node": "Upload to Archive",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize PayPal Data": {
      "main": [
        [
          {
            "node": "Combine Revenue Sources",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Normalize Stripe Data": {
      "main": [
        [
          {
            "node": "Combine Revenue Sources",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate Monthly Data": {
      "main": [
        [
          {
            "node": "Generate Audit Report",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Workflow Configuration": {
      "main": [
        [
          {
            "node": "Get Stripe Revenue",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get PayPal Revenue",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get Bank Statements",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Combine Revenue Sources": {
      "main": [
        [
          {
            "node": "Reconcile Revenue vs Bank",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Reconcile Revenue vs Bank": {
      "main": [
        [
          {
            "node": "Check for Mismatches",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This workflow automates monthly revenue reconciliation across Stripe, PayPal, and bank statements by standardizing data formats, detecting discrepancies, and producing audit-ready reports. It concurrently retrieves revenue data from multiple sources, normalizes datasets into…

Source: https://n8n.io/workflows/12033/ — original creator credit. Request a take-down →

More Finance workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Finance

This workflow automatically generates and delivers professional invoice PDFs whenever a Stripe checkout session completes. It fetches the line items from Stripe, formats them into a clean invoice with

Stripe Trigger, HTTP Request, N8N Nodes Templatefox +2
Finance

How It Works Trigger: Watches for new emails in Gmail with PDF/image attachments. OCR: Sends the attachment to OCR.space API (https://ocr.space/OCRAPI) to extract invoice text. Parsing: Extracts key f

Gmail Trigger, Google Sheets, Slack +3
Finance

Automatically track Stripe invoices and create Google Calendar reminders for upcoming due dates. This workflow ensures you never miss a payment deadline by running daily checks, filtering invoices due

HTTP Request, Item Lists, Google Calendar
Finance

This automation manages Stripe disputes by fetching dispute data, formatting it, logging it into Google Sheets, updating related payment records, and notifying the customer via email. It ensures finan

HTTP Request, Google Sheets, Gmail
Finance

Small business owners, finance teams, accountants, and bookkeepers who use Xero for invoicing and want to improve cash flow by automating payment reminders. If you're spending time manually following

Xero, HTTP Request, Microsoft Outlook