{
"Comment": "Batch payment settlement — processes an array of payments concurrently using a Map state",
"StartAt": "ProcessPaymentBatch",
"States": {
"ProcessPaymentBatch": {
"Type": "Map",
"ItemsPath": "$.payments",
"ItemSelector": {
"paymentId.$": "$$.Map.Item.Value.paymentId",
"merchantId.$": "$$.Map.Item.Value.merchantId",
"customerId.$": "$$.Map.Item.Value.customerId",
"amount.$": "$$.Map.Item.Value.amount",
"currency.$": "$$.Map.Item.Value.currency",
"paymentMethod.$": "$$.Map.Item.Value.paymentMethod",
"cardLastFour.$": "$$.Map.Item.Value.cardLastFour",
"metadata.$": "$$.Map.Item.Value.metadata"
},
"MaxConcurrency": 3,
"Iterator": {
"StartAt": "ValidatePayment",
"States": {
"ValidatePayment": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "${ValidatePaymentFunctionArn}",
"Payload.$": "$"
},
"ResultPath": "$.validation",
"Retry": [
{
"ErrorEquals": [
"Lambda.ServiceException",
"Lambda.AWSLambdaException",
"Lambda.SdkClientException"
],
"IntervalSeconds": 2,
"MaxAttempts": 3,
"BackoffRate": 2
}
],
"Catch": [
{
"ErrorEquals": [
"States.ALL"
],
"ResultPath": "$.error",
"Next": "RecordValidationFailure"
}
],
"Next": "IsPaymentValid"
},
"IsPaymentValid": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.validation.Payload.isValid",
"BooleanEquals": true,
"Next": "RunFraudCheck"
}
],
"Default": "RecordValidationFailure"
},
"RecordValidationFailure": {
"Type": "Pass",
"Parameters": {
"paymentId.$": "$.paymentId",
"status": "FAILED",
"reason": "Validation failed"
},
"End": true
},
"RunFraudCheck": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "${RunFraudCheckFunctionArn}",
"Payload.$": "$.validation.Payload"
},
"ResultPath": "$.fraud",
"Retry": [
{
"ErrorEquals": [
"States.ALL"
],
"IntervalSeconds": 2,
"MaxAttempts": 2,
"BackoffRate": 2
}
],
"Catch": [
{
"ErrorEquals": [
"States.ALL"
],
"ResultPath": "$.error",
"Next": "RecordFraudCheckError"
}
],
"Next": "IsFraudulent"
},
"RecordFraudCheckError": {
"Type": "Pass",
"Parameters": {
"paymentId.$": "$.paymentId",
"status": "FAILED",
"reason": "Fraud check service unavailable"
},
"End": true
},
"IsFraudulent": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.fraud.Payload.isFraudulent",
"BooleanEquals": true,
"Next": "PublishFraudDetectedEvent"
}
],
"Default": "AuthorizePayment"
},
"PublishFraudDetectedEvent": {
"Type": "Task",
"Resource": "arn:aws:states:::events:putEvents",
"Parameters": {
"Entries": [
{
"EventBusName": "default",
"Source": "thrubit.payments",
"DetailType": "PaymentFraudDetected",
"Detail": {
"paymentId.$": "$.fraud.Payload.paymentId",
"riskScore.$": "$.fraud.Payload.riskScore",
"riskLevel.$": "$.fraud.Payload.riskLevel",
"fraudSignals.$": "$.fraud.Payload.fraudSignals",
"amount.$": "$.validation.Payload.amount",
"currency.$": "$.validation.Payload.currency",
"merchantId.$": "$.validation.Payload.merchantId"
}
}
]
},
"ResultPath": "$.fraudEventResult",
"Next": "RecordFraudBlock"
},
"RecordFraudBlock": {
"Type": "Pass",
"Parameters": {
"paymentId.$": "$.fraud.Payload.paymentId",
"status": "BLOCKED",
"reason": "Fraud detected",
"riskScore.$": "$.fraud.Payload.riskScore"
},
"End": true
},
"AuthorizePayment": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "${AuthorizePaymentFunctionArn}",
"Payload.$": "$.validation.Payload"
},
"ResultPath": "$.authorization",
"Retry": [
{
"ErrorEquals": [
"Lambda.ServiceException",
"Lambda.AWSLambdaException",
"Lambda.SdkClientException"
],
"IntervalSeconds": 2,
"MaxAttempts": 3,
"BackoffRate": 2
}
],
"Catch": [
{
"ErrorEquals": [
"States.ALL"
],
"ResultPath": "$.error",
"Next": "RecordAuthorizationFailure"
}
],
"Next": "SettlePayment"
},
"RecordAuthorizationFailure": {
"Type": "Pass",
"Parameters": {
"paymentId.$": "$.paymentId",
"status": "FAILED",
"reason": "Authorization declined"
},
"End": true
},
"SettlePayment": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "${SettlePaymentFunctionArn}",
"Payload.$": "$.authorization.Payload"
},
"ResultPath": "$.settlement",
"Retry": [
{
"ErrorEquals": [
"Lambda.ServiceException",
"Lambda.AWSLambdaException",
"Lambda.SdkClientException"
],
"IntervalSeconds": 3,
"MaxAttempts": 3,
"BackoffRate": 2
}
],
"Catch": [
{
"ErrorEquals": [
"States.ALL"
],
"ResultPath": "$.error",
"Next": "RecordSettlementFailure"
}
],
"Next": "PublishSettlementCompleteEvent"
},
"RecordSettlementFailure": {
"Type": "Pass",
"Parameters": {
"paymentId.$": "$.paymentId",
"status": "FAILED",
"reason": "Settlement failed"
},
"End": true
},
"PublishSettlementCompleteEvent": {
"Type": "Task",
"Resource": "arn:aws:states:::events:putEvents",
"Parameters": {
"Entries": [
{
"EventBusName": "default",
"Source": "thrubit.payments",
"DetailType": "PaymentSettled",
"Detail": {
"paymentId.$": "$.settlement.Payload.paymentId",
"settlementId.$": "$.settlement.Payload.settlementId",
"amount.$": "$.settlement.Payload.amount",
"currency.$": "$.settlement.Payload.currency",
"merchantId.$": "$.settlement.Payload.merchantId",
"settledAt.$": "$.settlement.Payload.settledAt"
}
}
]
},
"ResultPath": "$.settlementEventResult",
"Next": "RecordSuccess"
},
"RecordSuccess": {
"Type": "Pass",
"Parameters": {
"paymentId.$": "$.settlement.Payload.paymentId",
"settlementId.$": "$.settlement.Payload.settlementId",
"status": "SETTLED",
"amount.$": "$.settlement.Payload.amount",
"currency.$": "$.settlement.Payload.currency"
},
"End": true
}
}
},
"ResultPath": "$.batchResults",
"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.

