{
"Comment": "Batch credit card processing: iterates over multiple transactions concurrently (Map), and within each transaction runs fraud and balance checks simultaneously (Parallel) before authorizing or rejecting.",
"StartAt": "ProcessTransactionBatch",
"States": {
"ProcessTransactionBatch": {
"Type": "Map",
"Comment": "Process each transaction in the batch concurrently, up to 3 at a time.",
"ItemsPath": "$.transactions",
"MaxConcurrency": 3,
"ItemProcessor": {
"ProcessorConfig": {
"Mode": "INLINE"
},
"StartAt": "ValidateItem",
"States": {
"ValidateItem": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "${ValidateTransactionFunctionArn}",
"Payload.$": "$"
},
"ResultPath": "$.validation",
"Retry": [
{
"ErrorEquals": [
"Lambda.ServiceException",
"Lambda.AWSLambdaException",
"Lambda.SdkClientException"
],
"IntervalSeconds": 2,
"MaxAttempts": 2,
"BackoffRate": 2
}
],
"Catch": [
{
"ErrorEquals": [
"States.ALL"
],
"ResultPath": "$.error",
"Next": "ItemValidationError"
}
],
"Next": "IsItemValid"
},
"IsItemValid": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.validation.Payload.isValid",
"BooleanEquals": true,
"Next": "EnrichItem"
}
],
"Default": "RejectInvalidItem"
},
"RejectInvalidItem": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "${RejectTransactionFunctionArn}",
"Payload": {
"reason": "INVALID_TRANSACTION",
"transaction.$": "$"
}
},
"ResultPath": "$.rejection",
"End": true
},
"ItemValidationError": {
"Type": "Fail",
"Error": "ValidationError",
"Cause": "Transaction validation lambda failed unexpectedly"
},
"EnrichItem": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "${EnrichTransactionFunctionArn}",
"Payload.$": "$"
},
"ResultPath": "$.enriched",
"Retry": [
{
"ErrorEquals": [
"States.ALL"
],
"IntervalSeconds": 2,
"MaxAttempts": 2,
"BackoffRate": 2
}
],
"Catch": [
{
"ErrorEquals": [
"States.ALL"
],
"ResultPath": "$.error",
"Next": "ItemEnrichmentError"
}
],
"Next": "ParallelRiskScreening"
},
"ItemEnrichmentError": {
"Type": "Fail",
"Error": "EnrichmentError",
"Cause": "Transaction enrichment lambda failed"
},
"ParallelRiskScreening": {
"Type": "Parallel",
"Comment": "Run fraud detection and balance check simultaneously to minimize latency.",
"Branches": [
{
"StartAt": "FraudCheck",
"States": {
"FraudCheck": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "${FraudCheckFunctionArn}",
"Payload.$": "$.enriched.Payload"
},
"ResultPath": "$.fraudResult",
"TimeoutSeconds": 10,
"Catch": [
{
"ErrorEquals": [
"States.ALL"
],
"ResultPath": "$.error",
"Next": "FraudCheckError"
}
],
"End": true
},
"FraudCheckError": {
"Type": "Fail",
"Error": "FraudCheckFailed",
"Cause": "Fraud detection service unavailable"
}
}
},
{
"StartAt": "BalanceCheck",
"States": {
"BalanceCheck": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "${BalanceCheckFunctionArn}",
"Payload": {
"accountId.$": "$.enriched.Payload.accountId",
"amount.$": "$.enriched.Payload.amount",
"currency.$": "$.enriched.Payload.currency"
}
},
"ResultPath": "$.balanceResult",
"Catch": [
{
"ErrorEquals": [
"States.ALL"
],
"ResultPath": "$.error",
"Next": "BalanceCheckError"
}
],
"End": true
},
"BalanceCheckError": {
"Type": "Fail",
"Error": "BalanceCheckFailed",
"Cause": "Balance check service unavailable"
}
}
}
],
"ResultPath": "$.screening",
"Catch": [
{
"ErrorEquals": [
"States.ALL"
],
"ResultPath": "$.error",
"Next": "RiskScreeningError"
}
],
"Next": "EvaluateRisk"
},
"RiskScreeningError": {
"Type": "Fail",
"Error": "RiskScreeningFailed",
"Cause": "One or more risk screening checks could not be completed"
},
"EvaluateRisk": {
"Type": "Choice",
"Comment": "Both fraud check must pass (not fraudulent) AND balance check must pass (sufficient funds).",
"Choices": [
{
"And": [
{
"Variable": "$.screening[0].fraudResult.Payload.isFraudulent",
"BooleanEquals": false
},
{
"Variable": "$.screening[1].balanceResult.Payload.hasSufficientFunds",
"BooleanEquals": true
}
],
"Next": "ReserveFunds"
}
],
"Default": "RejectRiskFailed"
},
"RejectRiskFailed": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "${RejectTransactionFunctionArn}",
"Payload": {
"reason": "RISK_SCREENING_FAILED",
"transaction.$": "$.enriched.Payload"
}
},
"ResultPath": "$.rejection",
"End": true
},
"ReserveFunds": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "${ReserveFundsFunction}",
"Payload": {
"accountId.$": "$.enriched.Payload.accountId",
"amount.$": "$.enriched.Payload.amount",
"currency.$": "$.enriched.Payload.currency",
"transactionId.$": "$.enriched.Payload.transactionId"
}
},
"ResultPath": "$.reservation",
"Catch": [
{
"ErrorEquals": [
"States.ALL"
],
"ResultPath": "$.error",
"Next": "ReservationError"
}
],
"Next": "PostTransaction"
},
"ReservationError": {
"Type": "Fail",
"Error": "ReservationFailed",
"Cause": "Unable to reserve funds for transaction"
},
"PostTransaction": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "${PostTransactionFunctionArn}",
"Payload": {
"transaction.$": "$.enriched.Payload",
"reservation.$": "$.reservation.Payload"
}
},
"ResultPath": "$.posting",
"Catch": [
{
"ErrorEquals": [
"States.ALL"
],
"ResultPath": "$.error",
"Next": "PostingError"
}
],
"Next": "ItemAuthorized"
},
"PostingError": {
"Type": "Fail",
"Error": "PostingFailed",
"Cause": "Failed to post transaction to ledger"
},
"ItemAuthorized": {
"Type": "Succeed"
}
}
},
"ResultPath": "$.results",
"Next": "BatchComplete"
},
"BatchComplete": {
"Type": "Succeed"
}
}
}JSONFinancial Services teams can use patterns like this to build reliable, compliant, and scalable automation for payment systems and can test and refine these flows locally with Thrubit to reduce cloud cost and speed up iteration.
Workflow Explanation
Processes entire transaction batches with controlled concurrency
This workflow extends the single-transaction pipeline into a high-throughput batch processing model, accepting an array of credit card transactions and processing up to three concurrently using a Map state with inline item processing. Each transaction in the batch moves through its own independent execution path covering validation, enrichment, risk screening, fund reservation, and ledger posting, so a failure or rejection of one item does not affect the rest of the batch. The result is a realistic model of how payment processors handle nightly settlement windows, bulk authorizations, or queued transaction sweeps.
Each item runs through the full transaction pipeline independently
Within each Map iteration, the workflow mirrors the complete single-transaction flow: the item is first validated against business rules, then enriched with additional account and merchant data, before moving into fraud and balance screening. Fraud detection and balance verification run simultaneously inside a Parallel state to minimize per-item latency, and the results of both checks are combined in a downstream Choice state that requires both to pass before funds can be reserved and the transaction posted to the ledger. Items that fail any checkpoint are routed to a Reject state rather than halting the batch.
Includes layered error handling at every level of the workflow
Because batch operations amplify the consequences of transient failures, the workflow applies retry and catch patterns at both the individual task level and the Parallel branch level. Lambda service exceptions trigger exponential backoff retries before escalating, and hard failures at the enrichment, fraud check, balance check, reservation, or posting stages each route to a dedicated Fail state with a structured error code and cause. This isolation means operational teams can quickly identify which stage of which item failed without sifting through undifferentiated batch logs.
Ideal for end-of-day settlement and high-volume authorization windows
Financial institutions routinely process large volumes of transactions in scheduled windows, such as nightly settlement runs, check clearing batches, or periodic fraud re-screening of pending authorizations. This workflow reflects those patterns directly, making it a practical starting point for teams building regulated batch pipelines. The explicit per-item state paths also make it straightforward for compliance teams to audit expected behavior, trace individual transaction outcomes, and verify that rejection reasons are recorded consistently.
Easy to test item-by-item or as a full batch using Thrubit
Because each Map iteration runs Standard Step Functions states and Lambda tasks, the entire workflow can be executed locally in Thrubit without cloud deployment. Developers can supply small arrays of two or three transactions to trace the full path of each item, simulate fraud or balance failures on specific entries, and verify that the MaxConcurrency limit behaves as expected without incurring AWS costs. This makes it practical to test edge cases, such as a mixed batch where some items pass and others are rejected, in a fast local feedback loop.
Serves as a foundation for distributed large-scale payment processing
Teams that need to scale beyond a few concurrent items can evolve this workflow by replacing the inline Map processor with a Distributed Map, which can fan out across thousands of items using S3 as the data source and process records with much higher concurrency. The state structure, error handling patterns, and per-item processing logic carry over directly, making this workflow a natural stepping stone toward the kind of high-volume distributed processing used in large payment networks and card network settlement systems.

