import { MmgCheckoutUtil } from "mobile-money-sdk";
import express from "express";
const app = express();
app.use(express.json()); // Ensure your server can parse JSON bodies
// Initialize the Checkout Utility
/**
process.env.* returns string | undefined — TypeScript will complain if you pass those directly to fields typed as string. The ! non-null assertion (or
runtime validation) is needed for publicKey, secretKey, merchantId, and clientId.
**/
const checkoutUtil = new MmgCheckoutUtil({
environment: "Live", // or "Sandbox"
publicKey: process.env.MMG_PUBLIC_KEY!,
privateKey: process.env.MMG_PRIVATE_KEY,
});
// The URL you provide to MMG during onboarding (or auto-generated)
app.post("/mmg-sdk/callback/:hash", async (req, res) => {
try {
// 1. Extract the encrypted token from the incoming request body
const encryptedToken = req.body.token;
if (!encryptedToken) {
return res.status(400).send("Missing token");
}
// 2. Decrypt the token using the SDK utility
const response = checkoutUtil.decryptWebhookToken(encryptedToken);
/* The decrypted response object looks like this:
{
merchantTransactionId: "ORD-1765472622",
transactionId: "999888777",
ResultCode: "0",
ResultMessage: "Transaction Successful",
htmlResponse: "..."
}
*/
// 3. Process the Result
if (response.ResultCode === "0") {
// Payment was successful!
console.log(`Payment confirmed for order: ${response.merchantTransactionId}`);
// TODO: Update your database, mark order as paid, and fulfill goods.
} else {
// Payment failed, timed out, or was cancelled
console.warn(`Payment failed with code ${response.ResultCode}: ${response.ResultMessage}`);
// TODO: Mark order as failed in your database.
}
// 4. Always return a 200 OK status to MMG so they know you received the webhook
res.status(200).send("OK");
} catch (error) {
console.error("Webhook decryption failed. Invalid key or payload.", error);
// Still return 200 to prevent MMG from endlessly retrying a bad payload
res.status(200).send("Processed with errors");
}
});