{
  "id": "Rq13RYAQcdpdp1lg",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "(Retail) Payment Failure Alert",
  "tags": [
    {
      "id": "F5TgwgrB2LTO3oA7",
      "name": "completed",
      "createdAt": "2026-01-02T08:54:40.571Z",
      "updatedAt": "2026-01-02T08:54:40.571Z"
    }
  ],
  "nodes": [
    {
      "id": "2bd93ac3-f66a-4e83-b1b3-1eb4208bd08d",
      "name": "Route by Failure Type",
      "type": "n8n-nodes-base.if",
      "position": [
        -768,
        1040
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "a98473ab-c316-46fe-9e92-61761ba2f6ce",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.failure_type }}",
              "rightValue": "soft"
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "21fd0fb4-1ecf-4728-b10d-a118f10329c9",
      "name": "Normalize Payment Data",
      "type": "n8n-nodes-base.set",
      "position": [
        -1216,
        1040
      ],
      "parameters": {
        "values": {
          "number": [
            {
              "name": "order_value",
              "value": "={{$json.body?.data?.object?.amount / 100}}"
            },
            {
              "name": "retry_count"
            }
          ],
          "string": [
            {
              "name": "order_id",
              "value": "={{$json.body?.data?.object?.id}}"
            },
            {
              "name": "customer_email",
              "value": "={{$json.body?.data?.object?.billing_details?.email}}"
            },
            {
              "name": "customer_name",
              "value": "={{$json.body?.data?.object?.billing_details?.name || 'Customer'}}"
            },
            {
              "name": "failure_reason",
              "value": "={{$json.body?.data?.object?.last_payment_error?.message || 'Unknown'}}"
            },
            {
              "name": "gateway",
              "value": "={{$json.body?.type?.includes('stripe') ? 'stripe' : 'stripe'}}"
            },
            {
              "name": "currency",
              "value": "={{$json.body?.data?.object?.currency || 'USD'}}"
            },
            {
              "name": "retry_url",
              "value": "={{$json.retry_url || 'https://yourstore.com/checkout'}}"
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 2
    },
    {
      "id": "0843f219-8800-4968-8146-0d50de5bf6ef",
      "name": "Identify Failure Type",
      "type": "n8n-nodes-base.function",
      "position": [
        -976,
        1040
      ],
      "parameters": {
        "functionCode": "const error = $json.body?.data?.object?.last_payment_error || {};\nconst declineCode = (error.decline_code || '').toLowerCase();\nconst message = (error.message || '').toLowerCase();\n\nlet type = 'hard';\n\n// FRAUD (highest priority)\nif (\n  ['fraudulent', 'restricted_card', 'stolen_card'].includes(declineCode) ||\n  message.includes('fraud') ||\n  message.includes('stolen') ||\n  message.includes('restricted')\n) {\n  type = 'fraud';\n}\n\n// SOFT (retryable only)\nelse if (\n  ['insufficient_funds', 'processing_error'].includes(declineCode) ||\n  message.includes('insufficient') ||\n  message.includes('temporary') ||\n  message.includes('timeout')\n) {\n  type = 'soft';\n}\n\n// HARD (default \u2013 includes expired_card)\nelse {\n  type = 'hard';\n}\n\nreturn [\n  {\n    json: {\n      ...$json,\n      failure_type: type,\n      decline_code: declineCode || null\n    }\n  }\n];\n"
      },
      "typeVersion": 1
    },
    {
      "id": "6556a7f8-3a39-4973-bde8-02a8f97c6fd1",
      "name": "Notify Team: Payment Failed",
      "type": "n8n-nodes-base.slack",
      "position": [
        -336,
        704
      ],
      "parameters": {
        "text": "=*Payment Failed|Order:* {{$json.order_id}}\nAmount: {{$json.order_value}} {{$json.currency}}\nType: {{$json.failure_type}}\nReason: {{$json.failure_reason}}\nCustomer: {{$json.customer_email}}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C09S57E2JQ2",
          "cachedResultName": "n8n"
        },
        "otherOptions": {
          "includeLinkToWorkflow": false
        }
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "5fac7b78-ffde-4be0-8f32-b14034ffe4af",
      "name": "Is This a High-Value Order?",
      "type": "n8n-nodes-base.if",
      "position": [
        -112,
        704
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "fe51ec7e-a9f1-41c1-a206-3bee35bc56d2",
              "operator": {
                "type": "number",
                "operation": "gte"
              },
              "leftValue": "={{ $('Route by Failure Type').item.json.order_value }}",
              "rightValue": 500
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "2cc80803-5f90-4907-97cd-ad152c78e76c",
      "name": "Notify Customer to Retry Payment",
      "type": "n8n-nodes-base.gmail",
      "position": [
        160,
        688
      ],
      "parameters": {
        "sendTo": "={{ $('Route by Failure Type').item.json.body.data.object.billing_details.email }}",
        "message": "=Hi {{ $('Route by Failure Type').item.json.body.data.object.billing_details.name }},  \n\nYour payment didn\u2019t go through due to a temporary issue.  \n\nRetry here: {{ $('Route by Failure Type').item.json.retry_url }}\n\nIf the issue continues, we\u2019re happy to help.  \n\n\u2014 Support Team",
        "options": {},
        "subject": "=Payment issue with your order",
        "emailType": "text"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "d1c2a98f-95b6-470d-a081-4a314fd6cbf0",
      "name": "Wait Before Next Retry Attempt",
      "type": "n8n-nodes-base.wait",
      "position": [
        384,
        688
      ],
      "parameters": {
        "unit": "minutes",
        "amount": 2
      },
      "typeVersion": 1
    },
    {
      "id": "2469687a-7103-4933-a3cd-a01039b5834d",
      "name": "Increase Retry Attempt Count",
      "type": "n8n-nodes-base.set",
      "position": [
        608,
        688
      ],
      "parameters": {
        "values": {
          "number": [
            {
              "name": "retry_count",
              "value": "={{ ($('Route by Failure Type').item.json.retry_count || 0) + 1}}"
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 2
    },
    {
      "id": "ddd834d1-dd35-4b08-b1ea-279819125b2a",
      "name": "Save Payment Failure Record",
      "type": "n8n-nodes-base.supabase",
      "position": [
        720,
        1248
      ],
      "parameters": {
        "tableId": "payment_failure",
        "fieldsUi": {
          "fieldValues": [
            {
              "fieldId": "created_at",
              "fieldValue": "={{ $json.logged_at }}"
            },
            {
              "fieldId": "payment_type",
              "fieldValue": "={{ $json.payment_status }}"
            },
            {
              "fieldId": "order_id",
              "fieldValue": "={{ $json.order_id }}"
            },
            {
              "fieldId": "customer_email",
              "fieldValue": "={{ $json.customer_email }}"
            },
            {
              "fieldId": "failure_reason",
              "fieldValue": "={{ $json.failure_reason }}"
            },
            {
              "fieldId": "retry_count",
              "fieldValue": "={{ $json.retry_count }}"
            },
            {
              "fieldId": "order_value",
              "fieldValue": "={{ $json.order_value }}"
            }
          ]
        }
      },
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "ae3d905f-054b-485d-94c0-786bc688260c",
      "name": "Log Retry Attempt Result",
      "type": "n8n-nodes-base.set",
      "position": [
        1152,
        672
      ],
      "parameters": {
        "values": {
          "string": [
            {
              "name": "logged_at",
              "value": "={{new Date().toISOString()}}"
            },
            {
              "name": "payment_status",
              "value": "=payment_failed-  {{ $('Route by Failure Type').item.json.failure_type }}"
            },
            {
              "name": "order_id",
              "value": "={{ $('Route by Failure Type').item.json.order_id }}"
            },
            {
              "name": "customer_email",
              "value": "={{ $('Route by Failure Type').item.json.customer_email }}"
            },
            {
              "name": "failure_reason",
              "value": "={{ $('Route by Failure Type').item.json.failure_reason }}"
            },
            {
              "name": "retry_count",
              "value": "={{ $json.retry_count }}"
            },
            {
              "name": "order_value",
              "value": "={{ $('Route by Failure Type').item.json.order_value }}"
            }
          ]
        },
        "options": {},
        "keepOnlySet": true
      },
      "typeVersion": 2
    },
    {
      "id": "c7c5b979-ac34-4e4d-b45d-1d096adbc36f",
      "name": "Is This a Fraudulent Payment?",
      "type": "n8n-nodes-base.if",
      "position": [
        -320,
        1232
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "2ffa8ca8-d565-4112-a04e-1821cd392058",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.failure_type }}",
              "rightValue": "fraud"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "c514fde9-1465-478c-bad8-ff31b96366c0",
      "name": "Log Fraud Case",
      "type": "n8n-nodes-base.set",
      "position": [
        192,
        1152
      ],
      "parameters": {
        "values": {
          "string": [
            {
              "name": "logged_at",
              "value": "={{new Date().toISOString()}}"
            },
            {
              "name": "payment_status",
              "value": "=payment_failed-  {{ $('Route by Failure Type').item.json.failure_type }}"
            },
            {
              "name": "order_id",
              "value": "={{ $('Route by Failure Type').item.json.order_id }}"
            },
            {
              "name": "customer_email",
              "value": "={{ $('Route by Failure Type').item.json.customer_email }}"
            },
            {
              "name": "failure_reason",
              "value": "={{ $('Route by Failure Type').item.json.failure_reason }}"
            },
            {
              "name": "order_value",
              "value": "={{ $('Route by Failure Type').item.json.order_value }}"
            }
          ]
        },
        "options": {},
        "keepOnlySet": true
      },
      "typeVersion": 2
    },
    {
      "id": "88281841-2a81-47e2-af39-4bbef3bc7dfe",
      "name": "Log Hard Failure Case",
      "type": "n8n-nodes-base.set",
      "position": [
        192,
        1328
      ],
      "parameters": {
        "values": {
          "string": [
            {
              "name": "logged_at",
              "value": "={{new Date().toISOString()}}"
            },
            {
              "name": "payment_status",
              "value": "=payment_failed-  {{ $('Route by Failure Type').item.json.failure_type }}"
            },
            {
              "name": "order_id",
              "value": "={{ $('Route by Failure Type').item.json.order_id }}"
            },
            {
              "name": "customer_email",
              "value": "={{ $('Route by Failure Type').item.json.customer_email }}"
            },
            {
              "name": "failure_reason",
              "value": "={{ $('Route by Failure Type').item.json.failure_reason }}"
            },
            {
              "name": "order_value",
              "value": "={{ $('Route by Failure Type').item.json.order_value }}"
            }
          ]
        },
        "options": {},
        "keepOnlySet": true
      },
      "typeVersion": 2
    },
    {
      "id": "6530d147-da01-4372-8f16-a788d010e430",
      "name": "Notify Team: Fraud Detected",
      "type": "n8n-nodes-base.slack",
      "position": [
        -48,
        1152
      ],
      "parameters": {
        "text": "=*FRAUD ALERT|Order:* {{$json.order_id}}\nAmount: {{$json.order_value}}\nType: {{$json.failure_type}}\nReason: {{$json.failure_reason}}\nCustomer: {{$json.customer_email}}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C09S57E2JQ2",
          "cachedResultName": "n8n"
        },
        "otherOptions": {
          "includeLinkToWorkflow": false
        }
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "fff993df-fc69-41ec-9c80-4fbad16f4b7f",
      "name": "Notify Team: Payment Failure",
      "type": "n8n-nodes-base.slack",
      "position": [
        -48,
        1328
      ],
      "parameters": {
        "text": "=*Hard ALERT|Order:* {{$json.order_id}}\nAmount: {{$json.order_value}}\nType: {{$json.failure_type}}\nReason: {{$json.failure_reason}}\nCustomer: {{$json.customer_email}}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C09S57E2JQ2",
          "cachedResultName": "n8n"
        },
        "otherOptions": {
          "includeLinkToWorkflow": false
        }
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "ba5d26ed-7160-437c-aa5a-772b9f013e75",
      "name": "Receive Payment Failure",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -1504,
        1040
      ],
      "parameters": {
        "path": "payment-failed-enterprise",
        "options": {},
        "httpMethod": "POST"
      },
      "typeVersion": 1
    },
    {
      "id": "d1e62813-d777-46e5-893c-21e45d0e7819",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1584,
        960
      ],
      "parameters": {
        "color": 7,
        "width": 256,
        "height": 224,
        "content": "Receives real-time payment failure events."
      },
      "typeVersion": 1
    },
    {
      "id": "7c3c7fd8-8b6d-4b88-bd3d-29571db9f89f",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1296,
        832
      ],
      "parameters": {
        "color": 7,
        "width": 688,
        "height": 400,
        "content": "## Payment Failure Processing & Classification\n\nStandardizes payment failure data from Stripe, analyzes the error details and determines whether the failure is retryable (soft), permanent (hard) or fraudulent\u2014then routes the flow based on retry eligibility."
      },
      "typeVersion": 1
    },
    {
      "id": "47b128d0-68c6-4082-8b5d-c11572feb60c",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -528,
        544
      ],
      "parameters": {
        "color": 7,
        "width": 1872,
        "height": 352,
        "content": "## Payment Recovery & Tracking Process\n\nSends a Slack alert about the failed payment, checks if the order is high value, emails the customer to retry the payment, waits between retries to avoid spamming, counts how many retry attempts were made, stops after a limit and saves the data for reporting or storage."
      },
      "typeVersion": 1
    },
    {
      "id": "eefb383f-1c4b-4086-a6c7-05c384ed4633",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        672,
        1168
      ],
      "parameters": {
        "color": 7,
        "width": 256,
        "height": 256,
        "content": "Stores all payment failure details for reporting"
      },
      "typeVersion": 1
    },
    {
      "id": "b01336a9-a862-4fcf-8cfa-19f373c7712e",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -528,
        992
      ],
      "parameters": {
        "color": 7,
        "width": 1152,
        "height": 512,
        "content": "## Fraud & Permanent Failure Handling Process\nIdentifies fraud-related and permanent payment failures, immediately alerts the team about suspicious activity, notifies the team of non-retryable failures and prepares both fraud and hard-failure data for storage and reporting."
      },
      "typeVersion": 1
    },
    {
      "id": "beba9efa-5e9b-4954-91a9-95da30a02b68",
      "name": "Has Retry Limit Been Reached?",
      "type": "n8n-nodes-base.if",
      "position": [
        864,
        688
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 1,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "634b7e70-5b4e-4675-93d5-b18ce4f36bad",
              "operator": {
                "type": "number",
                "operation": "lt"
              },
              "leftValue": "={{ $json.retry_count }}",
              "rightValue": 3
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "146e96d6-5ec3-455c-bc0f-51291b8a5188",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2144,
        112
      ],
      "parameters": {
        "width": 496,
        "height": 816,
        "content": "## How it works\n\nThis workflow automatically handles payment failures and makes sure nothing is missed.\nWhen a payment fails, it first collects all important details such as the order ID, customer email, amount, currency and the reason for failure. It then understands why the payment failed and classifies it as a retryable issue, a permanent failure or suspected fraud.\n\nIf the issue is retryable, the workflow notifies the team on Slack, checks if the order value is high and sends a friendly retry email to the customer. To avoid spamming, it waits between retries, counts how many times a retry has been attempted and automatically stops after three tries. All retry attempts are tracked for reporting.\n\nIf the failure is permanent or fraud-related, the workflow immediately alerts the team on Slack so they can take action quickly. Fraud cases are clearly separated from normal failures to avoid unnecessary retries.\n\nFinally, every outcome\u2014successful retries, hard failures and fraud cases\u2014is logged in the database in a clean and structured format. This helps with analytics, reporting and future improvements\n\n\n\n## Setup steps\n\n**1.** Connect the payment provider webhook (e.g., Stripe,Shopify, Paypal, Woocommerce) to trigger the workflow.\n\n**2.** Set up Slack and email credentials for alerts and customer communication.\n\n**3.** Configure retry limits, wait time and high-value order threshold.\n\n**4.** Connect the database (Supabase) for storing payment failure records.\n\n**5.** Test the workflow using a failed payment event before going live."
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "8cd9cbbc-8df3-43d5-80be-7e178ffc0285",
  "connections": {
    "Log Fraud Case": {
      "main": [
        [
          {
            "node": "Save Payment Failure Record",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Identify Failure Type": {
      "main": [
        [
          {
            "node": "Route by Failure Type",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Hard Failure Case": {
      "main": [
        [
          {
            "node": "Save Payment Failure Record",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route by Failure Type": {
      "main": [
        [
          {
            "node": "Notify Team: Payment Failed",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Is This a Fraudulent Payment?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Payment Data": {
      "main": [
        [
          {
            "node": "Identify Failure Type",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Receive Payment Failure": {
      "main": [
        [
          {
            "node": "Normalize Payment Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Retry Attempt Result": {
      "main": [
        [
          {
            "node": "Save Payment Failure Record",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is This a High-Value Order?": {
      "main": [
        [
          {
            "node": "Notify Customer to Retry Payment",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Notify Team: Fraud Detected": {
      "main": [
        [
          {
            "node": "Log Fraud Case",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Notify Team: Payment Failed": {
      "main": [
        [
          {
            "node": "Is This a High-Value Order?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Increase Retry Attempt Count": {
      "main": [
        [
          {
            "node": "Has Retry Limit Been Reached?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Notify Team: Payment Failure": {
      "main": [
        [
          {
            "node": "Log Hard Failure Case",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has Retry Limit Been Reached?": {
      "main": [
        [
          {
            "node": "Log Retry Attempt Result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is This a Fraudulent Payment?": {
      "main": [
        [
          {
            "node": "Notify Team: Fraud Detected",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Notify Team: Payment Failure",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait Before Next Retry Attempt": {
      "main": [
        [
          {
            "node": "Increase Retry Attempt Count",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Notify Customer to Retry Payment": {
      "main": [
        [
          {
            "node": "Wait Before Next Retry Attempt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}