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):
orderIdororder_idpaymentTypeorpayment_typechargeIdorcharge_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 OrderTablesave_process_paid_order(): Logs processing attempt to ProcessPaidOrderTablesend_notification(is_success: bool): Sends success/failure notifications and emailinvoke_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 identifierdata(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 identifierchargeId: Charge identifierstatus: 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 identifierstatus: Status string ("success" or other)success: Success booleanpaymentMethod: Payment methodamount: 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 datais_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
- Receive Lambda Invocation: Function receives event with orderId, paymentType, success, chargeId
- Parse Event: Extract and validate required fields
- Check Already Paid: Skip if order.payment.isPaid is True
- Initialize Processor: Create ProcessPaidOrder instance
- Determine Payment Status:
- QR: Query KBank API via
qr_order_status - Card: Query CheckStatusTable via
order_status
- QR: Query KBank API via
- Create Payment Header: Build payment header based on payment type
- Save Order: Update OrderTable with payment information
- Log Processing: Record in ProcessPaidOrderTable
- Send Notifications: Send success/failure notifications and email
- 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 callbackNotify: After credit card notificationQRNotify: After QR notification
IAM Policies
Required Permissions:
DynamoDBReadPolicyonCardPaymentRecordTableDynamoDBReadPolicyonCheckStatusTableDynamoDBCrudPolicyonProcessPaidOrderTableDynamoDBCrudPolicyonEmailLogTableDynamoDBReadPolicyonorder-table-devLambdaInvokePolicyonpayment3-check-status-${BRANCH}LambdaInvokePolicyonPayfunctionSESCrudPolicyon*SNS:Publishon*AWSSecretsManagerGetSecretValuePolicyon 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 modelssrc.updatePaymentHeader: Payment header creationsrc.savePaidOrder: Order saving logicsrc.notifications.*: Notification sendinglambdasdk.lambdasdk.Lambda: Lambda invocationrequests: HTTP requests to KBank APInicHelper.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
)
Related Functions
ProcessPaidOrder: Credit card payment processing (main entry point)ProcessPaidWalletOrderInternal: Wallet payment processingProcessPaidAmexOrderInternal: Amex payment processingQRCallback: Invokes this function after QR callbackNotify: 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