Architecture
System Context
The Subscription Service is deployed as a set of AWS Lambda functions and owns the full subscription billing lifecycle for the FloatMe platform. It exposes a REST API behind API Gateway and participates in the broader FloatMe platform as both an event-driven consumer (Kinesis streams, SQS queues, EventBridge rules) and producer (EventBridge, Kinesis). Inbound membership lifecycle events arrive via Kinesis from the User Service. ACH payment outcomes arrive via a second Kinesis stream from the Payments Service. Scheduled CloudWatch rules drive collection runs on weekdays and the daily notification pass. EventBridge routes income and balance events from the Insight and Transactions services to SQS queues consumed by the webhook collection workers.
Inbound Traffic
| Source | Description |
|---|---|
|
IAM-authenticated API Gateway consumed by internal FloatMe services and the mobile app via the global API gateway. All routes use |
|
Work queue consumed by the collections-worker Lambda for scheduled collection runs. Messages are enqueued by the collections-job Lambda after querying DynamoDB for due subscriptions. Visibility timeout 60 s, DLQ after 5 receives. |
|
Work queue consumed by the collections-worker for subscriptions in a retry-eligible state. Enqueued by the collections-job on the Monday–Friday retry run (07:00 UTC). Visibility timeout 60 s, DLQ after 5 receives. |
|
Work queue consumed by the collections-worker for subscriptions in a pause-eligible state. Enqueued by the collections-job on the Monday–Friday pause run (22:00 UTC). Visibility timeout 60 s, DLQ after 5 receives. |
|
Income detection events from the Insight Service, filtered and routed via an EventBridge rule ( |
|
Balance update events from the Transactions Service, filtered and routed via an EventBridge rule ( |
|
Batch operation requests consumed by the batch-worker Lambda (batch size 10, max concurrency 5). Visibility timeout 60 s. |
|
Notification jobs enqueued by the notifier-scheduler Lambda (batch size 10, max concurrency 5). Visibility timeout 900 s, DLQ after 5 receives. |
|
Membership lifecycle events from the User Service, filtered to event types: |
|
ACH payment outcome events from the Payments Service, filtered to subscription debit types: |
EventBridge (collections schedule) |
Three CloudWatch rules invoke the collections-job Lambda on weekdays: scheduled run at 08:00 UTC, retry run at 07:00 UTC, pause run at 22:00 UTC. Each rule injects a |
EventBridge (notifier cron) |
A daily CloudWatch rule ( |
Outbound Traffic
| Destination | Description |
|---|---|
|
Billing activity change events published by the kinesis-feeder Lambda from the |
Payments Service |
Collection attempt requests sent by the collections-worker and webhook workers. Disbursement requests sent by the API Lambda on subscription activation. All calls are IAM-authenticated API Gateway requests. |
User Service |
User profile and identity lookups performed by the API Lambda, memberships Lambda, and notifier-worker. IAM-authenticated API Gateway calls. |
Underwriting Service |
Eligibility and plan availability queries made by the API Lambda during subscription activation and plan upgrade/downgrade flows. |
Transactions Service |
Bank account balance lookups performed by the collections-worker and webhook workers to inform collection routing decisions. Also consumed by the notifier-worker. |
Insight Service |
Source of |
Admin Service |
Admin note attachment calls made by the ach-handler Lambda during ACH payment outcome processing. |
Segment |
User analytics events emitted by the ach-handler (ACH outcomes), collections-worker (collection outcomes), and notifier-worker (pre-subscription notifications). API key fetched from Secrets Manager. |
Iterable |
Email and in-app notifications triggered by the ach-handler (ACH payment events) and notifier-worker (pre-subscription notifications). API key fetched from Secrets Manager. |
AppsFlyer |
Mobile attribution events emitted by the ach-handler and webhook-balance-worker for subscription payment events. App IDs differ between production and non-production environments. |
SageMaker |
ML model endpoint ( |
Lambda Functions
| Function | Trigger | Responsibility |
|---|---|---|
|
API Gateway (IAM) |
Primary REST API for all subscription operations. Handles activation, reactivation, upgrade, downgrade, manual collection ( |
|
Kinesis ( |
Consumes membership lifecycle events from the User Service Kinesis stream. Filters for twelve event types — including UPGRADE, DOWNGRADE, CANCEL, RETRACT, AUTODOWNGRADED, GONETOCOLLECTIONS, PAYNOW, SUB_PAUSED, UNPAUSE, UNPAUSE_CHARGE, CLOSEACCOUNT, and REACTIVATE — and drives corresponding subscription state transitions in DynamoDB. Acquires a distributed lock per user before applying each transition to prevent concurrent modification. Queries the User Service for profile data when needed. |
|
Kinesis ( |
Consumes ACH payment outcome events from the Payments Service Kinesis stream. Filters for subscription debit event types (SUBSCRIPTION_COMPLETED, SUBSCRIPTION_RETURNED, SUBSCRIPTION_REFUNDED, SUBSCRIPTION_CHARGED_BACK) and maps each outcome to a |
|
DynamoDB Stream ( |
Reads change records from the |
|
SQS ( |
Processes bulk subscription operations and data management tasks dispatched to the batch queue. Reads and writes |
|
EventBridge (scheduled rules, Mon–Fri) + SQS paging queues |
Orchestrates all three collection runs (scheduled, retry, pause) by querying |
|
SQS ( |
Core collection executor. Dequeues individual subscription collection jobs from any of the three collection queues. Acquires a DynamoDB distributed lock per user to prevent concurrent attempts. Invokes the SageMaker ML model via the |
|
SQS ( |
Responds to income detection events sourced from the Insight Service and routed through EventBridge. Processes |
|
SQS ( |
Responds to |
|
EventBridge (daily, 12:00 UTC) + SQS paging queue |
Daily scheduler that finds users approaching their subscription billing date within the notification window. Queries |
|
SQS ( |
Processes individual pre-subscription notification jobs enqueued by the notifier-scheduler. For each user, queries the User Service for profile data and the Payments and Transactions services as needed. Evaluates GrowthBook feature flags to determine notification channel eligibility. Sends notifications via Iterable. GrowthBook and Iterable API keys are loaded from Secrets Manager at startup. |
Collections Architecture
The collections engine uses three parallel processing paths — scheduled (weekday runs at multiple times), webhook income-triggered, and webhook balance-triggered — all funneling into the Payments Service for execution.
The collections-job Lambda orchestrates the scheduled paths using a paging pattern that allows it to fan out across arbitrarily large subscription populations within Lambda’s execution time limits.
All three collection paths acquire a DynamoDB distributed lock per user before attempting collection, preventing concurrent debit attempts for the same subscriber.
The collections-worker consults a SageMaker ML model via the mldecider package before each scheduled attempt to determine whether collection is likely to succeed.
See Collections Engine for the detailed flow diagrams and decision logic for each collection path, including retry sequencing, pause handling, and the ML decider integration.
Data Storage
DynamoDB: billing-activity
The primary store for subscription records. Holds one row per subscription billing term per user, tracking the full lifecycle from SCHEDULED through collection attempts to COMPLETED, ERROR, or WAIVED. Both the API Lambda and all collection Lambdas read and write to this table. A DynamoDB Stream on this table feeds the kinesis-feeder Lambda.
Attribute Description ──────────────────────────────────────────────────────────────────── user_id Owning user (partition key) billing_date Subscription due date (sort key) subscription_id UUID identifying the subscription term billing_amount Amount to collect in cents billing_status Current status (SCHEDULED, ACHSENT, COMPLETED, ERROR, WAIVED) billing_period MM/YYYY representation of billing_date process Last process that updated the record (INITIAL, RETRY, WEBHOOK, etc.) transaction_id Payment reference from last collection attempt created_date Record creation timestamp completion_date Timestamp when billing reached a terminal state initial_run_date Timestamp of first collection attempt last_run_date Timestamp of last update (used as sort key in history table) usio_error Payment error detail from last attempt; cleared on success term Subscription plan term
DynamoDB: billing-activity-history
Append-only audit log of every state transition for every billing record.
Mirrors the schema of billing-activity but uses user_id + last_run_date as the primary key, allowing multiple history rows per user per subscription term.
Every write to billing-activity is accompanied by a corresponding write to this table.
The last_run_date field must be updated before writing to avoid overwriting prior history entries.
DynamoDB: locks
Distributed per-user collection locks. All collection paths — scheduled, retry, pause, webhook income, webhook balance — and the memberships and ach-handler Lambdas acquire a lock from this table before modifying subscription state. Prevents concurrent debit attempts for the same user.
| Table | Purpose |
|---|---|
|
Primary subscription records. One row per billing term per user. Queried by GSI for collection runs and notification scheduling. |
|
Append-only billing state transition history. PK: |
|
Distributed per-user locking table. Shared across all Lambdas that mutate subscription state. |
See DynamoDB Tables for the full attribute reference, GSI definitions, and access pattern details.
External Service Integrations
| Service | Integration |
|---|---|
Payments Service |
Core payment execution layer. The API Lambda submits disbursements on subscription activation. The collections-worker and webhook workers submit ACH collection attempts. The ach-handler processes payment outcome callbacks arriving on the |
User Service |
Queried by the API Lambda for user profile data on subscription operations, by the memberships Lambda on lifecycle events, and by the notifier-worker before sending pre-subscription notifications. IAM-authenticated API Gateway calls. |
Underwriting Service |
Queried by the API Lambda during subscription activation and plan upgrade/downgrade to validate eligibility and retrieve approved plan details. |
Transactions Service |
Queried by the collections-worker and webhook workers for current bank account balance data to inform collection routing decisions. Also called by the notifier-worker. IAM-authenticated API Gateway calls. |
Insight Service |
Source of |
Admin Service |
Internal admin API called by the ach-handler Lambda to attach notes to user accounts when processing certain ACH payment outcome events. |
GrowthBook |
Feature flag service consulted by the collections-worker, webhook workers, and notifier-worker. Controls collection eligibility gating, ACH routing decisions, ML decider activation, and notification channel selection. SDK key loaded from Secrets Manager at Lambda startup. |
SageMaker |
ML model endpoint ( |
Segment |
User analytics platform. Events emitted by the ach-handler (ACH payment outcomes), collections-worker (collection results), webhook workers (webhook-triggered collection outcomes), and notifier-worker (pre-subscription notification events). Write key loaded from Secrets Manager. |
Iterable |
Email and in-app notification delivery. Events triggered by the ach-handler for ACH payment outcomes and by the notifier-worker for pre-subscription notifications. API key loaded from Secrets Manager. |
AppsFlyer |
Mobile attribution tracking. Events emitted by the ach-handler and webhook-balance-worker for subscription payment events. App ID differs between production ( |
Related Pages
-
DynamoDB Tables — Full attribute reference, GSI definitions, and access patterns for
billing-activity,billing-activity-history, andlocks -
Infrastructure — Terraform layout, Lambda configuration, SQS queue definitions, Kinesis streams, EventBridge rules, and Secrets Manager paths
-
Collections Engine — Detailed collection flow diagrams and decision logic for all three collection paths
-
ACH Processing — Kinesis ACH callback handling, billing status mapping, and notification emission
-
Memberships — Membership Kinesis consumer, event types, and subscription state transition details
-
Notifications — Three-day pre-subscription notifier scheduler and worker
-
Subscription Lifecycle — Billing statuses, state transitions, and key API flows
-
Event Flows — Published events, EventBridge rules, and scheduled job flows
-
API Specification — Full OpenAPI spec (Swagger UI)