Simulation Flows

These endpoints let tests trigger the same collection and processing pipelines that run in production, but scoped to specific test users. Simulations are the bridge between test setup (creating users via /qa/integration/custom-user) and assertions (calling service APIs directly). Each endpoint sends messages to SQS queues for downstream Lambda consumption, directly invokes a Lambda function, injects events into a Kinesis stream, or runs a service’s collection job synchronously through that service’s QA API — closely mirroring what production schedulers do. The float endpoints take the synchronous path, so their effects are complete when they respond; the subscription and LOC endpoints are asynchronous. The POST /qa/test/event endpoint supports direct Kinesis injection for lower-level integration scenarios.

Subscription Simulations

Daily Collections (POST /qa/simulate/subscriptions/daily)

Simulates the nightly scheduled job that collects subscription payments. The endpoint accepts a list of user IDs, fetches each user’s subscription records from the Subscription Service, and routes them into one of three SQS queues based on their current status. The queues are then consumed by the production Subscription Service Lambda workers.

Async: Yes — SQS messages are sent and the endpoint returns immediately. The caller must poll or wait before asserting on downstream state.

Request body:

{
  "user_ids": ["uuid-1", "uuid-2"]
}

Behavior by subscription status:

Status SQS Queue (env var) Consumer

SCHEDULED

SubscriptionCollectionsScheduledQueueURL

Subscription Service scheduled-collections Lambda

PAUSED

SubscriptionCollectionsPausedQueueURL

Subscription Service paused-collections Lambda

ERROR (retry)

SubscriptionCollectionsRetryQueueURL

Subscription Service retry-collections Lambda

Eligibility rules:

  • SCHEDULED and PAUSED subscriptions must have a subscription_date strictly before the current time.

  • ERROR subscriptions are eligible for retry only if they fall within the last 30 days.

  • Subscriptions that do not meet eligibility are silently skipped.

Flow:

Caller → POST /qa/simulate/subscriptions/daily
  → Fetch subscriptions for each user_id (Subscription Service)
  → Sort into buckets: SCHEDULED / PAUSED / ERROR
  → Filter by eligibility (date < now; ERROR within 30 days)
  → SQS: scheduled-queue / paused-queue / retry-queue
    → Subscription Service Lambda consumes each queue

Subscription Notifier (POST /qa/simulate/subscriptions/notifier)

Simulates the three-day advance notification job that alerts users their subscription payment is upcoming. The endpoint accepts a list of user IDs, fetches their subscriptions from the Subscription Service, and for each subscription in SCHEDULED status it constructs an fmsdk event with type three_day_notification (version v1) keyed by subscription_id. All events are sent as a batch to the subscription notifier SQS queue.

Async: Yes — SQS batch message is sent and the endpoint returns immediately.

Request body:

{
  "user_ids": ["uuid-1", "uuid-2"]
}

Eligibility: Only subscriptions with SCHEDULED status produce notification events. Subscriptions in any other status are silently skipped.

SQS target: SubscriptionNotifierWorkerQueueURL

Use case: Test that users receive pre-payment notification emails/push alerts before the scheduled collection date.

Float Simulations

Daily Collections (POST /qa/simulate/floats/daily)

Simulates the Float Service’s scheduled collection jobs — the initial (due-date) and daily-retry (overdue) Fargate jobs — for specific users. The endpoint accepts a list of user IDs, calls the Float Service GetActiveFloats for each, selects the floats those jobs would pick up today, and runs each one through the Float Service’s test-only QA job runner (POST /qa/jobs/run). The runner executes the job’s per-float ProcessFloat — billing lock, rules, payment submission, float status write, and collection-history row — and returns the job’s result.

Async: No — every float is fully processed before the endpoint responds, so callers can assert on float status, payments, and collection history immediately.

Request body:

{
  "user_ids": ["uuid-1", "uuid-2"]
}

Eligibility and subcommand: mirrors the jobs' candidate selection. Only floats due today or earlier (compared by UTC calendar day) are eligible; a float due tomorrow belongs to the T-1 day-before-ach job, which the Cucumber suite drives directly against the Float Service QA job runner.

Float Status Subcommand Job behavior

SCHEDULING

initial

Pinless debit when a valid debit card is on file, next-day ACH otherwise; NSF-coded pinless declines fall back to ACH. Outcomes: COMPLETED, ACHSENT, or RETRY.

RETRY, FAILED, ACHFAILED, UNCOLLECTABLE

daily-retry

90-day default, recent-attempt and debit-card checks, then the Plaid/balance gate and a pinless or ACH attempt. Skips write their outcome (LOWBALANCE, NOCARD, …​) to collection history and leave the status unchanged.

Response: results holds one entry per float run — user_id, float_id, subcommand, skipped, skip_reason, action, chosen_reason, and submit_failed/submit_error when the payment provider rejected a submission. A skipped float or a rejected submission is a result, not an error; the endpoint returns 500 only when the job runner call itself fails for a float (the message names the float IDs).

Flow:

Caller → POST /qa/simulate/floats/daily
  → GetActiveFloats per user_id (Float Service)
  → Select: SCHEDULING due ≤ today → initial; overdue statuses due ≤ today → daily-retry
  → POST /qa/jobs/run {subcommand, float_id} per float (Float Service, synchronous)
    → ProcessFloat: billing lock → rules → payment submit → status + collection-history writes
  → Return per-float results

LOC Collections (POST /qa/simulate/loc/collections)

Simulates the Line of Credit collection job by directly invoking the LOC Service Lambda function. Unlike the subscription and float simulations, this endpoint does not send SQS messages — it performs a synchronous (fire-and-forget) async Lambda invocation using InvocationType: Event. The Lambda executes independently; the endpoint returns immediately after the invocation is dispatched.

Async: Fire-and-forget Lambda invoke — the endpoint returns 200 after the invocation is dispatched, before the Lambda completes.

Request body:

{
  "process": "due",
  "look_ahead_days": 3
}

Process types:

process value Meaning

upcoming

Collect payments that will become due within look_ahead_days days

due

Collect payments due today

overdue

Collect payments past their due date

retry

Retry previously failed collection attempts

look_ahead_days: Optional integer. Relevant for the upcoming process type to control how far ahead the job looks for due payments. Ignored for other process types.

Target Lambda: test-loc-service-loc-collections-job

Use case: Trigger LOC collection processing against specific test accounts without waiting for the production CloudWatch scheduled event.

Event Injection (POST /qa/test/event)

A low-level utility endpoint that injects arbitrary JSON payloads directly into Kinesis streams or SQS queues. This is used for debugging, for simulating third-party callback events (such as payment processor webhooks arriving on a Kinesis stream), and for driving any queue-consuming Lambda that does not have a dedicated simulation endpoint.

Request body:

{
  "service": "kinesis",
  "target": "prod-payments-stream",
  "event": { "type": "payment.completed", "amount": 9.99 },
  "partition_key": "user-uuid-1"
}

Parameters:

Field Type Required Description

service

kinesis | sqs

Yes

Destination service type

target

string

Yes

Stream name (Kinesis) or queue name (SQS)

event

object

Yes

Arbitrary JSON payload — sent as-is

partition_key

string

No

Kinesis shard partition key; ignored for SQS

Routing by service type:

service How target is resolved SDK call used

kinesis

Used directly as the stream name in PutRecord

kinesis.PutRecord(streamName, event, partitionKey)

sqs

Queue URL constructed from AWS account + region + queue name

sqs.SendMessage(constructedURL, event)

Use cases:

  • Inject a Kinesis payment callback event to test the payments processing pipeline end-to-end.

  • Send a crafted message to an SQS queue to exercise a specific Lambda error-handling branch.

  • Reproduce a production event shape in a test environment without waiting for a real external trigger.