票务后端
高级
这是一个Content Creation、Multimodal AI领域的自动化工作流,包含 35 个节点。主要使用 If、Code、Gmail、Filter、Webhook 等节点。 活动票务后端:使用Google Sheets和Gmail自动生成二维码门票
前置要求
- •Google 账号和 Gmail API 凭证
- •HTTP Webhook 端点(n8n 会自动生成)
- •可能需要目标 API 的认证凭证
- •Google Sheets API 凭证
使用的节点 (35 个)
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
"id": "INTOFdov9bwbgo9w",
"meta": {
"instanceId": "c2650793f644091dc80fb900fe63448ad1f4b774008de9608064d67294f8307c"
},
"name": "票务后端",
"tags": [],
"nodes": [
{
"id": "06cc3525-a2a8-4b35-8436-73d03e43dbc7",
"name": "获取票据",
"type": "n8n-nodes-base.googleSheets",
"position": [
-240,
-96
],
"parameters": {
"options": {},
"filtersUI": {
"values": [
{
"lookupValue": "={{ $json.ticket_id }}",
"lookupColumn": "Ticket ID"
}
]
},
"sheetName": {
"__rl": true,
"mode": "list",
"value": 2010454173,
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1rQqe5XWmhWgnsAI6Q8kxoeCTbcNMdHsfP2tHRP7y7Po/edit#gid=2010454173",
"cachedResultName": "Tickets"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1rQqe5XWmhWgnsAI6Q8kxoeCTbcNMdHsfP2tHRP7y7Po",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1rQqe5XWmhWgnsAI6Q8kxoeCTbcNMdHsfP2tHRP7y7Po/edit?usp=drivesdk",
"cachedResultName": "TABRAK LARI"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "gnUSHhCw4cKx3pwo",
"name": "Project GCP: n8n-khmuhtadin"
}
},
"typeVersion": 4.7
},
{
"id": "4fe22443-1f46-4c85-8b93-670e2dfff603",
"name": "解析条形码",
"type": "n8n-nodes-base.code",
"position": [
-448,
-96
],
"parameters": {
"jsCode": "const data = $input.all()[0].json.body;\n\n// Parse the barcode JSON string\nlet ticketData;\ntry {\n ticketData = JSON.parse(data.barcode);\n} catch (error) {\n return [{\n json: {\n success: false,\n message: \"Invalid QR code format\"\n }\n }];\n}\n\nreturn [{\n json: {\n ticket_id: ticketData.ticket_id,\n event: ticketData.event,\n name: ticketData.name,\n email: ticketData.email,\n ticket_number: ticketData.ticket_number,\n total_tickets: ticketData.total_tickets,\n scannedAt: data.scannedAt\n }\n}];"
},
"typeVersion": 2
},
{
"id": "2276b656-ea51-4570-8896-d1fe28ebee45",
"name": "票据可用?",
"type": "n8n-nodes-base.if",
"position": [
-48,
-96
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "5d1d995d-3ee8-4b56-8555-9fe5b919497a",
"operator": {
"type": "number",
"operation": "gt"
},
"leftValue": "={{ $('Get Tickets').all().length }}",
"rightValue": 0
},
{
"id": "982727a9-3593-4d1c-9ab3-d1c26d0798fc",
"operator": {
"type": "string",
"operation": "notEquals"
},
"leftValue": "={{ $json[\"Checked In\"] }}",
"rightValue": "YES"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "d5265744-f1a6-4590-9c45-462297c5b4c7",
"name": "解析输出",
"type": "n8n-nodes-base.code",
"position": [
144,
16
],
"parameters": {
"jsCode": "const getTicketsResult = $('Get Tickets').all();\nconst parsedData = $('Parse Barcode').first().json;\n\nlet errorMessage = \"Ticket tidak valid\";\nlet errorCode = \"INVALID_TICKET\";\n\n// Check if ticket exists but already checked in\nif (getTicketsResult.length > 0) {\n const ticket = getTicketsResult[0].json;\n if (ticket[\"Checked In\"] === \"YES\") {\n errorMessage = `Ticket sudah di-scan pada ${ticket[\"Checkin TIme\"]}`;\n errorCode = \"ALREADY_CHECKED_IN\";\n }\n}\n\nreturn [{\n json: {\n success: false,\n error_code: errorCode,\n message: errorMessage,\n ticket_id: parsedData.ticket_id\n }\n}];"
},
"typeVersion": 2
},
{
"id": "2638bce7-9b67-491a-9bc1-8ffa2d08d4f8",
"name": "已签到",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
320,
16
],
"parameters": {
"options": {}
},
"typeVersion": 1.4
},
{
"id": "81b4773f-4728-4413-8950-12d79a27d257",
"name": "已签到",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
320,
-176
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "={\n \"success\": true,\n \"message\": \"Check-in berhasil!\",\n \"data\": {\n \"ticket_id\": \"{{ $('Get Tickets').first().json['Ticket ID'] }}\",\n \"name\": \"{{ $('Get Tickets').first().json.Nama }}\",\n \"ticket_number\": \"{{ $('Get Tickets').first().json['Ticket Number'] }}\",\n \"email\": \"{{ $('Get Tickets').first().json.Email }}\",\n \"checked_in_at\": \"{{ $json[\"Checkin TIme\"] }}\"\n }\n}"
},
"typeVersion": 1.4
},
{
"id": "8f9403c3-65b9-4a5b-956f-c31c1089c190",
"name": "更新票据状态",
"type": "n8n-nodes-base.googleSheets",
"position": [
144,
-176
],
"parameters": {
"columns": {
"value": {
"Ticket ID": "={{ $json[\"Ticket ID\"] }}",
"Checked In": "YES",
"Checkin TIme": "={{ new Date($('SCAN TICKET').item.json.body.scannedAt).toLocaleString('en-GB', { hour12: false }).replace(',', '') }}"
},
"schema": [
{
"id": "Ticket ID",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Ticket ID",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Email",
"type": "string",
"display": true,
"required": false,
"displayName": "Email",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Nama",
"type": "string",
"display": true,
"required": false,
"displayName": "Nama",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Ticket Number",
"type": "string",
"display": true,
"required": false,
"displayName": "Ticket Number",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Total Tickets",
"type": "string",
"display": true,
"required": false,
"displayName": "Total Tickets",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Checked In",
"type": "string",
"display": true,
"required": false,
"displayName": "Checked In",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Checkin TIme",
"type": "string",
"display": true,
"required": false,
"displayName": "Checkin TIme",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "row_number",
"type": "number",
"display": true,
"removed": true,
"readOnly": true,
"required": false,
"displayName": "row_number",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"Ticket ID"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "update",
"sheetName": {
"__rl": true,
"mode": "list",
"value": 2010454173,
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1rQqe5XWmhWgnsAI6Q8kxoeCTbcNMdHsfP2tHRP7y7Po/edit#gid=2010454173",
"cachedResultName": "Tickets"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1rQqe5XWmhWgnsAI6Q8kxoeCTbcNMdHsfP2tHRP7y7Po",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1rQqe5XWmhWgnsAI6Q8kxoeCTbcNMdHsfP2tHRP7y7Po/edit?usp=drivesdk",
"cachedResultName": "TABRAK LARI"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "gnUSHhCw4cKx3pwo",
"name": "Project GCP: n8n-khmuhtadin"
}
},
"typeVersion": 4.7
},
{
"id": "b6b18e4b-f70e-4870-b649-83980feb360e",
"name": "扫描票据",
"type": "n8n-nodes-base.webhook",
"position": [
-656,
-96
],
"webhookId": "492de38d-21ee-4f7d-adfe-dbad80dfa146",
"parameters": {
"path": "v1/scanner",
"options": {},
"httpMethod": "POST",
"responseMode": "responseNode"
},
"typeVersion": 2.1
},
{
"id": "a77f808a-f8c8-4d68-9007-744aa8018245",
"name": "邮箱存在?",
"type": "n8n-nodes-base.if",
"position": [
-48,
1168
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "5d1d995d-3ee8-4b56-8555-9fe5b919497a",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $json.row_number !== undefined }}",
"rightValue": "undefined"
}
]
},
"looseTypeValidation": true
},
"typeVersion": 2.2
},
{
"id": "08943fee-59bc-4aac-b9cf-80662be21dca",
"name": "获取参与者",
"type": "n8n-nodes-base.googleSheets",
"position": [
-240,
1168
],
"parameters": {
"options": {},
"filtersUI": {
"values": [
{
"lookupValue": "={{ $json.Email }}",
"lookupColumn": "Email"
}
]
},
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1rQqe5XWmhWgnsAI6Q8kxoeCTbcNMdHsfP2tHRP7y7Po/edit#gid=0",
"cachedResultName": "Register"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1rQqe5XWmhWgnsAI6Q8kxoeCTbcNMdHsfP2tHRP7y7Po",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1rQqe5XWmhWgnsAI6Q8kxoeCTbcNMdHsfP2tHRP7y7Po/edit?usp=drivesdk",
"cachedResultName": "TABRAK LARI"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "gnUSHhCw4cKx3pwo",
"name": "Project GCP: n8n-khmuhtadin"
}
},
"typeVersion": 4.7,
"alwaysOutputData": true
},
{
"id": "86af65a2-c014-4714-be6d-16083640acb4",
"name": "验证输入",
"type": "n8n-nodes-base.code",
"position": [
-688,
1328
],
"parameters": {
"jsCode": "const data = $input.all()[0].json.body;\n\n// Validation\nconst errors = [];\n\nif (!data.nama || data.nama.trim() === '') {\n errors.push('Nama wajib diisi');\n}\n\nif (!data.email || !data.email.includes('@')) {\n errors.push('Email tidak valid');\n}\n\nif (!data.no_hp || data.no_hp.length < 10) {\n errors.push('Nomor HP tidak valid');\n}\n\nif (!data.jumlah_tiket || data.jumlah_tiket < 1) {\n errors.push('Jumlah tiket minimal 1');\n}\n\nif (!data.payment_method) {\n errors.push('Payment method wajib dipilih');\n}\n\n// Return error if validation fails\nif (errors.length > 0) {\n return [{\n json: {\n valid: false,\n validation_error: true,\n errors: errors\n }\n }];\n}\n\n// Transform data for sheet\nreturn [{\n json: {\n valid: true,\n validation_error: false,\n Nama: data.nama,\n \"No Hp\": data.no_hp,\n Email: data.email,\n \"Jumlah TIket\": data.jumlah_tiket,\n \"Harga Tiket\": data.total_price,\n \"Payment Method \": data.payment_method,\n \"Payment Status\": \"PENDING\",\n \"Email Sent\": \"NO\",\n \"Ticked ID\": \"\"\n }\n}];"
},
"typeVersion": 2
},
{
"id": "0bb8c7fa-59d8-4185-954f-3a0858924bc7",
"name": "验证错误",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
-240,
1344
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "{\n \"success\": false,\n \"error\": \"VALIDATION_ERROR\",\n \"message\": \"Validasi gagal\",\n \"errors\": [\"Email tidak valid\"]\n}"
},
"typeVersion": 1.4
},
{
"id": "76dbc60d-7078-4b6e-ad79-cbbcdde7a9e7",
"name": "输入有效?",
"type": "n8n-nodes-base.if",
"position": [
-480,
1328
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "0248f448-237b-46da-86b8-cdc5058ec170",
"operator": {
"type": "boolean",
"operation": "false",
"singleValue": true
},
"leftValue": "={{ $json.validation_error }}",
"rightValue": "false"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "431640c5-e0a7-44e2-9b51-c11d0f6f6b6c",
"name": "已注册",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
160,
992
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "{\n \"success\": false,\n \"error\": \"DUPLICATE_EMAIL\",\n \"message\": \"Email sudah terdaftar\",\n \"existing_data\": {\n \"nama\": \"khairul\",\n \"payment_status\": \"PAID\",\n \"jumlah_tiket\": 2\n }\n}"
},
"typeVersion": 1.4
},
{
"id": "b9f61154-8a8b-4ea9-ae62-a1b9b316af72",
"name": "票据已预订",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
368,
1184
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "={\n \"success\": true,\n \"message\": \"Registrasi berhasil! Silakan lakukan pembayaran.\",\n \"data\": {\n \"nama\": \"{{ $json.Nama }}\",\n \"email\": \"{{ $json.Email }}\",\n \"jumlah_tiket\": {{ $json['Jumlah TIket'] }},\n \"total_price\": {{ $json['Harga Tiket'] }},\n \"payment_method\": \"{{ $json['Payment Method '] }}\",\n \"payment_status\": \"PENDING\"\n }\n}"
},
"typeVersion": 1.4
},
{
"id": "63255ef1-df60-41cf-a262-e45e61636139",
"name": "存储数据",
"type": "n8n-nodes-base.googleSheets",
"position": [
160,
1184
],
"parameters": {
"columns": {
"value": {
"Nama": "={{ $('Validate Input').item.json.Nama }}",
"Email": "={{ $('Validate Input').item.json.Email }}",
"No Hp": "={{ $('Validate Input').item.json['No Hp'] }}",
"Email Sent": "={{ $('Validate Input').item.json['Email Sent'] }}",
"Harga Tiket": "={{ $('Validate Input').item.json['Harga Tiket'] }}",
"Jumlah TIket": "={{ $('Validate Input').item.json['Jumlah TIket'] }}",
"Payment Status": "={{ $('Validate Input').item.json['Payment Status'] }}",
"Payment Method ": "={{ $('Validate Input').item.json['Payment Method '] }}"
},
"schema": [
{
"id": "Nama",
"type": "string",
"display": true,
"required": false,
"displayName": "Nama",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "No Hp",
"type": "string",
"display": true,
"required": false,
"displayName": "No Hp",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Email",
"type": "string",
"display": true,
"required": false,
"displayName": "Email",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Jumlah TIket",
"type": "string",
"display": true,
"required": false,
"displayName": "Jumlah TIket",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Harga Tiket",
"type": "string",
"display": true,
"required": false,
"displayName": "Harga Tiket",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Payment Method ",
"type": "string",
"display": true,
"required": false,
"displayName": "Payment Method ",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Payment Status",
"type": "string",
"display": true,
"required": false,
"displayName": "Payment Status",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Email Sent",
"type": "string",
"display": true,
"required": false,
"displayName": "Email Sent",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Ticked ID",
"type": "string",
"display": true,
"required": false,
"displayName": "Ticked ID",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1rQqe5XWmhWgnsAI6Q8kxoeCTbcNMdHsfP2tHRP7y7Po/edit#gid=0",
"cachedResultName": "Register"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1rQqe5XWmhWgnsAI6Q8kxoeCTbcNMdHsfP2tHRP7y7Po",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1rQqe5XWmhWgnsAI6Q8kxoeCTbcNMdHsfP2tHRP7y7Po/edit?usp=drivesdk",
"cachedResultName": "TABRAK LARI"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "gnUSHhCw4cKx3pwo",
"name": "Project GCP: n8n-khmuhtadin"
}
},
"typeVersion": 4.7
},
{
"id": "f93e5fa3-4e4f-4909-9fbe-fb148cc743d3",
"name": "注册",
"type": "n8n-nodes-base.webhook",
"position": [
-912,
1328
],
"webhookId": "1f5ebc0c-2fed-4bcb-9741-e70d188db457",
"parameters": {
"path": "v1/register",
"options": {},
"httpMethod": "POST",
"responseMode": "responseNode"
},
"typeVersion": 2.1
},
{
"id": "dd84a672-34c0-422d-a178-0a181fd37882",
"name": "获取行",
"type": "n8n-nodes-base.googleSheets",
"position": [
-736,
416
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1rQqe5XWmhWgnsAI6Q8kxoeCTbcNMdHsfP2tHRP7y7Po/edit#gid=0",
"cachedResultName": "Register"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1rQqe5XWmhWgnsAI6Q8kxoeCTbcNMdHsfP2tHRP7y7Po",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1rQqe5XWmhWgnsAI6Q8kxoeCTbcNMdHsfP2tHRP7y7Po/edit?usp=drivesdk",
"cachedResultName": "TABRAK LARI"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "gnUSHhCw4cKx3pwo",
"name": "Project GCP: n8n-khmuhtadin"
}
},
"typeVersion": 4.7
},
{
"id": "5d549b2f-14ca-4f42-b96d-8d01051c0351",
"name": "筛选已支付未发送",
"type": "n8n-nodes-base.filter",
"position": [
-512,
416
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "75fc42dd-aeec-4109-970d-7eb026b6b097",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json[\"Payment Status\"] }}",
"rightValue": "PAID"
},
{
"id": "70bf28b6-59dc-4acc-8908-5e521d83c4fa",
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json[\"Email Sent\"] }}",
"rightValue": "NO"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "0adcf4d7-d1e6-4f8a-945a-b7346ffd69fc",
"name": "生成票据数据",
"type": "n8n-nodes-base.code",
"position": [
-752,
640
],
"parameters": {
"jsCode": "const items = $input.all();\nconst output = [];\n\nfor (let i = 0; i < items.length; i++) {\n const item = items[i].json;\n \n // Parse jumlah tiket\n const jumlahTiket = parseInt(item[\"Jumlah TIket\"]) || 1;\n \n // Generate ALL ticket IDs first for this person\n const allTicketsForThisPerson = [];\n \n for (let ticketNum = 1; ticketNum <= jumlahTiket; ticketNum++) {\n const now = new Date();\n const timestamp = now.toISOString().slice(0,10).replace(/-/g, '');\n const rowNum = item.row_number || (i + 2);\n const hash = Math.random().toString(36).substring(2, 10).toUpperCase();\n \n const ticketId = `TL-${timestamp}-${String(rowNum).padStart(4, '0')}-${ticketNum}-${hash}`;\n allTicketsForThisPerson.push(ticketId);\n \n const qrData = JSON.stringify({\n event: \"TABRAK_LARI\",\n ticket_id: ticketId,\n name: item.Nama,\n email: item.Email,\n ticket_number: ticketNum,\n total_tickets: jumlahTiket,\n timestamp: now.toISOString(),\n hash: hash\n });\n \n output.push({\n json: {\n ...item,\n ticket_id: ticketId,\n ticket_number: ticketNum,\n total_tickets: jumlahTiket,\n qr_data: qrData,\n qr_data_url_encoded: encodeURIComponent(qrData),\n event_name: \"TABRAK LARI\",\n event_date: \"15 November 2025\",\n event_location: \"GOR Pontianak\"\n }\n });\n }\n}\n\nreturn output;"
},
"typeVersion": 2
},
{
"id": "f9c1609a-b102-452f-972d-12625e9403c6",
"name": "生成二维码",
"type": "n8n-nodes-base.httpRequest",
"position": [
-480,
640
],
"parameters": {
"url": "=https://api.qrserver.com/v1/create-qr-code/?size=300x300&data={{ $json.qr_data_url_encoded }}",
"options": {
"response": {
"response": {
"responseFormat": "file"
}
}
}
},
"typeVersion": 4.2
},
{
"id": "43c28eec-ecaa-4de2-aab6-c7efa392b232",
"name": "构建 HTML 邮件",
"type": "n8n-nodes-base.code",
"position": [
-256,
560
],
"parameters": {
"jsCode": "// Build HTML Ticket Email - MULTIPLE TICKETS SUPPORT\nconst items = $input.all();\nconst output = [];\n\nfor (let i = 0; i < items.length; i++) {\n const item = items[i].json;\n \n // Get binary data\n let qrBase64 = '';\n \n if (items[i].binary && items[i].binary.data) {\n const binaryData = items[i].binary.data;\n \n if (binaryData.data) {\n qrBase64 = binaryData.data;\n } else if (Buffer.isBuffer(binaryData)) {\n qrBase64 = binaryData.toString('base64');\n }\n }\n \n const htmlTemplate = `\n<!DOCTYPE html>\n<html>\n<head>\n <style>\n body {\n font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n padding: 20px;\n margin: 0;\n }\n .ticket-container {\n max-width: 600px;\n margin: 0 auto;\n background: white;\n border-radius: 20px;\n overflow: hidden;\n box-shadow: 0 20px 60px rgba(0,0,0,0.3);\n }\n .ticket-header {\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n color: white;\n padding: 40px 30px;\n text-align: center;\n }\n .ticket-header h1 {\n margin: 0;\n font-size: 32px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 2px;\n }\n .ticket-number-badge {\n background: rgba(255,255,255,0.2);\n display: inline-block;\n padding: 8px 20px;\n border-radius: 20px;\n margin-top: 10px;\n font-size: 14px;\n font-weight: 600;\n }\n .ticket-body {\n padding: 40px 30px;\n }\n .qr-section {\n text-align: center;\n margin: 30px 0;\n padding: 30px;\n background: #f8f9fa;\n border-radius: 15px;\n }\n .qr-section img {\n max-width: 280px;\n border: 8px solid white;\n border-radius: 10px;\n box-shadow: 0 5px 15px rgba(0,0,0,0.1);\n }\n .ticket-info {\n background: #f8f9fa;\n padding: 25px;\n border-radius: 15px;\n margin: 20px 0;\n }\n .info-row {\n display: flex;\n justify-content: space-between;\n padding: 12px 0;\n border-bottom: 1px solid #dee2e6;\n }\n .info-row:last-child {\n border-bottom: none;\n }\n .info-label {\n font-weight: 600;\n color: #495057;\n }\n .info-value {\n color: #212529;\n font-weight: 500;\n }\n .ticket-id {\n text-align: center;\n font-size: 20px;\n font-weight: 700;\n color: #667eea;\n margin: 20px 0;\n padding: 15px;\n background: #f8f9fa;\n border-radius: 10px;\n letter-spacing: 1px;\n word-break: break-all;\n }\n .important-note {\n background: #fff3cd;\n border-left: 4px solid #ffc107;\n padding: 20px;\n margin: 25px 0;\n border-radius: 5px;\n }\n .important-note h3 {\n margin-top: 0;\n color: #856404;\n }\n .footer {\n text-align: center;\n padding: 30px;\n color: #6c757d;\n font-size: 14px;\n }\n </style>\n</head>\n<body>\n <div class=\"ticket-container\">\n <div class=\"ticket-header\">\n <h1>${item.event_name}</h1>\n <p style=\"margin: 10px 0 0 0; font-size: 18px;\">Tiketmu Telah Tersedia!</p>\n <div class=\"ticket-number-badge\">\n Tiket ${item.ticket_number} dari ${item.total_tickets}\n </div>\n </div>\n \n <div class=\"ticket-body\">\n <h2 style=\"color: #212529; margin-top: 0;\">Hi ${item.Nama || item.Name || 'Guest'}!</h2>\n <p style=\"color: #495057; font-size: 16px; line-height: 1.6;\">\n Terima kasih atas pembayaran Anda! Berikut adalah tiket <strong>#${item.ticket_number}</strong> dari total <strong>${item.total_tickets} tiket</strong> yang Anda pesan.\n </p>\n \n <div class=\"ticket-id\">\n ${item.ticket_id}\n </div>\n \n <div class=\"qr-section\">\n <h3 style=\"margin-top: 0; color: #495057;\">Scan QR Code Ini di Entrance</h3>\n <img src=\"data:image/png;base64,${qrBase64}\" alt=\"QR Code Ticket\" />\n </div>\n \n <div class=\"ticket-info\">\n <h3 style=\"margin-top: 0; color: #495057;\">Detail Tiket</h3>\n <div class=\"info-row\">\n <span class=\"info-label\">Nama:</span>\n <span class=\"info-value\">${item.Nama || item.Name || 'Guest'}</span>\n </div>\n <div class=\"info-row\">\n <span class=\"info-label\">Email:</span>\n <span class=\"info-value\">${item.Email}</span>\n </div>\n <div class=\"info-row\">\n <span class=\"info-label\">Tiket:</span>\n <span class=\"info-value\">#${item.ticket_number} dari ${item.total_tickets}</span>\n </div>\n <div class=\"info-row\">\n <span class=\"info-label\">Ticket ID:</span>\n <span class=\"info-value\">${item.ticket_id}</span>\n </div>\n </div>\n \n <div class=\"important-note\">\n <h3>Penting!</h3>\n <ul style=\"margin: 10px 0; padding-left: 20px;\">\n <li>Simpan email ini dengan baik</li>\n <li>Setiap tiket memiliki QR code unik</li>\n <li>Tunjukkan QR code saat masuk venue</li>\n <li>Satu tiket hanya berlaku untuk satu kali scan</li>\n <li>Datang 30 menit sebelum acara dimulai</li>\n </ul>\n </div>\n </div>\n \n <div class=\"footer\">\n <p style=\"margin: 5px 0;\">2025 TABRAK LARI Event. All rights reserved.</p>\n </div>\n </div>\n</body>\n</html>\n`;\n\n output.push({\n json: {\n ...item,\n html_content: htmlTemplate,\n qr_base64: qrBase64\n }\n });\n}\n\nreturn output;"
},
"typeVersion": 2
},
{
"id": "641a85a1-0f76-4894-bd17-c9715f39eea1",
"name": "发送邮件 (Gmail)",
"type": "n8n-nodes-base.gmail",
"position": [
0,
384
],
"webhookId": "28bf1adb-e6ca-4797-ade5-6f7d2746bf1e",
"parameters": {
"sendTo": "={{ $json.Email }}",
"message": "={{ $json.html_content }}",
"options": {
"appendAttribution": false
},
"subject": "=Tiketmu Telah Tersedia!"
},
"credentials": {
"gmailOAuth2": {
"id": "8p11S7abjY8KddB9",
"name": "festadjagad@gmail.com"
}
},
"typeVersion": 2.1
},
{
"id": "bcfc2a0f-474b-4b9a-a652-52715cdf7e29",
"name": "更新表格 (注册)",
"type": "n8n-nodes-base.googleSheets",
"position": [
272,
560
],
"parameters": {
"columns": {
"value": {
"Email": "={{ $json.Email }}",
"Ticked ID": "={{ $json.ticket_ids_combined }}",
"Email Sent": "YES"
},
"schema": [
{
"id": "Nama",
"type": "string",
"display": true,
"required": false,
"displayName": "Nama",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "No Hp",
"type": "string",
"display": true,
"required": false,
"displayName": "No Hp",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Email",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Email",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Jumlah TIket",
"type": "string",
"display": true,
"required": false,
"displayName": "Jumlah TIket",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Harga Tiket",
"type": "string",
"display": true,
"required": false,
"displayName": "Harga Tiket",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Payment Method ",
"type": "string",
"display": true,
"required": false,
"displayName": "Payment Method ",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Payment Status",
"type": "string",
"display": true,
"required": false,
"displayName": "Payment Status",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Email Sent",
"type": "string",
"display": true,
"required": false,
"displayName": "Email Sent",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Ticked ID",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "Ticked ID",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Checked In?",
"type": "string",
"display": true,
"required": false,
"displayName": "Checked In?",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "CHECKIN TIME",
"type": "string",
"display": true,
"required": false,
"displayName": "CHECKIN TIME",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "row_number",
"type": "number",
"display": true,
"removed": true,
"readOnly": true,
"required": false,
"displayName": "row_number",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [
"Email"
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "update",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1rQqe5XWmhWgnsAI6Q8kxoeCTbcNMdHsfP2tHRP7y7Po/edit#gid=0",
"cachedResultName": "Register"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1rQqe5XWmhWgnsAI6Q8kxoeCTbcNMdHsfP2tHRP7y7Po",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1rQqe5XWmhWgnsAI6Q8kxoeCTbcNMdHsfP2tHRP7y7Po/edit?usp=drivesdk",
"cachedResultName": "TABRAK LARI"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "gnUSHhCw4cKx3pwo",
"name": "Project GCP: n8n-khmuhtadin"
}
},
"typeVersion": 4.7
},
{
"id": "207cd6d4-0812-4547-8b28-c5c3725636bd",
"name": "更新表格 (票据)",
"type": "n8n-nodes-base.googleSheets",
"position": [
16,
736
],
"parameters": {
"columns": {
"value": {
"Nama": "={{ $json.Nama }}",
"Email": "={{ $json.Email }}",
"Ticket ID": "={{ $json.ticket_id }}",
"Ticket Number": "={{ $json.ticket_number }}/{{ $json.total_tickets }}",
"Total Tickets": "={{ $json.total_tickets }}"
},
"schema": [
{
"id": "Ticket ID",
"type": "string",
"display": true,
"required": false,
"displayName": "Ticket ID",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Email",
"type": "string",
"display": true,
"required": false,
"displayName": "Email",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Nama",
"type": "string",
"display": true,
"required": false,
"displayName": "Nama",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Ticket Number",
"type": "string",
"display": true,
"required": false,
"displayName": "Ticket Number",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Total Tickets",
"type": "string",
"display": true,
"required": false,
"displayName": "Total Tickets",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Checked In",
"type": "string",
"display": true,
"required": false,
"displayName": "Checked In",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "Checkin TIme",
"type": "string",
"display": true,
"required": false,
"displayName": "Checkin TIme",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "row_number",
"type": "number",
"display": true,
"removed": true,
"readOnly": true,
"required": false,
"displayName": "row_number",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "append",
"sheetName": {
"__rl": true,
"mode": "list",
"value": 2010454173,
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1rQqe5XWmhWgnsAI6Q8kxoeCTbcNMdHsfP2tHRP7y7Po/edit#gid=2010454173",
"cachedResultName": "Tickets"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1rQqe5XWmhWgnsAI6Q8kxoeCTbcNMdHsfP2tHRP7y7Po",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1rQqe5XWmhWgnsAI6Q8kxoeCTbcNMdHsfP2tHRP7y7Po/edit?usp=drivesdk",
"cachedResultName": "TABRAK LARI"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "gnUSHhCw4cKx3pwo",
"name": "Project GCP: n8n-khmuhtadin"
}
},
"typeVersion": 4.7
},
{
"id": "f99c03de-9578-456f-8443-e5fa2840e88e",
"name": "开始",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-960,
416
],
"parameters": {
"rule": {
"interval": [
{
"field": "minutes",
"minutesInterval": 1
}
]
}
},
"typeVersion": 1.2
},
{
"id": "75d7c162-962b-4a07-80db-43ed07565987",
"name": "解析数据",
"type": "n8n-nodes-base.code",
"position": [
16,
560
],
"parameters": {
"jsCode": "const items = $input.all();\nconst grouped = {};\n\nfor (let item of items) {\n const email = item.json.Email;\n \n if (!grouped[email]) {\n grouped[email] = {\n Email: email,\n ticket_ids: []\n };\n }\n \n grouped[email].ticket_ids.push(item.json.ticket_id);\n}\n\nconst output = Object.values(grouped).map(item => ({\n json: {\n Email: item.Email,\n ticket_ids_combined: item.ticket_ids.join(\", \")\n }\n}));\n\nreturn output;"
},
"typeVersion": 2
},
{
"id": "0b3858e2-2c61-4f1d-b6e6-45a7e3ce3ea6",
"name": "便签",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1008,
-352
],
"parameters": {
"width": 1552,
"height": 608,
"content": "确保您的表名与实际知识库结构匹配"
},
"typeVersion": 1
},
{
"id": "7b512e89-2626-4a2b-b24e-a4688d9e120b",
"name": "便签1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1008,
288
],
"parameters": {
"color": 4,
"width": 1552,
"height": 608,
"content": "# 票据生成器和发送器"
},
"typeVersion": 1
},
{
"id": "cc7ab6b0-47ea-4e08-8ba8-5cbc0e470e2c",
"name": "便签2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1008,
928
],
"parameters": {
"color": 3,
"width": 1552,
"height": 608,
"content": ""
},
"typeVersion": 1
},
{
"id": "34074899-d8b0-4ae0-89be-3ac61046fd03",
"name": "便签3",
"type": "n8n-nodes-base.stickyNote",
"position": [
576,
288
],
"parameters": {
"color": 5,
"width": 512,
"height": 608,
"content": "# 活动票务系统 - 完整流程"
},
"typeVersion": 1
},
{
"id": "33452a6b-f2cf-4acc-bfe1-fb2506e26cd6",
"name": "便签4",
"type": "n8n-nodes-base.stickyNote",
"position": [
576,
928
],
"parameters": {
"color": 5,
"width": 512,
"height": 608,
"content": "# 需要设置"
},
"typeVersion": 1
},
{
"id": "160db932-0e42-4b39-b39d-2654179c1c78",
"name": "便签5",
"type": "n8n-nodes-base.stickyNote",
"position": [
-992,
-336
],
"parameters": {
"color": 5,
"width": 480,
"height": 496,
"content": "# 票据扫描器工作原理"
},
"typeVersion": 1
},
{
"id": "84430c98-7876-4ea9-9908-7f59c7718752",
"name": "便签6",
"type": "n8n-nodes-base.stickyNote",
"position": [
-992,
944
],
"parameters": {
"color": 5,
"width": 512,
"height": 352,
"content": "# 注册端点"
},
"typeVersion": 1
},
{
"id": "9cd7ba10-a91c-4134-a481-94df3bfde3c3",
"name": "便签8",
"type": "n8n-nodes-base.stickyNote",
"position": [
576,
-352
],
"parameters": {
"color": 5,
"width": 512,
"height": 608,
"content": "# 自动票据生成"
},
"typeVersion": 1
}
],
"active": false,
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"versionId": "7577dfcb-a9b7-4cef-85ca-ecfc79c85cef",
"connections": {
"START": {
"main": [
[
{
"node": "Get Rows",
"type": "main",
"index": 0
}
]
]
},
"Get Rows": {
"main": [
[
{
"node": "Filter Paid Not Sent",
"type": "main",
"index": 0
}
]
]
},
"REGISTER": {
"main": [
[
{
"node": "Validate Input",
"type": "main",
"index": 0
}
]
]
},
"Parse Data": {
"main": [
[
{
"node": "Update Sheet (Register)",
"type": "main",
"index": 0
}
]
]
},
"Store Data": {
"main": [
[
{
"node": "Tiket Booked",
"type": "main",
"index": 0
}
]
]
},
"Get Tickets": {
"main": [
[
{
"node": "Ticket Available?",
"type": "main",
"index": 0
}
]
]
},
"SCAN TICKET": {
"main": [
[
{
"node": "Parse Barcode",
"type": "main",
"index": 0
}
]
]
},
"Email exist?": {
"main": [
[
{
"node": "Already Registered",
"type": "main",
"index": 0
}
],
[
{
"node": "Store Data",
"type": "main",
"index": 0
}
]
]
},
"Parse Output": {
"main": [
[
{
"node": "Already Checked IN",
"type": "main",
"index": 0
}
]
]
},
"Valid Input?": {
"main": [
[
{
"node": "Get Participant",
"type": "main",
"index": 0
}
],
[
{
"node": "Validation Error",
"type": "main",
"index": 0
}
]
]
},
"Parse Barcode": {
"main": [
[
{
"node": "Get Tickets",
"type": "main",
"index": 0
}
]
]
},
"Validate Input": {
"main": [
[
{
"node": "Valid Input?",
"type": "main",
"index": 0
}
]
]
},
"Get Participant": {
"main": [
[
{
"node": "Email exist?",
"type": "main",
"index": 0
}
]
]
},
"Build HTML Email": {
"main": [
[
{
"node": "Send Email (Gmail)",
"type": "main",
"index": 0
},
{
"node": "Parse Data",
"type": "main",
"index": 0
},
{
"node": "Update Sheet (Tickets)",
"type": "main",
"index": 0
}
]
]
},
"Generate QR Code": {
"main": [
[
{
"node": "Build HTML Email",
"type": "main",
"index": 0
}
]
]
},
"Ticket Available?": {
"main": [
[
{
"node": "Update Ticket Status",
"type": "main",
"index": 0
}
],
[
{
"node": "Parse Output",
"type": "main",
"index": 0
}
]
]
},
"Filter Paid Not Sent": {
"main": [
[
{
"node": "Generate Ticket Data",
"type": "main",
"index": 0
}
]
]
},
"Generate Ticket Data": {
"main": [
[
{
"node": "Generate QR Code",
"type": "main",
"index": 0
}
]
]
},
"Update Ticket Status": {
"main": [
[
{
"node": "Checked IN",
"type": "main",
"index": 0
}
]
]
}
}
}常见问题
如何使用这个工作流?
复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。
这个工作流适合什么场景?
这是一个高级难度的工作流,适用于Content Creation、Multimodal AI等场景。适合高级用户,包含 16+ 个节点的复杂工作流
需要付费吗?
本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。
相关工作流推荐
优惠码搜寻器:通过SerpAPI、Gemini和Telegram自动查找优惠码
优惠码搜寻器:通过SerpAPI、Gemini和Telegram自动查找优惠码
If
Set
Gmail
+13
18 节点Khairul Muhtadin
Content Creation
防欺诈潜在客户捕获与培育系统
通过AI评分、表格跟踪和多渠道提醒捕获和培育防欺诈潜在客户
If
Set
Code
+11
28 节点Jitesh Dugar
Content Creation
客户入职工作流
使用PDF、Trello、Slack、Gmail和Airtable简化客户入职流程
If
Code
Gmail
+10
22 节点Jitesh Dugar
Content Creation
使用GPT-4o-mini的技术SEO审计与多格式报告(Sheets-Email)
使用GPT-4o-mini的技术SEO审计与多格式报告(Sheets/Email)
Set
Xml
Code
+14
45 节点Oriol Seguí
Content Creation
来自多个招聘网站的求职自动化
使用 5 个招聘平台和 AI 简历生成器自动化求职与申请
If
Set
Code
+14
34 节点Gerald Denor
Personal Productivity
使用PageSpeed Insights监控网站性能并保存到Google Sheets并发送警报
使用PageSpeed Insights监控网站性能,发送警报到Google Sheets
If
Set
Code
+8
20 节点Dahiana
DevOps