mmpay-browser-sdk 1.0.3 → 1.0.5
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/README.md +130 -102
- package/dist/cjs/index.d.ts +84 -42
- package/dist/cjs/index.js +130 -47
- package/dist/esm/index.d.ts +84 -42
- package/dist/esm/index.js +130 -47
- package/dist/mmpay-sdk.js +130 -47
- package/dist/mmpay-sdk.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +174 -76
- package/test/home.html +35 -23
package/src/index.ts
CHANGED
|
@@ -1,42 +1,50 @@
|
|
|
1
|
-
export interface
|
|
1
|
+
export interface ICorePayParams {
|
|
2
2
|
amount: number;
|
|
3
|
-
currency: string;
|
|
4
3
|
orderId: string;
|
|
5
4
|
callbackUrl?: string;
|
|
6
5
|
}
|
|
7
|
-
|
|
6
|
+
|
|
7
|
+
export interface ICreatePaymentRequestParams {
|
|
8
|
+
amount: number;
|
|
9
|
+
currency?: string;
|
|
10
|
+
orderId: string;
|
|
11
|
+
callbackUrl?: string;
|
|
12
|
+
nonce?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface ICreatePaymentResponse {
|
|
8
15
|
_id: string;
|
|
9
16
|
amount: number;
|
|
10
17
|
orderId: string;
|
|
11
|
-
currency
|
|
12
|
-
|
|
18
|
+
currency?: string;
|
|
19
|
+
transactionRefId: string;
|
|
13
20
|
qr: string;
|
|
14
|
-
url: string;
|
|
15
21
|
}
|
|
16
|
-
export interface
|
|
17
|
-
|
|
18
|
-
|
|
22
|
+
export interface ICreateTokenRequestParams {
|
|
23
|
+
amount: number;
|
|
24
|
+
currency?: string;
|
|
25
|
+
orderId: string;
|
|
26
|
+
callbackUrl?: string;
|
|
27
|
+
nonce?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface ICreateTokenResponse {
|
|
19
30
|
orderId: string;
|
|
31
|
+
token: string;
|
|
32
|
+
}
|
|
33
|
+
export interface IPollingRequest {
|
|
20
34
|
amount: number;
|
|
21
|
-
currency
|
|
22
|
-
|
|
23
|
-
vendor?: string;
|
|
35
|
+
currency?: string;
|
|
36
|
+
orderId: string;
|
|
24
37
|
callbackUrl?: string;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
merchantId: string;
|
|
38
|
+
nonce?: string;
|
|
39
|
+
}
|
|
40
|
+
export interface IPollingResponse {
|
|
41
|
+
orderId: string;
|
|
42
|
+
transactionRefId: string;
|
|
31
43
|
status: 'PENDING' | 'SUCCESS' | 'FAILED' | 'EXPIRED';
|
|
32
|
-
createdAt: Date;
|
|
33
|
-
transactionRefId?: string;
|
|
34
|
-
qr?: string;
|
|
35
|
-
redirectUrl?: string;
|
|
36
44
|
}
|
|
37
45
|
export interface PolliongResult {
|
|
38
46
|
success: boolean;
|
|
39
|
-
transaction:
|
|
47
|
+
transaction: IPollingResponse;
|
|
40
48
|
}
|
|
41
49
|
export interface SDKOptions {
|
|
42
50
|
pollInterval?: number;
|
|
@@ -57,6 +65,7 @@ declare const window: Window & {
|
|
|
57
65
|
export class MMPaySDK {
|
|
58
66
|
|
|
59
67
|
private POLL_INTERVAL_MS: number;
|
|
68
|
+
private tokenKey: string;
|
|
60
69
|
private publishableKey: string;
|
|
61
70
|
private baseUrl: string;
|
|
62
71
|
private merchantName: string;
|
|
@@ -65,12 +74,16 @@ export class MMPaySDK {
|
|
|
65
74
|
private onCompleteCallback: ((result: PolliongResult) => void) | null = null;
|
|
66
75
|
private overlayElement: HTMLDivElement | null = null;
|
|
67
76
|
|
|
68
|
-
|
|
69
|
-
private
|
|
70
|
-
private pendingPaymentPayload: PaymentData | null = null;
|
|
77
|
+
private pendingApiResponse: ICreatePaymentResponse | null = null;
|
|
78
|
+
private pendingPaymentPayload: ICreatePaymentRequestParams | null = null;
|
|
71
79
|
|
|
72
|
-
private readonly QR_SIZE: number =
|
|
80
|
+
private readonly QR_SIZE: number = 290;
|
|
73
81
|
|
|
82
|
+
/**
|
|
83
|
+
* constructor
|
|
84
|
+
* @param publishableKey
|
|
85
|
+
* @param options
|
|
86
|
+
*/
|
|
74
87
|
constructor(publishableKey: string, options: SDKOptions = {}) {
|
|
75
88
|
if (!publishableKey) {
|
|
76
89
|
throw new Error("A Publishable Key is required to initialize [MMPaySDK].");
|
|
@@ -79,7 +92,7 @@ export class MMPaySDK {
|
|
|
79
92
|
this.environment = options.environment || 'production';
|
|
80
93
|
this.baseUrl = options.baseUrl || 'https://api.mm-pay.com';
|
|
81
94
|
this.merchantName = options.merchantName || 'Your Merchant';
|
|
82
|
-
this.POLL_INTERVAL_MS = options.pollInterval ||
|
|
95
|
+
this.POLL_INTERVAL_MS = options.pollInterval || 5000;
|
|
83
96
|
}
|
|
84
97
|
/**
|
|
85
98
|
* _callApi
|
|
@@ -88,12 +101,20 @@ export class MMPaySDK {
|
|
|
88
101
|
* @returns
|
|
89
102
|
*/
|
|
90
103
|
private async _callApi<T>(endpoint: string, data: object = {}): Promise<T> {
|
|
104
|
+
let config: any = {
|
|
105
|
+
'Content-Type': 'application/json',
|
|
106
|
+
'Authorization': `Bearer ${this.publishableKey}`
|
|
107
|
+
}
|
|
108
|
+
if (this.tokenKey) {
|
|
109
|
+
config = {
|
|
110
|
+
'Content-Type': 'application/json',
|
|
111
|
+
'Authorization': `Bearer ${this.publishableKey}`,
|
|
112
|
+
'X-MMPay-Btoken': `${this.tokenKey}`
|
|
113
|
+
}
|
|
114
|
+
}
|
|
91
115
|
const response = await fetch(`${this.baseUrl}${endpoint}`, {
|
|
92
116
|
method: 'POST',
|
|
93
|
-
headers:
|
|
94
|
-
'Content-Type': 'application/json',
|
|
95
|
-
'Authorization': `Bearer ${this.publishableKey}`
|
|
96
|
-
},
|
|
117
|
+
headers: config,
|
|
97
118
|
body: JSON.stringify(data)
|
|
98
119
|
});
|
|
99
120
|
if (!response.ok) {
|
|
@@ -103,26 +124,125 @@ export class MMPaySDK {
|
|
|
103
124
|
return response.json() as Promise<T>;
|
|
104
125
|
}
|
|
105
126
|
/**
|
|
106
|
-
*
|
|
107
|
-
* @param {
|
|
108
|
-
* @
|
|
127
|
+
* _callApiTokenRequest
|
|
128
|
+
* @param {ICreateTokenRequestParams} payload
|
|
129
|
+
* @param {number} payload.amount
|
|
130
|
+
* @param {string} payload.currency
|
|
131
|
+
* @param {string} payload.orderId
|
|
132
|
+
* @param {string} payload.nonce
|
|
133
|
+
* @param {string} payload.callbackUrl
|
|
134
|
+
* @returns {Promise<ICreateTokenResponse>}
|
|
135
|
+
*/
|
|
136
|
+
private async _callApiTokenRequest(payload: ICreateTokenRequestParams): Promise<ICreateTokenResponse> {
|
|
137
|
+
try {
|
|
138
|
+
const endpoint = this.environment === 'sandbox'
|
|
139
|
+
? '/xpayments/sandbox-token-request'
|
|
140
|
+
: '/xpayments/production-token-request';
|
|
141
|
+
return await this._callApi<ICreateTokenResponse>(endpoint, payload);
|
|
142
|
+
} catch (error) {
|
|
143
|
+
console.error("Token request failed:", error);
|
|
144
|
+
throw error;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* _callApiPaymentRequest
|
|
149
|
+
* @param {ICreatePaymentRequestParams} payload
|
|
150
|
+
* @param {number} payload.amount
|
|
151
|
+
* @param {string} payload.currency
|
|
152
|
+
* @param {string} payload.orderId
|
|
153
|
+
* @param {string} payload.nonce
|
|
154
|
+
* @param {string} payload.callbackUrl
|
|
155
|
+
* @returns {Promise<ICreatePaymentResponse>}
|
|
109
156
|
*/
|
|
110
|
-
async
|
|
157
|
+
private async _callApiPaymentRequest(payload: ICreatePaymentRequestParams): Promise<ICreatePaymentResponse> {
|
|
111
158
|
try {
|
|
112
159
|
const endpoint = this.environment === 'sandbox'
|
|
113
160
|
? '/xpayments/sandbox-payment-create'
|
|
114
161
|
: '/xpayments/production-payment-create';
|
|
162
|
+
return await this._callApi<ICreatePaymentResponse>(endpoint, payload);
|
|
163
|
+
} catch (error) {
|
|
164
|
+
console.error("Payment request failed:", error);
|
|
165
|
+
throw error;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
115
168
|
|
|
116
|
-
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* createPayment
|
|
173
|
+
* @param {ICorePayParams} params
|
|
174
|
+
* @param {number} params.amount
|
|
175
|
+
* @param {string} params.orderId
|
|
176
|
+
* @param {string} params.callbackUrl
|
|
177
|
+
* @returns {Promise<ICreatePaymentResponse>}
|
|
178
|
+
*/
|
|
179
|
+
public async createPayment(params: ICorePayParams): Promise<ICreatePaymentResponse> {
|
|
180
|
+
const payload: ICreatePaymentRequestParams = {
|
|
181
|
+
amount: params.amount,
|
|
182
|
+
orderId: params.orderId,
|
|
183
|
+
callbackUrl: params.callbackUrl,
|
|
184
|
+
currency: 'MMK',
|
|
185
|
+
nonce: new Date().getTime().toString() + '_mmp'
|
|
186
|
+
}
|
|
187
|
+
try {
|
|
188
|
+
const tokenResponse = await this._callApiTokenRequest(payload);
|
|
189
|
+
this.tokenKey = tokenResponse.token as string;
|
|
190
|
+
const apiResponse = await this._callApiPaymentRequest(payload);
|
|
191
|
+
return apiResponse
|
|
117
192
|
} catch (error) {
|
|
118
193
|
console.error("Payment request failed:", error);
|
|
119
194
|
throw error;
|
|
120
195
|
}
|
|
121
196
|
}
|
|
197
|
+
/**
|
|
198
|
+
* showPaymentModal
|
|
199
|
+
* @param {ICorePayParams} params
|
|
200
|
+
* @param {number} params.amount
|
|
201
|
+
* @param {string} params.orderId
|
|
202
|
+
* @param {string} params.callbackUrl
|
|
203
|
+
* @param {Function} onComplete
|
|
204
|
+
*/
|
|
205
|
+
public async showPaymentModal(
|
|
206
|
+
params: ICorePayParams,
|
|
207
|
+
onComplete: (result: PolliongResult) => void
|
|
208
|
+
): Promise<void> {
|
|
209
|
+
const initialContent = `<div class="mmpay-overlay-content"><div style="text-align: center; color: #fff;">ငွေပေးချေမှု စတင်နေသည်...</div></div>`;
|
|
210
|
+
this._createAndRenderModal(initialContent, false);
|
|
211
|
+
this.onCompleteCallback = onComplete;
|
|
212
|
+
const payload: ICreatePaymentRequestParams = {
|
|
213
|
+
amount: params.amount,
|
|
214
|
+
orderId: params.orderId,
|
|
215
|
+
callbackUrl: params.callbackUrl,
|
|
216
|
+
currency: 'MMK',
|
|
217
|
+
nonce: new Date().getTime().toString() + '_mmp'
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
try {
|
|
221
|
+
const tokenResponse = await this._callApiTokenRequest(payload);
|
|
222
|
+
this.tokenKey = tokenResponse.token as string;
|
|
223
|
+
const apiResponse = await this._callApiPaymentRequest(payload);
|
|
224
|
+
if (apiResponse && apiResponse.qr && apiResponse.transactionRefId) {
|
|
225
|
+
this.pendingApiResponse = apiResponse;
|
|
226
|
+
this.pendingPaymentPayload = payload;
|
|
227
|
+
this._renderQrModalContent(apiResponse, payload, this.merchantName);
|
|
228
|
+
this._startPolling(payload, onComplete);
|
|
229
|
+
} else {
|
|
230
|
+
this._showTerminalMessage(apiResponse.orderId || 'N/A', 'FAILED', 'ငွေပေးချေမှု စတင်ရန် မအောင်မြင်ပါ။ QR ဒေတာ မရရှိပါ။');
|
|
231
|
+
}
|
|
232
|
+
} catch (error) {
|
|
233
|
+
this.tokenKey = null;
|
|
234
|
+
this._showTerminalMessage(payload.orderId || 'N/A', 'FAILED', 'ငွေပေးချေမှု စတင်စဉ် အမှားအယွင်း ဖြစ်ပွားသည်။ ကွန်ဆိုးလ်တွင် ကြည့်ပါ။');
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
|
|
122
242
|
/**
|
|
123
243
|
* _createAndRenderModal
|
|
124
244
|
* @param {string} contentHtml
|
|
125
|
-
* @param isTerminal
|
|
245
|
+
* @param {boolean} isTerminal
|
|
126
246
|
* @returns
|
|
127
247
|
*/
|
|
128
248
|
private _createAndRenderModal(contentHtml: string, isTerminal: boolean = false): HTMLDivElement {
|
|
@@ -134,7 +254,6 @@ export class MMPaySDK {
|
|
|
134
254
|
const style = document.createElement('style');
|
|
135
255
|
style.innerHTML = `
|
|
136
256
|
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&family=Padauk:wght@400;700&display=swap');
|
|
137
|
-
|
|
138
257
|
#mmpay-full-modal {
|
|
139
258
|
position: fixed;
|
|
140
259
|
top: 0;
|
|
@@ -233,42 +352,15 @@ export class MMPaySDK {
|
|
|
233
352
|
document.body.style.overflow = 'hidden'; // FIX: Prevent body scroll when modal is open
|
|
234
353
|
return overlay;
|
|
235
354
|
}
|
|
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
355
|
/**
|
|
264
356
|
* _renderQrModalContent
|
|
265
|
-
* @param {
|
|
266
|
-
* @param {
|
|
357
|
+
* @param {ICreatePaymentResponse} apiResponse
|
|
358
|
+
* @param {CreatePaymentRequest} payload
|
|
267
359
|
* @param {string} merchantName
|
|
268
360
|
*/
|
|
269
|
-
private _renderQrModalContent(apiResponse:
|
|
361
|
+
private _renderQrModalContent(apiResponse: ICreatePaymentResponse, payload: ICreatePaymentRequestParams, merchantName: string): void {
|
|
270
362
|
const qrData = apiResponse.qr;
|
|
271
|
-
const amountDisplay = `${apiResponse.amount.toFixed(2)}
|
|
363
|
+
const amountDisplay = `${apiResponse.amount.toFixed(2)} MMK`;
|
|
272
364
|
const qrCanvasId = 'mmpayQrCanvas';
|
|
273
365
|
const orderId = payload.orderId;
|
|
274
366
|
window.MMPayDownloadQR = function () {
|
|
@@ -333,7 +425,7 @@ export class MMPaySDK {
|
|
|
333
425
|
<span class="mmpay-text-myanmar">မှာယူမှုနံပါတ်:</span> <strong>${apiResponse.orderId}</strong>
|
|
334
426
|
</div>
|
|
335
427
|
<div class="mmpay-detail">
|
|
336
|
-
<span class="mmpay-text-myanmar">ငွေပေးငွေယူနံပါတ်:</span> <strong>${apiResponse.
|
|
428
|
+
<span class="mmpay-text-myanmar">ငွေပေးငွေယူနံပါတ်:</span> <strong>${apiResponse.transactionRefId}</strong>
|
|
337
429
|
</div>
|
|
338
430
|
|
|
339
431
|
<p class="mmpay-warning mmpay-text-myanmar">
|
|
@@ -441,7 +533,7 @@ export class MMPaySDK {
|
|
|
441
533
|
}
|
|
442
534
|
/**
|
|
443
535
|
* Cleans up the modal and stops polling.
|
|
444
|
-
* @param restoreBodyScroll
|
|
536
|
+
* @param {boolean} restoreBodyScroll
|
|
445
537
|
*/
|
|
446
538
|
private _cleanupModal(restoreBodyScroll: boolean): void {
|
|
447
539
|
if (this.pollIntervalId !== undefined) {
|
|
@@ -486,10 +578,15 @@ export class MMPaySDK {
|
|
|
486
578
|
}
|
|
487
579
|
/**
|
|
488
580
|
* _startPolling
|
|
489
|
-
* @param {
|
|
581
|
+
* @param {IPollingRequest} payload
|
|
582
|
+
* @param {number} payload.amount
|
|
583
|
+
* @param {string} payload.currency
|
|
584
|
+
* @param {string} payload.orderId
|
|
585
|
+
* @param {string} payload.nonce
|
|
586
|
+
* @param {string} payload.callbackUrl
|
|
490
587
|
* @param {Function} onComplete
|
|
491
588
|
*/
|
|
492
|
-
private async _startPolling(
|
|
589
|
+
private async _startPolling(payload: IPollingRequest, onComplete: (result: PolliongResult) => void): Promise<void> {
|
|
493
590
|
if (this.pollIntervalId !== undefined) {
|
|
494
591
|
window.clearInterval(this.pollIntervalId);
|
|
495
592
|
}
|
|
@@ -499,7 +596,7 @@ export class MMPaySDK {
|
|
|
499
596
|
? '/xpayments/sandbox-payment-polling'
|
|
500
597
|
: '/xpayments/production-payment-polling';
|
|
501
598
|
|
|
502
|
-
const response = await this._callApi<
|
|
599
|
+
const response = await this._callApi<IPollingResponse>(endpoint, payload);
|
|
503
600
|
const status = (response.status || '').toUpperCase();
|
|
504
601
|
if (status === 'SUCCESS' || status === 'FAILED' || status === 'EXPIRED') {
|
|
505
602
|
window.clearInterval(this.pollIntervalId);
|
|
@@ -512,7 +609,8 @@ export class MMPaySDK {
|
|
|
512
609
|
this._showTerminalMessage(response.orderId || 'N/A', status as 'SUCCESS' | 'FAILED' | 'EXPIRED', message);
|
|
513
610
|
|
|
514
611
|
if (onComplete) {
|
|
515
|
-
|
|
612
|
+
this.tokenKey = null;
|
|
613
|
+
onComplete({success: success, transaction: response as IPollingResponse});
|
|
516
614
|
}
|
|
517
615
|
return;
|
|
518
616
|
}
|
package/test/home.html
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<html lang="en">
|
|
2
|
+
|
|
2
3
|
<head>
|
|
3
4
|
<meta charset="UTF-8">
|
|
4
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
@@ -12,6 +13,7 @@
|
|
|
12
13
|
display: flex;
|
|
13
14
|
justify-content: center;
|
|
14
15
|
}
|
|
16
|
+
|
|
15
17
|
.container {
|
|
16
18
|
display: flex;
|
|
17
19
|
flex-direction: column;
|
|
@@ -19,12 +21,15 @@
|
|
|
19
21
|
max-width: 1000px;
|
|
20
22
|
width: 100%;
|
|
21
23
|
}
|
|
24
|
+
|
|
22
25
|
@media (min-width: 768px) {
|
|
23
26
|
.container {
|
|
24
27
|
flex-direction: row;
|
|
25
28
|
}
|
|
26
29
|
}
|
|
27
|
-
|
|
30
|
+
|
|
31
|
+
.order-summary,
|
|
32
|
+
.payment-area {
|
|
28
33
|
background-color: #ffffff;
|
|
29
34
|
padding: 25px;
|
|
30
35
|
border-radius: 10px;
|
|
@@ -32,6 +37,7 @@
|
|
|
32
37
|
flex: 1;
|
|
33
38
|
min-height: 450px;
|
|
34
39
|
}
|
|
40
|
+
|
|
35
41
|
h2 {
|
|
36
42
|
color: #2c3e50;
|
|
37
43
|
border-bottom: 2px solid #ecf0f1;
|
|
@@ -40,6 +46,7 @@
|
|
|
40
46
|
font-size: 1.5rem;
|
|
41
47
|
font-weight: 700;
|
|
42
48
|
}
|
|
49
|
+
|
|
43
50
|
.item-list div {
|
|
44
51
|
display: flex;
|
|
45
52
|
justify-content: space-between;
|
|
@@ -47,9 +54,11 @@
|
|
|
47
54
|
border-bottom: 1px dashed #ecf0f1;
|
|
48
55
|
color: #7f8c8d;
|
|
49
56
|
}
|
|
57
|
+
|
|
50
58
|
.item-list div:last-child {
|
|
51
59
|
border-bottom: none;
|
|
52
60
|
}
|
|
61
|
+
|
|
53
62
|
.total {
|
|
54
63
|
margin-top: 20px;
|
|
55
64
|
padding-top: 15px;
|
|
@@ -60,6 +69,7 @@
|
|
|
60
69
|
display: flex;
|
|
61
70
|
justify-content: space-between;
|
|
62
71
|
}
|
|
72
|
+
|
|
63
73
|
.checkout-button {
|
|
64
74
|
width: 100%;
|
|
65
75
|
background-color: #2ecc71;
|
|
@@ -74,10 +84,12 @@
|
|
|
74
84
|
margin-top: 20px;
|
|
75
85
|
box-shadow: 0 2px 5px rgba(46, 204, 113, 0.4);
|
|
76
86
|
}
|
|
87
|
+
|
|
77
88
|
.checkout-button:hover {
|
|
78
89
|
background-color: #27ae60;
|
|
79
90
|
box-shadow: 0 4px 8px rgba(39, 174, 96, 0.6);
|
|
80
91
|
}
|
|
92
|
+
|
|
81
93
|
.payment-status {
|
|
82
94
|
margin-top: 20px;
|
|
83
95
|
padding: 15px;
|
|
@@ -89,10 +101,19 @@
|
|
|
89
101
|
max-height: 200px;
|
|
90
102
|
overflow-y: auto;
|
|
91
103
|
}
|
|
92
|
-
|
|
93
|
-
.
|
|
104
|
+
|
|
105
|
+
.success {
|
|
106
|
+
color: #28a745;
|
|
107
|
+
font-weight: bold;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.failure {
|
|
111
|
+
color: #dc3545;
|
|
112
|
+
font-weight: bold;
|
|
113
|
+
}
|
|
94
114
|
</style>
|
|
95
115
|
</head>
|
|
116
|
+
|
|
96
117
|
<body>
|
|
97
118
|
|
|
98
119
|
<div class="container">
|
|
@@ -115,7 +136,8 @@
|
|
|
115
136
|
<div class="payment-area">
|
|
116
137
|
<h2>💳 Payment Details</h2>
|
|
117
138
|
<div id="payment-container">
|
|
118
|
-
<p style="color: #6c757d; padding: 20px;">Click the "Pay Securely" button to initialize the transaction
|
|
139
|
+
<p style="color: #6c757d; padding: 20px;">Click the "Pay Securely" button to initialize the transaction
|
|
140
|
+
and display the MMPay QR code.</p>
|
|
119
141
|
</div>
|
|
120
142
|
<div class="payment-status" id="result-container">
|
|
121
143
|
Awaiting transaction polling result...
|
|
@@ -123,9 +145,7 @@
|
|
|
123
145
|
</div>
|
|
124
146
|
</div>
|
|
125
147
|
|
|
126
|
-
|
|
127
|
-
<!-- <script src="../dist/mmpay-sdk.js"></script> -->
|
|
128
|
-
<script src="https://cdn.jsdelivr.net/npm/mmpay-browser-sdk@latest/mmpay-browser-sdk/master/dist/mmpay-sdk.js"></script>
|
|
148
|
+
<script src="../dist/mmpay-sdk.js"></script>
|
|
129
149
|
|
|
130
150
|
<!-- Client-Side Application Logic -->
|
|
131
151
|
<script>
|
|
@@ -140,11 +160,11 @@
|
|
|
140
160
|
const mockOrder = {
|
|
141
161
|
currency: 'MMK',
|
|
142
162
|
items: [
|
|
143
|
-
{ name: "Fastify T-Shirt", price:
|
|
144
|
-
{ name: "Node.js Dev Sticker Pack", price:
|
|
145
|
-
{ name: "TypeScript Handbook (Digital)", price:
|
|
163
|
+
{ name: "Fastify T-Shirt", price: 500, qty: 1 },
|
|
164
|
+
{ name: "Node.js Dev Sticker Pack", price: 500, qty: 3 },
|
|
165
|
+
{ name: "TypeScript Handbook (Digital)", price: 500, qty: 1 }
|
|
146
166
|
],
|
|
147
|
-
shipping:
|
|
167
|
+
shipping: 500,
|
|
148
168
|
taxRate: 0.08
|
|
149
169
|
};
|
|
150
170
|
|
|
@@ -207,26 +227,17 @@
|
|
|
207
227
|
try {
|
|
208
228
|
|
|
209
229
|
const sdk = new MMPaySDK(PUBLISHABLE_KEY, {
|
|
210
|
-
baseUrl: 'https://
|
|
230
|
+
baseUrl: 'https://ezapi.myanmyanpay.com',
|
|
211
231
|
environment: 'sandbox',
|
|
212
232
|
merchantName: 'Fastify E-Shop',
|
|
213
233
|
pollInterval: 5000
|
|
214
234
|
});
|
|
215
235
|
await sdk.showPaymentModal({
|
|
216
236
|
amount: parseFloat(finalAmount.toFixed(2)),
|
|
217
|
-
currency: mockOrder.currency,
|
|
218
237
|
orderId: 'FSTY' + Date.now(),
|
|
219
238
|
callbackUrl: 'https://your-fastify-backend.com/api/mmpay/webhook'
|
|
220
|
-
},(result) => {
|
|
221
|
-
|
|
222
|
-
document.getElementById(PAY_BUTTON_ID).disabled = false;
|
|
223
|
-
if (result.success) {
|
|
224
|
-
resultContainer.innerHTML = '<span class="success">✅ PAYMENT SUCCESSFUL!</span><br><br>' + JSON.stringify(result.transaction, null, 2);
|
|
225
|
-
document.getElementById(CONTAINER_ID).innerHTML = '<div style="color: #2ecc71; font-size: 1.5rem; font-weight: bold; padding: 50px 0;">Transaction Complete! Thank you for your order!</div>';
|
|
226
|
-
} else {
|
|
227
|
-
resultContainer.innerHTML = '<span class="failure">❌ PAYMENT FAILED / EXPIRED.</span><br><br>' + JSON.stringify(result.transaction, null, 2);
|
|
228
|
-
document.getElementById(CONTAINER_ID).innerHTML = '<div style="color: #e74c3c; font-size: 1.5rem; font-weight: bold; padding: 50px 0;">Transaction Failed. Please try again.</div>';
|
|
229
|
-
}
|
|
239
|
+
}, (result) => {
|
|
240
|
+
|
|
230
241
|
});
|
|
231
242
|
|
|
232
243
|
} catch (error) {
|
|
@@ -240,4 +251,5 @@
|
|
|
240
251
|
window.onload = renderOrderSummary;
|
|
241
252
|
</script>
|
|
242
253
|
</body>
|
|
254
|
+
|
|
243
255
|
</html>
|