Skip to main content

ProcessPaidOrderInternal Lambda Function

Quick Reference

  • Function Name: payment3-process-paid-order-internal-${BRANCH}
  • Handler: process_paid_internal.lambda_handler
  • Runtime: Python 3.13 (Docker Image)
  • Trigger: Lambda invocation (asynchronous)
  • Timeout: 200 seconds
  • Template.yaml: Lines 774-818

Function Overview

The ProcessPaidOrderInternal function processes paid orders for QR and Card payments. It is invoked internally by callback functions (QRCallback, Notify) to handle order processing after payment confirmation. This function supports both QR and card payment types and includes comprehensive debug logging.

Entry Point

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

def lambda_handler(event, context):
payload = json.loads(event) if isinstance(event, str) else event
orderId = payload.get("orderId")
payment_type = payload.get("paymentType") # "qr" or "card"
success = payload.get("success")
chargeId = payload.get("chargeId")

processor = ProcessPaidOrder(orderId, payment_type, success, chargeId)
processor.save_paid_order()
processor.save_process_paid_order()
processor.send_notification(is_success)
return {"statusCode": 200, "body": json.dumps("success")}

Event Structure

Lambda Invocation Event

Format: JSON string or dict

Required Fields:

{
"orderId": "002500048744",
"paymentType": "qr",
"success": true,
"chargeId": "chrg_prod_11430784e550fb6ea4f69902e0deda2591aa6"
}

Field Aliases (supported):

  • orderId or order_id
  • paymentType or payment_type
  • chargeId or charge_id

Example Event (QR Payment):

{
"orderId": "002500048744",
"paymentType": "qr",
"success": true,
"chargeId": "chrg_prod_11430784e550fb6ea4f69902e0deda2591aa6"
}

Example Event (Card Payment):

{
"orderId": "492500005643",
"paymentType": "card",
"success": true,
"chargeId": "chrg_abc123"
}

Core Classes

ProcessPaidOrder

Main processor class for handling paid orders (QR and Card).

Initialization:

processor = ProcessPaidOrder(
orderId="002500048744",
payment_type="qr", # or "card"
success=True,
chargeId="chrg_prod_11430784e550fb6ea4f69902e0deda2591aa6"
)

Properties:

  • order (cached_property): Retrieves order from OrderTable

    @cached_property
    def order(self) -> dict:
    return OrderTable.get(self.orderId).data
  • card_record (property): Gets card payment record for card payments

    @property
    def card_record(self) -> CardPaymentRecordTable:
    return next(CardPaymentRecordTable.orderId_index.query(self.orderId))
  • order_status (cached_property): Gets CheckStatusTable record for card payments

    @cached_property
    def order_status(self) -> CheckStatusTable:
    status = next(CheckStatusTable.query(self.orderId))
    if status.status == "success":
    status.success = True
    return status
  • qr_order_status (cached_property): Queries KBank API for QR payment status

    @cached_property
    def qr_order_status(self) -> tuple[bool, float]:
    # Makes GET request to KBank API: /qr/v2/qr/{charge_id}
    # Returns: (success: bool, paid_amount: float)
  • qr_success (property): QR payment success status

  • qr_paid_amount (property): QR payment amount

  • payment_header (cached_property): Creates payment header based on payment type

    @cached_property
    def payment_header(self) -> dict:
    if self.payment_type == "qr":
    return create_payment_header(
    order=self.order,
    paymentId=self.chargeId,
    isPaid=self.qr_success,
    paymentMethod="qr",
    totalPaid=self.qr_paid_amount
    )
    else: # card
    return create_payment_header(
    order=self.order,
    paymentId=self.order_status.chargeId,
    isPaid=self.order_status.success,
    paymentMethod=self.order_status.paymentMethod
    )

Methods:

  • save_paid_order(): Saves order with payment header to OrderTable
  • save_process_paid_order(): Logs processing attempt to ProcessPaidOrderTable
  • send_notification(is_success: bool): Sends success/failure notifications and email
  • invoke_check_status(): Invokes CheckStatus Lambda for card payments

DynamoDB Tables

OrderTable

Table Name: order-table-dev (shared)

Access Pattern: Get by orderId (hash key)

Schema:

  • orderId (hash_key): Order identifier
  • data (JSON): Order data

CardPaymentRecordTable

Table Name: payment3-card-payment-record-${BRANCH}

Access Pattern: Query by orderId via orderId-index GSI

Schema:

  • tokenId (hash_key)
  • orderId: Order identifier
  • chargeId: Charge identifier
  • status: Payment status (boolean)
  • checksumVerified: Checksum verification status (boolean)

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
  • status: Status string ("success" or other)
  • success: Success boolean
  • paymentMethod: Payment method
  • amount: Payment amount

ProcessPaidOrderTable

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

Access Pattern: Put item

Schema:

  • orderId (hash_key)
  • created_at (range_key)
  • data (JSON): Order data
  • is_error (boolean): Error flag

External API Calls

KBank QR Status API

Endpoint: GET {BASE_URL}/qr/v2/qr/{charge_id}

Headers:

{
"content-type": "application/json",
"x-api-key": os.environ["KBANK_SECRET_KEY"]
}

Response:

{
"status": "success",
"amount": 300.0
}

Error Handling: Raises exception if status_code != 200 or response.object == "error"

Process Flow

  1. Receive Lambda Invocation: Function receives event with orderId, paymentType, success, chargeId
  2. Parse Event: Extract and validate required fields
  3. Check Already Paid: Skip if order.payment.isPaid is True
  4. Initialize Processor: Create ProcessPaidOrder instance
  5. Determine Payment Status:
    • QR: Query KBank API via qr_order_status
    • Card: Query CheckStatusTable via order_status
  6. Create Payment Header: Build payment header based on payment type
  7. Save Order: Update OrderTable with payment information
  8. Log Processing: Record in ProcessPaidOrderTable
  9. Send Notifications: Send success/failure notifications and email
  10. Return Success: Return HTTP 200

Error Handling

  • Validation Errors: Raises ValueError for missing required fields
  • Order Not Found: Raises exception if order doesn't exist
  • KBank API Errors: Raises exception if API call fails
  • Error Logging: Errors are logged to ProcessPaidOrderTable with is_error=True
  • Exception Propagation: All exceptions are caught, logged, and returned as HTTP 500

Lambda Invocations

Invoked Functions

  • payment3-check-status-${BRANCH}: For card payment status verification

Invoked By

  • QRCallback: After QR payment callback
  • Notify: After credit card notification
  • QRNotify: After QR notification

IAM Policies

Required Permissions:

  • DynamoDBReadPolicy on CardPaymentRecordTable
  • DynamoDBReadPolicy on CheckStatusTable
  • DynamoDBCrudPolicy on ProcessPaidOrderTable
  • DynamoDBCrudPolicy on EmailLogTable
  • DynamoDBReadPolicy on order-table-dev
  • LambdaInvokePolicy on payment3-check-status-${BRANCH}
  • LambdaInvokePolicy on Pay function
  • SESCrudPolicy on *
  • SNS:Publish on *
  • AWSSecretsManagerGetSecretValuePolicy on KBank secrets

Environment Variables

  • BRANCH: Deployment branch name (default: "dev")
  • KBANK_API_KEY: KBank API key (from secrets)
  • KBANK_SECRET_KEY: KBank secret key (from secrets)
  • BASE_URL: KBank API base URL (from secrets)
  • MID: KBank merchant ID (from secrets)

Dependencies

  • src.tables.*: DynamoDB table models
  • src.updatePaymentHeader: Payment header creation
  • src.savePaidOrder: Order saving logic
  • src.notifications.*: Notification sending
  • lambdasdk.lambdasdk.Lambda: Lambda invocation
  • requests: HTTP requests to KBank API
  • nicHelper.secrets.getSecret: Secret retrieval

Debug Logging

The function includes comprehensive debug logging via debug_log() helper:

def debug_log(message, data=None):
timestamp = datetime.now().isoformat()
log_entry = f"[DEBUG {timestamp}] {message}"
if data is not None:
log_entry += f" | Data: {data}"
print(log_entry)

All major operations are logged with timestamps and relevant data.

Testing

Local Testing:

if __name__ == "__main__":
lambda_handler(
event=json.dumps({
"orderId": "002500048744",
"paymentType": "qr",
"success": True,
"chargeId": "chrg_prod_11430784e550fb6ea4f69902e0deda2591aa6"
}),
context=None
)
  • ProcessPaidOrder: Credit card payment processing (main entry point)
  • ProcessPaidWalletOrderInternal: Wallet payment processing
  • ProcessPaidAmexOrderInternal: Amex payment processing
  • QRCallback: Invokes this function after QR callback
  • Notify: Invokes this function after credit card notification

Notes

  • This function handles both QR and Card payments
  • QR payments query KBank API directly for status
  • Card payments use CheckStatusTable for status
  • Comprehensive debug logging is included for troubleshooting
  • Errors are logged to ProcessPaidOrderTable with is_error=True
  • Payment status determination differs by payment type