Architecture

System Overview

The LOC Service is built as a collection of AWS Lambda functions that work together to provide installment loan functionality. The service uses DynamoDB as its primary data store with a single-table design pattern.

                                  ┌─────────────────────────────────────┐
                                  │           External Services         │
                                  │  ┌─────────┐ ┌─────────┐ ┌────────┐ │
                                  │  │ Users   │ │Payments │ │Iterable│ │
                                  │  │ Service │ │ Service │ │  API   │ │
                                  │  └────┬────┘ └───┬─────┘ └──┬─────┘ │
                                  └───────┼──────────┼──────────┼───────┘
                                          │          │          │
    ┌─────────────────────────────────────┼──────────┼──────────┼──────────────┐
    │                                LOC Service                               │
    │                                     │          │          │              │
    │  ┌──────────────┐              ┌────┴──────────┴──────────┴─────┐        │
    │  │  API Gateway │◄────────────►│           API Lambda           │        │
    │  └──────────────┘              │  (Applications, Loans, etc.)   │        │
    │                                └───────────────┬────────────────┘        │
    │                                                │                         │
    │  ┌─────────────────────────────────────────────┼─────────────────────┐   │
    │  │                         DynamoDB                                  │   │
    │  │  ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌─────────┐  │   │
    │  │  │  Loans   │ │Installmts│ │ Payments │ │ LoanApps │ │  Logs   │  │   │
    │  │  └──────────┘ └──────────┘ └──────────┘ └──────────┘ └─────────┘  │   │
    │  └───────────────────────────────┬───────────────────────────────────┘   │
    │                                  │                                       │
    │                         ┌────────▼────────┐                              │
    │                         │ DynamoDB Stream │                              │
    │                         └────────┬────────┘                              │
    │                    ┌─────────────┼─────────────┐                         │
    │                    ▼             ▼             ▼                         │
    │           ┌────────────┐ ┌────────────┐ ┌────────────┐                   │
    │           │Log Handler │ │ Expiration │ │   Other    │                   │
    │           │  (Audit)   │ │  Handler   │ │  Handlers  │                   │
    │           └────────────┘ └────────────┘ └────────────┘                   │
    │                                                                          │
    │  ┌─────────────────────────────────────────────────────────────────────┐ │
    │  │                     Collections System                              │ │
    │  │  ┌─────────────┐    ┌─────────┐    ┌─────────────┐                  │ │
    │  │  │ CloudWatch  │───►│  Job    │───►│  SQS Queue  │                  │ │
    │  │  │   Events    │    │ Lambda  │    │             │                  │ │
    │  │  └─────────────┘    └─────────┘    └──────┬──────┘                  │ │
    │  │                                           │                         │ │
    │  │                                   ┌───────▼─────┐                   │ │
    │  │                                   │   Worker    │                   │ │
    │  │                                   │   Lambda    │                   │ │
    │  │                                   └─────────────┘                   │ │
    │  └─────────────────────────────────────────────────────────────────────┘ │
    │                                                                          │
    │  ┌─────────────────────────────────────────────────────────────────────┐ │
    │  │                     ACH Webhook Handler                             │ │
    │  │  ┌─────────────┐    ┌─────────────┐                                 │ │
    │  │  │  Kinesis    │───►│ ACH Handler │                                 │ │
    │  │  │   Stream    │    │   Lambda    │                                 │ │
    │  │  └─────────────┘    └─────────────┘                                 │ │
    │  └─────────────────────────────────────────────────────────────────────┘ │
    └──────────────────────────────────────────────────────────────────────────┘

Lambda Functions

API Lambda (cmd/api)

The main REST API handling all loan operations:

  • Loan Applications - Create, approve, reject applications

  • Loan Creation - Disburse funds and create loan records

  • Loan Queries - Get loan details, installments, payments

  • Manual Payments - User-initiated loan payments

  • QA Endpoints - Testing and development utilities

Collections Job (cmd/loc-collections-job)

Scheduled Lambda that runs collection processes. See Collections for details.

Collections Worker (cmd/loc-collections-worker)

SQS-triggered Lambda that processes payment events from the collections queue.

ACH Handler (cmd/ach-handler)

Kinesis-triggered Lambda that processes payment webhooks:

  • ACH returns and completions

  • Pinless debit chargebacks

  • Payment status updates

Expiration Handler (cmd/expiration-handler)

DynamoDB Stream-triggered Lambda that handles TTL expirations:

  • Marks expired loan applications as ABANDONED

  • Cleans up orphaned records

Agreements Generator (cmd/agreements-generator)

SQS/Direct-invoke Lambda that generates loan agreement PDFs. See Loan Agreements for details.

Log Handler (cmd/log-handler)

DynamoDB Stream-triggered Lambda that creates audit log entries for payment and installment changes.

Payment and Collections Flow

The collections system handles automated payment processing through scheduled jobs:

Collection Processes

Process Description Creates Payments Sends Notifications

upcoming

Notifies users of payments coming due

No

Yes

due

Processes installments due today (autopay or notify)

Yes (autopay)

Yes (non-autopay)

overdue

Notifies users of past-due installments

No

Yes (scheduled)

retry

Retries failed payments

Yes

No

Daily Schedule

Time (CST) Process Description

5:00 AM

retry

Retry failed payments

7:00 AM

due

Process today’s installments

8:00 AM

upcoming

Notify of payments in 3 days

9:00 AM

overdue

Notify of overdue payments

For detailed collection system documentation, see Collections.

Payment Flow

User Payment Request              Autopay Collection
        │                                │
        ▼                                ▼
┌───────────────┐               ┌───────────────┐
│  API Lambda   │               │  Collections  │
│ POST /payback │               │     Job       │
└───────┬───────┘               └───────┬───────┘
        │                               │
        │                        ┌──────▼──────┐
        │                        │  SQS Queue  │
        │                        └──────┬──────┘
        │                               │
        │                        ┌──────▼──────┐
        │                        │   Worker    │
        │                        └──────┬──────┘
        │                               │
        └───────────────┬───────────────┘
                        │
                ┌───────▼───────┐
                │    Engine     │
                │  (Payback)    │
                └───────┬───────┘
                        │
                ┌───────▼───────┐
                │   Payments    │
                │   Service     │
                └───────┬───────┘
                        │
           ┌────────────┼────────────┐
           │            │            │
           ▼            ▼            ▼
     ┌──────────┐ ┌──────────┐ ┌──────────┐
     │ Success  │ │  Retry   │ │  Failed  │
     └──────────┘ └──────────┘ └──────────┘
payback flow diagram

DynamoDB Design

The service uses a single-table design with composite keys. See the Data Model documentation for details on each entity.

Key Prefixes

Prefix Entity

USER#

User identifier

LOAN#

Loan record

LOANAPP#

Loan application

PAYMENT#

Payment record

INSTALLMENT#

Installment record

PAYMENTLOG#

Payment audit log

INSTALLMENTLOG#

Installment audit log

Global Secondary Indexes

The table uses 4 GSIs for different access patterns:

  • GSI1 - Status-based queries (loans by status, installments by status)

  • GSI2 - Cross-user queries (all payments, all installments by date)

  • GSI3 - Lookup by external ID (confirmation ID, application ID)

  • GSI4 - Reserved for future use

External Service Integration

Users Service

Used to retrieve user details for: * Payment processing (bank account info) * Notification personalization * Underwriting decisions

Payments Service

Handles actual money movement: * ACH debits and credits * Pinless debit transactions * Returns payment webhooks via Kinesis

Iterable

Email and notification delivery: * Upcoming payment reminders * Payment due notifications * Overdue payment alerts * Payment receipts

Distributed Locking

The service uses cirello.io/dynamolock/v2 for distributed locking to prevent concurrent modifications to loans during payment processing.

lock, err := e.DBLock.LockRecord(ctx, userID, loanID, "loan")
defer lock.Close()
// ... perform payment operations