Skip to main content

AmexCallback Lambda Function

Quick Reference

  • Function Name: payment3-amex-callback-${BRANCH}
  • Handler: amexcallback.lambda_handler
  • Runtime: Python 3.12 (Docker Image)
  • Trigger: API Gateway (POST /amex/callback)
  • Template.yaml: Lines 244-286

Function Overview

The AmexCallback function processes American Express payment callbacks. It saves callback records to AmexCallbackRecordTable and invokes ProcessPaidAmexOrderInternal for successful payments. This function uses the BaseCallbackProcessor pattern shared with Wallet and BankApp callbacks.

Entry Point

File: functions/callback/amexcallback.py
Handler: lambda_handler(event, _)

def lambda_handler(event, _):
"""
Record Amex callback.
"""
processor = None
try:
print(f"Processing amex callback event: {json.dumps(event)}")
processor = BaseCallbackProcessor(
event=event,
record_table_class=AmexCallbackRecordTable,
function_name="Amex"
)
processor.save()
response = processor.response
print(f"Amex callback processed successfully: {json.dumps(response)}")
if processor.success:
processor.call_processpaid("amex-order-internal")
return response
except Exception as e:
error_msg = f"Error processing amex callback: {e} {errorString()}"
print(error_msg)

# Extract orderId and chargeId for error logging
orderId = None
chargeId = None
if processor:
try:
orderId = processor.order_id
chargeId = processor.payment_id
except (AttributeError, KeyError):
# Processor may not have these attributes if initialization failed
pass

# Add context to Sentry
with sentry_sdk.push_scope() as scope:
if orderId:
scope.set_tag("orderId", orderId)
if chargeId:
scope.set_tag("chargeId", chargeId)
scope.set_tag("function", "amexcallback")
scope.set_context("callback_processor", {
"has_processor": processor is not None,
"chargeId": chargeId,
"orderId": orderId,
})
sentry_sdk.capture_exception(e)

# Log to callback error log table
log_callback_error(
error=e,
function_name="amexcallback",
event=event,
orderId=orderId,
chargeId=chargeId,
response_data=None,
metadata={"errorString": errorString()}
)

# Send email notification to admin
try:
send_callback_error_email(
error=e,
function_name="amexcallback",
orderId=orderId,
chargeId=chargeId,
event=event
)
except Exception as email_error:
print(f"Failed to send error email: {email_error}")
# Don't fail the whole function if email fails

return {
"statusCode": 500,
"headers": {"Content-Type": "application/json"},
"body": json.dumps(
{
"success": False,
"error": str(e),
"message": "Failed to process amex callback",
}
),
}

Event Structure

API Gateway Event

Path: POST /amex/callback
Auth: NONE (public endpoint - called by payment gateway)

Request Body (JSON):

{
"orderId": "482500007436",
"paymentId": "pay_amex_1234567890",
"success": true,
"amount": 2339.0,
"userId": "b5f392f7-6d89-433c-b24f-bf6a772cab1a",
"message": "Payment successful"
}

Required Fields:

  • orderId - Villa order ID (required)
  • paymentId - Payment gateway payment ID (required)
  • success - Payment success status: true or false (required)
  • userId - Villa user ID (required)
  • amount - Paid amount (optional)
  • message - Optional message

Response (Success):

{
"statusCode": 200,
"headers": {"Content-Type": "application/json"},
"body": "{\"success\": true, \"message\": \"Amex callback recorded successfully\", \"orderId\": \"...\", \"paymentId\": \"...\", \"amount\": ...}"
}

DynamoDB Tables

AmexCallbackRecordTable

Table Name: payment3-amex-callback-record-{BRANCH}
Region: ap-southeast-1

PynamoDB Model:

class AmexCallbackRecordTable(Model):
class Meta:
table_name = f"payment3-amex-callback-record-{BRANCH}"
region = "ap-southeast-1"

orderId = UnicodeAttribute(hash_key=True)
paymentId = UnicodeAttribute()
success = BooleanAttribute()
userId = UnicodeAttribute()
message = UnicodeAttribute(null=True)
created_at = UnicodeAttribute()
updated_at = UnicodeAttribute()
event = JSONAttribute(null=True)
body = JSONAttribute(null=True)
amount = NumberAttribute(null=True)

def __post_init__(self):
if not self.created_at:
self.created_at = datetime.now().isoformat()
if not self.updated_at:
self.updated_at = datetime.now().isoformat()

Key Fields: Same structure as WalletCallbackRecordTable

  • Hash Key: orderId (UnicodeAttribute)
  • paymentId - Payment gateway payment ID
  • success - Payment success status
  • userId - Villa user ID
  • message - Optional message
  • amount - Paid amount (optional)
  • event - Full event data (optional)
  • body - Request body (optional)
  • created_at - Creation timestamp
  • updated_at - Update timestamp

Lambda Invocations

ProcessPaidAmexOrderInternal

Function: payment3-process-paid-amex-order-internal-${BRANCH}
Invoked When: processor.success is True

Payload: Full request body (JSON string)

Invocation Type: Event (asynchronous)

Processing Flow

Same as WalletCallback - uses BaseCallbackProcessor pattern:

  1. Create BaseCallbackProcessor with AmexCallbackRecordTable
  2. Validate required fields
  3. Save to AmexCallbackRecordTable
  4. Invoke ProcessPaidAmexOrderInternal if success is True
  5. Return JSON response

Error Handling

Same error handling as WalletCallback:

  • Validation errors logged to CallbackErrorLogTable
  • Sent to Sentry with context tags
  • Error email sent via SES
  • Returns 500 error response

IAM Policies

From template.yaml (lines 253-273):

  • DynamoDBWritePolicy for AmexCallbackRecordTable (line 254-255)
  • DynamoDBReadPolicy for order-table-dev (line 256-257)
  • DynamoDBCrudPolicy for payment3-card-payment-record-master (line 258-259)
  • LambdaInvokePolicy for ProcessPaidAmexOrderInternal (line 260-261)
  • AWSSecretsManagerGetSecretValuePolicy for kbank-dev (line 262-263)
  • AWSSecretsManagerGetSecretValuePolicy for kbank-prod (line 264-265)
  • DynamoDBWritePolicy for CallbackErrorLogTable (line 266-268)
  • SESCrudPolicy for sending error emails (line 269-271)

Dependencies

External Lambda Functions

  • payment3-process-paid-amex-order-internal-${BRANCH} - Process paid Amex orders (invoked)

DynamoDB Tables

  • payment3-amex-callback-record-${BRANCH} - Callback records (write)
  • order-table-dev - Order data (read)
  • payment3-card-payment-record-master - Master payment records (read)
  • payment3-callback-error-log-${BRANCH} - Error logs (write)

AWS Services

  • SES: Send error notification emails

Testing

Test Event Example

{
"body": "{\"orderId\": \"482500007436\", \"paymentId\": \"pay_amex_123\", \"success\": true, \"amount\": 2339.0, \"userId\": \"b5f392f7-6d89-433c-b24f-bf6a772cab1a\", \"message\": \"Payment successful\"}"
}

Code Structure

File Organization:

functions/callback/
├── amexcallback.py # Main handler
└── src/
├── base_callback_processor.py # Base processor (shared)
└── amexCallbackRecordTable.py # Amex callback table
  • ProcessPaidAmexOrderInternal - Processes paid Amex orders
  • WalletCallback - Similar callback handler
  • BankAppCallback - Similar callback handler

References

  • Template.yaml: Lines 244-286
  • Related Functions: ProcessPaidAmexOrderInternal, WalletCallback, BankAppCallback
  • Base Processor: src/base_callback_processor.py