Notify Lambda Function
Quick Reference
- Function Name:
payment3-notify-${BRANCH} - Handler:
notify.lambda_handler - Runtime: Python 3.12 (Docker Image)
- Timeout: 240 seconds
- Trigger: API Gateway (POST /notify)
- Template.yaml: Lines 348-413
Function Overview
The Notify function receives webhook notifications from KBank payment gateway for credit card payments. It performs checksum verification, saves notification records, processes paid orders, and triggers delayed notification processing. This is the primary entry point for credit card payment webhooks.
Entry Point
File: functions/callback/notify.py
Handler: lambda_handler(event, _)
def lambda_handler(event, _):
"""Record credit card callback."""
# Reset delayed notify flag for this invocation
reset_delayed_notify_flag()
processor = None
try:
# Verify checksum FIRST before any other processing
checksum_valid, checksum_error, charge_id = verify_and_handle_checksum(
event=event,
function_name="notify",
branch=BRANCH,
debug_mode=DEBUG_MODE
)
if not checksum_valid:
# Log warning but continue processing
print(f"[WARNING] Checksum verification failed: {checksum_error}. Continuing with processing.")
# Process credit card callback
processor = CreditCardCallbackProcessor(
event=event,
branch=BRANCH,
debug_mode=DEBUG_MODE
)
print("start saving")
processor.save()
if processor.card_payment_record:
print("start processing paid order")
r = processor.process_paid_order()
print("process paid order response", r)
else:
print("card payment record not found, skipping process_paid_order")
# Call delayed notify AFTER all processing is complete (only once)
call_delayed_notify(event, BRANCH, DEBUG_MODE)
return processor.response
except Exception as e:
# Handle error (logging and notifications)
handle_notify_error(e, event, processor, function_name="notify")
# Re-raise the error
raise
Event Structure
API Gateway Event
Path: POST /notify
Auth: NONE (public endpoint - called by KBank)
Request Body (JSON):
{
"id": "chrg_prod_1143084777d1604dd41328c9a760e1eeb2fbe",
"object": "charge",
"amount": 1583.0,
"currency": "THB",
"status": "success",
"transaction_state": "Settled",
...
}
Key Fields:
idorobjectId- KBank charge ID (required)status- Payment status: "success" or "failure" (required)amount- Charged amountcurrency- Currency code (typically "THB")
Response (Success - HTML redirect):
<html>
<head>
<meta http-equiv="Refresh" content="0; URL='https://shop.villamarket.com/thankyou?orderId=...&status=success&amount=...'">
</head>
<body>
<h2>Payment Processing Complete</h2>
<p>Redirecting to Villa Market...</p>
</body>
</html>
Checksum Verification
Critical: This function performs checksum verification FIRST before any processing.
Process:
- Extract charge_id from event body
- Verify checksum using KBank secret key
- Update
checksumVerifiedfield in CardPaymentRecordTable - Log errors if checksum fails (but continue processing)
See: functions/callback/docs/CHECKSUM_MARKING_DOCUMENTATION.md for detailed documentation.
DynamoDB Tables
CreditCardNotifyTable
Table Name: payment3-credit-card-notify-{BRANCH}
Region: ap-southeast-1
PynamoDB Model:
class CreditCardNotifyTable(Model):
class Meta:
table_name = f"payment3-credit-card-notify-{BRANCH}"
region = "ap-southeast-1"
id = UnicodeAttribute(hash_key=True)
data = JSONAttribute()
chargeId = UnicodeAttribute(null=True)
raw_input = JSONAttribute(null=True)
orderId = UnicodeAttribute(null=True)
note = UnicodeAttribute(default="")
Save Pattern:
from src.credit_card_notify_table import CreditCardNotifyTable
CreditCardNotifyTable(
id=charge_id,
chargeId=charge_id,
data=body_data,
raw_input=event,
orderId=order_id,
note=""
).save()
CardPaymentRecordTable
Table Name: payment3-card-payment-record-{BRANCH} or payment3-card-payment-record-master
Region: ap-southeast-1
Update: checksumVerified field is updated during checksum verification.
Query: Records are queried by chargeId using GSI chargeId_index.
Lambda Invocations
ProcessPaidOrder
Function: payment3-process-paid-order-${BRANCH}
Invoked When: processor.card_payment_record exists
Payload: DynamoDB Stream event format
Invocation Type: Synchronous (via processor.process_paid_order())
DelayedNotify
Function: payment3-delayed-notify-${BRANCH}
Invoked When: After all processing is complete
Payload: Original event
Invocation Type: Event (asynchronous)
Note: Only called once per invocation (uses environment variable flag)
Processing Flow
Main Flow
- Reset Flag: Reset delayed notify flag for new invocation
- Verify Checksum: Verify webhook checksum (updates checksumVerified)
- Create Processor: Initialize CreditCardCallbackProcessor
- Save Notification: Save to CreditCardNotifyTable
- Process Paid Order: Invoke ProcessPaidOrder if card_payment_record exists
- Call Delayed Notify: Invoke DelayedNotify (only once)
- Return Response: Return HTML redirect response
Detailed Algorithm
1. Reset DELAYED_NOTIFY_CALLED environment variable
2. Extract charge_id from event body
3. Verify checksum:
- Get KBank secret from Secrets Manager
- Calculate expected checksum
- Compare with received checksum
- Update CardPaymentRecordTable.checksumVerified
4. Create CreditCardCallbackProcessor:
- Parse event body (JSON or URL-encoded)
- Query CardPaymentRecordTable by chargeId
- Get orderId from card_payment_record
5. Save notification:
- Create CreditCardNotifyTable record
- id = charge_id
- data = body data
- orderId = order_id
6. If card_payment_record exists:
- Invoke ProcessPaidOrder synchronously
7. Call delayed notify:
- Set DELAYED_NOTIFY_CALLED = "true"
- Invoke DelayedNotify asynchronously
8. Return HTML redirect response
Error Handling
Checksum Verification Failures
Warning: Checksum failures are logged but processing continues
if not checksum_valid:
# Log warning but continue processing
print(f"[WARNING] Checksum verification failed: {checksum_error}. Continuing with processing.")
Handling:
- Checksum error logged
checksumVerifiedset to False in CardPaymentRecordTable- Error logged to CallbackErrorLogTable
- Error email sent via SES
- Exception captured in Sentry
- Processing continues
Processor Errors
Exception: Caught in lambda_handler
Handling:
- Error logged via
handle_notify_error() - Logged to CallbackErrorLogTable
- Error email sent via SES
- Exception captured in Sentry
- Error re-raised
IAM Policies
From template.yaml (lines 358-399):
DynamoDBWritePolicyforCreditCardNotifyTable(line 360-361)DynamoDBReadPolicyfororder-table-dev(line 363-364)DynamoDBWritePolicyforCreditCardCallbackTable(line 366-367)DynamoDBWritePolicyforQRCallbackRecordTable(line 369-370)DynamoDBCrudPolicyforCardPaymentRecordTable(line 372-373)DynamoDBCrudPolicyforpayment3-card-payment-record-master(line 375-376)DynamoDBWritePolicyforPaymentErrorLogTable(line 378-379)DynamoDBWritePolicyforCallbackErrorLogTable(line 381-382)LambdaInvokePolicyforProcessPaidOrder(line 384-385)LambdaInvokePolicyforProcessPaidOrderInternal(line 387-388)LambdaInvokePolicyforDelayedNotify(line 390-391)SESCrudPolicyfor sending error emails (line 393-394)AWSSecretsManagerGetSecretValuePolicyfor kbank-dev (line 396-397)AWSSecretsManagerGetSecretValuePolicyfor kbank-prod (line 398-399)
Dependencies
External Lambda Functions
payment3-process-paid-order-${BRANCH}- Process paid orders (invoked)payment3-delayed-notify-${BRANCH}- Delayed notification processing (invoked)
DynamoDB Tables
payment3-credit-card-notify-${BRANCH}- Notification records (write)payment3-card-payment-record-${BRANCH}- Payment records (read/update)payment3-card-payment-record-master- Master payment records (read/update)order-table-dev- Order data (read)payment3-error-log-${BRANCH}- Error logs (write)payment3-callback-error-log-${BRANCH}- Callback error logs (write)
External Services
- KBank Payment Gateway: Webhook source
- AWS Secrets Manager: KBank API credentials for checksum verification
- SES: Send error notification emails
Environment Variables
BRANCH- Git branch nameDEBUG_MODE- Debug mode flag (skip table writes if True)DELAYED_NOTIFY_CALLED- Flag to prevent duplicate delayed notify calls
Testing
Local Testing
File: functions/callback/notify.py (lines 58-106)
if __name__ == "__main__":
import json
from src.credit_card_callback_processor import CreditCardCallbackProcessor
# Enable debug mode
os.environ["DEBUG_MODE"] = "true"
os.environ["BRANCH"] = "master"
# Sample input
sample_body = {
"id": "chrg_prod_1143084777d1604dd41328c9a760e1eeb2fbe",
"object": "charge",
"amount": 1583.0,
"currency": "THB"
}
event = {"body": json.dumps(sample_body)}
result = lambda_handler(event, None)
Test Event Example
{
"body": "{\"id\": \"chrg_prod_1143084777d1604dd41328c9a760e1eeb2fbe\", \"object\": \"charge\", \"amount\": 1583.0, \"currency\": \"THB\", \"status\": \"success\"}"
}
Code Structure
File Organization:
functions/callback/
├── notify.py # Main handler
└── src/
├── checksum_handler.py # Checksum verification
├── credit_card_callback_processor.py # Callback processor
├── delayed_notify_handler.py # Delayed notify invocation
├── notify_processor.py # Error handling
└── credit_card_notify_table.py # Notification table
Related Functions
- DelayedNotify - Called after processing completes
- ProcessPaidOrder - Processes successfully paid orders
- Callback - Handles 3D Secure callbacks
- Checksum Documentation:
functions/callback/docs/CHECKSUM_MARKING_DOCUMENTATION.md
Common Patterns
Checksum Verification Pattern
# Verify checksum FIRST before any other processing
checksum_valid, checksum_error, charge_id = verify_and_handle_checksum(
event=event,
function_name="notify",
branch=BRANCH,
debug_mode=DEBUG_MODE
)
Delayed Notify Pattern
# Call delayed notify AFTER all processing is complete (only once)
call_delayed_notify(event, BRANCH, DEBUG_MODE)
Troubleshooting
Common Issues
-
Checksum Verification Failures
- Check KBank secret keys in Secrets Manager
- Verify checksum algorithm matches KBank specification
- Check CloudWatch logs for checksum calculation details
-
Card Payment Record Not Found
- Verify chargeId exists in CardPaymentRecordTable
- Check if record was created in master table
- Verify chargeId matches between CardPayment and Notify
-
Delayed Notify Not Called
- Check DELAYED_NOTIFY_CALLED flag is reset
- Verify Lambda invocation permissions
- Check CloudWatch logs for invocation errors
Debugging
- Check CloudWatch logs for checksum verification details
- Review CallbackErrorLogTable for error history
- Check Sentry for exception tracking
- Verify CreditCardNotifyTable records are being saved
- Test with known good charge IDs
References
- Template.yaml: Lines 348-413
- Checksum Documentation:
functions/callback/docs/CHECKSUM_MARKING_DOCUMENTATION.md - Related Functions: DelayedNotify, ProcessPaidOrder, Callback