mmpay-browser-sdk 1.0.5 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,14 +1,24 @@
1
- export interface ICorePayParams {
1
+ export interface SDKOptions {
2
+ pollInterval?: number;
3
+ environment?: 'sandbox' | 'production';
4
+ baseUrl?: string;
5
+ merchantName?: string;
6
+ }
7
+ export interface ICreateTokenRequestParams {
2
8
  amount: number;
3
9
  orderId: string;
4
- callbackUrl?: string;
10
+ nonce?: string;
11
+ }
12
+ export interface ICreateTokenResponse {
13
+ orderId: string;
14
+ token: string;
5
15
  }
6
16
  export interface ICreatePaymentRequestParams {
7
17
  amount: number;
8
- currency?: string;
9
18
  orderId: string;
10
19
  callbackUrl?: string;
11
20
  nonce?: string;
21
+ customMessage?: string;
12
22
  }
13
23
  export interface ICreatePaymentResponse {
14
24
  _id: string;
@@ -18,17 +28,6 @@ export interface ICreatePaymentResponse {
18
28
  transactionRefId: string;
19
29
  qr: string;
20
30
  }
21
- export interface ICreateTokenRequestParams {
22
- amount: number;
23
- currency?: string;
24
- orderId: string;
25
- callbackUrl?: string;
26
- nonce?: string;
27
- }
28
- export interface ICreateTokenResponse {
29
- orderId: string;
30
- token: string;
31
- }
32
31
  export interface IPollingRequest {
33
32
  amount: number;
34
33
  currency?: string;
@@ -45,12 +44,6 @@ export interface PolliongResult {
45
44
  success: boolean;
46
45
  transaction: IPollingResponse;
47
46
  }
48
- export interface SDKOptions {
49
- pollInterval?: number;
50
- environment?: 'sandbox' | 'production';
51
- baseUrl?: string;
52
- merchantName?: string;
53
- }
54
47
  export declare class MMPaySDK {
55
48
  private POLL_INTERVAL_MS;
56
49
  private tokenKey;
@@ -59,113 +52,26 @@ export declare class MMPaySDK {
59
52
  private merchantName;
60
53
  private environment;
61
54
  private pollIntervalId;
55
+ private countdownIntervalId;
62
56
  private onCompleteCallback;
63
57
  private overlayElement;
64
58
  private pendingApiResponse;
65
59
  private pendingPaymentPayload;
66
60
  private readonly QR_SIZE;
67
- /**
68
- * constructor
69
- * @param publishableKey
70
- * @param options
71
- */
61
+ private readonly TIMEOUT_SECONDS;
72
62
  constructor(publishableKey: string, options?: SDKOptions);
73
- /**
74
- * _callApi
75
- * @param endpoint
76
- * @param data
77
- * @returns
78
- */
79
63
  private _callApi;
80
- /**
81
- * _callApiTokenRequest
82
- * @param {ICreateTokenRequestParams} payload
83
- * @param {number} payload.amount
84
- * @param {string} payload.currency
85
- * @param {string} payload.orderId
86
- * @param {string} payload.nonce
87
- * @param {string} payload.callbackUrl
88
- * @returns {Promise<ICreateTokenResponse>}
89
- */
90
64
  private _callApiTokenRequest;
91
- /**
92
- * _callApiPaymentRequest
93
- * @param {ICreatePaymentRequestParams} payload
94
- * @param {number} payload.amount
95
- * @param {string} payload.currency
96
- * @param {string} payload.orderId
97
- * @param {string} payload.nonce
98
- * @param {string} payload.callbackUrl
99
- * @returns {Promise<ICreatePaymentResponse>}
100
- */
101
65
  private _callApiPaymentRequest;
102
- /**
103
- * createPayment
104
- * @param {ICorePayParams} params
105
- * @param {number} params.amount
106
- * @param {string} params.orderId
107
- * @param {string} params.callbackUrl
108
- * @returns {Promise<ICreatePaymentResponse>}
109
- */
110
- createPayment(params: ICorePayParams): Promise<ICreatePaymentResponse>;
111
- /**
112
- * showPaymentModal
113
- * @param {ICorePayParams} params
114
- * @param {number} params.amount
115
- * @param {string} params.orderId
116
- * @param {string} params.callbackUrl
117
- * @param {Function} onComplete
118
- */
119
- showPaymentModal(params: ICorePayParams, onComplete: (result: PolliongResult) => void): Promise<void>;
120
- /**
121
- * _createAndRenderModal
122
- * @param {string} contentHtml
123
- * @param {boolean} isTerminal
124
- * @returns
125
- */
66
+ createPayment(params: ICreatePaymentRequestParams): Promise<ICreatePaymentResponse>;
67
+ showPaymentModal(params: ICreatePaymentRequestParams, onComplete: (result: PolliongResult) => void): Promise<void>;
126
68
  private _createAndRenderModal;
127
- /**
128
- * _renderQrModalContent
129
- * @param {ICreatePaymentResponse} apiResponse
130
- * @param {CreatePaymentRequest} payload
131
- * @param {string} merchantName
132
- */
133
69
  private _renderQrModalContent;
134
- /**
135
- * _showTerminalMessage
136
- * @param {string} orderId
137
- * @param {string} status
138
- * @param {string} message
139
- */
140
70
  private _showTerminalMessage;
141
- /**
142
- * _showCancelConfirmationModal
143
- */
144
71
  private _showCancelConfirmationModal;
145
- /**
146
- * _reRenderPendingModalInstance
147
- */
148
72
  private _reRenderPendingModalInstance;
149
- /**
150
- * Cleans up the modal and stops polling.
151
- * @param {boolean} restoreBodyScroll
152
- */
153
73
  private _cleanupModal;
154
- /**
155
- * _injectQrScript
156
- * @param {string} qrData
157
- * @param {string} qrCanvasId
158
- */
159
74
  private _injectQrScript;
160
- /**
161
- * _startPolling
162
- * @param {IPollingRequest} payload
163
- * @param {number} payload.amount
164
- * @param {string} payload.currency
165
- * @param {string} payload.orderId
166
- * @param {string} payload.nonce
167
- * @param {string} payload.callbackUrl
168
- * @param {Function} onComplete
169
- */
170
75
  private _startPolling;
76
+ private _startCountdown;
171
77
  }
package/dist/esm/index.js CHANGED
@@ -1,16 +1,13 @@
1
1
  export class MMPaySDK {
2
- /**
3
- * constructor
4
- * @param publishableKey
5
- * @param options
6
- */
7
2
  constructor(publishableKey, options = {}) {
8
3
  this.pollIntervalId = undefined;
4
+ this.countdownIntervalId = undefined;
9
5
  this.onCompleteCallback = null;
10
6
  this.overlayElement = null;
11
7
  this.pendingApiResponse = null;
12
8
  this.pendingPaymentPayload = null;
13
9
  this.QR_SIZE = 290;
10
+ this.TIMEOUT_SECONDS = 300;
14
11
  if (!publishableKey) {
15
12
  throw new Error("A Publishable Key is required to initialize [MMPaySDK].");
16
13
  }
@@ -20,12 +17,6 @@ export class MMPaySDK {
20
17
  this.merchantName = options.merchantName || 'Your Merchant';
21
18
  this.POLL_INTERVAL_MS = options.pollInterval || 5000;
22
19
  }
23
- /**
24
- * _callApi
25
- * @param endpoint
26
- * @param data
27
- * @returns
28
- */
29
20
  async _callApi(endpoint, data = {}) {
30
21
  let config = {
31
22
  'Content-Type': 'application/json',
@@ -49,16 +40,6 @@ export class MMPaySDK {
49
40
  }
50
41
  return response.json();
51
42
  }
52
- /**
53
- * _callApiTokenRequest
54
- * @param {ICreateTokenRequestParams} payload
55
- * @param {number} payload.amount
56
- * @param {string} payload.currency
57
- * @param {string} payload.orderId
58
- * @param {string} payload.nonce
59
- * @param {string} payload.callbackUrl
60
- * @returns {Promise<ICreateTokenResponse>}
61
- */
62
43
  async _callApiTokenRequest(payload) {
63
44
  try {
64
45
  const endpoint = this.environment === 'sandbox'
@@ -71,16 +52,6 @@ export class MMPaySDK {
71
52
  throw error;
72
53
  }
73
54
  }
74
- /**
75
- * _callApiPaymentRequest
76
- * @param {ICreatePaymentRequestParams} payload
77
- * @param {number} payload.amount
78
- * @param {string} payload.currency
79
- * @param {string} payload.orderId
80
- * @param {string} payload.nonce
81
- * @param {string} payload.callbackUrl
82
- * @returns {Promise<ICreatePaymentResponse>}
83
- */
84
55
  async _callApiPaymentRequest(payload) {
85
56
  try {
86
57
  const endpoint = this.environment === 'sandbox'
@@ -93,61 +64,55 @@ export class MMPaySDK {
93
64
  throw error;
94
65
  }
95
66
  }
96
- /**
97
- * createPayment
98
- * @param {ICorePayParams} params
99
- * @param {number} params.amount
100
- * @param {string} params.orderId
101
- * @param {string} params.callbackUrl
102
- * @returns {Promise<ICreatePaymentResponse>}
103
- */
104
67
  async createPayment(params) {
105
- const payload = {
68
+ const tokenPayload = {
69
+ amount: params.amount,
70
+ orderId: params.orderId,
71
+ nonce: new Date().getTime().toString() + '_mmp'
72
+ };
73
+ const paymentPayload = {
106
74
  amount: params.amount,
107
75
  orderId: params.orderId,
108
76
  callbackUrl: params.callbackUrl,
109
- currency: 'MMK',
77
+ customMessage: params.customMessage,
110
78
  nonce: new Date().getTime().toString() + '_mmp'
111
79
  };
112
80
  try {
113
- const tokenResponse = await this._callApiTokenRequest(payload);
81
+ const tokenResponse = await this._callApiTokenRequest(tokenPayload);
114
82
  this.tokenKey = tokenResponse.token;
115
- const apiResponse = await this._callApiPaymentRequest(payload);
116
- return apiResponse;
83
+ return await this._callApiPaymentRequest(paymentPayload);
117
84
  }
118
85
  catch (error) {
119
86
  console.error("Payment request failed:", error);
120
87
  throw error;
121
88
  }
122
89
  }
123
- /**
124
- * showPaymentModal
125
- * @param {ICorePayParams} params
126
- * @param {number} params.amount
127
- * @param {string} params.orderId
128
- * @param {string} params.callbackUrl
129
- * @param {Function} onComplete
130
- */
131
90
  async showPaymentModal(params, onComplete) {
132
91
  const initialContent = `<div class="mmpay-overlay-content"><div style="text-align: center; color: #fff;">ငွေပေးချေမှု စတင်နေသည်...</div></div>`;
133
92
  this._createAndRenderModal(initialContent, false);
134
93
  this.onCompleteCallback = onComplete;
135
- const payload = {
94
+ const tokenPayload = {
95
+ amount: params.amount,
96
+ orderId: params.orderId,
97
+ nonce: new Date().getTime().toString() + '_mmp'
98
+ };
99
+ const paymentPayload = {
136
100
  amount: params.amount,
137
101
  orderId: params.orderId,
138
102
  callbackUrl: params.callbackUrl,
139
- currency: 'MMK',
103
+ customMessage: params.customMessage,
140
104
  nonce: new Date().getTime().toString() + '_mmp'
141
105
  };
142
106
  try {
143
- const tokenResponse = await this._callApiTokenRequest(payload);
107
+ const tokenResponse = await this._callApiTokenRequest(tokenPayload);
144
108
  this.tokenKey = tokenResponse.token;
145
- const apiResponse = await this._callApiPaymentRequest(payload);
109
+ const apiResponse = await this._callApiPaymentRequest(paymentPayload);
146
110
  if (apiResponse && apiResponse.qr && apiResponse.transactionRefId) {
147
111
  this.pendingApiResponse = apiResponse;
148
- this.pendingPaymentPayload = payload;
149
- this._renderQrModalContent(apiResponse, payload, this.merchantName);
150
- this._startPolling(payload, onComplete);
112
+ this.pendingPaymentPayload = paymentPayload;
113
+ this._renderQrModalContent(apiResponse, paymentPayload, this.merchantName);
114
+ this._startPolling(paymentPayload, onComplete);
115
+ this._startCountdown(paymentPayload.orderId);
151
116
  }
152
117
  else {
153
118
  this._showTerminalMessage(apiResponse.orderId || 'N/A', 'FAILED', 'ငွေပေးချေမှု စတင်ရန် မအောင်မြင်ပါ။ QR ဒေတာ မရရှိပါ။');
@@ -155,15 +120,9 @@ export class MMPaySDK {
155
120
  }
156
121
  catch (error) {
157
122
  this.tokenKey = null;
158
- this._showTerminalMessage(payload.orderId || 'N/A', 'FAILED', 'ငွေပေးချေမှု စတင်စဉ် အမှားအယွင်း ဖြစ်ပွားသည်။ ကွန်ဆိုးလ်တွင် ကြည့်ပါ။');
123
+ this._showTerminalMessage(paymentPayload.orderId || 'N/A', 'FAILED', 'ငွေပေးချေမှု စတင်စဉ် အမှားအယွင်း ဖြစ်ပွားသည်။ ကွန်ဆိုးလ်တွင် ကြည့်ပါ။');
159
124
  }
160
125
  }
161
- /**
162
- * _createAndRenderModal
163
- * @param {string} contentHtml
164
- * @param {boolean} isTerminal
165
- * @returns
166
- */
167
126
  _createAndRenderModal(contentHtml, isTerminal = false) {
168
127
  this._cleanupModal(false);
169
128
  const overlay = document.createElement('div');
@@ -197,7 +156,6 @@ export class MMPaySDK {
197
156
  width: 100%;
198
157
  padding: 20px 0;
199
158
  }
200
- /* Card Base Styles */
201
159
  .mmpay-card {
202
160
  background: #ffffff;
203
161
  border-radius: 16px;
@@ -269,15 +227,9 @@ export class MMPaySDK {
269
227
  };
270
228
  window.MMPayReRenderModal = () => this._reRenderPendingModalInstance();
271
229
  overlay.innerHTML += `<div class="mmpay-overlay-content">${contentHtml}</div>`;
272
- document.body.style.overflow = 'hidden'; // FIX: Prevent body scroll when modal is open
230
+ document.body.style.overflow = 'hidden';
273
231
  return overlay;
274
232
  }
275
- /**
276
- * _renderQrModalContent
277
- * @param {ICreatePaymentResponse} apiResponse
278
- * @param {CreatePaymentRequest} payload
279
- * @param {string} merchantName
280
- */
281
233
  _renderQrModalContent(apiResponse, payload, merchantName) {
282
234
  const qrData = apiResponse.qr;
283
235
  const amountDisplay = `${apiResponse.amount.toFixed(2)} MMK`;
@@ -304,7 +256,7 @@ export class MMPaySDK {
304
256
  <style>
305
257
  .mmpay-card { max-width: 350px; padding: 16px; }
306
258
  .mmpay-header { color: #1f2937; font-size: 1rem; font-weight: bold; margin-bottom: 8px; }
307
- .mmpay-qr-container { padding: 0; margin: 10px auto; display: inline-block; line-height: 0; width: 300px; height: 300px; }
259
+ .mmpay-qr-container { padding: 0; margin: 5px auto 10px auto; display: inline-block; line-height: 0; width: 300px; height: 300px; }
308
260
  #${qrCanvasId} { display: block; background: white; border-radius: 8px; width: 100%; height: 100%; }
309
261
  .mmpay-amount { font-size: 1.2rem; font-weight: 800; color: #1f2937; margin: 0; }
310
262
  .mmpay-separator { border-top: 1px solid #f3f4f6; margin: 12px 0; }
@@ -313,10 +265,28 @@ export class MMPaySDK {
313
265
  .mmpay-detail span { text-align: left; }
314
266
  .mmpay-secure-text { color: #757575; border-radius: 9999px; font-size: 0.8rem; font-weight: 600; display: inline-flex; align-items: center; justify-content: center; }
315
267
  .mmpay-warning { font-size: 0.75rem; color: #9ca3af; font-weight: 500; margin-top: 12px; line-height: 1.5; }
268
+
269
+ .mmpay-timer-badge {
270
+ background-color: #fef2f2;
271
+ color: #b91c1c;
272
+ padding: 4px 10px;
273
+ border-radius: 12px;
274
+ font-weight: 700;
275
+ font-size: 0.85rem;
276
+ display: inline-flex;
277
+ align-items: center;
278
+ gap: 5px;
279
+ margin: 8px 0;
280
+ border: 1px solid #fee2e2;
281
+ }
282
+ .mmpay-timer-icon {
283
+ width: 14px;
284
+ height: 14px;
285
+ fill: currentColor;
286
+ }
316
287
  </style>
317
288
 
318
289
  <div class="mmpay-card">
319
- <!-- Close Button - Triggers Confirmation Modal -->
320
290
  <button class="mmpay-close-btn" onclick="MMPayCloseModal(false)">
321
291
  <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 16 16">
322
292
  <path d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z"/>
@@ -331,6 +301,14 @@ export class MMPaySDK {
331
301
  ${merchantName} သို့ပေးချေပါ
332
302
  </div>
333
303
 
304
+ <div class="mmpay-timer-badge" id="mmpay-timer-badge">
305
+ <svg class="mmpay-timer-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
306
+ <path d="M8 3.5a.5.5 0 0 0-1 0V9a.5.5 0 0 0 .252.434l3.5 2a.5.5 0 0 0 .496-.868L8 8.71V3.5z"/>
307
+ <path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm7-8A7 7 0 1 1 1 8a7 7 0 0 1 14 0z"/>
308
+ </svg>
309
+ <span id="mmpay-countdown-text">05:00</span>
310
+ </div>
311
+
334
312
  <div class="mmpay-amount">${amountDisplay}</div>
335
313
 
336
314
  <div class="mmpay-qr-container">
@@ -363,17 +341,11 @@ export class MMPaySDK {
363
341
  this._createAndRenderModal(qrContentHtml, false);
364
342
  this._injectQrScript(qrData, qrCanvasId);
365
343
  }
366
- /**
367
- * _showTerminalMessage
368
- * @param {string} orderId
369
- * @param {string} status
370
- * @param {string} message
371
- */
372
344
  _showTerminalMessage(orderId, status, message) {
373
345
  this._cleanupModal(true);
374
- const successColor = '#10b981'; // Tailwind Green 500
375
- const failureColor = '#ef4444'; // Tailwind Red 500
376
- const expiredColor = '#f59e0b'; // Tailwind Amber 500
346
+ const successColor = '#10b981';
347
+ const failureColor = '#ef4444';
348
+ const expiredColor = '#f59e0b';
377
349
  let color;
378
350
  let iconSvg;
379
351
  let statusText;
@@ -385,7 +357,6 @@ export class MMPaySDK {
385
357
  </svg>`;
386
358
  }
387
359
  else {
388
- // Shared icon for FAILED and EXPIRED (X mark)
389
360
  color = status === 'FAILED' ? failureColor : expiredColor;
390
361
  statusText = status === 'FAILED' ? 'မအောင်မြင်' : 'သက်တမ်းကုန်';
391
362
  iconSvg = `<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" fill="${color}" viewBox="0 0 16 16">
@@ -409,16 +380,17 @@ export class MMPaySDK {
409
380
  </button>
410
381
  </div>
411
382
  `;
412
- this._createAndRenderModal(content, true); // Set isTerminal=true so the close button always forces cleanup
383
+ this._createAndRenderModal(content, true);
413
384
  }
414
- /**
415
- * _showCancelConfirmationModal
416
- */
417
385
  _showCancelConfirmationModal() {
418
386
  if (this.pollIntervalId !== undefined) {
419
387
  window.clearInterval(this.pollIntervalId);
420
388
  this.pollIntervalId = undefined;
421
389
  }
390
+ if (this.countdownIntervalId !== undefined) {
391
+ window.clearInterval(this.countdownIntervalId);
392
+ this.countdownIntervalId = undefined;
393
+ }
422
394
  this._cleanupModal(false);
423
395
  const content = `
424
396
  <div class="mmpay-card mmpay-terminal-card" style="
@@ -440,11 +412,8 @@ export class MMPaySDK {
440
412
  </div>
441
413
  </div>
442
414
  `;
443
- this._createAndRenderModal(content, false); // Set isTerminal=false so the close button calls MMPayCloseModal(true)
415
+ this._createAndRenderModal(content, false);
444
416
  }
445
- /**
446
- * _reRenderPendingModalInstance
447
- */
448
417
  _reRenderPendingModalInstance() {
449
418
  if (this.pendingApiResponse && this.pendingPaymentPayload && this.onCompleteCallback) {
450
419
  this._cleanupModal(true);
@@ -454,15 +423,15 @@ export class MMPaySDK {
454
423
  this._cleanupModal(true);
455
424
  }
456
425
  }
457
- /**
458
- * Cleans up the modal and stops polling.
459
- * @param {boolean} restoreBodyScroll
460
- */
461
426
  _cleanupModal(restoreBodyScroll) {
462
427
  if (this.pollIntervalId !== undefined) {
463
428
  window.clearInterval(this.pollIntervalId);
464
429
  this.pollIntervalId = undefined;
465
430
  }
431
+ if (this.countdownIntervalId !== undefined) {
432
+ window.clearInterval(this.countdownIntervalId);
433
+ this.countdownIntervalId = undefined;
434
+ }
466
435
  if (this.overlayElement && this.overlayElement.parentNode) {
467
436
  this.overlayElement.parentNode.removeChild(this.overlayElement);
468
437
  this.overlayElement = null;
@@ -473,11 +442,6 @@ export class MMPaySDK {
473
442
  delete window.MMPayCloseModal;
474
443
  delete window.MMPayReRenderModal;
475
444
  }
476
- /**
477
- * _injectQrScript
478
- * @param {string} qrData
479
- * @param {string} qrCanvasId
480
- */
481
445
  _injectQrScript(qrData, qrCanvasId) {
482
446
  const script = document.createElement('script');
483
447
  script.src = "https://cdn.jsdelivr.net/npm/qrious@4.0.2/dist/qrious.min.js";
@@ -487,7 +451,7 @@ export class MMPaySDK {
487
451
  if (typeof QRious !== 'undefined' && canvas) {
488
452
  new QRious({
489
453
  element: canvas,
490
- value: qrData,
454
+ value: unescape(encodeURIComponent(qrData)),
491
455
  size: this.QR_SIZE,
492
456
  padding: 15,
493
457
  level: 'H'
@@ -500,16 +464,6 @@ export class MMPaySDK {
500
464
  };
501
465
  document.head.appendChild(script);
502
466
  }
503
- /**
504
- * _startPolling
505
- * @param {IPollingRequest} payload
506
- * @param {number} payload.amount
507
- * @param {string} payload.currency
508
- * @param {string} payload.orderId
509
- * @param {string} payload.nonce
510
- * @param {string} payload.callbackUrl
511
- * @param {Function} onComplete
512
- */
513
467
  async _startPolling(payload, onComplete) {
514
468
  if (this.pollIntervalId !== undefined) {
515
469
  window.clearInterval(this.pollIntervalId);
@@ -543,6 +497,28 @@ export class MMPaySDK {
543
497
  checkStatus();
544
498
  this.pollIntervalId = window.setInterval(checkStatus, this.POLL_INTERVAL_MS);
545
499
  }
500
+ _startCountdown(orderId) {
501
+ if (this.countdownIntervalId !== undefined) {
502
+ window.clearInterval(this.countdownIntervalId);
503
+ }
504
+ let remaining = this.TIMEOUT_SECONDS;
505
+ const timerElement = document.getElementById('mmpay-countdown-text');
506
+ const updateDisplay = () => {
507
+ if (!timerElement)
508
+ return;
509
+ const minutes = Math.floor(remaining / 60);
510
+ const seconds = remaining % 60;
511
+ timerElement.innerText = `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
512
+ };
513
+ this.countdownIntervalId = window.setInterval(() => {
514
+ remaining--;
515
+ updateDisplay();
516
+ if (remaining <= 0) {
517
+ window.clearInterval(this.countdownIntervalId);
518
+ this.countdownIntervalId = undefined;
519
+ this._showTerminalMessage(orderId, 'EXPIRED', 'သတ်မှတ်ချိန်ကုန်သွားပါပြီ။');
520
+ }
521
+ }, 1000);
522
+ }
546
523
  }
547
- // Make the SDK class and its instance methods accessible globally
548
524
  window.MMPaySDK = MMPaySDK;