mmpay-node-sdk
v1.1.3
Published
JavaScript SDK for integrating the MMQR Merchant and Redirect payment gateway
Readme
MyanMyanPay Node SDK
📋 Implementation Documentation
This documentation details the steps for integrating the mmpay-node-sdk into your application to securely send callbacks to the MyanMyanPay SDK server and to verify incoming callbacks from MyanMyanPay.
// TypeScript OR Esm Module
import { MMPaySDK } from 'mmpay-node-sdk';⬇️ 1. Installation
Install the package via npm:
npm install mmpay-node-sdk --save⚙️ 2. Configuration
Before use, you must configure the shared Secret Key. This key is used for HMAC-SHA256 signature calculation and verification and must match the key configured on the MMPay platform. It is CRITICAL that this key is loaded from an environment variable for security.
// Load the SDK and configuration
const { MMPaySDK } = require('mmpay-node-sdk');
const MMPay = new MMPaySdk({
appId: "MMxxxxxxx",
publishableKey: "pk_live_abcxxxxx",
secretKey: "sk_live_abcxxxxx",
apiBaseUrl: "https://xxxxxx"
})💳 3. Make Payment
let options = {
orderId: 'ORD-199399933',
amount: 5000,
items: [
{ name: "Pencil", amount: 5000, quantity: 1 }
],
customMessage: '', // max 150 char string
callbackUrl: 'https://abcdef/callback' // [optional] overrides default callbackURL
}
// sync
MMPay.pay(options)
.then((response) => {
console.log(response)
}).catch((error) => {
console.log(error)
})
// async
try {
await MMPay.pay(options)
} catch (error) {
console.log(error)
}Request Body (payload structure)
The request body should be a JSON object containing the transaction details. Based on your IPTrx interface, the required fields are:
| Field | Type | Required | Description | Example |
| :--- | :--- | :--- | :--- | :--- |
| orderId | string | Yes | Your generated order ID for the order or system initiating the payment. | "ORD-3983833" |
| amount | number | Yes | The total transaction amount. | 1500.50 |
| callbackUrl | string | No | The URL where the payment gateway will send transaction status updates. | "https://yourserver.com/webhook" |
| currency | string | No | The currency code (e.g., 'MMK'). | "MMK" |
| customMessage | string | No | Your Customization String |
| items | Array<Object> | No | List of items included in the purchase. | [{name: "Hat", amount: 1000, quantity: 1}] |
items Object Structure
| Field | Type | Description |
| :--- | :--- | :--- |
| name | string | The name of the item. |
| amount | number | The unit price of the item. |
| quantity | number | The number of units purchased. |
Response Codes
| Code | Status | Description |
| :--- | :--- | :--- |
| 201 | Created | Transaction initiated successfully. Response contains QR code URL/details. |
| 401 | Unauthorized | Invalid or missing Publishable Key. |
| 400 | Bad Request | Missing required body fields (validated by schema, if implemented). |
| 503 | Service Unavailable | Upstream payment API failed or is unreachable. |
| 500 | Internal Server Error | General server error during payment initiation. |
Successful Response (201) Example
{
"orderId": "_trx_0012345",
"amount": 2800,
"currency": "MMK",
"qr": "base64:StringxxxIt_Is_A_QR_Code",
"status": "PENDING"
}🚀 4. Requesting On Sandbox Environment
// Load the SDK and configuration
const { MMPaySDK } = require('mmpay-node-sdk');
const MMPay = new MMPaySdk({
appId: "MMxxxxxxx",
publishableKey: "pk_test_abcxxxxx", // <<< When your input key is test key, our SDK knows automatically to switch to testing environment
secretKey: "sk_test_abcxxxxx", // <<< When your input key is test key, our SDK knows automatically to switch to testing environment
apiBaseUrl: "https://xxxxxx"
})
let options = {
orderId: 'ORD-199399933',
amount: 5000,
items: [
{ name: "Pencil", amount: 5000, quantity: 1 }
],
customMessage: '', // max 150 char string
callbackUrl: 'https://abcdef/callback' // [optional] overrides default callbackURL
}
// sync
MMPay.pay(options)
.then((response) => {
console.log(response)
}).catch((error) => {
console.log(error)
});
// async
try {
await MMPay.pay(options)
} catch (error) {
console.log(error)
}🔐 4. Verifying Incoming Callbacks (Webhooks)
To secure your webhook endpoint that receives callbacks from the MMPay server, use the built-in Express middleware provided by the SDK. This middleware performs the mandatory Signature and Nonce verification.
Handling callbacks
Incoming HTTP POST Parameters
Header
| Field Name | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| Content-Type | string | Yes | 'application/json' |
| X-Mmpay-Signature | string | Yes | '34834890vfgh9hnf94irfg_48932i4rt90349849' |
| X-Mmpay-Nonce | string | Yes | '94843943949349' |
Body
| Field Name | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| orderId | string | Yes | Unique identifier for the specific order. |
| amount | number | Yes | The transaction amount. |
| currency | string | Yes | The 3-letter currency code (e.g., MMK, USD). |
| vendor | string | Yes | Identifier for the vendor initiating the request. |
| method | 'QR', 'PIN', 'PWA', 'CARD' | Yes | Identifier for the method. |
| status | 'PENDING','SUCCESS','FAILED','REFUNDED' | Yes | Current status of the transaction. |
| condition | 'PRESTINE', 'TOUCHED' | Yes | Used QR Code scan again or not |
| transactionRefId | string | Yes | The reference ID generated by the payment provider. |
| callbackUrl | string | No | Optional URL to receive webhooks or updates. |
| customMessage | string | No | User provided custom message |
interface MMPayIncomingCallbackScheme {
orderId: string;
amount: number;
method: 'QR' | 'PIN' | 'PWA' | 'CARD';
currency: 'MMK';
vendor: string;
status: 'PENDING' | 'SUCCESS' | 'FAILED' | 'REFUNDED' | 'CANCELLED' | 'EXPIRED';
condition: 'PRISTINE' | 'TOUCHED' | 'EXPIRED';
transactionRefId: string;
callbackUrl?: string;
customMessage?: string;
}
MMPay
.onTxCreate((tx: MMPayIncomingCallbackScheme) => console.log('Created:', tx.orderId))
.onTxSuccess((tx: MMPayIncomingCallbackScheme) => console.log('Success:', tx.orderId))
.onTxFail((tx: MMPayIncomingCallbackScheme) => console.log('Failed:', tx.orderId))
.onTxRefund((tx: MMPayIncomingCallbackScheme) => console.log('Refunded:', tx.orderId))
.onTxCancel((tx: MMPayIncomingCallbackScheme) => console.log('Cancelled:', tx.orderId))
.onTxExpire((tx: MMPayIncomingCallbackScheme) => console.log('Expired:', tx.orderId))
.onHeartbeat((tx: MMPayIncomingCallbackScheme) => console.log(tx.orderId)) // This means already send event coming in again
.on('error', (err) => console.error(err));
app.post('/webhooks/mmpay-callback', async (req: Request, res: Response) => {
const payload = JSON.stringify(req.body);
const nonce = req.headers['x-mmpay-nonce'] as string;
const signature = req.headers['x-mmpay-signature'] as string;
await MMPay.listen(payload, nonce, signature);
res.json({ received: true }); // please respond with 200 status
});
Error Codes
Api Key Layer Authentication [SERVER SDK]
| Code | Description |
| :--- | :--- |
| KA0001 | Bearer Token Not Included In Your Request |
| KA0002 | API Key Not 'LIVE' |
| KA0003 | Signature mismatch |
| KA0004 | Internal Server Error ( Talk to our support immediately fot this ) |
| KA0005 | IP Not whitelisted |
| 429 | Ratelimit hit only 1000 request / minute allowed |
JWT Layer Authentication [SERVER SDK]
| Code | Description |
| :--- | :--- |
| BA001 | Btoken is nonce one time token is not included |
| BA002 | Btoken one time nonce mismatch |
| BA000 | Internal Server Error ( Talk to our support immediately fot this ) |
| 429 | Ratelimit hit only 1000 request / minute allowed |
💡 Implementation IDEA
We Love Typescript, so here are our favourite framework plugins implementations
Express JS Framwork Usage Full Example
const express = require("express");
const bodyParser = require("body-parser");
const app = express();
const PORT = process.env.PORT || 3000;
app.use(bodyParser.json());
const { MMPaySDK } = require('mmpay-node-sdk');
const MMPay = new MMPaySDK({
appId: "MMxxxxxxx",
publishableKey: "pk_live_abcxxxxx",
secretKey: "sk_live_abcxxxxx",
apiBaseUrl: "https://xxxxxx"
});
const MMPaySandbox = new MMPaySDK({
appId: "MMxxxxxxx",
publishableKey: "pk_test_abcxxxxx",
secretKey: "sk_test_abcxxxxx",
apiBaseUrl: "https://xxxxxx"
})
interface MMPayIncomingCallbackScheme {
orderId: string;
amount: number;
method: 'QR' | 'PIN' | 'PWA' | 'CARD';
currency: 'MMK';
vendor: string;
status: 'PENDING' | 'SUCCESS' | 'FAILED' | 'REFUNDED' | 'CANCELLED' | 'EXPIRED';
condition: 'PRISTINE' | 'TOUCHED';
transactionRefId: string;
callbackUrl?: string;
customMessage?: string;
}
MMPay
.onTxCreate((tx: MMPayIncomingCallbackScheme) => console.log('Created:', tx.orderId))
.onTxSuccess((tx: MMPayIncomingCallbackScheme) => console.log('Success:', tx.orderId))
.onTxFail((tx: MMPayIncomingCallbackScheme) => console.log('Failed:', tx.orderId))
.onTxRefund((tx: MMPayIncomingCallbackScheme) => console.log('Refunded:', tx.orderId))
.onTxCancel((tx: MMPayIncomingCallbackScheme) => console.log('Cancelled:', tx.orderId))
.onTxExpire((tx: MMPayIncomingCallbackScheme) => console.log('Expired:', tx.orderId))
.onHeartbeat((tx: MMPayIncomingCallbackScheme) => console.log('Heartbeat:', tx.orderId))
.on('error', (err) => console.error(err));
MMPaySandbox
.onTxCreate((tx: MMPayIncomingCallbackScheme) => console.log('Created:', tx.orderId))
.onTxSuccess((tx: MMPayIncomingCallbackScheme) => console.log('Success:', tx.orderId))
.onTxFail((tx: MMPayIncomingCallbackScheme) => console.log('Failed:', tx.orderId))
.onTxRefund((tx: MMPayIncomingCallbackScheme) => console.log('Refunded:', tx.orderId))
.onTxCancel((tx: MMPayIncomingCallbackScheme) => console.log('Cancelled:', tx.orderId))
.onTxExpire((tx: MMPayIncomingCallbackScheme) => console.log('Expired:', tx.orderId))
.onHeartbeat((tx: MMPayIncomingCallbackScheme) => console.log('Heartbeat:', tx.orderId))
.on('error', (err) => console.error(err));
app.post("/create-order", async (req, res) => {
const { amount, items } = req.body;
const orderId = ''; // GET YOUR ORDER ID FROM YOUR BIZ LOGIC
const payload = {
orderId: 'ORD-199399933',
amount: 5000,
items: [
{ name: "Pencil", amount: 5000, quantity: 1 }
],
customMessage: '', // max 150 char string
callbackUrl: 'https://abcdef/callback' // [optional] overrides default callbackURL
}
let payResponse = await MMPay.pay(payload);
res.status(200).json(payResponse);
});
// Listening Callback
app.post('/webhooks/mmpay-callback', async (req: Request, res: Response) => {
const payload = JSON.stringify(req.body);
const nonce = req.headers['x-mmpay-nonce'] as string;
const signature = req.headers['x-mmpay-signature'] as string;
await MMPay.listen(payload, nonce, signature);
res.json({ received: true }); // please respond with 200 status
});
app.post("/create-order-sandbox", async (req, res) => {
const { amount, items } = req.body;
const orderId = ''; // GET YOUR ORDER ID FROM YOUR BIZ LOGIC
const payload = {
orderId: 'ORD-199399933',
amount: 5000,
items: [
{ name: "Pencil", amount: 5000, quantity: 1 }
],
customMessage: '', // max 150 char string
callbackUrl: 'https://abcdef/callback' // [optional] overrides default callbackURL
}
let payResponse = await MMPaySandbox.pay(payload);
res.status(200).json(payResponse);
});
// Listening Callback
app.post('/webhooks/mmpay-callback-sandbox', async (req: Request, res: Response) => {
const payload = JSON.stringify(req.body);
const nonce = req.headers['x-mmpay-nonce'] as string;
const signature = req.headers['x-mmpay-signature'] as string;
await MMPaySandbox.listen(payload, nonce, signature);
res.json({ received: true }); // please respond with 200 status
});
app.listen(PORT, () => console.log(`Server is running on port ${PORT}`));
