ACH Processing

The ach-handler Lambda consumes the site-payments Kinesis stream and processes ACH settlement callbacks for subscription collection attempts. It is the mechanism by which a subscription’s billing_status changes after an ACH debit or refund settles.

ACH Callbacks

A Kinesis event-source filter delivers only the four subscription payment event types; other payment events on the stream (e.g. float disbursements) never invoke the Lambda. The handler dispatches on the payment’s status:

Kinesis event type payment.Status billing_status transition Side effects

SUBSCRIPTION_COMPLETED

COMPLETED

ACHSENT → COMPLETED

completion_date/last_run_date set, usio_error cleared, mask updated; digital receipt + attribution events emitted

SUBSCRIPTION_RETURNED

FAILED

ACHSENT → ERROR

usio_error set to the ACH return code; failure notifications emitted; user banned when the code is in Bannable ACH Return Codes

SUBSCRIPTION_REFUNDED

REFUNDED

COMPLETED → REFUNDED

Status-only update (updated_event payment-refunded); no notifications

SUBSCRIPTION_CHARGED_BACK

CHARGED_BACK

ACHSENT → ERROR

User banned unconditionally; admin note added; no user-facing notifications

Source: pkg/payments/achhandler.go.

A record whose user or subscription lookup returns not-found is logged and skipped as a no-op. Status descriptions live in Subscription Lifecycle; the notification and attribution event contracts (Segment, Iterable, AppsFlyer) live in Events.

The handler does no return attribution — that happens in the warehouse by joining the attempt’s decision context to the payments record; see DynamoDB — Decision Context.

Every processed callback emits the subscriptions.collections.attempt Datadog metric, tagged with method:ACH, the outcome status, institution, and process.

Bannable ACH Return Codes

A returned debit whose return code is in this set bans the user via the User Service and adds an admin note. Any SUBSCRIPTION_CHARGED_BACK event also bans, regardless of code. The set is a flat map of codes — JPMorgan reason codes (AG*, DS*) are independent entries, not aliases:

Return code Meaning

R05

Unauthorized debit to consumer account using corporate SEC code

R07

Authorization revoked by customer

R08

Payment stopped

R10

Customer advises not authorized

R11

Customer advises entry not in accordance with the terms of authorization

R29

Corporate customer advises not authorized

R51

Item is ineligible, notice not provided, signature not genuine, or item altered

AG03

JPMorgan reason code corresponding to R05

DS02

JPMorgan reason code corresponding to R08

AG01

JPMorgan reason code corresponding to R10, R20, and R29

Source: pkg/payments/achhandler.go (isBannableACHReturn).

Non-bannable return codes (e.g. R01 insufficient funds, R02 account closed) set billing_status to ERROR and record the code in usio_error without a ban; the subscription re-enters the retry cycle via the collections-job.