ACH Processing

The site-floats-ach-handler Lambda consumes the prod-payments Kinesis stream and processes ACH settlement callbacks for float disbursements and collection attempts. It is the mechanism by which floats leave ACHSENT — to COMPLETED, RETRY, or DEFAULTED (see Float Statuses).

Callback Handling

The Lambda’s Kinesis event-source mapping filters the stream to six event types (source: deploy/lambda.tf):

FLOAT_DEBIT_COMPLETED, FLOAT_DEBIT_COMPLETED_PLAID, FLOAT_DEBIT_RETURNED, FLOAT_CREDIT_COMPLETED, FLOAT_CREDIT_RETURNED, FLOAT_DEBIT_CHARGED_BACK

The handler dispatches on the payment’s Status field:

payment.Status Float status Collection-log outcome Side effects

COMPLETED

COMPLETED

Accepted (AcceptedPlaid for the _PLAID event)

Digital receipt (Collection-Success Notifications)

FAILED

RETRY, or DEFAULTED on a bannable return code

The ACH return code (e.g. R01)

User ban on a bannable code (Bannable ACH Return Codes)

CHARGED_BACK

DEFAULTED

CHARGED_BACK

User ban + admin note

Processing steps per event:

  1. Look up the float by the payment’s loan_id; no match logs and skips the event.

  2. Idempotency guard: a COMPLETED event for a float already COMPLETED is skipped entirely — no status update, collection log, receipt, or recalculation.

  3. Update the float in RDS: new status + the processor confirmation ID.

  4. Write a collection-history row (schema) — process is Check-ach-cleared for debit events, Chargeback-detector for chargebacks.

  5. On a bannable return or chargeback: ban the user (User Service) and add an account note (Admin Service).

  6. Trigger an underwriting recalculation for the user.

The receipt, ban, admin note, and recalculation are best-effort — failures are logged without halting processing. Failures updating RDS or writing the collection log fail the record, and Kinesis redelivers it.

Collection-Success Notifications

A settled collection (COMPLETED) sends a digital receipt across three channels:

Channel Purpose

Segment

Analytics events with float and payment details

Iterable

In-app and email notification that the float was collected

AppsFlyer

Mobile attribution event

Bannable ACH Return Codes

A debit returned with one of these codes sets the float to DEFAULTED — NACHA prohibits re-initiating the debit — and bans the user. Any chargeback also triggers a ban. Source: pkg/payments/achhandler.go.

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

Check truncation entry returned

R29

Corporate customer advises not authorized

R51

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

Non-bannable return codes (e.g. R01 insufficient funds, R02 account closed) set the float to RETRY with no ban, and it re-enters the retry cycle. A bannable code defaults the float directly — it never passes through RETRY, so it is excluded from all retry collection runs.