Skip to main content

Callback Error Logging - Implementation Status

Overview

A new CallbackErrorLogTable has been created to log errors from callback functions. The table has a secondary index on orderId for easy querying of errors by order.

Table Definition

  • Table Name: callback-error-log-{BRANCH}
  • Primary Key: id (function_name-timestamp format)
  • Global Secondary Index: orderId-index (orderId, created_at)
  • Location: functions/callback/src/callback_error_log_table.py
  • Helper Function: functions/callback/src/callback_error_logger.py

Implementation Status

✅ Completed

  • callback.py - Error logging integrated
  • notify.py - Error logging integrated
  • delayednotify.py - Error logging integrated

⏳ TODO (Not Yet Implemented)

The following callback functions do NOT currently log errors to CallbackErrorLogTable. They were intentionally excluded to minimize dependencies, but can be added later when needed:

  • qrcallback.py - Error logging not implemented
  • amexcallback.py - Error logging not implemented
  • walletcallback.py - Error logging not implemented

Note: These functions are currently back to their normal state (no error logging integration). To add error logging later, follow the implementation steps below.

Usage

Errors are automatically logged when exceptions occur in the implemented functions using log_callback_error() from src/callback_error_logger.py.

Example:

from src.callback_error_logger import log_callback_error

try:
# ... function logic ...
except Exception as e:
log_callback_error(
error=e,
function_name="callback",
event=event,
orderId=orderId,
chargeId=chargeId,
response_data=None
)
raise

IAM Policies

The following Lambda functions have write permissions to CallbackErrorLogTable:

  • Callback function
  • Notify function
  • DelayedNotify function

Future Work - Adding Error Logging to Other Functions

When ready to expand error logging to qrcallback, amexcallback, or walletcallback:

Step 1: Update the Lambda Function Code

For qrcallback.py:

from src.callback_error_logger import log_callback_error

def lambda_handler(event, _):
processor = None
try:
body = dict(urllib.parse.parse_qsl(event["body"]))
order_id = body["orderId"]
status_string = body["status"]
amount = float(body["amount"])
charge_id = body["chargeId"]
processor = Processor(order_id, status_string, amount, charge_id)
except Exception as e:
charge_id = errorString()
order_id = body if isinstance(body, str) else body.get("orderId", "unknown")
processor = Processor(order_id, status_string, amount, charge_id)

# Log error
log_callback_error(
error=e,
function_name="qrcallback",
event=event,
orderId=order_id,
chargeId=charge_id,
response_data=None
)
return processor.error_html_response(str(e) + errorString())

try:
qr_processor = processor.qr_processor
success, paid_amount = qr_processor.check_kbank_record
if not success:
error_msg = f"Kbank record not found for charge id {charge_id}"
log_callback_error(
error=Exception(error_msg),
function_name="qrcallback",
event=event,
orderId=processor.order_id,
chargeId=processor.charge_id,
response_data=None
)
return processor.error_html_response(error_msg)
if abs(float(paid_amount) - float(processor.amount)) > 0.01:
error_msg = f"Kbank record amount {paid_amount} does not match order amount {processor.amount}"
log_callback_error(
error=Exception(error_msg),
function_name="qrcallback",
event=event,
orderId=processor.order_id,
chargeId=processor.charge_id,
response_data=None
)
return processor.error_html_response(error_msg)
qr_processor.save_record()
qr_processor.process_paid_order()
except Exception as e:
log_callback_error(
error=e,
function_name="qrcallback",
event=event,
orderId=processor.order_id if processor else None,
chargeId=processor.charge_id if processor else None,
response_data=None
)
return processor.error_html_response(str(e) + errorString())
return processor.response

For amexcallback.py:

from src.callback_error_logger import log_callback_error

def lambda_handler(event, _):
processor = None
try:
# ... existing code ...
processor = AmexCallbackProcessor(event=event)
processor.save()
# ... rest of existing code ...
except Exception as e:
# Extract orderId and paymentId if processor exists
orderId = None
chargeId = None
if processor:
try:
orderId = processor.order_id
chargeId = getattr(processor, 'payment_id', None)
except:
pass

log_callback_error(
error=e,
function_name="amexcallback",
event=event,
orderId=orderId,
chargeId=chargeId,
response_data=None
)
# ... existing error handling ...

For walletcallback.py:

from src.callback_error_logger import log_callback_error

def lambda_handler(event, _):
processor = None
try:
# ... existing code ...
processor = WalletCallbackProcessor(event=event)
processor.save()
# ... rest of existing code ...
except Exception as e:
# Extract orderId and paymentId if processor exists
orderId = None
chargeId = None
if processor:
try:
orderId = processor.order_id
chargeId = getattr(processor, 'payment_id', None)
except:
pass

log_callback_error(
error=e,
function_name="walletcallback",
event=event,
orderId=orderId,
chargeId=chargeId,
response_data=None
)
# ... existing error handling ...

Step 2: Update template.yaml

Add DynamoDBWritePolicy for CallbackErrorLogTable to the function's Policies:

QRCallback:  # or AmexCallback, WalletCallback
Type: AWS::Serverless::Function
Properties:
Policies:
# ... existing policies ...
- DynamoDBWritePolicy:
TableName: !Ref CallbackErrorLogTable

Step 3: Test

After implementation, test each function to ensure errors are properly logged to CallbackErrorLogTable.

Querying Errors

To query errors by orderId:

from src.callback_error_log_table import CallbackErrorLogTable

# Query all errors for a specific order
errors = list(CallbackErrorLogTable.orderId_index.query(
orderId,
scan_index_forward=False # Sort descending (newest first)
))