{
"Comment": "End-to-end payment settlement pipeline with EventBridge notifications at each milestone",
"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": "PaymentInvalid"
}
],
"Next": "IsPaymentValid"
},
"IsPaymentValid": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.validation.Payload.isValid",
"BooleanEquals": true,
"Next": "RunFraudCheck"
}
],
"Default": "PaymentInvalid"
},
"PaymentInvalid": {
"Type": "Fail",
"Error": "InvalidPayment",
"Cause": "Payment failed field validation"
},
"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": "FraudCheckError"
}
],
"Next": "IsFraudulent"
},
"FraudCheckError": {
"Type": "Fail",
"Error": "FraudCheckUnavailable",
"Cause": "Fraud detection service was unavailable"
},
"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",
"customerId.$": "$.validation.Payload.customerId"
}
}
]
},
"ResultPath": "$.fraudEventResult",
"Next": "FlagFraudulentPayment"
},
"FlagFraudulentPayment": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Parameters": {
"FunctionName": "${FlagFraudulentPaymentFunctionArn}",
"Payload.$": "$.fraud.Payload"
},
"ResultPath": "$.flagResult",
"Next": "PaymentFraudBlocked"
},
"PaymentFraudBlocked": {
"Type": "Fail",
"Error": "FraudDetected",
"Cause": "Payment blocked due to fraud signals exceeding risk threshold"
},
"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": "AuthorizationError"
}
],
"Next": "PublishPaymentAuthorizedEvent"
},
"AuthorizationError": {
"Type": "Fail",
"Error": "AuthorizationFailed",
"Cause": "Payment authorization was declined by the payment rail"
},
"PublishPaymentAuthorizedEvent": {
"Type": "Task",
"Resource": "arn:aws:states:::events:putEvents",
"Parameters": {
"Entries": [
{
"EventBusName": "default",
"Source": "thrubit.payments",
"DetailType": "PaymentAuthorized",
"Detail": {
"paymentId.$": "$.authorization.Payload.paymentId",
"authCode.$": "$.authorization.Payload.authCode",
"networkTransactionId.$": "$.authorization.Payload.networkTransactionId",
"amount.$": "$.validation.Payload.amount",
"currency.$": "$.validation.Payload.currency",
"merchantId.$": "$.validation.Payload.merchantId",
"customerId.$": "$.validation.Payload.customerId"
}
}
]
},
"ResultPath": "$.authEventResult",
"Next": "SettlePayment"
},
"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": "SettlementError"
}
],
"Next": "PublishSettlementCompleteEvent"
},
"SettlementError": {
"Type": "Fail",
"Error": "SettlementFailed",
"Cause": "Payment settled with the payment rail but ledger entry could not be recorded"
},
"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",
"ledgerEntryId.$": "$.settlement.Payload.ledgerEntryId",
"authCode.$": "$.settlement.Payload.authCode",
"amount.$": "$.settlement.Payload.amount",
"currency.$": "$.settlement.Payload.currency",
"merchantId.$": "$.settlement.Payload.merchantId",
"customerId.$": "$.settlement.Payload.customerId",
"settledAt.$": "$.settlement.Payload.settledAt"
}
}
]
},
"ResultPath": "$.settlementEventResult",
"Next": "PaymentSettled"
},
"PaymentSettled": {
"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.

