mmpay-browser-sdk 1.0.3

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.
package/src/index.ts ADDED
@@ -0,0 +1,529 @@
1
+ export interface PaymentData {
2
+ amount: number;
3
+ currency: string;
4
+ orderId: string;
5
+ callbackUrl?: string;
6
+ }
7
+ export interface CreatePaymentResponse {
8
+ _id: string;
9
+ amount: number;
10
+ orderId: string;
11
+ currency: string;
12
+ transactionId: string;
13
+ qr: string;
14
+ url: string;
15
+ }
16
+ export interface PollingResponse {
17
+ _id: string;
18
+ appId: string;
19
+ orderId: string;
20
+ amount: number;
21
+ currency: string;
22
+ method?: string;
23
+ vendor?: string;
24
+ callbackUrl?: string;
25
+ callbackUrlStatus?: 'PENDING' | 'SUCCESS' | 'FAILED';
26
+ callbackAt?: Date;
27
+ disbursementStatus?: 'NONE' | 'PENDING' | 'COMPLETED' | 'FAILED' | 'CANCELLED';
28
+ disburseAt?: Date;
29
+ items: {name: string, amount: number, quantity: number}[];
30
+ merchantId: string;
31
+ status: 'PENDING' | 'SUCCESS' | 'FAILED' | 'EXPIRED';
32
+ createdAt: Date;
33
+ transactionRefId?: string;
34
+ qr?: string;
35
+ redirectUrl?: string;
36
+ }
37
+ export interface PolliongResult {
38
+ success: boolean;
39
+ transaction: PollingResponse;
40
+ }
41
+ export interface SDKOptions {
42
+ pollInterval?: number;
43
+ environment?: 'sandbox' | 'production';
44
+ baseUrl?: string;
45
+ merchantName?: string;
46
+ }
47
+
48
+ declare const QRious: any;
49
+ declare const window: Window & {
50
+ MMPayDownloadQR: () => void;
51
+ MMPayCloseModal: (forceClose?: boolean) => void;
52
+ MMPayReRenderModal: () => void;
53
+ setInterval: (handler: TimerHandler, timeout?: number) => number;
54
+ clearInterval: (id: number) => void;
55
+ };
56
+
57
+ export class MMPaySDK {
58
+
59
+ private POLL_INTERVAL_MS: number;
60
+ private publishableKey: string;
61
+ private baseUrl: string;
62
+ private merchantName: string;
63
+ private environment: 'sandbox' | 'production';
64
+ private pollIntervalId: number | undefined = undefined;
65
+ private onCompleteCallback: ((result: PolliongResult) => void) | null = null;
66
+ private overlayElement: HTMLDivElement | null = null;
67
+
68
+ // Properties to store pending data for re-rendering after cancel attempt
69
+ private pendingApiResponse: CreatePaymentResponse | null = null;
70
+ private pendingPaymentPayload: PaymentData | null = null;
71
+
72
+ private readonly QR_SIZE: number = 300;
73
+
74
+ constructor(publishableKey: string, options: SDKOptions = {}) {
75
+ if (!publishableKey) {
76
+ throw new Error("A Publishable Key is required to initialize [MMPaySDK].");
77
+ }
78
+ this.publishableKey = publishableKey;
79
+ this.environment = options.environment || 'production';
80
+ this.baseUrl = options.baseUrl || 'https://api.mm-pay.com';
81
+ this.merchantName = options.merchantName || 'Your Merchant';
82
+ this.POLL_INTERVAL_MS = options.pollInterval || 3000;
83
+ }
84
+ /**
85
+ * _callApi
86
+ * @param endpoint
87
+ * @param data
88
+ * @returns
89
+ */
90
+ private async _callApi<T>(endpoint: string, data: object = {}): Promise<T> {
91
+ const response = await fetch(`${this.baseUrl}${endpoint}`, {
92
+ method: 'POST',
93
+ headers: {
94
+ 'Content-Type': 'application/json',
95
+ 'Authorization': `Bearer ${this.publishableKey}`
96
+ },
97
+ body: JSON.stringify(data)
98
+ });
99
+ if (!response.ok) {
100
+ const errorText = await response.text();
101
+ throw new Error(`API error (${response.status}): ${response.statusText}. Details: ${errorText}`);
102
+ }
103
+ return response.json() as Promise<T>;
104
+ }
105
+ /**
106
+ * createPaymentRequest
107
+ * @param {PaymentData} payload
108
+ * @returns
109
+ */
110
+ async createPaymentRequest(payload: PaymentData): Promise<CreatePaymentResponse> {
111
+ try {
112
+ const endpoint = this.environment === 'sandbox'
113
+ ? '/xpayments/sandbox-payment-create'
114
+ : '/xpayments/production-payment-create';
115
+
116
+ return await this._callApi<CreatePaymentResponse>(endpoint, payload);
117
+ } catch (error) {
118
+ console.error("Payment request failed:", error);
119
+ throw error;
120
+ }
121
+ }
122
+ /**
123
+ * _createAndRenderModal
124
+ * @param {string} contentHtml
125
+ * @param isTerminal
126
+ * @returns
127
+ */
128
+ private _createAndRenderModal(contentHtml: string, isTerminal: boolean = false): HTMLDivElement {
129
+ this._cleanupModal(false);
130
+ const overlay = document.createElement('div');
131
+ overlay.id = 'mmpay-full-modal';
132
+ document.body.appendChild(overlay);
133
+ this.overlayElement = overlay;
134
+ const style = document.createElement('style');
135
+ style.innerHTML = `
136
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&family=Padauk:wght@400;700&display=swap');
137
+
138
+ #mmpay-full-modal {
139
+ position: fixed;
140
+ top: 0;
141
+ left: 0;
142
+ width: 100vw;
143
+ height: 100vh;
144
+ background-color: rgba(0, 0, 0, 0.85);
145
+ z-index: 9999;
146
+ display: flex;
147
+ align-items: center;
148
+ justify-content: center;
149
+ transition: opacity 0.3s;
150
+ padding: 15px;
151
+ box-sizing: border-box;
152
+ overflow: auto;
153
+ }
154
+ .mmpay-overlay-content {
155
+ display: flex;
156
+ align-items: center;
157
+ justify-content: center;
158
+ min-height: 100%;
159
+ width: 100%;
160
+ padding: 20px 0;
161
+ }
162
+ /* Card Base Styles */
163
+ .mmpay-card {
164
+ background: #ffffff;
165
+ border-radius: 16px;
166
+ box-shadow: 0 20px 40px -10px rgba(0, 0, 0, 0.4);
167
+ text-align: center;
168
+ font-family: 'Inter', 'Padauk', sans-serif;
169
+ border: 1px solid #f3f4f6;
170
+ animation: fadeInScale 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
171
+ box-sizing: border-box;
172
+ position: relative;
173
+ width: min(90vw, 330px);
174
+ margin: auto;
175
+ }
176
+ @keyframes fadeInScale {
177
+ from { opacity: 0; transform: scale(0.9); }
178
+ to { opacity: 1; transform: scale(1); }
179
+ }
180
+ .mmpay-close-btn {
181
+ position: absolute;
182
+ top: 10px;
183
+ right: 10px;
184
+ background: none;
185
+ border: none;
186
+ cursor: pointer;
187
+ padding: 8px;
188
+ color: #9ca3af;
189
+ border-radius: 50%;
190
+ transition: color 0.2s, background-color 0.2s;
191
+ line-height: 1;
192
+ z-index: 10;
193
+ }
194
+ .mmpay-close-btn:hover {
195
+ color: #4b5563;
196
+ background-color: #f3f4f6;
197
+ }
198
+ .mmpay-button {
199
+ background-color: #4f46e5;
200
+ color: white;
201
+ border: none;
202
+ padding: 10px 20px;
203
+ border-radius: 8px;
204
+ font-size: 0.95rem;
205
+ font-weight: 700;
206
+ cursor: pointer;
207
+ margin-top: 15px;
208
+ transition: background-color 0.2s, box-shadow 0.2s, transform 0.1s;
209
+ box-shadow: 0 5px 15px rgba(79, 70, 229, 0.3);
210
+ width: 100%;
211
+ }
212
+ .mmpay-button:hover {
213
+ background-color: #4338ca;
214
+ box-shadow: 0 8px 18px rgba(67, 56, 202, 0.4);
215
+ transform: translateY(-1px);
216
+ }
217
+ .mmpay-button:active {
218
+ transform: translateY(0);
219
+ background-color: #3f35c7;
220
+ }
221
+ .mmpay-text-myanmar { font-family: 'Padauk', sans-serif; }
222
+ `;
223
+ overlay.appendChild(style);
224
+ window.MMPayCloseModal = (forceClose = false) => {
225
+ if (isTerminal || forceClose) {
226
+ this._cleanupModal(true);
227
+ } else {
228
+ this._showCancelConfirmationModal();
229
+ }
230
+ };
231
+ window.MMPayReRenderModal = () => this._reRenderPendingModalInstance();
232
+ overlay.innerHTML += `<div class="mmpay-overlay-content">${contentHtml}</div>`;
233
+ document.body.style.overflow = 'hidden'; // FIX: Prevent body scroll when modal is open
234
+ return overlay;
235
+ }
236
+ /**
237
+ * showPaymentModal
238
+ * @param {PaymentData} payload
239
+ * @param {Function} onComplete
240
+ */
241
+ public async showPaymentModal(
242
+ payload: PaymentData,
243
+ onComplete: (result: PolliongResult) => void
244
+ ): Promise<void> {
245
+ const initialContent = `<div class="mmpay-overlay-content"><div style="text-align: center; color: #fff;">ငွေပေးချေမှု စတင်နေသည်...</div></div>`;
246
+ this._createAndRenderModal(initialContent, false);
247
+ this.onCompleteCallback = onComplete;
248
+ try {
249
+ const apiResponse = await this.createPaymentRequest(payload);
250
+ if (apiResponse && apiResponse.qr && apiResponse.transactionId) {
251
+ this.pendingApiResponse = apiResponse;
252
+ this.pendingPaymentPayload = payload;
253
+ this._renderQrModalContent(apiResponse, payload, this.merchantName);
254
+ this._startPolling(apiResponse._id, onComplete);
255
+ } else {
256
+ this._showTerminalMessage(apiResponse.orderId || 'N/A', 'FAILED', 'ငွေပေးချေမှု စတင်ရန် မအောင်မြင်ပါ။ QR ဒေတာ မရရှိပါ။');
257
+ }
258
+ } catch (error) {
259
+ // Myanmar translation for "Error during payment initiation. See console."
260
+ this._showTerminalMessage(payload.orderId || 'N/A', 'FAILED', 'ငွေပေးချေမှု စတင်စဉ် အမှားအယွင်း ဖြစ်ပွားသည်။ ကွန်ဆိုးလ်တွင် ကြည့်ပါ။');
261
+ }
262
+ }
263
+ /**
264
+ * _renderQrModalContent
265
+ * @param {CreatePaymentResponse} apiResponse
266
+ * @param {PaymentData} payload
267
+ * @param {string} merchantName
268
+ */
269
+ private _renderQrModalContent(apiResponse: CreatePaymentResponse, payload: PaymentData, merchantName: string): void {
270
+ const qrData = apiResponse.qr;
271
+ const amountDisplay = `${apiResponse.amount.toFixed(2)} ${apiResponse.currency}`;
272
+ const qrCanvasId = 'mmpayQrCanvas';
273
+ const orderId = payload.orderId;
274
+ window.MMPayDownloadQR = function () {
275
+ const canvas = document.getElementById(qrCanvasId) as HTMLCanvasElement;
276
+ if (!canvas) return;
277
+ try {
278
+ const dataURL = canvas.toDataURL('image/png');
279
+ const link = document.createElement('a');
280
+ link.href = dataURL;
281
+ link.download = `MMPay-QR-${orderId}.png`;
282
+ document.body.appendChild(link);
283
+ link.click();
284
+ document.body.removeChild(link);
285
+ } catch (e) {
286
+ console.error("Failed to download QR image:", e);
287
+ }
288
+ }
289
+ const qrContentHtml = `
290
+ <style>
291
+ .mmpay-card { max-width: 350px; padding: 16px; }
292
+ .mmpay-header { color: #1f2937; font-size: 1rem; font-weight: bold; margin-bottom: 8px; }
293
+ .mmpay-qr-container { padding: 0; margin: 10px auto; display: inline-block; line-height: 0; width: 300px; height: 300px; }
294
+ #${qrCanvasId} { display: block; background: white; border-radius: 8px; width: 100%; height: 100%; }
295
+ .mmpay-amount { font-size: 1.2rem; font-weight: 800; color: #1f2937; margin: 0; }
296
+ .mmpay-separator { border-top: 1px solid #f3f4f6; margin: 12px 0; }
297
+ .mmpay-detail { font-size: 0.8rem; color: #6b7280; margin: 3px 0; display: flex; justify-content: space-between; align-items: center; padding: 0 5px; }
298
+ .mmpay-detail strong { color: #374151; font-weight: 600; text-align: right; }
299
+ .mmpay-detail span { text-align: left; }
300
+ .mmpay-secure-text { color: #757575; border-radius: 9999px; font-size: 0.8rem; font-weight: 600; display: inline-flex; align-items: center; justify-content: center; }
301
+ .mmpay-warning { font-size: 0.75rem; color: #9ca3af; font-weight: 500; margin-top: 12px; line-height: 1.5; }
302
+ </style>
303
+
304
+ <div class="mmpay-card">
305
+ <!-- Close Button - Triggers Confirmation Modal -->
306
+ <button class="mmpay-close-btn" onclick="MMPayCloseModal(false)">
307
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 16 16">
308
+ <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"/>
309
+ </svg>
310
+ </button>
311
+
312
+ <div style="padding:0px auto 16px auto">
313
+ <img src="https://upload.wikimedia.org/wikipedia/commons/2/2f/MMQR_Logo.svg" style="width:40px">
314
+ </div>
315
+
316
+ <div class="mmpay-header mmpay-text-myanmar">
317
+ ${merchantName} သို့ပေးချေပါ
318
+ </div>
319
+
320
+ <div class="mmpay-amount">${amountDisplay}</div>
321
+
322
+ <div class="mmpay-qr-container">
323
+ <canvas id="${qrCanvasId}" width="${this.QR_SIZE}" height="${this.QR_SIZE}"></canvas>
324
+ </div>
325
+
326
+ <button class="mmpay-button mmpay-text-myanmar" onclick="MMPayDownloadQR()">
327
+ QR ကုဒ်ကို ဒေါင်းလုဒ်လုပ်ပါ
328
+ </button>
329
+
330
+ <div class="mmpay-separator"></div>
331
+
332
+ <div class="mmpay-detail">
333
+ <span class="mmpay-text-myanmar">မှာယူမှုနံပါတ်:</span> <strong>${apiResponse.orderId}</strong>
334
+ </div>
335
+ <div class="mmpay-detail">
336
+ <span class="mmpay-text-myanmar">ငွေပေးငွေယူနံပါတ်:</span> <strong>${apiResponse.transactionId}</strong>
337
+ </div>
338
+
339
+ <p class="mmpay-warning mmpay-text-myanmar">
340
+ ကျေးဇူးပြု၍ သင့်ဖုန်းဖြင့် ငွေပေးချေမှုကို အပြီးသတ်ပေးပါ။
341
+ </p>
342
+
343
+ <div class="mmpay-secure-text">
344
+ လုံခြုံသော ငွေပေးချေမှု
345
+ </div>
346
+ </div>
347
+ `;
348
+ this._cleanupModal(false);
349
+ this._createAndRenderModal(qrContentHtml, false);
350
+ this._injectQrScript(qrData, qrCanvasId);
351
+ }
352
+ /**
353
+ * _showTerminalMessage
354
+ * @param {string} orderId
355
+ * @param {string} status
356
+ * @param {string} message
357
+ */
358
+ private _showTerminalMessage(orderId: string, status: 'SUCCESS' | 'FAILED' | 'EXPIRED', message: string): void {
359
+ this._cleanupModal(true);
360
+ const successColor = '#10b981'; // Tailwind Green 500
361
+ const failureColor = '#ef4444'; // Tailwind Red 500
362
+ const expiredColor = '#f59e0b'; // Tailwind Amber 500
363
+ let color: string;
364
+ let iconSvg: string;
365
+ let statusText: string;
366
+ if (status === 'SUCCESS') {
367
+ color = successColor;
368
+ statusText = 'အောင်မြင်';
369
+ iconSvg = `<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" fill="${color}" viewBox="0 0 16 16">
370
+ <path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-3.97-3.03a.75.75 0 0 0-1.08.022l-3.473 4.425-2.094-2.094a.75.75 0 0 0-1.06 1.06L6.92 10.865l.764.764a.75.75 0 0 0 1.06 0l4.5-5.5a.75.75 0 0 0-.01-1.05z"/>
371
+ </svg>`;
372
+ } else {
373
+ // Shared icon for FAILED and EXPIRED (X mark)
374
+ color = status === 'FAILED' ? failureColor : expiredColor;
375
+ statusText = status === 'FAILED' ? 'မအောင်မြင်' : 'သက်တမ်းကုန်';
376
+ iconSvg = `<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" fill="${color}" viewBox="0 0 16 16">
377
+ <path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.354 4.146a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293 5.354 4.146z"/>
378
+ </svg>`;
379
+ }
380
+
381
+ const content = `
382
+ <div class="mmpay-card mmpay-terminal-card" style="
383
+ background: white; padding: 25px; box-sizing: border-box;
384
+ ">
385
+ <div style="margin-bottom: 20px;">${iconSvg}</div>
386
+
387
+ <h2 style="font-size: 1.5rem; font-weight: 800; color: ${color}; margin-bottom: 10px;">
388
+ ငွေပေးချေမှု ${statusText}
389
+ </h2>
390
+ <p style="color: #4b5563; font-size: 0.95rem; font-weight: 600;">မှာယူမှုနံပါတ်: ${orderId}</p>
391
+ <p style="color: #6b7280; margin-top: 15px; margin-bottom: 25px; font-size: 0.9rem;">${message}</p>
392
+
393
+ <button class="mmpay-button mmpay-text-myanmar" style="background-color: ${color};" onclick="MMPayCloseModal(true)">
394
+ ပိတ်မည်
395
+ </button>
396
+ </div>
397
+ `;
398
+ this._createAndRenderModal(content, true); // Set isTerminal=true so the close button always forces cleanup
399
+ }
400
+ /**
401
+ * _showCancelConfirmationModal
402
+ */
403
+ private _showCancelConfirmationModal(): void {
404
+ if (this.pollIntervalId !== undefined) {
405
+ window.clearInterval(this.pollIntervalId);
406
+ this.pollIntervalId = undefined;
407
+ }
408
+ this._cleanupModal(false);
409
+ const content = `
410
+ <div class="mmpay-card mmpay-terminal-card" style="
411
+ background: white; padding: 25px; box-sizing: border-box;
412
+ ">
413
+ <h2 style="font-size: 1.25rem; font-weight: 800; color: #f59e0b; margin-bottom: 10px;">
414
+ ငွေပေးချေမှုကို ပယ်ဖျက်မည်လား။
415
+ </h2>
416
+ <p style="color: #6b7280; margin-top: 15px; margin-bottom: 25px; font-size: 0.9rem;">
417
+ သင်သည် QR ဖြင့် ငွေပေးချေခြင်း မပြုရသေးကြောင်း သေချာပါသလား။ ပယ်ဖျက်ပြီးပါက ပြန်လည် စတင်ရပါမည်။
418
+ </p>
419
+
420
+ <div style="display: flex; gap: 10px;">
421
+ <button class="mmpay-button mmpay-text-myanmar"
422
+ style="flex-grow: 1; background-color: #f3f4f6; color: #1f2937; box-shadow: none; margin-top: 0;"
423
+ onclick="MMPayCloseModal(true)">
424
+ ပယ်ဖျက်မည်
425
+ </button>
426
+ </div>
427
+ </div>
428
+ `;
429
+ this._createAndRenderModal(content, false); // Set isTerminal=false so the close button calls MMPayCloseModal(true)
430
+ }
431
+ /**
432
+ * _reRenderPendingModalInstance
433
+ */
434
+ private _reRenderPendingModalInstance(): void {
435
+ if (this.pendingApiResponse && this.pendingPaymentPayload && this.onCompleteCallback) {
436
+ this._cleanupModal(true);
437
+ this.showPaymentModal(this.pendingPaymentPayload, this.onCompleteCallback);
438
+ } else {
439
+ this._cleanupModal(true);
440
+ }
441
+ }
442
+ /**
443
+ * Cleans up the modal and stops polling.
444
+ * @param restoreBodyScroll
445
+ */
446
+ private _cleanupModal(restoreBodyScroll: boolean): void {
447
+ if (this.pollIntervalId !== undefined) {
448
+ window.clearInterval(this.pollIntervalId);
449
+ this.pollIntervalId = undefined;
450
+ }
451
+ if (this.overlayElement && this.overlayElement.parentNode) {
452
+ this.overlayElement.parentNode.removeChild(this.overlayElement);
453
+ this.overlayElement = null;
454
+ }
455
+ if (restoreBodyScroll) {
456
+ document.body.style.overflow = '';
457
+ }
458
+ delete window.MMPayCloseModal;
459
+ delete window.MMPayReRenderModal;
460
+ }
461
+ /**
462
+ * _injectQrScript
463
+ * @param {string} qrData
464
+ * @param {string} qrCanvasId
465
+ */
466
+ private _injectQrScript(qrData: string, qrCanvasId: string): void {
467
+ const script = document.createElement('script');
468
+ script.src = "https://cdn.jsdelivr.net/npm/qrious@4.0.2/dist/qrious.min.js";
469
+ script.onload = () => {
470
+ setTimeout(() => {
471
+ const canvas = document.getElementById(qrCanvasId);
472
+ if (typeof QRious !== 'undefined' && canvas) {
473
+ new QRious({
474
+ element: canvas,
475
+ value: qrData,
476
+ size: this.QR_SIZE,
477
+ padding: 15,
478
+ level: 'H'
479
+ });
480
+ } else {
481
+ console.error('Failed to load QRious or find canvas.');
482
+ }
483
+ }, 10);
484
+ };
485
+ document.head.appendChild(script);
486
+ }
487
+ /**
488
+ * _startPolling
489
+ * @param {string} _id
490
+ * @param {Function} onComplete
491
+ */
492
+ private async _startPolling(_id: string, onComplete: (result: PolliongResult) => void): Promise<void> {
493
+ if (this.pollIntervalId !== undefined) {
494
+ window.clearInterval(this.pollIntervalId);
495
+ }
496
+ const checkStatus = async () => {
497
+ try {
498
+ const endpoint = this.environment === 'sandbox'
499
+ ? '/xpayments/sandbox-payment-polling'
500
+ : '/xpayments/production-payment-polling';
501
+
502
+ const response = await this._callApi<PollingResponse>(endpoint, {_id: _id});
503
+ const status = (response.status || '').toUpperCase();
504
+ if (status === 'SUCCESS' || status === 'FAILED' || status === 'EXPIRED') {
505
+ window.clearInterval(this.pollIntervalId);
506
+ this.pollIntervalId = undefined;
507
+ const success = status === 'SUCCESS';
508
+ const message = success ?
509
+ `ငွေပေးချေမှု အောင်မြင်ပါပြီ။ ငွေပေးငွေယူ ရည်ညွှန်းနံပါတ်: ${response.transactionRefId || 'N/A'}` :
510
+ `ငွေပေးချေမှု ${status === 'FAILED' ? 'မအောင်မြင်ပါ' : 'သက်တမ်းကုန်သွားပါပြီ'}.`;
511
+
512
+ this._showTerminalMessage(response.orderId || 'N/A', status as 'SUCCESS' | 'FAILED' | 'EXPIRED', message);
513
+
514
+ if (onComplete) {
515
+ onComplete({success: success, transaction: response as PollingResponse});
516
+ }
517
+ return;
518
+ }
519
+ } catch (error) {
520
+ console.error("Polling error:", error);
521
+ }
522
+ };
523
+ checkStatus();
524
+ this.pollIntervalId = window.setInterval(checkStatus, this.POLL_INTERVAL_MS);
525
+ }
526
+ }
527
+
528
+ // Make the SDK class and its instance methods accessible globally
529
+ (window as any).MMPaySDK = MMPaySDK;