@mopay/node-sdk 0.1.0
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/LICENSE +21 -0
- package/README.md +246 -0
- package/dist/base.d.ts +13 -0
- package/dist/base.d.ts.map +1 -0
- package/dist/base.js +122 -0
- package/dist/checkout.d.ts +6 -0
- package/dist/checkout.d.ts.map +1 -0
- package/dist/checkout.js +402 -0
- package/dist/errors.d.ts +15 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +15 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +58 -0
- package/dist/payments/index.d.ts +7 -0
- package/dist/payments/index.d.ts.map +1 -0
- package/dist/payments/index.js +14 -0
- package/dist/redirects/index.d.ts +7 -0
- package/dist/redirects/index.d.ts.map +1 -0
- package/dist/redirects/index.js +11 -0
- package/dist/sessions/index.d.ts +11 -0
- package/dist/sessions/index.d.ts.map +1 -0
- package/dist/sessions/index.js +58 -0
- package/dist/types.d.ts +132 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/utils.d.ts +8 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +125 -0
- package/package.json +45 -0
package/dist/checkout.js
ADDED
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
const DEFAULT_POPUP_WIDTH = 520;
|
|
2
|
+
const DEFAULT_POPUP_HEIGHT = 720;
|
|
3
|
+
const DEFAULT_DIALOG_WIDTH = 571.4;
|
|
4
|
+
const DEFAULT_DIALOG_HEIGHT = 418.4;
|
|
5
|
+
const DEFAULT_DIALOG_MIN_WIDTH = 320;
|
|
6
|
+
const DEFAULT_DIALOG_MIN_HEIGHT = 420;
|
|
7
|
+
const CHECKOUT_STYLE_ID = "mopay-checkout-styles";
|
|
8
|
+
export function checkout(paymentUrl, options = {}) {
|
|
9
|
+
return MoPayCheckout.open({ ...options, checkoutUrl: paymentUrl });
|
|
10
|
+
}
|
|
11
|
+
export class MoPayCheckout {
|
|
12
|
+
static open(options) {
|
|
13
|
+
const checkoutUrl = resolveCheckoutUrl(options);
|
|
14
|
+
const mode = options.mode || "dialog";
|
|
15
|
+
assertPaymentUrl(checkoutUrl);
|
|
16
|
+
if (mode === "redirect") {
|
|
17
|
+
const target = options.target || window;
|
|
18
|
+
target.location.href = checkoutUrl;
|
|
19
|
+
return {
|
|
20
|
+
mode,
|
|
21
|
+
close: () => undefined,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
const trustedOrigins = resolveTrustedOrigins(checkoutUrl, options);
|
|
25
|
+
const controller = new CheckoutMessageController(options, trustedOrigins);
|
|
26
|
+
if (mode === "dialog") {
|
|
27
|
+
const handle = openDialog(checkoutUrl, options, controller);
|
|
28
|
+
options.onOpen?.(handle);
|
|
29
|
+
return handle;
|
|
30
|
+
}
|
|
31
|
+
const handle = openPopup(checkoutUrl, options, controller);
|
|
32
|
+
options.onOpen?.(handle);
|
|
33
|
+
return handle;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function openPopup(paymentUrl, options, controller) {
|
|
37
|
+
const width = options.width || DEFAULT_POPUP_WIDTH;
|
|
38
|
+
const height = options.height || DEFAULT_POPUP_HEIGHT;
|
|
39
|
+
const left = Math.max(0, window.screenX + (window.outerWidth - width) / 2);
|
|
40
|
+
const top = Math.max(0, window.screenY + (window.outerHeight - height) / 2);
|
|
41
|
+
const popup = window.open(paymentUrl, options.popupName || "mopay_checkout", [
|
|
42
|
+
`width=${width}`,
|
|
43
|
+
`height=${height}`,
|
|
44
|
+
`left=${left}`,
|
|
45
|
+
`top=${top}`,
|
|
46
|
+
"resizable=yes",
|
|
47
|
+
"scrollbars=yes",
|
|
48
|
+
"status=no",
|
|
49
|
+
"toolbar=no",
|
|
50
|
+
"menubar=no",
|
|
51
|
+
].join(","));
|
|
52
|
+
if (!popup) {
|
|
53
|
+
controller.destroy();
|
|
54
|
+
throw new Error("MoPay checkout popup was blocked by the browser");
|
|
55
|
+
}
|
|
56
|
+
controller.setSource(popup);
|
|
57
|
+
const timer = window.setInterval(() => {
|
|
58
|
+
if (popup.closed) {
|
|
59
|
+
window.clearInterval(timer);
|
|
60
|
+
controller.destroy();
|
|
61
|
+
options.onClose?.();
|
|
62
|
+
}
|
|
63
|
+
}, 500);
|
|
64
|
+
return {
|
|
65
|
+
mode: "popup",
|
|
66
|
+
close: () => {
|
|
67
|
+
window.clearInterval(timer);
|
|
68
|
+
controller.destroy();
|
|
69
|
+
popup.close();
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function openDialog(paymentUrl, options, controller) {
|
|
74
|
+
injectDialogStyles();
|
|
75
|
+
const dialog = document.createElement("dialog");
|
|
76
|
+
const shell = document.createElement("div");
|
|
77
|
+
const frame = document.createElement("iframe");
|
|
78
|
+
const closeButton = document.createElement("button");
|
|
79
|
+
const closeLineA = document.createElement("span");
|
|
80
|
+
const closeLineB = document.createElement("span");
|
|
81
|
+
const loader = document.createElement("div");
|
|
82
|
+
dialog.setAttribute("aria-label", "MoPay checkout");
|
|
83
|
+
dialog.className = `mopay-checkout-dialog${options.resizable ? " is-resizable" : ""}`;
|
|
84
|
+
dialog.style.setProperty("--mopay-checkout-width", toPixels(options.width || DEFAULT_DIALOG_WIDTH));
|
|
85
|
+
dialog.style.setProperty("--mopay-checkout-height", toPixels(options.height || DEFAULT_DIALOG_HEIGHT));
|
|
86
|
+
dialog.style.setProperty("--mopay-checkout-min-width", toPixels(options.minWidth || DEFAULT_DIALOG_MIN_WIDTH));
|
|
87
|
+
dialog.style.setProperty("--mopay-checkout-min-height", toPixels(options.minHeight || DEFAULT_DIALOG_MIN_HEIGHT));
|
|
88
|
+
dialog.style.setProperty("--mopay-checkout-max-width", toOptionalPixels(options.maxWidth) || "calc(100vw - 32px)");
|
|
89
|
+
dialog.style.setProperty("--mopay-checkout-max-height", toOptionalPixels(options.maxHeight) || "calc(100vh - 32px)");
|
|
90
|
+
shell.className = "mopay-checkout-shell";
|
|
91
|
+
frame.src = paymentUrl;
|
|
92
|
+
frame.title = "MoPay checkout";
|
|
93
|
+
frame.allow = "payment";
|
|
94
|
+
frame.className = "mopay-checkout-frame";
|
|
95
|
+
frame.addEventListener("load", () => {
|
|
96
|
+
frame.classList.add("is-loaded");
|
|
97
|
+
loader.remove();
|
|
98
|
+
}, { once: true });
|
|
99
|
+
closeButton.type = "button";
|
|
100
|
+
closeButton.className = "mopay-checkout-close";
|
|
101
|
+
closeButton.setAttribute("aria-label", options.closeButtonLabel || "Close MoPay checkout");
|
|
102
|
+
closeLineA.setAttribute("aria-hidden", "true");
|
|
103
|
+
closeLineB.setAttribute("aria-hidden", "true");
|
|
104
|
+
closeButton.append(closeLineA, closeLineB);
|
|
105
|
+
closeButton.addEventListener("click", () => dialog.close());
|
|
106
|
+
loader.className = "mopay-checkout-loader";
|
|
107
|
+
loader.setAttribute("aria-hidden", "true");
|
|
108
|
+
shell.append(loader, closeButton, frame);
|
|
109
|
+
dialog.append(shell);
|
|
110
|
+
document.body.append(dialog);
|
|
111
|
+
controller.setSource(frame.contentWindow);
|
|
112
|
+
dialog.addEventListener("close", () => {
|
|
113
|
+
controller.destroy();
|
|
114
|
+
dialog.remove();
|
|
115
|
+
options.onClose?.();
|
|
116
|
+
}, { once: true });
|
|
117
|
+
dialog.showModal();
|
|
118
|
+
return {
|
|
119
|
+
mode: "dialog",
|
|
120
|
+
close: () => dialog.close(),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function injectDialogStyles() {
|
|
124
|
+
if (document.getElementById(CHECKOUT_STYLE_ID)) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const style = document.createElement("style");
|
|
128
|
+
style.id = CHECKOUT_STYLE_ID;
|
|
129
|
+
style.textContent = `
|
|
130
|
+
.mopay-checkout-dialog {
|
|
131
|
+
width: min(var(--mopay-checkout-width), var(--mopay-checkout-max-width));
|
|
132
|
+
height: min(var(--mopay-checkout-height), var(--mopay-checkout-max-height));
|
|
133
|
+
min-width: min(var(--mopay-checkout-min-width), calc(100vw - 32px));
|
|
134
|
+
min-height: min(var(--mopay-checkout-min-height), calc(100vh - 32px));
|
|
135
|
+
max-width: calc(100vw - 32px);
|
|
136
|
+
max-height: calc(100vh - 32px);
|
|
137
|
+
margin: auto;
|
|
138
|
+
padding: 0;
|
|
139
|
+
border: 0;
|
|
140
|
+
border-radius: 10px;
|
|
141
|
+
background: transparent;
|
|
142
|
+
color: #111827;
|
|
143
|
+
overflow: hidden;
|
|
144
|
+
box-shadow: 0 28px 90px rgb(15 23 42 / 18%), 0 2px 12px rgb(15 23 42 / 8%);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.mopay-checkout-dialog::backdrop {
|
|
148
|
+
background: rgb(255 255 255 / 72%);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.mopay-checkout-dialog.is-resizable {
|
|
152
|
+
resize: both;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.mopay-checkout-shell {
|
|
156
|
+
position: relative;
|
|
157
|
+
width: 100%;
|
|
158
|
+
height: 100%;
|
|
159
|
+
border: 1px solid rgb(255 255 255 / 82%);
|
|
160
|
+
border-radius: inherit;
|
|
161
|
+
background: #ffffff;
|
|
162
|
+
overflow: hidden;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.mopay-checkout-frame {
|
|
166
|
+
display: block;
|
|
167
|
+
width: 100%;
|
|
168
|
+
height: 100%;
|
|
169
|
+
border: 0;
|
|
170
|
+
background: #ffffff;
|
|
171
|
+
opacity: 0;
|
|
172
|
+
transition: opacity 160ms ease;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.mopay-checkout-frame.is-loaded {
|
|
176
|
+
opacity: 1;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.mopay-checkout-close {
|
|
180
|
+
position: absolute;
|
|
181
|
+
top: 6px;
|
|
182
|
+
right: 12px;
|
|
183
|
+
z-index: 2;
|
|
184
|
+
display: grid;
|
|
185
|
+
width: 20px;
|
|
186
|
+
height: 20px;
|
|
187
|
+
place-items: center;
|
|
188
|
+
border: 0;
|
|
189
|
+
background: none;
|
|
190
|
+
color: #4b5563;
|
|
191
|
+
cursor: pointer;
|
|
192
|
+
font-size: 5px;
|
|
193
|
+
transition: background 140ms ease, color 140ms ease, transform 140ms ease;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.mopay-checkout-close:hover {
|
|
197
|
+
color: #111827;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.mopay-checkout-close:active {
|
|
201
|
+
transform: scale(0.96);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.mopay-checkout-close:focus-visible {
|
|
205
|
+
outline: 3px solid rgb(59 130 246 / 32%);
|
|
206
|
+
outline-offset: 2px;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.mopay-checkout-close span {
|
|
210
|
+
position: absolute;
|
|
211
|
+
width: 15px;
|
|
212
|
+
height: 2px;
|
|
213
|
+
background: currentColor;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.mopay-checkout-close span:first-child {
|
|
217
|
+
transform: rotate(45deg);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.mopay-checkout-close span:last-child {
|
|
221
|
+
transform: rotate(-45deg);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.mopay-checkout-loader {
|
|
225
|
+
position: absolute;
|
|
226
|
+
inset: 0;
|
|
227
|
+
z-index: 1;
|
|
228
|
+
display: grid;
|
|
229
|
+
place-items: center;
|
|
230
|
+
background: #ffffff;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.mopay-checkout-loader::after {
|
|
234
|
+
content: "";
|
|
235
|
+
width: 38px;
|
|
236
|
+
height: 38px;
|
|
237
|
+
border: 3px solid #e5e7eb;
|
|
238
|
+
border-top-color: #10b981;
|
|
239
|
+
border-radius: 999px;
|
|
240
|
+
animation: mopay-checkout-spin 800ms linear infinite;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
@keyframes mopay-checkout-spin {
|
|
244
|
+
to {
|
|
245
|
+
transform: rotate(360deg);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
@media (max-width: 560px) {
|
|
250
|
+
.mopay-checkout-dialog {
|
|
251
|
+
width: calc(100vw - 24px);
|
|
252
|
+
height: calc(100vh - 24px);
|
|
253
|
+
min-width: 0;
|
|
254
|
+
min-height: 0;
|
|
255
|
+
border-radius: 18px;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.mopay-checkout-close {
|
|
259
|
+
top: 12px;
|
|
260
|
+
right: 12px;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
`;
|
|
264
|
+
document.head.append(style);
|
|
265
|
+
}
|
|
266
|
+
class CheckoutMessageController {
|
|
267
|
+
options;
|
|
268
|
+
trustedOrigins;
|
|
269
|
+
source = null;
|
|
270
|
+
constructor(options, trustedOrigins) {
|
|
271
|
+
this.options = options;
|
|
272
|
+
this.trustedOrigins = trustedOrigins;
|
|
273
|
+
window.addEventListener("message", this.handleMessage);
|
|
274
|
+
}
|
|
275
|
+
setSource(source) {
|
|
276
|
+
this.source = source;
|
|
277
|
+
}
|
|
278
|
+
destroy() {
|
|
279
|
+
window.removeEventListener("message", this.handleMessage);
|
|
280
|
+
}
|
|
281
|
+
handleMessage = (event) => {
|
|
282
|
+
if (!this.trustedOrigins.has(event.origin)) {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
if (this.source && event.source !== this.source) {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
const message = normalizeCheckoutMessage(event.data);
|
|
289
|
+
if (!message) {
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
this.options.onMessage?.(message);
|
|
293
|
+
switch (message.status) {
|
|
294
|
+
case "success":
|
|
295
|
+
this.options.onSuccess?.(message);
|
|
296
|
+
break;
|
|
297
|
+
case "failed":
|
|
298
|
+
this.options.onFailed?.(message);
|
|
299
|
+
break;
|
|
300
|
+
case "cancelled":
|
|
301
|
+
this.options.onCancel?.(message);
|
|
302
|
+
break;
|
|
303
|
+
case "pending":
|
|
304
|
+
this.options.onPending?.(message);
|
|
305
|
+
break;
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
function normalizeCheckoutMessage(data) {
|
|
310
|
+
const parsed = parseMessageData(data);
|
|
311
|
+
if (!parsed || typeof parsed !== "object") {
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
314
|
+
const record = parsed;
|
|
315
|
+
const type = asString(record.type ?? record.event);
|
|
316
|
+
const status = normalizeStatus(asString(record.status));
|
|
317
|
+
if (!type?.startsWith("mopay.") && !status) {
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
return {
|
|
321
|
+
type,
|
|
322
|
+
status,
|
|
323
|
+
sessionId: asString(record.sessionId),
|
|
324
|
+
transactionId: asString(record.transactionId),
|
|
325
|
+
reference: asString(record.reference),
|
|
326
|
+
amount: asString(record.amount),
|
|
327
|
+
paymentMethod: asString(record.paymentMethod),
|
|
328
|
+
error: asString(record.error),
|
|
329
|
+
raw: data,
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
function parseMessageData(data) {
|
|
333
|
+
if (typeof data !== "string") {
|
|
334
|
+
return data;
|
|
335
|
+
}
|
|
336
|
+
try {
|
|
337
|
+
return JSON.parse(data);
|
|
338
|
+
}
|
|
339
|
+
catch {
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
function normalizeStatus(status) {
|
|
344
|
+
if (!status) {
|
|
345
|
+
return undefined;
|
|
346
|
+
}
|
|
347
|
+
const normalized = status.toLowerCase();
|
|
348
|
+
if (normalized === "cancelled" || normalized === "canceled") {
|
|
349
|
+
return "cancelled";
|
|
350
|
+
}
|
|
351
|
+
return normalized;
|
|
352
|
+
}
|
|
353
|
+
function asString(value) {
|
|
354
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
355
|
+
}
|
|
356
|
+
function toPixels(value) {
|
|
357
|
+
return `${Math.max(0, value)}px`;
|
|
358
|
+
}
|
|
359
|
+
function toOptionalPixels(value) {
|
|
360
|
+
if (value === undefined) {
|
|
361
|
+
return undefined;
|
|
362
|
+
}
|
|
363
|
+
return toPixels(value);
|
|
364
|
+
}
|
|
365
|
+
function resolveCheckoutUrl(options) {
|
|
366
|
+
const checkoutUrl = options.checkoutUrl ?? options.checkout_url;
|
|
367
|
+
if (checkoutUrl) {
|
|
368
|
+
return checkoutUrl;
|
|
369
|
+
}
|
|
370
|
+
const token = options.checkoutToken ?? options.checkout_token;
|
|
371
|
+
if (!token) {
|
|
372
|
+
throw new Error("checkoutUrl or checkoutToken is required to open MoPay checkout");
|
|
373
|
+
}
|
|
374
|
+
const baseUrl = normalizeBaseUrl(options.baseUrl ?? "https://mopay.co.ls");
|
|
375
|
+
return new URL(`/pay/${encodeURIComponent(token)}`, baseUrl).href;
|
|
376
|
+
}
|
|
377
|
+
function resolveTrustedOrigins(checkoutUrl, options) {
|
|
378
|
+
const origins = [
|
|
379
|
+
new URL(checkoutUrl).origin,
|
|
380
|
+
options.allowedOrigin,
|
|
381
|
+
...(options.allowedOrigins || []),
|
|
382
|
+
];
|
|
383
|
+
return new Set(origins
|
|
384
|
+
.filter((origin) => Boolean(origin))
|
|
385
|
+
.map((origin) => new URL(origin).origin));
|
|
386
|
+
}
|
|
387
|
+
function normalizeBaseUrl(baseUrl) {
|
|
388
|
+
const normalized = /^https?:\/\//i.test(baseUrl) ? baseUrl : `https://${baseUrl}`;
|
|
389
|
+
const url = new URL(normalized);
|
|
390
|
+
return url.href.endsWith("/") ? url.href : `${url.href}/`;
|
|
391
|
+
}
|
|
392
|
+
function assertPaymentUrl(paymentUrl) {
|
|
393
|
+
if (!paymentUrl) {
|
|
394
|
+
throw new Error("paymentUrl is required to open MoPay checkout");
|
|
395
|
+
}
|
|
396
|
+
try {
|
|
397
|
+
new URL(paymentUrl);
|
|
398
|
+
}
|
|
399
|
+
catch {
|
|
400
|
+
throw new Error(`paymentUrl must be a valid absolute URL. Received: ${paymentUrl}`);
|
|
401
|
+
}
|
|
402
|
+
}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare class MoPayAPIError extends Error {
|
|
2
|
+
readonly name = "MoPayAPIError";
|
|
3
|
+
readonly statusCode: number | undefined;
|
|
4
|
+
readonly code: string | undefined;
|
|
5
|
+
readonly response: unknown;
|
|
6
|
+
constructor(message: string, options?: {
|
|
7
|
+
statusCode?: number;
|
|
8
|
+
code?: string;
|
|
9
|
+
response?: unknown;
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
export declare class MoPayValidationError extends Error {
|
|
13
|
+
readonly name = "MoPayValidationError";
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,aAAc,SAAQ,KAAK;IACtC,QAAQ,CAAC,IAAI,mBAAmB;IAChC,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;gBAGzB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAO;CAO3E;AAED,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,QAAQ,CAAC,IAAI,0BAA0B;CACxC"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export class MoPayAPIError extends Error {
|
|
2
|
+
name = "MoPayAPIError";
|
|
3
|
+
statusCode;
|
|
4
|
+
code;
|
|
5
|
+
response;
|
|
6
|
+
constructor(message, options = {}) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.statusCode = options.statusCode;
|
|
9
|
+
this.code = options.code;
|
|
10
|
+
this.response = options.response;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export class MoPayValidationError extends Error {
|
|
14
|
+
name = "MoPayValidationError";
|
|
15
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Base } from "./base.js";
|
|
2
|
+
import type { CheckoutHandle, CheckoutFlowOptions, CreatePaymentSessionParams, CreatePaymentSessionResponse, RequestOptions, RetrieveSessionResponse, Session, VerifiedRedirect, WaitForSessionOptions } from "./types.js";
|
|
3
|
+
declare class MoPay extends Base {
|
|
4
|
+
createPaymentSession(params: CreatePaymentSessionParams, options?: RequestOptions): Promise<CreatePaymentSessionResponse>;
|
|
5
|
+
createPayment(params: CreatePaymentSessionParams, options?: RequestOptions): Promise<CreatePaymentSessionResponse>;
|
|
6
|
+
retrieveSession(sessionId: string, options?: RequestOptions): Promise<RetrieveSessionResponse>;
|
|
7
|
+
parseRedirect(input: string | URL | URLSearchParams | Record<string, string>): VerifiedRedirect;
|
|
8
|
+
checkout(input: string | CreatePaymentSessionParams, options?: CheckoutFlowOptions): Promise<CheckoutHandle>;
|
|
9
|
+
getSession(sessionId: string, options?: RequestOptions): Promise<RetrieveSessionResponse>;
|
|
10
|
+
getTransaction(sessionId: string, options?: RequestOptions): Promise<RetrieveSessionResponse>;
|
|
11
|
+
isSuccessful(session: RetrieveSessionResponse | Session): boolean;
|
|
12
|
+
isPaymentSuccessful(sessionId: string, options?: RequestOptions): Promise<boolean>;
|
|
13
|
+
waitForSession(sessionId: string, options?: WaitForSessionOptions): Promise<RetrieveSessionResponse>;
|
|
14
|
+
}
|
|
15
|
+
export default MoPay;
|
|
16
|
+
export { MoPay, MoPay as Mopay };
|
|
17
|
+
export { MoPayAPIError, MoPayValidationError } from "./errors.js";
|
|
18
|
+
export { checkout, MoPayCheckout, } from "./checkout.js";
|
|
19
|
+
export { formatAmount, isValidReference, parseRedirect, validateCreatePaymentSessionParams, } from "./utils.js";
|
|
20
|
+
export type { CreatePaymentSessionParams, CreatePaymentSessionResponse, CheckoutHandle, CheckoutFlowOptions, CheckoutMode, CheckoutMessage, CheckoutOptions, ErrorResponse, FetchLike, MoPayConfig, MoPayCheckoutOpenOptions, PaymentFrequency, PaymentMethod, RequestOptions, RedirectStatus, RetrieveSessionResponse, Session, SessionStatus, TransactionStatus, VerifiedRedirect, WaitForSessionOptions, } from "./types.js";
|
|
21
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAMjC,OAAO,KAAK,EACV,cAAc,EACd,mBAAmB,EAEnB,0BAA0B,EAC1B,4BAA4B,EAC5B,cAAc,EACd,uBAAuB,EACvB,OAAO,EACP,gBAAgB,EAChB,qBAAqB,EACtB,MAAM,YAAY,CAAC;AAEpB,cAAM,KAAM,SAAQ,IAAI;IACtB,oBAAoB,CAClB,MAAM,EAAE,0BAA0B,EAClC,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,4BAA4B,CAAC;IAIxC,aAAa,CACX,MAAM,EAAE,0BAA0B,EAClC,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,4BAA4B,CAAC;IAIxC,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,uBAAuB,CAAC;IAI9F,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,gBAAgB;IAIzF,QAAQ,CACZ,KAAK,EAAE,MAAM,GAAG,0BAA0B,EAC1C,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,cAAc,CAAC;IAoB1B,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,uBAAuB,CAAC;IAIzF,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,uBAAuB,CAAC;IAI7F,YAAY,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,GAAG,OAAO;IAIjE,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC;IAIlF,cAAc,CACZ,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,uBAAuB,CAAC;CAGpC;AAID,eAAe,KAAK,CAAC;AACrB,OAAO,EAAE,KAAK,EAAE,KAAK,IAAI,KAAK,EAAE,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EACL,QAAQ,EACR,aAAa,GACd,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,aAAa,EACb,kCAAkC,GACnC,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,0BAA0B,EAC1B,4BAA4B,EAC5B,cAAc,EACd,mBAAmB,EACnB,YAAY,EACZ,eAAe,EACf,eAAe,EACf,aAAa,EACb,SAAS,EACT,WAAW,EACX,wBAAwB,EACxB,gBAAgB,EAChB,aAAa,EACb,cAAc,EACd,cAAc,EACd,uBAAuB,EACvB,OAAO,EACP,aAAa,EACb,iBAAiB,EACjB,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Base } from "./base.js";
|
|
2
|
+
import { Payments } from "./payments/index.js";
|
|
3
|
+
import { Redirects } from "./redirects/index.js";
|
|
4
|
+
import { Sessions } from "./sessions/index.js";
|
|
5
|
+
import { checkout as openCheckoutDialog, MoPayCheckout } from "./checkout.js";
|
|
6
|
+
import { applyMixins } from "./utils.js";
|
|
7
|
+
class MoPay extends Base {
|
|
8
|
+
createPaymentSession(params, options) {
|
|
9
|
+
return Payments.prototype.createPaymentSession.call(this, params, options);
|
|
10
|
+
}
|
|
11
|
+
createPayment(params, options) {
|
|
12
|
+
return Payments.prototype.createPayment.call(this, params, options);
|
|
13
|
+
}
|
|
14
|
+
retrieveSession(sessionId, options) {
|
|
15
|
+
return Sessions.prototype.retrieveSession.call(this, sessionId, options);
|
|
16
|
+
}
|
|
17
|
+
parseRedirect(input) {
|
|
18
|
+
return Redirects.prototype.parseRedirect.call(this, input);
|
|
19
|
+
}
|
|
20
|
+
async checkout(input, options = {}) {
|
|
21
|
+
const { onSessionCreated, ...checkoutOptions } = options;
|
|
22
|
+
const mergedOptions = {
|
|
23
|
+
...this.getDefaultCheckoutOptions(),
|
|
24
|
+
...checkoutOptions,
|
|
25
|
+
};
|
|
26
|
+
if (typeof input === "string") {
|
|
27
|
+
return openCheckoutDialog(input, mergedOptions);
|
|
28
|
+
}
|
|
29
|
+
const session = await this.createPaymentSession(input, options);
|
|
30
|
+
onSessionCreated?.(session);
|
|
31
|
+
return MoPayCheckout.open({
|
|
32
|
+
...mergedOptions,
|
|
33
|
+
checkoutUrl: session.paymentUrl ?? session.checkoutUrl ?? session.checkout_url,
|
|
34
|
+
checkoutToken: session.checkoutToken ?? session.checkout_token,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
getSession(sessionId, options) {
|
|
38
|
+
return Sessions.prototype.getSession.call(this, sessionId, options);
|
|
39
|
+
}
|
|
40
|
+
getTransaction(sessionId, options) {
|
|
41
|
+
return Sessions.prototype.getTransaction.call(this, sessionId, options);
|
|
42
|
+
}
|
|
43
|
+
isSuccessful(session) {
|
|
44
|
+
return Sessions.prototype.isSuccessful.call(this, session);
|
|
45
|
+
}
|
|
46
|
+
isPaymentSuccessful(sessionId, options) {
|
|
47
|
+
return Sessions.prototype.isPaymentSuccessful.call(this, sessionId, options);
|
|
48
|
+
}
|
|
49
|
+
waitForSession(sessionId, options) {
|
|
50
|
+
return Sessions.prototype.waitForSession.call(this, sessionId, options);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
applyMixins(MoPay, [Payments, Sessions, Redirects]);
|
|
54
|
+
export default MoPay;
|
|
55
|
+
export { MoPay, MoPay as Mopay };
|
|
56
|
+
export { MoPayAPIError, MoPayValidationError } from "./errors.js";
|
|
57
|
+
export { checkout, MoPayCheckout, } from "./checkout.js";
|
|
58
|
+
export { formatAmount, isValidReference, parseRedirect, validateCreatePaymentSessionParams, } from "./utils.js";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Base } from "../base.js";
|
|
2
|
+
import type { CreatePaymentSessionParams, CreatePaymentSessionResponse, RequestOptions } from "../types.js";
|
|
3
|
+
export declare class Payments extends Base {
|
|
4
|
+
createPaymentSession(params: CreatePaymentSessionParams, options?: RequestOptions): Promise<CreatePaymentSessionResponse>;
|
|
5
|
+
createPayment(params: CreatePaymentSessionParams, options?: RequestOptions): Promise<CreatePaymentSessionResponse>;
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/payments/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAClC,OAAO,KAAK,EACV,0BAA0B,EAC1B,4BAA4B,EAC5B,cAAc,EACf,MAAM,aAAa,CAAC;AAKrB,qBAAa,QAAS,SAAQ,IAAI;IAChC,oBAAoB,CAClB,MAAM,EAAE,0BAA0B,EAClC,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,4BAA4B,CAAC;IAOxC,aAAa,CACX,MAAM,EAAE,0BAA0B,EAClC,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,4BAA4B,CAAC;CAGzC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Base } from "../base.js";
|
|
2
|
+
import { serializeCreatePaymentSessionParams } from "../utils.js";
|
|
3
|
+
const resourceName = "/api/external/payment";
|
|
4
|
+
export class Payments extends Base {
|
|
5
|
+
createPaymentSession(params, options) {
|
|
6
|
+
return this.request(resourceName, {
|
|
7
|
+
method: "POST",
|
|
8
|
+
body: JSON.stringify(serializeCreatePaymentSessionParams(params)),
|
|
9
|
+
}, options);
|
|
10
|
+
}
|
|
11
|
+
createPayment(params, options) {
|
|
12
|
+
return this.createPaymentSession(params, options);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Base } from "../base.js";
|
|
2
|
+
import type { CheckoutHandle, CheckoutOptions, VerifiedRedirect } from "../types.js";
|
|
3
|
+
export declare class Redirects extends Base {
|
|
4
|
+
parseRedirect(input: string | URL | URLSearchParams | Record<string, string>): VerifiedRedirect;
|
|
5
|
+
checkout(paymentUrl: string, options?: CheckoutOptions): CheckoutHandle;
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/redirects/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAElC,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGrF,qBAAa,SAAU,SAAQ,IAAI;IACjC,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,gBAAgB;IAI/F,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,cAAc;CAGxE"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Base } from "../base.js";
|
|
2
|
+
import { checkout } from "../checkout.js";
|
|
3
|
+
import { parseRedirect } from "../utils.js";
|
|
4
|
+
export class Redirects extends Base {
|
|
5
|
+
parseRedirect(input) {
|
|
6
|
+
return parseRedirect(input);
|
|
7
|
+
}
|
|
8
|
+
checkout(paymentUrl, options) {
|
|
9
|
+
return checkout(paymentUrl, options);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Base } from "../base.js";
|
|
2
|
+
import type { RequestOptions, RetrieveSessionResponse, Session, WaitForSessionOptions } from "../types.js";
|
|
3
|
+
export declare class Sessions extends Base {
|
|
4
|
+
retrieveSession(sessionId: string, options?: RequestOptions): Promise<RetrieveSessionResponse>;
|
|
5
|
+
getSession(sessionId: string, options?: RequestOptions): Promise<RetrieveSessionResponse>;
|
|
6
|
+
getTransaction(sessionId: string, options?: RequestOptions): Promise<RetrieveSessionResponse>;
|
|
7
|
+
isSuccessful(session: RetrieveSessionResponse | Session): boolean;
|
|
8
|
+
isPaymentSuccessful(sessionId: string, options?: RequestOptions): Promise<boolean>;
|
|
9
|
+
waitForSession(sessionId: string, options?: WaitForSessionOptions): Promise<RetrieveSessionResponse>;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sessions/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAElC,OAAO,KAAK,EACV,cAAc,EACd,uBAAuB,EACvB,OAAO,EACP,qBAAqB,EACtB,MAAM,aAAa,CAAC;AAOrB,qBAAa,QAAS,SAAQ,IAAI;IAChC,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,uBAAuB,CAAC;IAU9F,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,uBAAuB,CAAC;IAIzF,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,uBAAuB,CAAC;IAI7F,YAAY,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,GAAG,OAAO;IAK3D,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC;IAKlF,cAAc,CAClB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,uBAAuB,CAAC;CAmBpC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Base } from "../base.js";
|
|
2
|
+
import { MoPayValidationError } from "../errors.js";
|
|
3
|
+
const resourceName = "/api/external/session/v1";
|
|
4
|
+
const DEFAULT_POLL_INTERVAL_MS = 2000;
|
|
5
|
+
const DEFAULT_POLL_TIMEOUT_MS = 60000;
|
|
6
|
+
const DEFAULT_TERMINAL_STATUSES = ["COMPLETED", "FAILED", "CANCELLED", "EXPIRED"];
|
|
7
|
+
export class Sessions extends Base {
|
|
8
|
+
retrieveSession(sessionId, options) {
|
|
9
|
+
if (!sessionId) {
|
|
10
|
+
throw new MoPayValidationError("sessionId is required");
|
|
11
|
+
}
|
|
12
|
+
return this.request(`${resourceName}/${encodeURIComponent(sessionId)}`, {
|
|
13
|
+
method: "GET",
|
|
14
|
+
}, options);
|
|
15
|
+
}
|
|
16
|
+
getSession(sessionId, options) {
|
|
17
|
+
return this.retrieveSession(sessionId, options);
|
|
18
|
+
}
|
|
19
|
+
getTransaction(sessionId, options) {
|
|
20
|
+
return this.retrieveSession(sessionId, options);
|
|
21
|
+
}
|
|
22
|
+
isSuccessful(session) {
|
|
23
|
+
const data = isRetrieveSessionResponse(session) ? session.session : session;
|
|
24
|
+
return data.status === "COMPLETED" || data.transactionStatus === "success";
|
|
25
|
+
}
|
|
26
|
+
async isPaymentSuccessful(sessionId, options) {
|
|
27
|
+
const session = await this.retrieveSession(sessionId, options);
|
|
28
|
+
return this.isSuccessful(session);
|
|
29
|
+
}
|
|
30
|
+
async waitForSession(sessionId, options = {}) {
|
|
31
|
+
const intervalMs = options.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
32
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_POLL_TIMEOUT_MS;
|
|
33
|
+
const terminalStatuses = options.terminalStatuses ?? DEFAULT_TERMINAL_STATUSES;
|
|
34
|
+
const startedAt = Date.now();
|
|
35
|
+
while (true) {
|
|
36
|
+
const response = await this.retrieveSession(sessionId, options);
|
|
37
|
+
if (terminalStatuses.includes(response.session.status)) {
|
|
38
|
+
return response;
|
|
39
|
+
}
|
|
40
|
+
if (Date.now() - startedAt >= timeoutMs) {
|
|
41
|
+
return response;
|
|
42
|
+
}
|
|
43
|
+
await delay(intervalMs, options.signal);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function isRetrieveSessionResponse(session) {
|
|
48
|
+
return "session" in session && typeof session.session === "object" && session.session !== null;
|
|
49
|
+
}
|
|
50
|
+
function delay(ms, signal) {
|
|
51
|
+
return new Promise((resolve, reject) => {
|
|
52
|
+
const timeout = setTimeout(resolve, ms);
|
|
53
|
+
signal?.addEventListener("abort", () => {
|
|
54
|
+
clearTimeout(timeout);
|
|
55
|
+
reject(signal.reason);
|
|
56
|
+
}, { once: true });
|
|
57
|
+
});
|
|
58
|
+
}
|