Handling Webhooks

Learn how to listen for and process asynchronous checkout callbacks using FastAPI.

When a customer completes a hosted checkout, MMG sends an asynchronous callback to your server with the payment result.

Because SDKs are strictly client-side tools, the SDK does not include a callable function to “receive” this webhook. Instead, your backend must act as a server to listen for the incoming request.

However, the Python SDK does provide strictly-typed Pydantic models for the webhook payloads, which integrate seamlessly with modern frameworks like FastAPI.

FastAPI Implementation

The following example demonstrates how to create a webhook listener using FastAPI and the SDK’s generated Pydantic models (CheckoutCallbackRequest and DecryptedCheckoutResponse).

By using CheckoutCallbackRequest as the route’s payload type, FastAPI will automatically validate that the incoming MMG request contains the required token field.

1import os
2 from fastapi import FastAPI, HTTPException
3
4 from mmg import MmgCheckoutUtil
5 from mmg.types import CheckoutCallbackRequest
6
7 app = FastAPI()
8
9 checkout_util = MmgCheckoutUtil(
10 environment="Live", # or "Sandbox"
11 public_key=os.getenv("MMG_PUBLIC_KEY"),
12 private_key=os.getenv("MMG_PRIVATE_KEY"),
13 )
14
15
16 @app.post("/mmg-sdk/callback/{hash}")
17 async def mmg_checkout_callback(payload: CheckoutCallbackRequest):
18 """Receive the asynchronous payment result from MMG."""
19 try:
20 # Decrypt and parse the token in one call
21 result = checkout_util.decrypt_callback_token(payload.token)
22
23 if result.result_code == "0":
24 print(f"Success! Merchant TXN: {result.merchant_transaction_id}")
25 # Fulfill the order in your database
26 else:
27 print(f"Payment failed: {result.result_message}")
28
29 # MMG requires a 200 OK to prevent webhook retries
30 return {"status": "success"}
31
32 except Exception as e:
33 raise HTTPException(status_code=400, detail="Failed to process webhook")

Security Best Practices

  1. Keep your Private Key secure: Never hardcode your RSA private key in your application code. Always load it securely via environment variables or a secret manager.
  2. Dynamic Webhook URLs: When you initiate the checkout, you pass a dynamically generated webhook URL to MMG (e.g., https://api.yourdomain.com/mmg-sdk/callback/{hash}). Ensure this {hash} is difficult to guess and mapped to the specific checkout session in your database to prevent replay attacks.
  3. Idempotency: Webhooks can sometimes be delivered more than once. Ensure your database logic checks if a merchantTransactionId has already been processed before fulfilling an order.