季度/月度收入摘要发送至 Slack
高级
这是一个Content Creation, Multimodal AI领域的自动化工作流,包含 18 个节点。主要使用 Code, Merge, Slack, Stripe, HttpRequest 等节点。 使用财务洞察自动将月度及季度 Stripe 收入报告发送至 Slack
前置要求
- •Slack Bot Token 或 Webhook URL
- •Stripe API Key
- •可能需要目标 API 的认证凭证
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
"id": "2K34ps0eKEbH4n1w",
"meta": {
"instanceId": "8443f10082278c46aa5cf3acf8ff0f70061a2c58bce76efac814b16290845177",
"templateCredsSetupCompleted": true
},
"name": "季度/月度收入摘要发送至 Slack",
"tags": [],
"nodes": [
{
"id": "4ab84e5a-6892-4428-bda6-3a8bdb8a2255",
"name": "工作流描述",
"type": "n8n-nodes-base.stickyNote",
"position": [
-336,
272
],
"parameters": {
"color": 6,
"width": 310,
"height": 480,
"content": "## 📊 Stripe 财务报告工作流"
},
"typeVersion": 1
},
{
"id": "c7612bb2-02bc-4ca2-b8fe-cd1ae7562c41",
"name": "设置说明",
"type": "n8n-nodes-base.stickyNote",
"position": [
-384,
-96
],
"parameters": {
"color": 7,
"width": 358,
"height": 320,
"content": "## ⚙️ 设置说明"
},
"typeVersion": 1
},
{
"id": "29f2e1af-d3a6-4c02-b727-3b0f35084544",
"name": "月度调度 (每月第 1 天, 上午 9 点)",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
128,
128
],
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 9 1 */1 *"
}
]
}
},
"typeVersion": 1
},
{
"id": "7012ee9d-438f-40e8-9f17-f29be330a3a1",
"name": "季度调度 (每 3 个月的第 1 天, 上午 9 点)",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
128,
320
],
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 9 1 */3 *"
}
]
}
},
"typeVersion": 1
},
{
"id": "edac5bee-0e1f-468f-884a-ef99558c12e8",
"name": "调度说明",
"type": "n8n-nodes-base.stickyNote",
"position": [
16,
-144
],
"parameters": {
"color": 7,
"width": 342,
"height": 240,
"content": "## 🗓️ 调度配置"
},
"typeVersion": 1
},
{
"id": "eeef03a3-45f4-4bdd-ab84-0c43e7ad1afa",
"name": "计算日期范围",
"type": "n8n-nodes-base.code",
"position": [
352,
224
],
"parameters": {
"jsCode": "// Determine if this is monthly or quarterly based on current date\nconst now = new Date();\nconst currentMonth = now.getMonth() + 1; // getMonth() returns 0-11\n\n// Check if current month is a quarter start (Jan=1, Apr=4, Jul=7, Oct=10)\nconst isQuarterly = currentMonth % 3 === 1;\n\n// Calculate date ranges\nlet startDate, endDate, period;\n\nif (isQuarterly && [1, 4, 7, 10].includes(currentMonth)) {\n // Quarterly report - get previous quarter\n const quarterStartMonth = currentMonth - 3;\n startDate = new Date(now.getFullYear(), quarterStartMonth - 1, 1);\n if (quarterStartMonth <= 0) {\n startDate = new Date(now.getFullYear() - 1, quarterStartMonth + 12 - 1, 1);\n }\n endDate = new Date(now.getFullYear(), currentMonth - 1, 0); // Last day of previous month\n period = 'quarterly';\n} else {\n // Monthly report - get previous month\n startDate = new Date(now.getFullYear(), currentMonth - 2, 1);\n if (currentMonth === 1) {\n startDate = new Date(now.getFullYear() - 1, 11, 1); // December of previous year\n }\n endDate = new Date(now.getFullYear(), currentMonth - 1, 0); // Last day of previous month\n period = 'monthly';\n}\n\n// Convert to Unix timestamps for Stripe API\nconst startTimestamp = Math.floor(startDate.getTime() / 1000);\nconst endTimestamp = Math.floor(endDate.getTime() / 1000);\n\nreturn [{\n json: {\n startDate: startTimestamp,\n endDate: endTimestamp,\n period: period,\n startDateFormatted: startDate.toISOString().split('T')[0],\n endDateFormatted: endDate.toISOString().split('T')[0]\n }\n}];"
},
"typeVersion": 2
},
{
"id": "a66fb369-570e-4494-a2a9-ce2071757e4e",
"name": "日期范围说明",
"type": "n8n-nodes-base.stickyNote",
"position": [
288,
384
],
"parameters": {
"color": 7,
"width": 246,
"height": 288,
"content": "## 📅 日期范围逻辑"
},
"typeVersion": 1
},
{
"id": "5d537942-36cb-40e5-b20c-d36c26a1c94b",
"name": "获取 Stripe 费用",
"type": "n8n-nodes-base.stripe",
"position": [
576,
128
],
"parameters": {
"resource": "charge",
"operation": "getAll",
"returnAll": true
},
"credentials": {
"stripeApi": {
"id": "DV4tPpxjbOUkGfAx",
"name": "Stripe account"
}
},
"typeVersion": 1
},
{
"id": "f6301611-55a1-4598-ba5c-14ece63f4e55",
"name": "Stripe 数据说明",
"type": "n8n-nodes-base.stickyNote",
"position": [
464,
-128
],
"parameters": {
"color": 7,
"width": 326,
"height": 240,
"content": "## 💳 Stripe 数据收集"
},
"typeVersion": 1
},
{
"id": "d1f56e15-1867-4677-a044-0be5fc745288",
"name": "格式化 Slack 消息",
"type": "n8n-nodes-base.code",
"position": [
1248,
224
],
"parameters": {
"jsCode": "const data = $json;\n\n// Format currency\nconst formatCurrency = (amount) => {\n return new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: 'USD'\n }).format(amount);\n};\n\n// Format percentage\nconst formatPercentage = (value) => {\n return `${value}%`;\n};\n\n// Create period title from metadata\nconst periodTitle = `Revenue Report - ${data.metadata.period}`;\n\n// Build top customers text\nlet topCustomersText = '';\nif (data.customers.topCustomers && data.customers.topCustomers.length > 0) {\n topCustomersText = data.customers.topCustomers.map((customer, index) => {\n const customerName = customer.customer === 'Unknown Customer' ? \n 'Unknown Customer' : \n `Customer ${customer.customer.substring(4, 14)}...`; // Show more meaningful part of customer ID\n return ` ${index + 1}. ${customerName}: ${formatCurrency(customer.amount)}`;\n }).join('\\n');\n} else {\n topCustomersText = ' No customer data available';\n}\n\n// Build payment methods breakdown\nlet paymentMethodsText = '';\nif (data.payments.paymentMethodBreakdown) {\n paymentMethodsText = Object.entries(data.payments.paymentMethodBreakdown)\n .map(([method, amount]) => ` • ${method.charAt(0).toUpperCase() + method.slice(1)}: ${formatCurrency(amount)}`)\n .join('\\n');\n}\n\n// Build refund reasons text\nlet refundReasonsText = '';\nif (data.refundAnalysis.refundReasons && Object.keys(data.refundAnalysis.refundReasons).length > 0) {\n refundReasonsText = Object.entries(data.refundAnalysis.refundReasons)\n .map(([reason, count]) => ` • ${reason.replace(/_/g, ' ').replace(/\\b\\w/g, l => l.toUpperCase())}: ${count}`)\n .join('\\n');\n} else {\n refundReasonsText = ' No refunds in this period';\n}\n\n// Risk level summary\nconst riskSummary = `Low: ${data.payments.riskAnalysis.low} | Medium: ${data.payments.riskAnalysis.medium} | High: ${data.payments.riskAnalysis.high}`;\n\n// Build the comprehensive Slack message\nconst slackMessage = `📊 **${periodTitle}**\n📅 *Generated: ${new Date(data.metadata.dataProcessedAt).toLocaleDateString()}*\n\n💰 **Financial Summary:**\n• Total Revenue: ${formatCurrency(data.summary.totalRevenue)}\n• Total Refunds: ${formatCurrency(data.summary.totalRefunds)}\n• Net Revenue: ${formatCurrency(data.summary.netRevenue)}\n• Average Transaction: ${formatCurrency(data.summary.averageTransactionValue)}\n\n📈 **Growth Metrics:**\n• Estimated MRR: ${formatCurrency(data.growth.estimatedMRR)}\n• Estimated ARR: ${formatCurrency(data.growth.estimatedARR)}\n\n📊 **Transaction Analysis:**\n• Successful Transactions: ${data.summary.totalTransactions}\n• Refund Transactions: ${data.summary.totalRefundTransactions}\n• Refund Rate: ${formatPercentage(data.summary.refundRate)}\n• Unique Customers: ${data.customers.uniqueCustomers}\n\n🏆 **Top Customers by Revenue:**\n${topCustomersText}\n\n💳 **Payment Methods:**\n${paymentMethodsText}\n\n⚠️ **Risk Analysis:**\n${riskSummary}\n\n🔄 **Refund Analysis:**\n• Refund Rate: ${formatPercentage(data.refundAnalysis.refundRate)}\n**Refund Reasons:**\n${refundReasonsText}\n\n---\n*Data processed: ${data.metadata.chargesAnalyzed} charges, ${data.metadata.refundsAnalyzed} refunds*\n*Auto-generated from Stripe API*`;\n\nreturn [{\n json: {\n text: slackMessage,\n // Include original data for reference\n originalData: data,\n // Additional Slack formatting options\n blocks: [\n {\n \"type\": \"section\",\n \"text\": {\n \"type\": \"mrkdwn\",\n \"text\": slackMessage\n }\n }\n ]\n }\n}];"
},
"typeVersion": 2
},
{
"id": "ba6697a5-1aac-4b74-859f-6344c20fadfc",
"name": "Slack 格式化说明",
"type": "n8n-nodes-base.stickyNote",
"position": [
1184,
384
],
"parameters": {
"color": 7,
"width": 278,
"height": 288,
"content": "## 💬 Slack 消息格式化"
},
"typeVersion": 1
},
{
"id": "85f38b2b-9e5a-4cb6-a2cf-56687a822544",
"name": "计算财务指标",
"type": "n8n-nodes-base.code",
"position": [
1024,
224
],
"parameters": {
"jsCode": "// Get all merged data from the merge node\nconst allData = $input.all();\n\n// Separate charges and refunds based on the data structure\nlet charges = [];\nlet refunds = [];\n\n// Process each item from the merged data\nallData.forEach(item => {\n const data = item.json;\n \n // Check if this is a charge object\n if (data.object === 'charge') {\n charges.push(data);\n }\n // Check if this is refunds list object\n else if (data.object === 'list' && data.data) {\n // Extract refund objects from the list\n refunds = refunds.concat(data.data);\n }\n // Check if this is a single refund object\n else if (data.object === 'refund') {\n refunds.push(data);\n }\n});\n\n// Filter only successful/paid charges that are not refunded\nconst paidCharges = charges.filter(charge => \n charge.status === 'succeeded' && \n charge.paid === true &&\n charge.amount_refunded === 0 // This means not refunded\n);\n\n// Calculate total revenue from non-refunded charges\nconst totalRevenue = paidCharges.reduce((sum, charge) => {\n return sum + (charge.amount / 100); // Convert from cents to dollars\n}, 0);\n\n// Calculate total refunds amount\nconst totalRefundsAmount = refunds.reduce((sum, refund) => {\n return sum + (refund.amount / 100); // Convert from cents to dollars\n}, 0);\n\n// Calculate net revenue\nconst netRevenue = totalRevenue - totalRefundsAmount;\n\n// Get customer revenue breakdown\nconst customerRevenue = {};\npaidCharges.forEach(charge => {\n const customer = charge.customer || 'Unknown Customer';\n const amount = charge.amount / 100;\n customerRevenue[customer] = (customerRevenue[customer] || 0) + amount;\n});\n\n// Get top 3 customers by revenue\nconst topCustomers = Object.entries(customerRevenue)\n .sort(([,a], [,b]) => b - a)\n .slice(0, 3)\n .map(([customer, amount]) => ({ \n customer, \n amount: Math.round(amount * 100) / 100 \n }));\n\n// Calculate payment method breakdown\nconst paymentMethodBreakdown = {};\npaidCharges.forEach(charge => {\n const brand = charge.payment_method_details?.card?.brand || 'unknown';\n const amount = charge.amount / 100;\n paymentMethodBreakdown[brand] = (paymentMethodBreakdown[brand] || 0) + amount;\n});\n\n// Calculate average transaction value\nconst averageTransactionValue = paidCharges.length > 0 ? totalRevenue / paidCharges.length : 0;\n\n// Get current date for period estimation\nconst currentDate = new Date();\nconst currentMonth = currentDate.getMonth() + 1;\nconst currentYear = currentDate.getFullYear();\n\n// Simple MRR estimation (assuming monthly data)\nconst estimatedMRR = totalRevenue;\nconst estimatedARR = estimatedMRR * 12;\n\n// Risk analysis based on risk scores\nconst riskAnalysis = {\n low: paidCharges.filter(c => c.outcome?.risk_score <= 20).length,\n medium: paidCharges.filter(c => c.outcome?.risk_score > 20 && c.outcome?.risk_score <= 50).length,\n high: paidCharges.filter(c => c.outcome?.risk_score > 50).length\n};\n\n// Refund reasons breakdown\nconst refundReasons = {};\nrefunds.forEach(refund => {\n const reason = refund.reason || 'no_reason_given';\n refundReasons[reason] = (refundReasons[reason] || 0) + 1;\n});\n\nreturn [{\n json: {\n // Summary metrics\n summary: {\n totalRevenue: Math.round(totalRevenue * 100) / 100,\n totalRefunds: Math.round(totalRefundsAmount * 100) / 100,\n netRevenue: Math.round(netRevenue * 100) / 100,\n totalTransactions: paidCharges.length,\n totalRefundTransactions: refunds.length,\n averageTransactionValue: Math.round(averageTransactionValue * 100) / 100,\n refundRate: paidCharges.length > 0 ? Math.round((refunds.length / paidCharges.length) * 100 * 100) / 100 : 0\n },\n \n // Growth metrics\n growth: {\n estimatedMRR: Math.round(estimatedMRR * 100) / 100,\n estimatedARR: Math.round(estimatedARR * 100) / 100\n },\n \n // Customer insights\n customers: {\n topCustomers: topCustomers,\n uniqueCustomers: Object.keys(customerRevenue).length\n },\n \n // Payment analysis\n payments: {\n paymentMethodBreakdown: paymentMethodBreakdown,\n riskAnalysis: riskAnalysis\n },\n \n // Refund analysis\n refundAnalysis: {\n refundReasons: refundReasons,\n refundRate: totalRevenue > 0 ? Math.round((totalRefundsAmount / totalRevenue) * 100 * 100) / 100 : 0\n },\n \n // Metadata\n metadata: {\n dataProcessedAt: new Date().toISOString(),\n chargesAnalyzed: charges.length,\n refundsAnalyzed: refunds.length,\n period: `${currentMonth}/${currentYear}`\n }\n }\n}];"
},
"typeVersion": 2
},
{
"id": "7695ff48-1b74-4a29-9d1c-963515e0c7de",
"name": "财务指标说明",
"type": "n8n-nodes-base.stickyNote",
"position": [
960,
-240
],
"parameters": {
"color": 7,
"width": 246,
"height": 432,
"content": "## 📊 财务分析引擎"
},
"typeVersion": 1
},
{
"id": "ce84cff7-e8e7-474a-9c30-e93a032f2074",
"name": "合并",
"type": "n8n-nodes-base.merge",
"position": [
800,
224
],
"parameters": {},
"typeVersion": 3.2
},
{
"id": "6d4051e6-7972-47c4-99e1-57e109edcbb5",
"name": "合并说明",
"type": "n8n-nodes-base.stickyNote",
"position": [
752,
384
],
"parameters": {
"color": 7,
"width": 246,
"content": "## 🔄 数据合并器"
},
"typeVersion": 1
},
{
"id": "eb6df223-4d46-47f9-bd11-44a1b16b2c8b",
"name": "发送到 Slack",
"type": "n8n-nodes-base.slack",
"position": [
1472,
224
],
"webhookId": "4e77062d-a1d4-46ca-8d70-1a3bf3beaa71",
"parameters": {
"text": "={{ $json.text }}",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "list",
"value": "C09H21LK9BJ",
"cachedResultName": "reply-needed"
},
"otherOptions": {
"mrkdwn": true
}
},
"credentials": {
"slackApi": {
"id": "rNqvWj9TfChPVRYY",
"name": "Slack account"
}
},
"typeVersion": 2
},
{
"id": "c57a3d62-a880-46fd-95a6-5d9ea10b5e85",
"name": "Slack 投递说明",
"type": "n8n-nodes-base.stickyNote",
"position": [
1472,
-64
],
"parameters": {
"color": 7,
"width": 294,
"height": 240,
"content": "## 🚀 Slack 投递"
},
"typeVersion": 1
},
{
"id": "25c482b2-ffed-4faf-afd3-5148ded842f1",
"name": "获取 Stripe 退款",
"type": "n8n-nodes-base.httpRequest",
"position": [
576,
320
],
"parameters": {
"url": "https://api.stripe.com/v1/refunds",
"options": {},
"sendQuery": true,
"authentication": "predefinedCredentialType",
"queryParameters": {
"parameters": [
{
"name": "limit",
"value": "100"
}
]
},
"nodeCredentialType": "stripeApi"
},
"credentials": {
"stripeApi": {
"id": "DV4tPpxjbOUkGfAx",
"name": "Stripe account"
}
},
"typeVersion": 4.1
}
],
"active": false,
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"versionId": "7edbbd68-e5ad-4b54-a498-8d1b69da06a2",
"connections": {
"Merge": {
"main": [
[
{
"node": "Calculate Financial Metrics",
"type": "main",
"index": 0
}
]
]
},
"Get Stripe Charges": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 0
}
]
]
},
"Get Stripe Refunds": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 1
}
]
]
},
"Calculate Date Range": {
"main": [
[
{
"node": "Get Stripe Charges",
"type": "main",
"index": 0
},
{
"node": "Get Stripe Refunds",
"type": "main",
"index": 0
}
]
]
},
"Format Slack Message": {
"main": [
[
{
"node": "Send To Slack",
"type": "main",
"index": 0
}
]
]
},
"Calculate Financial Metrics": {
"main": [
[
{
"node": "Format Slack Message",
"type": "main",
"index": 0
}
]
]
},
"Monthly Schedule (1st day, 9 AM)": {
"main": [
[
{
"node": "Calculate Date Range",
"type": "main",
"index": 0
}
]
]
},
"Quarterly Schedule (1st day every 3 months, 9 AM)": {
"main": [
[
{
"node": "Calculate Date Range",
"type": "main",
"index": 0
}
]
]
}
}
}常见问题
如何使用这个工作流?
复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。
这个工作流适合什么场景?
高级 - 内容创作, 多模态 AI
需要付费吗?
本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。
相关工作流推荐
Airtable订单到Stripe发票(B2B/手动收款)
从Airtable订单创建Stripe发票,并使用Google表格记录
If
Code
Stripe
+5
19 节点Rahul Joshi
内容创作
Stripe 税务摘要到 Google Sheets 并附带 Slack 提醒
从 Stripe 生成税务摘要,存储到 Google Sheets,并发送 Slack 提醒
If
Set
Code
+5
17 节点Rahul Joshi
内容创作
从 Stripe 支付自动交付模板给客户
使用Stripe、GPT-4o和Gmail的自动化模板交付系统
If
Code
Gmail
+12
44 节点Rahul Joshi
客户关系管理
GoHighLevel管道速度跟踪器和自动化停滞交易提醒
使用GoHighLevel、Gmail和Slack分析管道速度并提醒停滞交易
If
Code
Gmail
+5
25 节点Rahul Joshi
内容创作
使用 GoHighLevel、Gmail 和 Notion 自动化 NPS 调查收集与响应处理
使用GoHighLevel、Gmail和Notion自动化NPS调查收集与响应处理
If
Code
Gmail
+5
27 节点Rahul Joshi
内容创作
CRM交易阶段更新
使用Stripe和Google Sheets的自动CRM交易阶段更新
Code
Split Out
Http Request
+3
13 节点Rahul Joshi
内容创作
工作流信息
难度等级
高级
节点数量18
分类2
节点类型7
作者
Rahul Joshi
@rahul08Rahul Joshi is a seasoned technology leader specializing in the n8n automation tool and AI-driven workflow automation. With deep expertise in building open-source workflow automation and self-hosted automation platforms, he helps organizations eliminate manual processes through intelligent n8n ai agent automation solutions.
外部链接
在 n8n.io 查看 →
分享此工作流