Meter Billing Pipeline

Full end-to-end smart meter billing pipeline. Validates incoming meter readings, checks meter hardware status, calculates energy usage with peak/off-peak breakdown, runs anomaly detection and usage analysis in parallel, generates the customer bill, processes payment, and notifies the customer.
{
  "Comment": "Full smart meter billing pipeline: validates the reading, checks meter hardware status, calculates usage, runs anomaly detection and usage analysis in parallel, generates the bill, processes payment, and notifies the customer.",
  "StartAt": "ValidateMeterReading",
  "States": {
    "ValidateMeterReading": {
      "Type": "Task",
      "Resource": "arn:aws:states:::lambda:invoke",
      "Parameters": {
        "FunctionName": "${ValidateMeterReadingFunctionArn}",
        "Payload.$": "$"
      },
      "ResultPath": "$.validation",
      "Retry": [
        {
          "ErrorEquals": [
            "Lambda.ServiceException",
            "Lambda.AWSLambdaException",
            "Lambda.SdkClientException"
          ],
          "IntervalSeconds": 2,
          "MaxAttempts": 3,
          "BackoffRate": 2
        }
      ],
      "Catch": [
        {
          "ErrorEquals": [
            "States.ALL"
          ],
          "ResultPath": "$.error",
          "Next": "ValidationError"
        }
      ],
      "Next": "IsReadingValid"
    },
    "IsReadingValid": {
      "Type": "Choice",
      "Choices": [
        {
          "Variable": "$.validation.Payload.isValid",
          "BooleanEquals": true,
          "Next": "CheckMeterStatus"
        }
      ],
      "Default": "ValidationError"
    },
    "ValidationError": {
      "Type": "Fail",
      "Error": "InvalidMeterReading",
      "Cause": "Incoming meter reading failed validation checks"
    },
    "CheckMeterStatus": {
      "Type": "Task",
      "Resource": "arn:aws:states:::lambda:invoke",
      "Parameters": {
        "FunctionName": "${CheckMeterStatusFunctionArn}",
        "Payload.$": "$"
      },
      "ResultPath": "$.meterStatus",
      "Retry": [
        {
          "ErrorEquals": [
            "States.ALL"
          ],
          "IntervalSeconds": 2,
          "MaxAttempts": 2,
          "BackoffRate": 2
        }
      ],
      "Catch": [
        {
          "ErrorEquals": [
            "States.ALL"
          ],
          "ResultPath": "$.error",
          "Next": "MeterStatusError"
        }
      ],
      "Next": "IsMeterOnline"
    },
    "IsMeterOnline": {
      "Type": "Choice",
      "Choices": [
        {
          "And": [
            {
              "Variable": "$.meterStatus.Payload.online",
              "BooleanEquals": true
            },
            {
              "Variable": "$.meterStatus.Payload.tamperDetected",
              "BooleanEquals": false
            }
          ],
          "Next": "CalculateUsage"
        }
      ],
      "Default": "MeterStatusError"
    },
    "MeterStatusError": {
      "Type": "Fail",
      "Error": "MeterStatusError",
      "Cause": "Meter is offline or tamper has been detected — manual inspection required"
    },
    "CalculateUsage": {
      "Type": "Task",
      "Resource": "arn:aws:states:::lambda:invoke",
      "Parameters": {
        "FunctionName": "${CalculateUsageFunctionArn}",
        "Payload.$": "$"
      },
      "ResultPath": "$.usage",
      "Retry": [
        {
          "ErrorEquals": [
            "States.ALL"
          ],
          "IntervalSeconds": 2,
          "MaxAttempts": 2,
          "BackoffRate": 2
        }
      ],
      "Catch": [
        {
          "ErrorEquals": [
            "States.ALL"
          ],
          "ResultPath": "$.error",
          "Next": "UsageCalculationError"
        }
      ],
      "Next": "AnomalyAndBillingAnalysis"
    },
    "UsageCalculationError": {
      "Type": "Fail",
      "Error": "UsageCalculationError",
      "Cause": "Usage calculation lambda encountered an unexpected error"
    },
    "AnomalyAndBillingAnalysis": {
      "Type": "Parallel",
      "Comment": "Run anomaly detection and bill generation concurrently after usage is calculated.",
      "Branches": [
        {
          "StartAt": "DetectAnomaly",
          "States": {
            "DetectAnomaly": {
              "Type": "Task",
              "Resource": "arn:aws:states:::lambda:invoke",
              "Parameters": {
                "FunctionName": "${DetectAnomalyFunctionArn}",
                "Payload.$": "$"
              },
              "End": true
            }
          }
        },
        {
          "StartAt": "GenerateBill",
          "States": {
            "GenerateBill": {
              "Type": "Task",
              "Resource": "arn:aws:states:::lambda:invoke",
              "Parameters": {
                "FunctionName": "${GenerateBillFunctionArn}",
                "Payload.$": "$"
              },
              "End": true
            }
          }
        }
      ],
      "ResultPath": "$.billingAnalysis",
      "Catch": [
        {
          "ErrorEquals": [
            "States.ALL"
          ],
          "ResultPath": "$.error",
          "Next": "BillingAnalysisError"
        }
      ],
      "Next": "IsAnomalyFlagged"
    },
    "BillingAnalysisError": {
      "Type": "Fail",
      "Error": "BillingAnalysisError",
      "Cause": "Anomaly detection or bill generation lambda failed"
    },
    "IsAnomalyFlagged": {
      "Type": "Choice",
      "Choices": [
        {
          "Variable": "$.billingAnalysis[0].Payload.isAnomaly",
          "BooleanEquals": false,
          "Next": "ProcessPayment"
        }
      ],
      "Default": "FlagForReview"
    },
    "FlagForReview": {
      "Type": "Task",
      "Resource": "arn:aws:states:::lambda:invoke",
      "Parameters": {
        "FunctionName": "${NotifyCustomerFunctionArn}",
        "Payload": {
          "notificationType": "ANOMALY_ALERT",
          "reading.$": "$"
        }
      },
      "ResultPath": "$.anomalyNotification",
      "Next": "AnomalyFlagged"
    },
    "AnomalyFlagged": {
      "Type": "Fail",
      "Error": "AnomalyDetected",
      "Cause": "Unusual usage detected — bill held for manual review before payment processing"
    },
    "ProcessPayment": {
      "Type": "Task",
      "Resource": "arn:aws:states:::lambda:invoke",
      "Parameters": {
        "FunctionName": "${ProcessPaymentFunctionArn}",
        "Payload": {
          "reading.$": "$",
          "bill.$": "$.billingAnalysis[1].Payload.bill"
        }
      },
      "ResultPath": "$.payment",
      "Retry": [
        {
          "ErrorEquals": [
            "States.ALL"
          ],
          "IntervalSeconds": 2,
          "MaxAttempts": 3,
          "BackoffRate": 2
        }
      ],
      "Catch": [
        {
          "ErrorEquals": [
            "States.ALL"
          ],
          "ResultPath": "$.error",
          "Next": "PaymentError"
        }
      ],
      "Next": "NotifyCustomer"
    },
    "PaymentError": {
      "Type": "Fail",
      "Error": "PaymentError",
      "Cause": "Payment processing lambda encountered an unexpected error"
    },
    "NotifyCustomer": {
      "Type": "Task",
      "Resource": "arn:aws:states:::lambda:invoke",
      "Parameters": {
        "FunctionName": "${NotifyCustomerFunctionArn}",
        "Payload": {
          "notificationType": "BILL_READY",
          "reading.$": "$",
          "bill.$": "$.billingAnalysis[1].Payload.bill"
        }
      },
      "ResultPath": "$.notification",
      "Retry": [
        {
          "ErrorEquals": [
            "States.ALL"
          ],
          "IntervalSeconds": 2,
          "MaxAttempts": 2,
          "BackoffRate": 2
        }
      ],
      "Next": "BillingComplete"
    },
    "BillingComplete": {
      "Type": "Succeed"
    }
  }
}
JSON
Expand
100%

Energy 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.