
Company News
Free Business Plan Upgrades for Open Source Maintainers
Open source maintainers are under more pressure than ever. We're raising our open source program from the Team plan to the Business plan, free.
ach-conform
Advanced tools
Static check for the Stripe ACH "succeeded isn't final" trap: webhook handlers that treat payment_intent.succeeded as terminal for us_bank_account charges without also handling the later payment_intent.payment_failed / charge.failed flip.
A static check for one specific Stripe footgun: webhook handlers that treat
payment_intent.succeeded as final for an ACH (us_bank_account) charge.
Card payments settle in seconds and succeeded really is the end of the story. ACH debits
don't — Stripe can flip a succeeded PaymentIntent to failed up to several business days later
when the bank returns it (R01 insufficient funds, R02/R03 account closed or unauthorized). If
your webhook handler fulfils the order or marks the invoice paid on succeeded and never listens
for the later payment_intent.payment_failed / charge.failed, you ship a silent false-success
bug: paid-looking orders that were never actually paid.
ach-conform scans your webhook handler source for that pattern (and for a second one: ACH
intents that never account for requires_action, the micro-deposit verification state card
payments never hit) and fails your CI when it finds them.
npm install --save-dev ach-conform
Or run it without installing:
npx ach-conform src/webhooks/
npx ach-conform [path...]
Defaults to scanning the current directory. Exits 1 if it finds a succeeded-without-failed
handler, 0 otherwise (the requires_action check is a warning and doesn't fail the build on
its own).
$ npx ach-conform src/
[error] src/webhooks/stripe.js:14 (ach-succeeded-not-final)
Handles payment_intent.succeeded for an ACH (us_bank_account) charge but never handles
payment_intent.payment_failed or charge.failed. ACH debits can flip from succeeded to failed
days later (R01 insufficient funds, R02/R03 closed or unauthorized). Treating succeeded as
terminal ships a silent false-success bug -- fulfil/mark-paid logic needs a corresponding
failure-path reversal.
ach-conform: 1 finding(s) in src/.
# .github/workflows/ach-conform.yml
name: ach-conform
on: [push, pull_request]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npx ach-conform src/
ach-succeeded-not-final (error) — a file references us_bank_account and handles
payment_intent.succeeded, but never references payment_intent.payment_failed or
charge.failed.ach-missing-requires-action (warn) — a file handles ACH succeeded/processing states
but never references requires_action or micro-deposits, so verification-pending customers may
have no path forward.This is deliberately a narrow, textual check — a couple of regexes over your source, not a full AST analysis of control flow. It will miss handlers split across multiple files or built with heavy indirection, and it can false-flag a file that handles the failure case somewhere it doesn't expect to look. Treat a clean run as "no smoking gun found," not a correctness proof.
A static check only catches the anti-pattern in your code, not whether your Stripe integration
actually behaves as expected end to end. examples/test-clock-replay.js uses Stripe Test
Clocks to advance a test-mode ACH
PaymentIntent through the multi-day settlement window and confirm your webhook handler receives
both events and reverses fulfilment correctly:
STRIPE_SECRET_KEY=sk_test_... node examples/test-clock-replay.js
examples/bad-handler.js and examples/good-handler.js are minimal handlers ach-conform flags
and clears, respectively — useful as a reference for what the fix looks like.
Built autonomously by an AI agent (fernforge).
MIT
FAQs
Static check for the Stripe ACH "succeeded isn't final" trap: webhook handlers that treat payment_intent.succeeded as terminal for us_bank_account charges without also handling the later payment_intent.payment_failed / charge.failed flip.
The npm package ach-conform receives a total of 10 weekly downloads. As such, ach-conform popularity was classified as not popular.
We found that ach-conform demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Company News
Open source maintainers are under more pressure than ever. We're raising our open source program from the Team plan to the Business plan, free.

Security News
The supply chain control that delays freshly published gems now covers lockfile generation and gem vendoring in Ruby projects.

Security News
During a UK cyber test, a Mythos 5 agent used sockpuppets, social engineering, and prompt injection to try to get a maintainer to merge malware.