ACH Processing
The prod-float-service-ach-handler Lambda consumes the prod-payments Kinesis stream and processes ACH settlement callbacks for float disbursements and collection attempts. It is the primary mechanism by which float statuses transition to COMPLETED or back to RETRY after an ACH attempt.
Event Types
The ACH Handler filters for four event types from the prod-payments stream. Events for other payment types (e.g. subscription payments) are ignored.
| Kinesis Event Type | Category | Description |
|---|---|---|
|
Collection |
An ACH debit submitted for float collection has settled successfully. The float is fully collected. |
|
Collection |
An ACH debit was returned by the bank (e.g. insufficient funds, account closed). The float is placed back into retry. |
|
Disbursement |
An ACH credit (disbursement) has settled. Logged but no float status change — the float was already in |
|
Disbursement |
An ACH credit was returned — effectively a chargeback on the disbursement. The float is defaulted and the user is banned. |
ACH Callback Flow
Kinesis event received (FLOAT_DEBIT_* or FLOAT_CREDIT_*)
│
▼
Look up float in RDS by loan_id
│
├── Not found ──► log and skip (no-op)
│
▼
Determine new float status and outcome:
│
├── payment.Status == COMPLETED
│ float status ──► COMPLETED
│ outcome ──────► "Accepted"
│
├── payment.Status == FAILED
│ float status ──► RETRY
│ outcome ──────► ACH return code (e.g. "R01", "R05")
│
└── payment.Status == CHARGED_BACK
float status ──► DEFAULTED
outcome ──────► "CHARGED_BACK"
│
▼
Update float in RDS (ach_debit_status + ach_debit_id)
│
▼
Write collection log to DynamoDB (collection-history)
process: "Check-ach-cleared" or "Chargeback-detector"
outcome: "Accepted", ACH return code, or "CHARGED_BACK"
│
▼
If COMPLETED: send digital receipt
├── Segment notification
├── Iterable in-app / email notification
├── AppsFlyer attribution event
└── Trigger underwriting recalculation (Underwriting Service)
│
▼
Is return code bannable? ──No──► Done
│ Yes
▼
Ban user (User Service)
│
▼
Add ban note (Admin Service)
| All external calls after the RDS update (receipt notifications, underwriting recalculation, user ban, admin note) are best-effort — failures are logged and tracked in Segment but do not halt processing or roll back the float status update. |
Status Transitions
| Event Type | payment.Status | Resulting Float Status | Notes |
|---|---|---|---|
|
|
|
ACH collection settled. Digital receipt sent. Underwriting recalculation triggered. |
|
|
|
ACH debit returned by the bank. Float re-enters the retry cycle. ACH return code recorded as the outcome. |
|
|
|
Disbursement ACH was returned. User is banned via the User Service and a note is added via the Admin Service. |
|
|
(no change) |
Disbursement confirmed. Logged but float status is not updated — it remains |
Collection Log Entry
Every processed event writes a record to the collection-history DynamoDB table regardless of outcome.
| Field | Value |
|---|---|
|
Float ID from the payment event |
|
Current UTC time as Unix nanoseconds |
|
From the float record in RDS |
|
Float’s |
|
Today’s date ( |
|
|
|
|
|
Payment processor confirmation ID from the event |
Notifications on Collection Success
When an ACH debit settles (FLOAT_DEBIT_COMPLETED), the ACH Handler sends a digital receipt across three channels:
| Channel | Purpose |
|---|---|
Segment |
User event analytics — notification events with float and payment details |
Iterable |
In-app and email notification informing the user their float has been collected |
AppsFlyer |
Mobile attribution event for the collection outcome |
After notifications are sent, the ACH Handler calls the Underwriting Service to trigger a recalculation of the user’s float eligibility, reflecting the now-cleared balance.
Bannable ACH Return Codes
When an ACH debit is returned with one of the following codes, the user is automatically banned via the User Service. Any chargeback (CHARGED_BACK) also triggers a ban.
| Return Code | Meaning |
|---|---|
|
Unauthorized debit to consumer account using corporate SEC code |
|
Authorization revoked by customer |
|
Payment stopped |
|
Customer advises not authorized |
|
Check truncation entry returned |
|
Corporate customer advises not authorized |
|
Item is ineligible, notice not provided, signature not genuine, or item altered |
Non-bannable return codes (e.g. R01 — insufficient funds, R02 — account closed) result in a RETRY status without triggering a ban. The float re-enters the normal retry cycle.
|
Related Pages
-
Float Lifecycle — End-to-end float status transitions
-
Collections Engine — How ACH attempts are submitted before the callback
-
DynamoDB Tables — Collection history table schema