How to Detect Failed Stripe Payments and Alert Finance in Slack

Build a scheduled Spojit workflow that lists recent Stripe payment intents, keeps only the failed ones, and posts a single grouped alert to your finance Slack channel.

What This Integration Does

Failed card payments are easy to miss until a customer complains or a subscription lapses. This workflow gives your finance team a regular, automatic sweep: on a fixed schedule it pulls the most recent Stripe payment intents, filters down to the ones that did not succeed, and drops a tidy summary into a Slack channel so someone can follow up while the failure is still fresh. Instead of logging into the Stripe dashboard and scanning by hand, your team gets the list pushed to them.

The run model is a Schedule trigger: Spojit fires the workflow on a cron expression you choose (for example every weekday morning), with no inbound request. Each run is stateless and reads from Stripe over a fixed lookback window, so two runs can report the same failed payment if it stays unresolved between them. The workflow only reads from Stripe and writes a message to Slack; it never changes a charge or a customer. Re-runs are therefore safe to repeat, and you can widen or narrow the lookback by adjusting how many payment intents you request.

Prerequisites

  • A Stripe connection in Spojit (API key). See the Stripe connector docs for the key type and permissions, which must allow reading payment intents.
  • A Slack connection in Spojit with permission to post to the target channel, and the channel ID or name of your finance channel (for example #finance-alerts).
  • If you are new to adding integrations, read Adding a New Connection first.
  • A decision on how often to sweep and in which timezone (the Schedule trigger uses 5-field cron plus an IANA timezone).

Step 1: Add a Schedule trigger

On the canvas, set the Trigger node type to Schedule. Add a cron expression and an IANA timezone so the sweep runs when your finance team is at their desks. For a weekday 9am sweep in Sydney, use:

0 9 * * 1-5
Australia/Sydney

A single trigger can hold multiple schedules if you want a morning and an afternoon pass. The trigger output is { scheduledAt }, which you can reference downstream as {{ scheduledAt }} to timestamp the alert. For more detail, see Setting Up a Schedule Trigger.

Step 2: List recent payment intents from Stripe

Add a Connector node in Direct mode, choose the Stripe connection, and select the list-payment-intents tool. Direct mode is the right choice here: it is a single, predictable read with no AI cost. Set limit to the number of recent intents to scan in one pass (max 100, default 10); a value like 100 gives you a wider window per run. Leave customer empty to scan all customers. Name the output variable so later steps can read it, for example stripe_intents.

Each returned payment intent includes a status field. The statuses that indicate a payment did not go through are requires_payment_method (the card was declined and a new method is needed) and canceled. A status of succeeded means the payment cleared.

Step 3: Keep only the failed payment intents

Add a Connector node in Direct mode on the array utility connector and select the filter tool to pull out just the records you care about. Point it at the list of payment intents returned by the previous step and keep rows whose status is one of the failure values. If your environment prefers raw logic over a filter expression, you can instead use the code connector's execute-javascript tool to return only the failed entries:

const failed = input.stripe_intents.data.filter(
  (pi) => pi.status === "requires_payment_method" || pi.status === "canceled"
);
return { failed, count: failed.length };

Name the output variable failed_payments. You now have a clean list plus a count you can branch on.

Step 4: Skip the alert when nothing failed

Add a Condition node so you do not spam the channel with empty reports. Configure the if/else test to check that the failed count is greater than zero, for example {{ failed_payments.count }} is greater than 0. Wire the true branch to the next step. Leave the false branch unconnected so quiet runs end cleanly. See Using Condition Nodes for how to set up the comparison.

Step 5: Build the grouped alert text

Add a Transform node to turn the list of failed payments into one readable Slack message rather than many separate posts. Loop over {{ failed_payments.failed }} and produce one line per payment, including the amount, currency, status, and Stripe payment intent ID so finance can find it quickly. A formatted body might look like this:

Failed Stripe payments as of {{ scheduledAt }}
Count: {{ failed_payments.count }}

- pi_3Q...  $49.00 USD  requires_payment_method
- pi_3R...  $12.50 USD  canceled

Remember that Stripe amounts are in the smallest currency unit (for example cents for USD), so divide by 100 when you format the display value. Save the result to a variable such as alert_text. For templating help, see Working with Variables and Templates.

Step 6: Post the alert to Slack

Add a Connector node in Direct mode, choose the Slack connection, and select the send-message tool. Set the channel to your finance channel (for example #finance-alerts or its channel ID) and set the message text to {{ alert_text }}. Because the upstream Condition only allows this branch to run when at least one payment failed, the channel receives one grouped alert per sweep instead of a flood of single-line messages. If you want the channel name resolved by email or to confirm the channel exists first, the Slack connector also exposes list-channels and get-channel-info.

Tips

  • Tune limit to your volume: a busy account may need 100 per run plus a more frequent schedule, while a low-volume account is fine scanning 10 to 25.
  • To page beyond the first 100 results, capture the last payment intent ID and pass it as starting_after on a follow-up list-payment-intents call inside a Loop.
  • Open Miraxa, the intelligent layer across your automation, and ask it to "Add a Condition node that checks if {{ failed_payments.count }} is over 0 and connect the true branch to a Slack send-message node" to scaffold the branching for you, then fine-tune in the properties panel.
  • If you want a private heads-up in addition to the channel post, add a Send Email node on the true branch to email the same {{ alert_text }} to your finance lead.

Common Pitfalls

  • Lookback gaps and duplicates. Because each run reads a fixed window, a failure that persists across sweeps will be reported again, and a failure older than your window can be missed. Match limit and schedule frequency to your payment volume.
  • Timezone drift. The cron expression is evaluated in the IANA timezone you set, not your browser's. Set Australia/Sydney (or your own) explicitly so the 9am sweep really lands at 9am locally.
  • Wrong status assumptions. Do not treat every non-succeeded intent as failed: states like processing or requires_action are in progress, not failed. Filter only on the failure statuses you actually want to alert on.
  • Amount formatting. Stripe returns amounts in the smallest currency unit, so an unformatted value of 4900 is really $49.00. Divide by 100 before posting to Slack.

Testing

Before turning on the schedule, validate the logic on a small scope. Use the Run button to trigger a manual test run and inspect the execution log to confirm list-payment-intents returns data and the filter step isolates the failed entries. In a Stripe test environment you can create a payment intent that requires a payment method so you have a guaranteed failed record to catch. Point the Slack send-message step at a private test channel first, confirm the grouped message renders cleanly with the right amounts and IDs, then switch to your real finance channel and enable the schedule.

Learn More

Did this answer your question? Thanks for the feedback There was a problem submitting your feedback. Please try again later.