Skip to main content

ProcessUnpaidOrder Lambda Function

Quick Reference

  • Function Name: payment3-process-unpaid-order-${BRANCH}
  • Handler: process_failed.lambda_handler
  • Runtime: Python 3.13 (Docker Image)
  • Trigger: DynamoDB Stream (currently disabled)
  • Timeout: 60 seconds (default)
  • Template.yaml: Lines 917-962

Function Overview

The ProcessUnpaidOrder function processes failed/unpaid orders. It handles orders that have failed payment attempts, updates the order with failure status, sends failure notifications, and logs the processing attempt. Currently, the DynamoDB stream trigger is disabled in template.yaml.

Entry Point

File: functions/processPaidOrder/process_failed.py
Handler: lambda_handler(event, context)

def lambda_handler(event, context):
orderIds = getOrderFromEvent(event)
for orderId in orderIds:
processor = ProcessFailedOrder(orderId=orderId)
processor.save_failed_order()
processor.save_process_failed_order()
processor.send_failure_notifications()
return {"statusCode": 200, "body": json.dumps("success")}

Event Structure

DynamoDB Stream Event

Format: DynamoDB stream event (currently disabled)

Event Structure:

{
"Records": [
{
"eventName": "INSERT",
"dynamodb": {
"Keys": {
"orderId": {"S": "492500005643"}
}
}
}
]
}

Note: Only INSERT events are processed (new failed order records).

Core Classes

ProcessFailedOrder

Main processor class for handling failed orders.

Initialization:

processor = ProcessFailedOrder(orderId="492500005643")

Properties:

  • order (cached_property): Retrieves order from OrderTable

    @cached_property
    def order(self) -> dict:
    return OrderTable.get(self.orderId).data
  • order_status (cached_property): Gets CheckStatusTable record

    @cached_property
    def order_status(self) -> CheckStatusTable:
    return next(CheckStatusTable.query(self.orderId))
  • payment_header (cached_property): Creates payment header with failure status

    @cached_property
    def payment_header(self) -> dict:
    return create_payment_header(
    order=self.order,
    paymentId=self.order_status.chargeId,
    isPaid=False, # Always False for failed orders
    paymentMethod=self.order_status.paymentMethod,
    totalPaid=0 # No amount paid for failed orders
    )
  • failed_order (property): Order with failure payment header

    @property
    def failed_order(self) -> dict:
    order = self.order
    order["payment"] = self.payment_header
    return order

Methods:

  • save_failed_order(): Saves order with failure payment header to OrderTable
  • save_process_failed_order(): Logs processing attempt to ProcessUnpaidOrderTable
  • send_failure_notifications(): Sends failure notifications and email

DynamoDB Tables

OrderTable

Table Name: order-table-dev (shared)

Access Pattern: Get by orderId (hash key)

CheckStatusTable

Table Name: payment3-check-status-${BRANCH}

Access Pattern: Query by orderId (hash key)

Schema:

  • orderId (hash_key)
  • created_at (range_key)
  • chargeId: Charge identifier
  • paymentMethod: Payment method
  • status: Status string

ProcessUnpaidOrderTable

Table Name: payment3-process-unpaid-order-${BRANCH}

Access Pattern: Put item

Schema:

  • orderId (hash_key)
  • created_at (range_key)
  • data (JSON): Order data

Process Flow

  1. Receive DynamoDB Stream Event: Function receives stream event with failed order records
  2. Extract Order IDs: Get orderIds from INSERT events only
  3. Process Each Order:
    • Initialize ProcessFailedOrder instance
    • Retrieve order from OrderTable
    • Get order status from CheckStatusTable
    • Create payment header with isPaid=False
    • Save failed order to OrderTable
    • Log processing attempt to ProcessUnpaidOrderTable
    • Send failure notifications and email
  4. Return Success: Return HTTP 200

Error Handling

  • Order Not Found: Raises exception if order doesn't exist in OrderTable
  • CheckStatus Not Found: Raises CheckStatusNotFound if status record doesn't exist
  • Notification Errors: Exceptions are caught and logged, then re-raised
  • Continue on Error: If one order fails, processing continues with next order

Lambda Invocations

Invoked Functions

  • use-voucher-master: For voucher/coupon handling (if applicable)
  • record-usage-coupon2-master: For coupon usage recording (if applicable)
  • payment3-check-status-${BRANCH}: For status verification (if applicable)
  • Pay: For order processing (if applicable)

Invoked By

  • DynamoDB Stream (currently disabled in template.yaml)

IAM Policies

Required Permissions:

  • DynamoDBReadPolicy on CardPaymentRecordTable
  • DynamoDBReadPolicy on CheckStatusTable
  • DynamoDBCrudPolicy on ProcessUnpaidOrderTable
  • DynamoDBReadPolicy on order-table-dev
  • DynamoDBCrudPolicy on EmailLogTable
  • LambdaInvokePolicy on use-voucher-master
  • LambdaInvokePolicy on record-usage-coupon2-master
  • LambdaInvokePolicy on payment3-check-status-${BRANCH}
  • LambdaInvokePolicy on Pay function
  • SESCrudPolicy on *
  • SNS:Publish on *

Environment Variables

  • BRANCH: Deployment branch name (default: "dev")

Dependencies

  • src.tables.*: DynamoDB table models
  • src.updatePaymentHeader: Payment header creation
  • src.savePaidOrder: Order saving logic
  • src.notifications.notify.notifyFailure: Failure notification sending
  • src.notifications.sendEmail.send_email: Email sending

Helper Functions

getOrderFromEvent

Extracts order IDs from DynamoDB stream events:

def getOrderFromEvent(event, *args):
records: list[dict] = event["Records"]
orderFromRecord = lambda record: record["dynamodb"]["Keys"]["orderId"]["S"]
orderIds: list[str] = [
orderFromRecord(record)
for record in records
if record["eventName"] == "INSERT"
]
return orderIds
  • ProcessPaidOrder: Processes successfully paid orders
  • ProcessPaidOrderInternal: Processes paid orders (QR/Card)
  • CheckStatus: Checks payment status

Notes

  • This function handles failed/unpaid orders only
  • Payment header always has isPaid=False and totalPaid=0
  • DynamoDB stream trigger is currently disabled in template.yaml
  • Only processes INSERT events from the stream
  • Sends failure notifications via SNS and email via SES
  • Email is sent to order.email field