{ "name": "Ko-fi to Paper Dynasty", "nodes": [ { "parameters": { "httpMethod": "POST", "path": "kofi-paperdy", "responseMode": "lastNode", "options": {} }, "id": "webhook-trigger", "name": "Ko-fi Webhook", "type": "n8n-nodes-base.webhook", "typeVersion": 1, "position": [250, 300], "webhookId": "kofi-paperdy" }, { "parameters": { "jsCode": "// Parse Ko-fi form data\nconst formData = $input.item.json.body;\nconst kofiData = JSON.parse(formData.data);\n\n// Ko-fi verification token\nconst KOFI_TOKEN = '44d1f957-ac15-497e-8306-4dc667de55d1';\n\n// Validate token\nif (kofiData.verification_token !== KOFI_TOKEN) {\n throw new Error('Invalid Ko-fi verification token');\n}\n\n// Add metadata\nreturn [{\n json: {\n ...kofiData,\n _parsed_at: new Date().toISOString(),\n _execution_id: $execution.id\n }\n}];" }, "id": "parse-and-validate", "name": "Parse & Validate", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [450, 300] }, { "parameters": { "conditions": { "string": [ { "value1": "={{ $json.type }}", "operation": "equals", "value2": "Shop Order" } ] } }, "id": "filter-shop-orders", "name": "Shop Order?", "type": "n8n-nodes-base.if", "typeVersion": 1, "position": [650, 300] }, { "parameters": { "jsCode": "// Lookup Paper Dynasty team\nconst PD_API_BASE = 'https://pd.manticorum.com/api/v2';\nconst API_TOKEN = 'Bearer Tp3aO3jhYve5NJF1IqOmJTmk';\n\nlet teamId = null;\nlet teamAbbrev = null;\nlet identificationMethod = null;\n\n// Method 1: Discord User ID -> gmid lookup\nif ($json.discord_userid) {\n try {\n const response = await $http.request({\n method: 'GET',\n url: `${PD_API_BASE}/teams?gmid=${$json.discord_userid}`,\n headers: {\n 'Authorization': API_TOKEN,\n 'Content-Type': 'application/json'\n }\n });\n\n if (response.body && response.body.length > 0) {\n teamId = response.body[0].id;\n teamAbbrev = response.body[0].abbrev;\n identificationMethod = 'discord_userid';\n }\n } catch (error) {\n console.log('Discord lookup failed:', error.message);\n }\n}\n\n// Method 2: Extract team abbrev from message\nif (!teamId && $json.message) {\n const abbrevMatch = $json.message.match(/\\b([A-Z]{2,4})\\b/);\n \n if (abbrevMatch) {\n const extractedAbbrev = abbrevMatch[1];\n \n try {\n const response = await $http.request({\n method: 'GET',\n url: `${PD_API_BASE}/teams?abbrev=${extractedAbbrev}`,\n headers: {\n 'Authorization': API_TOKEN,\n 'Content-Type': 'application/json'\n }\n });\n\n if (response.body && response.body.length > 0) {\n teamId = response.body[0].id;\n teamAbbrev = response.body[0].abbrev;\n identificationMethod = 'message_abbrev';\n }\n } catch (error) {\n console.log('Team abbrev lookup failed:', error.message);\n }\n }\n}\n\nreturn [{\n json: {\n ...$json,\n pd_team_id: teamId,\n pd_team_abbrev: teamAbbrev,\n identification_method: identificationMethod,\n needs_manual_review: !teamId\n }\n}];" }, "id": "lookup-team", "name": "Lookup PD Team", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [850, 200] }, { "parameters": { "conditions": { "boolean": [ { "value1": "={{ $json.needs_manual_review }}", "value2": false } ] } }, "id": "check-team-found", "name": "Team Found?", "type": "n8n-nodes-base.if", "typeVersion": 1, "position": [1050, 200] }, { "parameters": { "jsCode": "// Product mapping\nconst PRODUCT_MAP = {\n '61de350207': {\n name: 'Premium Pack',\n pack_type_id: 3,\n packs_per_quantity: 1\n },\n '2bdb7a4916': {\n name: 'Standard Pack',\n pack_type_id: 1,\n packs_per_quantity: 1\n },\n '3d7a4935aa': {\n name: 'In-Game Currency',\n type: 'currency',\n needs_manual_review: true\n }\n};\n\nconst shopItems = $json.shop_items;\nconst packRequests = [];\nlet unknownProducts = [];\nlet needsReview = [];\n\nfor (const item of shopItems) {\n const productConfig = PRODUCT_MAP[item.direct_link_code];\n \n if (!productConfig) {\n unknownProducts.push({\n code: item.direct_link_code,\n variation: item.variation_name,\n quantity: item.quantity\n });\n continue;\n }\n \n // Handle currency products\n if (productConfig.type === 'currency') {\n needsReview.push({\n product: productConfig.name,\n quantity: item.quantity,\n reason: 'Currency system not implemented yet'\n });\n continue;\n }\n \n // Calculate total packs\n const totalPacks = item.quantity * productConfig.packs_per_quantity;\n \n // Create pack requests\n for (let i = 0; i < totalPacks; i++) {\n packRequests.push({\n team_id: $json.pd_team_id,\n pack_type_id: productConfig.pack_type_id,\n pack_cardset_id: null\n });\n }\n}\n\nreturn [{\n json: {\n ...$json,\n pack_requests: packRequests,\n unknown_products: unknownProducts,\n needs_review_items: needsReview,\n has_unknown_products: unknownProducts.length > 0,\n has_review_items: needsReview.length > 0,\n total_packs: packRequests.length\n }\n}];" }, "id": "map-products", "name": "Map Products", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [1250, 100] }, { "parameters": { "method": "POST", "url": "https://pd.manticorum.com/api/v2/packs", "authentication": "predefinedCredentialType", "nodeCredentialType": "httpHeaderAuth", "sendBody": true, "bodyParameters": { "parameters": [ { "name": "packs", "value": "={{ JSON.stringify($json.pack_requests) }}" } ] }, "options": { "response": { "response": { "fullResponse": true } }, "retry": { "retry": { "maxRetries": 3, "retryInterval": 2000 } } } }, "id": "grant-packs", "name": "Grant Packs", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4, "position": [1450, 100], "credentials": { "httpHeaderAuth": { "id": "1", "name": "Paper Dynasty API" } } }, { "parameters": { "conditions": { "number": [ { "value1": "={{ $json.statusCode }}", "operation": "between", "value2": 200, "value3": 299 } ] } }, "id": "check-api-success", "name": "API Success?", "type": "n8n-nodes-base.if", "typeVersion": 1, "position": [1650, 100] }, { "parameters": { "method": "POST", "url": "https://discord.com/api/webhooks/1438578498108133488/MGx1zyAa1ewzVy3RqCsH50ZJUiVvT5J5Vl2jExGIkIsu6v0x8A9J3-ruRftClPEq91ej", "sendBody": true, "contentType": "json", "body": "={{ JSON.stringify({\n content: '✅ Ko-fi Order Processed',\n embeds: [{\n title: 'Paper Dynasty Packs Granted',\n color: 3066993,\n fields: [\n {\n name: 'Customer',\n value: $('Parse & Validate').item.json.from_name,\n inline: true\n },\n {\n name: 'Team',\n value: $('Lookup PD Team').item.json.pd_team_abbrev,\n inline: true\n },\n {\n name: 'Amount',\n value: '$' + $('Parse & Validate').item.json.amount + ' ' + $('Parse & Validate').item.json.currency,\n inline: true\n },\n {\n name: 'Packs Granted',\n value: $('Map Products').item.json.total_packs + ' packs',\n inline: false\n },\n {\n name: 'Transaction ID',\n value: '`' + $('Parse & Validate').item.json.kofi_transaction_id + '`',\n inline: false\n }\n ],\n timestamp: new Date().toISOString()\n }]\n}) }}" }, "id": "discord-success", "name": "Discord Success", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4, "position": [1850, 0] }, { "parameters": { "method": "POST", "url": "https://discord.com/api/webhooks/1438578498108133488/MGx1zyAa1ewzVy3RqCsH50ZJUiVvT5J5Vl2jExGIkIsu6v0x8A9J3-ruRftClPEq91ej", "sendBody": true, "contentType": "json", "body": "={{ JSON.stringify({\n content: '@here ⚠️ Ko-fi Order Processing Error',\n embeds: [{\n title: 'Paper Dynasty API Error',\n color: 15158332,\n fields: [\n {\n name: 'Customer',\n value: $('Parse & Validate').item.json.from_name + ' (' + $('Parse & Validate').item.json.email + ')',\n inline: false\n },\n {\n name: 'Amount',\n value: '$' + $('Parse & Validate').item.json.amount,\n inline: true\n },\n {\n name: 'Team ID',\n value: String($('Lookup PD Team').item.json.pd_team_id || 'Unknown'),\n inline: true\n },\n {\n name: 'API Status',\n value: String($json.statusCode || 'Request Failed'),\n inline: true\n },\n {\n name: 'Transaction ID',\n value: '`' + $('Parse & Validate').item.json.kofi_transaction_id + '`',\n inline: false\n },\n {\n name: 'Action Required',\n value: 'Manual pack distribution needed. Use transaction ID to process manually.',\n inline: false\n }\n ],\n timestamp: new Date().toISOString()\n }]\n}) }}" }, "id": "discord-error", "name": "Discord Error", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4, "position": [1850, 200] }, { "parameters": { "method": "POST", "url": "https://discord.com/api/webhooks/1438578498108133488/MGx1zyAa1ewzVy3RqCsH50ZJUiVvT5J5Vl2jExGIkIsu6v0x8A9J3-ruRftClPEq91ej", "sendBody": true, "contentType": "json", "body": "={{ JSON.stringify({\n content: '@here 🔍 Ko-fi Order Needs Manual Review',\n embeds: [{\n title: 'Unable to Identify Paper Dynasty Team',\n color: 16776960,\n fields: [\n {\n name: 'Customer',\n value: $json.from_name + ' (' + $json.email + ')',\n inline: false\n },\n {\n name: 'Amount',\n value: '$' + $json.amount + ' ' + $json.currency,\n inline: true\n },\n {\n name: 'Discord User ID',\n value: $json.discord_userid || 'Not provided',\n inline: true\n },\n {\n name: 'Message',\n value: $json.message || 'No message',\n inline: false\n },\n {\n name: 'Products',\n value: $json.shop_items.map(i => i.quantity + 'x ' + i.variation_name + ' (' + i.direct_link_code + ')').join('\\n'),\n inline: false\n },\n {\n name: 'Transaction ID',\n value: '`' + $json.kofi_transaction_id + '`',\n inline: false\n },\n {\n name: 'Action Required',\n value: '1. Identify user\\'s Paper Dynasty team\\n2. Manually grant packs using PD API\\n3. Reply when resolved',\n inline: false\n }\n ],\n timestamp: new Date().toISOString()\n }]\n}) }}" }, "id": "discord-manual-review", "name": "Discord Manual Review", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4, "position": [1250, 300] }, { "parameters": { "respondWith": "json", "responseBody": "={{ JSON.stringify({\n status: 'success',\n message: 'Order received and processed',\n execution_id: $execution.id\n}) }}" }, "id": "respond-webhook", "name": "Return 200 OK", "type": "n8n-nodes-base.respondToWebhook", "typeVersion": 1, "position": [2050, 150] } ], "connections": { "Ko-fi Webhook": { "main": [ [ { "node": "Parse & Validate", "type": "main", "index": 0 } ] ] }, "Parse & Validate": { "main": [ [ { "node": "Shop Order?", "type": "main", "index": 0 } ] ] }, "Shop Order?": { "main": [ [ { "node": "Lookup PD Team", "type": "main", "index": 0 } ], [ { "node": "Return 200 OK", "type": "main", "index": 0 } ] ] }, "Lookup PD Team": { "main": [ [ { "node": "Team Found?", "type": "main", "index": 0 } ] ] }, "Team Found?": { "main": [ [ { "node": "Map Products", "type": "main", "index": 0 } ], [ { "node": "Discord Manual Review", "type": "main", "index": 0 } ] ] }, "Map Products": { "main": [ [ { "node": "Grant Packs", "type": "main", "index": 0 } ] ] }, "Grant Packs": { "main": [ [ { "node": "API Success?", "type": "main", "index": 0 } ] ] }, "API Success?": { "main": [ [ { "node": "Discord Success", "type": "main", "index": 0 } ], [ { "node": "Discord Error", "type": "main", "index": 0 } ] ] }, "Discord Success": { "main": [ [ { "node": "Return 200 OK", "type": "main", "index": 0 } ] ] }, "Discord Error": { "main": [ [ { "node": "Return 200 OK", "type": "main", "index": 0 } ] ] }, "Discord Manual Review": { "main": [ [ { "node": "Return 200 OK", "type": "main", "index": 0 } ] ] } }, "pinData": {}, "settings": { "executionOrder": "v1" }, "staticData": null, "tags": [], "triggerCount": 0, "updatedAt": "2025-11-13T00:00:00.000Z", "versionId": "1" }