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 →
{
"name": "WooCommerce \u2192 Faktury KSeF",
"nodes": [
{
"parameters": {
"content": "## Automatycznie wysy\u0142aj zam\u00f3wienia WooCommerce do KSeF\n\n### Jak to dzia\u0142a\nTen workflow nas\u0142uchuje podpisanego webhooka zam\u00f3wienia z WooCommerce, weryfikuje jego nag\u0142\u00f3wek `X-WC-Webhook-Signature`, pobiera NIP kupuj\u0105cego z metadanych zam\u00f3wienia i przekazuje zam\u00f3wienie do Twojego self-hostowanego [KSeF Gateway](https://github.com/jurczykpawel/ksef-gateway) - niewielkiej nak\u0142adki HTTP na oficjalne API polskiego e-fakturowania. Zam\u00f3wienia bez NIP-u s\u0105 traktowane jako zakupy konsumenckie i pomijane, bo KSeF wymagany jest tylko dla sprzeda\u017cy B2B. Stawka VAT jest wykrywana automatycznie dla ka\u017cdej pozycji na podstawie sum podatkowych WooCommerce.\n\n### Konfiguracja\n1. Wdr\u00f3\u017c instancj\u0119 KSeF Gateway (Docker, Render albo StackPilot - patrz README projektu) i zanotuj jej adres URL. Nowy w KSeF albo potrzebujesz certyfikatu (tokeny KSeF b\u0119d\u0105 wycofywane)? Zobacz [g\u0142\u00f3wny README](https://github.com/jurczykpawel/ksef-gateway#certificate-based-auth-alternative-to-tokens).\n2. Otw\u00f3rz **Configuration (EDIT ME)** i uzupe\u0142nij URL bramki, klucz API bramki, dane sprzedawcy oraz sekret webhooka WooCommerce.\n3. W WooCommerce dodaj niestandardowe pole checkoutu zapisuj\u0105ce NIP kupuj\u0105cego jako metadane zam\u00f3wienia `_billing_nip` (albo `billing_nip`), a potem utw\u00f3rz webhook (Ustawienia \u2192 Zaawansowane \u2192 Webhooks) wskazuj\u0105cy na Production URL tego workflowa z tym samym sekretem.\n4. Zobacz czerwon\u0105 notatk\u0119 przy **Verify Signature** - jedno wymagane ustawienie \u015brodowiska n8n.\n\n### Wskaz\u00f3wki dotycz\u0105ce personalizacji\nDodaj node Slack albo email po **Respond Skipped**, je\u015bli chcesz dostawa\u0107 powiadomienie za ka\u017cdym razem, gdy zam\u00f3wieniu brakuje NIP-u.",
"height": 720,
"width": 700,
"color": 1
},
"id": "sticky-overview",
"name": "Overview",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-760,
-380
]
},
{
"parameters": {
"content": "## Odbi\u00f3r i weryfikacja\nPrzyjmuje webhook WooCommerce i sprawdza jego podpis HMAC, zanim zaufa danym.",
"height": 510,
"width": 1120,
"color": 7
},
"id": "sticky-section-a",
"name": "Section: Receive & Verify",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-760,
360
]
},
{
"parameters": {
"content": "## Budowa i sprawdzenie\nMapuje zam\u00f3wienie na payload faktury KSeF i sprawdza NIP kupuj\u0105cego.",
"height": 510,
"width": 520,
"color": 7
},
"id": "sticky-section-b",
"name": "Section: Build & Check",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
440,
360
]
},
{
"parameters": {
"content": "## Wysy\u0142ka i odpowied\u017a\nWysy\u0142a faktur\u0119 do Twojego KSeF Gateway i zwraca wynik do WooCommerce.",
"height": 350,
"width": 520,
"color": 7
},
"id": "sticky-section-c",
"name": "Section: Send & Respond",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
1040,
360
]
},
{
"parameters": {
"content": "## Wymagane ustawienie node'a\nTen node Code potrzebuje `require('crypto')`. Self-hostowany n8n: ustaw `NODE_FUNCTION_ALLOW_BUILTIN=crypto` w \u015brodowisku n8n i zrestartuj.",
"height": 220,
"width": 300,
"color": 3
},
"id": "sticky-warning",
"name": "Warning: crypto module",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-130,
710
]
},
{
"parameters": {
"httpMethod": "POST",
"path": "woo-ksef",
"responseMode": "responseNode",
"options": {
"rawBody": true
}
},
"id": "webhook",
"name": "WooCommerce Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
-700,
550
],
"notes": "Raw Body is required so the signature check below sees the exact bytes WooCommerce signed.",
"onError": "continueRegularOutput"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "a1",
"name": "ksefGatewayUrl",
"value": "https://your-ksef-gateway.example.com",
"type": "string"
},
{
"id": "a2",
"name": "sellerNip",
"value": "0000000000",
"type": "string"
},
{
"id": "a3",
"name": "sellerName",
"value": "Your Company sp. z o.o.",
"type": "string"
},
{
"id": "a4",
"name": "sellerStreet",
"value": "ul. Example 1",
"type": "string"
},
{
"id": "a5",
"name": "sellerCity",
"value": "00-001 Warszawa",
"type": "string"
},
{
"id": "a6",
"name": "webhookSecret",
"value": "change-me-webhook-secret",
"type": "string"
},
{
"id": "a7",
"name": "gatewayApiKey",
"value": "your-gateway-api-key",
"type": "string"
}
]
},
"includeOtherFields": true,
"options": {
"stripBinary": false
}
},
"id": "config-1",
"name": "Configuration (EDIT ME)",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
-400,
550
],
"notes": "Fill in your KSeF Gateway URL, seller details, and the WooCommerce webhook secret before activating this workflow."
},
{
"parameters": {
"jsCode": "const crypto = require('crypto');\nconst config = $('Configuration (EDIT ME)').first().json;\nconst item = $input.first();\n\nconst signatureHeader = (item.json.headers && item.json.headers['x-wc-webhook-signature']) || '';\nconst rawBody = item.binary && item.binary.data\n ? Buffer.from(item.binary.data.data, 'base64').toString('utf8')\n : JSON.stringify(item.json.body || {});\n\nconst expected = crypto.createHmac('sha256', config.webhookSecret).update(rawBody).digest('base64');\n\nlet valid = false;\nif (signatureHeader && expected.length === signatureHeader.length) {\n valid = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader));\n}\n\nreturn [{ json: { valid, body: JSON.parse(rawBody) } }];"
},
"id": "code-verify",
"name": "Verify Signature",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-100,
550
],
"notes": "Verifies WooCommerce's base64 HMAC-SHA256 X-WC-Webhook-Signature header and re-parses the raw body for downstream nodes."
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"leftValue": "={{ $json.valid }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "true"
}
}
]
},
"options": {}
},
"id": "if-sig",
"name": "Signature Valid?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
200,
550
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "{\"success\":false,\"error\":\"invalid signature\"}",
"options": {
"responseCode": 401
}
},
"id": "res-unauth",
"name": "Respond Unauthorized",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
200,
710
],
"notes": "Dead end: bad or missing signature. Nothing downstream trusts this payload."
},
{
"parameters": {
"jsCode": "// Transform WooCommerce order to KSeF invoice format\nconst order = $input.first().json.body;\nconst config = $('Configuration (EDIT ME)').first().json;\n\n// Extract NIP from billing fields or meta_data\nconst nipField = (order.meta_data || []).find(m => m.key === '_billing_nip' || m.key === 'billing_nip');\nconst buyerNip = nipField?.value || '';\n\nif (!buyerNip) {\n // No NIP = consumer (osoba fizyczna), skip KSeF\n return [{ json: { skip: true, reason: 'No buyer NIP - consumer purchase, KSeF not required' } }];\n}\n\n// Map WooCommerce line items to KSeF items\nconst items = (order.line_items || []).map(item => {\n const netTotal = parseFloat(item.total) || 0;\n const taxTotal = parseFloat(item.total_tax) || 0;\n const quantity = item.quantity || 1;\n const unitPrice = Math.round((netTotal / quantity) * 100) / 100;\n\n // Detect VAT rate from tax amount\n let vatRate = 23; // default\n if (netTotal > 0) {\n const rate = Math.round((taxTotal / netTotal) * 100);\n if ([23, 8, 5, 0].includes(rate)) vatRate = rate;\n }\n\n return {\n name: item.name,\n quantity: quantity,\n unitPrice: unitPrice,\n vatRate: vatRate\n };\n});\n\nconst today = new Date().toISOString().split('T')[0];\n\nconst invoice = {\n invoiceNumber: `WOO/${order.number || order.id}`,\n issueDate: today,\n saleDate: order.date_created?.split('T')[0] || today,\n issuePlace: order.billing?.city || 'Warszawa',\n currency: order.currency || 'PLN',\n type: 'VAT',\n seller: {\n nip: config.sellerNip,\n name: config.sellerName,\n address: {\n street: config.sellerStreet,\n city: config.sellerCity\n }\n },\n buyer: {\n nip: buyerNip,\n name: order.billing?.company || `${order.billing?.first_name} ${order.billing?.last_name}`,\n address: {\n street: order.billing?.address_1 || '',\n city: `${order.billing?.postcode || ''} ${order.billing?.city || ''}`\n }\n },\n items: items,\n payment: {\n paid: order.status === 'completed' || order.date_paid != null,\n date: order.date_paid?.split('T')[0] || today,\n method: order.payment_method === 'cod' ? 'cash' : 'transfer'\n }\n};\n\nreturn [{ json: { skip: false, invoice } }];"
},
"id": "transform",
"name": "Transform to KSeF",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
500,
550
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"leftValue": "={{ $json.skip }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "false"
}
}
]
},
"options": {}
},
"id": "has-nip",
"name": "Has NIP?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
800,
550
],
"notes": "IF, not Filter - a Filter node only has one output and silently drops non-matching items, which broke the 'skipped' response path."
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ JSON.stringify({ success: true, skipped: true, reason: $json.reason }) }}",
"options": {
"responseCode": 200
}
},
"id": "respond-skip",
"name": "Respond Skipped",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
800,
710
]
},
{
"parameters": {
"method": "POST",
"url": "={{ $('Configuration (EDIT ME)').item.json.ksefGatewayUrl }}/ksef/invoice",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify($json.invoice) }}",
"options": {
"timeout": 120000
},
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "X-Api-Key",
"value": "={{ $('Configuration (EDIT ME)').item.json.gatewayApiKey }}"
}
]
}
},
"id": "send-invoice",
"name": "Send to KSeF Gateway",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1100,
550
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ JSON.stringify({ success: $json.data?.ksefNumber ? true : false, ksefNumber: $json.data?.ksefNumber || null, error: $json.error || null }) }}"
},
"id": "respond-ok",
"name": "Respond OK",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
1400,
550
]
}
],
"connections": {
"WooCommerce Webhook": {
"main": [
[
{
"node": "Configuration (EDIT ME)",
"type": "main",
"index": 0
}
]
]
},
"Configuration (EDIT ME)": {
"main": [
[
{
"node": "Verify Signature",
"type": "main",
"index": 0
}
]
]
},
"Verify Signature": {
"main": [
[
{
"node": "Signature Valid?",
"type": "main",
"index": 0
}
]
]
},
"Signature Valid?": {
"main": [
[
{
"node": "Transform to KSeF",
"type": "main",
"index": 0
}
],
[
{
"node": "Respond Unauthorized",
"type": "main",
"index": 0
}
]
]
},
"Transform to KSeF": {
"main": [
[
{
"node": "Has NIP?",
"type": "main",
"index": 0
}
]
]
},
"Has NIP?": {
"main": [
[
{
"node": "Send to KSeF Gateway",
"type": "main",
"index": 0
}
],
[
{
"node": "Respond Skipped",
"type": "main",
"index": 0
}
]
]
},
"Send to KSeF Gateway": {
"main": [
[
{
"node": "Respond OK",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
},
"staticData": null,
"tags": [],
"triggerCount": 0
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
WooCommerce → Faktury KSeF. Uses httpRequest. Webhook trigger; 15 nodes.
Source: https://github.com/jurczykpawel/ksef-gateway/blob/afca51b445f6b89255b326b4f3bae52aa79cb682/examples/n8n/woocommerce-ksef-PL.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
This n8n template provides enterprise-level version control for your workflows using GitHub integration. Stop losing hours to broken workflows and manual exports – get proper commit history, visual di
This flow creates dummy files for every item added in your *Arrs (Radarr/Sonarr) with the tag .
eek-Go v2 (Batch-Then-Review). Uses httpRequest. Webhook trigger; 75 nodes.
This workflow receives webhook requests from a content calendar and uses the X API v2 to publish text posts, threads, image/video posts, and polls, as well as delete existing posts and run a credentia
This workflow acts as a central API gateway for all technical indicator agents in the Binance Spot Market Quant AI system. It listens for incoming webhook requests and dynamically routes them to the c