@mygigsters/card-sdk 1.0.1 → 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/README.md +519 -646
- package/cdn/card-sdk-entry.js +1 -1
- package/cdn/card-sdk-entry.js.map +1 -1
- package/dist/index.cjs.js +133 -12
- package/dist/index.d.ts +40 -29
- package/dist/index.esm.js +352 -178
- package/dist/index.umd.js +130 -9
- package/index.d.ts +40 -29
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
class
|
|
1
|
+
import m from "airwallex-payment-elements";
|
|
2
|
+
class y {
|
|
3
3
|
constructor(t) {
|
|
4
4
|
this.baseUrl = t.replace(/\/+$/, "");
|
|
5
5
|
}
|
|
@@ -12,69 +12,61 @@ class g {
|
|
|
12
12
|
* @param {object} [extraHeaders] - Additional headers
|
|
13
13
|
* @returns {Promise<object>}
|
|
14
14
|
*/
|
|
15
|
-
async _post(t, e,
|
|
16
|
-
const
|
|
15
|
+
async _post(t, e, o = null, r = {}) {
|
|
16
|
+
const i = {
|
|
17
17
|
"Content-Type": "application/json",
|
|
18
|
-
...
|
|
18
|
+
...r
|
|
19
19
|
};
|
|
20
|
-
|
|
21
|
-
const
|
|
20
|
+
o && (i.Authorization = o.startsWith("Basic ") || o.startsWith("Bearer ") ? o : `Bearer ${o}`);
|
|
21
|
+
const s = await fetch(`${this.baseUrl}/api/v1${t}`, {
|
|
22
22
|
method: "POST",
|
|
23
|
-
headers:
|
|
23
|
+
headers: i,
|
|
24
24
|
body: JSON.stringify(e)
|
|
25
|
-
}),
|
|
26
|
-
let
|
|
27
|
-
if (
|
|
25
|
+
}), n = await s.text();
|
|
26
|
+
let a = null;
|
|
27
|
+
if (n && n.trim().length > 0)
|
|
28
28
|
try {
|
|
29
|
-
|
|
29
|
+
a = JSON.parse(n);
|
|
30
30
|
} catch {
|
|
31
|
-
|
|
31
|
+
a = null;
|
|
32
32
|
}
|
|
33
|
-
if (!
|
|
34
|
-
const l =
|
|
33
|
+
if (!s.ok) {
|
|
34
|
+
const l = a && (a.message || a.error) || n || s.statusText || `HTTP ${s.status}`;
|
|
35
35
|
throw new Error(l);
|
|
36
36
|
}
|
|
37
|
-
return
|
|
37
|
+
return a ?? {};
|
|
38
38
|
}
|
|
39
|
-
// ── Public methods ────────────────────────────────────────────────────────
|
|
40
39
|
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* @param {
|
|
45
|
-
* @
|
|
46
|
-
* @returns {Promise<{ token: string, clientId: string, expiresIn: number, tokenType: string }>}
|
|
40
|
+
* Makes a JSON GET request.
|
|
41
|
+
* @param {string} path - API path (appended to baseUrl + /api/v1)
|
|
42
|
+
* @param {string|null} [token] - Optional Bearer token
|
|
43
|
+
* @param {object} [extraHeaders] - Additional headers
|
|
44
|
+
* @returns {Promise<object>}
|
|
47
45
|
*/
|
|
48
|
-
async
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
46
|
+
async _get(t, e = null, o = {}) {
|
|
47
|
+
const r = {
|
|
48
|
+
"Content-Type": "application/json",
|
|
49
|
+
...o
|
|
50
|
+
};
|
|
51
|
+
e && (r.Authorization = e.startsWith("Basic ") || e.startsWith("Bearer ") ? e : `Bearer ${e}`);
|
|
52
|
+
const i = await fetch(`${this.baseUrl}/api/v1${t}`, {
|
|
53
|
+
method: "GET",
|
|
54
|
+
headers: r
|
|
55
|
+
}), s = await i.text();
|
|
56
|
+
let n = null;
|
|
58
57
|
if (s && s.trim().length > 0)
|
|
59
58
|
try {
|
|
60
|
-
|
|
59
|
+
n = JSON.parse(s);
|
|
61
60
|
} catch {
|
|
62
|
-
|
|
61
|
+
n = null;
|
|
63
62
|
}
|
|
64
|
-
if (!
|
|
65
|
-
const
|
|
66
|
-
throw new Error(
|
|
63
|
+
if (!i.ok) {
|
|
64
|
+
const a = n && (n.message || n.error) || s || i.statusText || `HTTP ${i.status}`;
|
|
65
|
+
throw new Error(a);
|
|
67
66
|
}
|
|
68
|
-
|
|
69
|
-
throw new Error("Authentication failed: Unexpected response from server");
|
|
70
|
-
return {
|
|
71
|
-
token: r.data.token,
|
|
72
|
-
clientId: r.data.clientId,
|
|
73
|
-
expiresIn: r.data.expiresIn,
|
|
74
|
-
tokenType: r.data.tokenType,
|
|
75
|
-
paymentsIndia: r.data.paymentsIndia || !1
|
|
76
|
-
};
|
|
67
|
+
return n ?? {};
|
|
77
68
|
}
|
|
69
|
+
// ── Public methods ────────────────────────────────────────────────────────
|
|
78
70
|
/**
|
|
79
71
|
* Retrieve a client secret for Airwallex card element initialization.
|
|
80
72
|
*
|
|
@@ -86,23 +78,23 @@ class g {
|
|
|
86
78
|
* @param {string|null} [token] - Bearer token if authenticated
|
|
87
79
|
* @returns {Promise<{ clientSecret: string }>}
|
|
88
80
|
*/
|
|
89
|
-
async getClientSecret(t, e,
|
|
81
|
+
async getClientSecret(t, e, o = null) {
|
|
90
82
|
console.log("🔐 CardAPI.getClientSecret()", {
|
|
91
83
|
customerId: t,
|
|
92
84
|
accountId: e,
|
|
93
|
-
hasToken: !!
|
|
85
|
+
hasToken: !!o
|
|
94
86
|
});
|
|
95
|
-
const
|
|
87
|
+
const r = await this._post(
|
|
96
88
|
"/customer/client-secret",
|
|
97
89
|
{
|
|
98
90
|
customerId: t,
|
|
99
91
|
accountId: e
|
|
100
92
|
},
|
|
101
|
-
|
|
102
|
-
),
|
|
103
|
-
if (!
|
|
93
|
+
o
|
|
94
|
+
), i = r.client_secret || r.clientSecret || r?.data?.client_secret || r?.data?.clientSecret;
|
|
95
|
+
if (!i)
|
|
104
96
|
throw new Error("Failed to retrieve client secret from server");
|
|
105
|
-
return { clientSecret:
|
|
97
|
+
return { clientSecret: i };
|
|
106
98
|
}
|
|
107
99
|
/**
|
|
108
100
|
* Save the payment consent after Airwallex createPaymentConsent succeeds.
|
|
@@ -115,16 +107,45 @@ class g {
|
|
|
115
107
|
* @param {string|null} [token] - Bearer token if authenticated
|
|
116
108
|
* @returns {Promise<object>}
|
|
117
109
|
*/
|
|
118
|
-
async saveConsent(t, e,
|
|
110
|
+
async saveConsent(t, e, o = null) {
|
|
119
111
|
return this._post(
|
|
120
112
|
"/customer/save-consent",
|
|
121
113
|
{
|
|
122
114
|
customerId: t,
|
|
123
115
|
paymentConsentId: e
|
|
124
116
|
},
|
|
125
|
-
|
|
117
|
+
o
|
|
126
118
|
);
|
|
127
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* Fetch profile details to verify user identity (myGigsterId).
|
|
122
|
+
*
|
|
123
|
+
* GET /api/v1/client/profile
|
|
124
|
+
* Response schema: { success: true, status: 200, data: { myGigsterId: "..." } }
|
|
125
|
+
*
|
|
126
|
+
* @param {string|null} [token] - Bearer token if authenticated
|
|
127
|
+
* @returns {Promise<{ myGigsterId: string, data: object, raw: object }>}
|
|
128
|
+
*/
|
|
129
|
+
async profileDetails(t = null) {
|
|
130
|
+
console.log("👤 CardAPI.profileDetails() calling /client/profile", { hasToken: !!t });
|
|
131
|
+
let e = null;
|
|
132
|
+
try {
|
|
133
|
+
e = await this._get("/client/profile", t);
|
|
134
|
+
} catch (r) {
|
|
135
|
+
if (r.message && (r.message.includes("405") || r.message.includes("Method Not Allowed")))
|
|
136
|
+
e = await this._post("/client/profile", {}, t);
|
|
137
|
+
else
|
|
138
|
+
throw r;
|
|
139
|
+
}
|
|
140
|
+
const o = e?.data?.myGigsterId || e?.data?.mygigsterId || e?.accountId;
|
|
141
|
+
if (!o)
|
|
142
|
+
throw new Error("Failed to retrieve myGigsterId from profile API response");
|
|
143
|
+
return {
|
|
144
|
+
myGigsterId: o,
|
|
145
|
+
data: e?.data || e,
|
|
146
|
+
raw: e
|
|
147
|
+
};
|
|
148
|
+
}
|
|
128
149
|
}
|
|
129
150
|
const p = {
|
|
130
151
|
close: `<svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
@@ -134,7 +155,7 @@ const p = {
|
|
|
134
155
|
<path stroke-linecap="round" stroke-linejoin="round" d="M20 6L9 17l-5-5"/>
|
|
135
156
|
</svg>`
|
|
136
157
|
};
|
|
137
|
-
class
|
|
158
|
+
class w {
|
|
138
159
|
constructor(t, e) {
|
|
139
160
|
this.shadowRoot = t, this.callbacks = e, this.state = {
|
|
140
161
|
loading: !0,
|
|
@@ -200,21 +221,21 @@ class b {
|
|
|
200
221
|
`, this._attachListeners();
|
|
201
222
|
}
|
|
202
223
|
_attachListeners() {
|
|
203
|
-
this.shadowRoot.querySelectorAll(".mc-close-btn, #mc-close-btn").forEach((
|
|
204
|
-
|
|
205
|
-
|
|
224
|
+
this.shadowRoot.querySelectorAll(".mc-close-btn, #mc-close-btn").forEach((r) => {
|
|
225
|
+
r.addEventListener("click", (i) => {
|
|
226
|
+
i.preventDefault(), i.stopPropagation(), this.callbacks.onClose();
|
|
206
227
|
});
|
|
207
228
|
});
|
|
208
229
|
const e = this.shadowRoot.querySelector("#mc-backdrop") || this.shadowRoot.querySelector(".mc-backdrop");
|
|
209
|
-
e && e.addEventListener("click", (
|
|
210
|
-
|
|
230
|
+
e && e.addEventListener("click", (r) => {
|
|
231
|
+
r.target === e && (r.preventDefault(), r.stopPropagation(), this.callbacks.onClose());
|
|
211
232
|
});
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
}), this.shadowRoot.addEventListener("click", (
|
|
216
|
-
const
|
|
217
|
-
|
|
233
|
+
const o = this.shadowRoot.querySelector("#mc-submit-btn");
|
|
234
|
+
o && o.addEventListener("click", (r) => {
|
|
235
|
+
r.preventDefault(), r.stopPropagation(), this.callbacks.onSubmit();
|
|
236
|
+
}), this.shadowRoot.addEventListener("click", (r) => {
|
|
237
|
+
const i = r.target;
|
|
238
|
+
i && (i.closest(".mc-close-btn") || i.closest("#mc-close-btn") || i.closest(".mc-dismiss-btn") || i.closest("#mc-dismiss-btn")) && (r.preventDefault(), r.stopPropagation(), this.callbacks.onClose());
|
|
218
239
|
});
|
|
219
240
|
}
|
|
220
241
|
// ── Surgical state patches (safe after card mount) ──────────────────────
|
|
@@ -224,8 +245,8 @@ class b {
|
|
|
224
245
|
*/
|
|
225
246
|
setCardLoaded() {
|
|
226
247
|
this.state.cardLoaded = !0, this.state.loading = !1;
|
|
227
|
-
const t = this.shadowRoot.querySelector("#mc-loading"), e = this.shadowRoot.querySelector("#mc-card-wrapper"),
|
|
228
|
-
t && (t.style.display = "none"), e && (e.style.display = "block"),
|
|
248
|
+
const t = this.shadowRoot.querySelector("#mc-loading"), e = this.shadowRoot.querySelector("#mc-card-wrapper"), o = this.shadowRoot.querySelector("#mc-submit-btn");
|
|
249
|
+
t && (t.style.display = "none"), e && (e.style.display = "block"), o && (o.style.display = "flex");
|
|
229
250
|
}
|
|
230
251
|
/**
|
|
231
252
|
* Mark the Airwallex card as mounted — after this, no full re-renders.
|
|
@@ -261,18 +282,45 @@ class b {
|
|
|
261
282
|
e && (t ? (e.textContent = t, e.style.display = "block") : (e.textContent = "", e.style.display = "none"));
|
|
262
283
|
}
|
|
263
284
|
/**
|
|
264
|
-
* Show an initialization error when the SDK fails to load.
|
|
285
|
+
* Show an initialization error when the SDK fails to load or verification fails.
|
|
286
|
+
* Replaces the modal body with a production-grade error card.
|
|
265
287
|
* Only safe to call BEFORE the card element is mounted.
|
|
266
288
|
*/
|
|
267
|
-
showInitError(t) {
|
|
289
|
+
showInitError(t, e = "Verification Failed") {
|
|
268
290
|
this.state.loading = !1, this.state.error = t;
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
291
|
+
const o = this.shadowRoot.querySelector(".mc-modal");
|
|
292
|
+
if (!o) return;
|
|
293
|
+
o.innerHTML = `
|
|
294
|
+
<button class="mc-close-btn" aria-label="Close" id="mc-close-btn" type="button">${p.close}</button>
|
|
295
|
+
|
|
296
|
+
<div class="mc-body">
|
|
297
|
+
<div class="mc-error-wrap">
|
|
298
|
+
<div class="mc-error-icon">
|
|
299
|
+
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2">
|
|
300
|
+
<circle cx="12" cy="12" r="10" stroke="#ef4444"/>
|
|
301
|
+
<line x1="12" y1="8" x2="12" y2="12" stroke="#ef4444"/>
|
|
302
|
+
<line x1="12" y1="16" x2="12.01" y2="16" stroke="#ef4444"/>
|
|
303
|
+
</svg>
|
|
304
|
+
</div>
|
|
305
|
+
<h2 class="mc-error-title">${this._escapeHtml(e)}</h2>
|
|
306
|
+
<div class="mc-error-detail">${this._escapeHtml(t)}</div>
|
|
307
|
+
<p class="mc-error-sub">SDK initialization has been blocked. Please verify your credentials and try again.</p>
|
|
308
|
+
<button class="mc-dismiss-btn" id="mc-dismiss-btn" type="button">Close</button>
|
|
274
309
|
</div>
|
|
275
|
-
|
|
310
|
+
</div>
|
|
311
|
+
|
|
312
|
+
<div class="mc-powered-by">
|
|
313
|
+
Powered by <a href="https://mygigsters.com.au" target="_blank" rel="noopener">MyGigsters</a>
|
|
314
|
+
</div>
|
|
315
|
+
`, o.querySelectorAll(".mc-close-btn, #mc-close-btn").forEach((i) => {
|
|
316
|
+
i.addEventListener("click", (s) => {
|
|
317
|
+
s.preventDefault(), s.stopPropagation(), this.callbacks.onClose();
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
const r = o.querySelector("#mc-dismiss-btn");
|
|
321
|
+
r && r.addEventListener("click", (i) => {
|
|
322
|
+
i.preventDefault(), i.stopPropagation(), this.callbacks.onClose();
|
|
323
|
+
});
|
|
276
324
|
}
|
|
277
325
|
/**
|
|
278
326
|
* Replace the entire modal body with a success state.
|
|
@@ -296,12 +344,12 @@ class b {
|
|
|
296
344
|
<div class="mc-powered-by">
|
|
297
345
|
Powered by <a href="https://mygigsters.com.au" target="_blank" rel="noopener">MyGigsters</a>
|
|
298
346
|
</div>
|
|
299
|
-
`, e.querySelectorAll(".mc-close-btn, #mc-close-btn").forEach((
|
|
300
|
-
|
|
301
|
-
|
|
347
|
+
`, e.querySelectorAll(".mc-close-btn, #mc-close-btn").forEach((o) => {
|
|
348
|
+
o.addEventListener("click", (r) => {
|
|
349
|
+
r.preventDefault(), r.stopPropagation(), this.callbacks.onClose();
|
|
302
350
|
});
|
|
303
|
-
}), e.querySelector("#mc-done-btn")?.addEventListener("click", (
|
|
304
|
-
|
|
351
|
+
}), e.querySelector("#mc-done-btn")?.addEventListener("click", (o) => {
|
|
352
|
+
o.preventDefault(), o.stopPropagation(), this.callbacks.onClose();
|
|
305
353
|
}));
|
|
306
354
|
}
|
|
307
355
|
// ── Helpers ─────────────────────────────────────────────────────────────
|
|
@@ -315,7 +363,7 @@ class b {
|
|
|
315
363
|
t && (t.innerHTML = ""), this._cardMounted = !1;
|
|
316
364
|
}
|
|
317
365
|
}
|
|
318
|
-
const
|
|
366
|
+
const x = `
|
|
319
367
|
/* ── Reset inside shadow DOM ───────────────────────────────────────────────── */
|
|
320
368
|
*, *::before, *::after {
|
|
321
369
|
box-sizing: border-box;
|
|
@@ -585,6 +633,79 @@ const y = `
|
|
|
585
633
|
animation: mc-fade-in 0.2s ease;
|
|
586
634
|
}
|
|
587
635
|
|
|
636
|
+
/* ── Error state (init / verification failure) ─────────────────────────────── */
|
|
637
|
+
.mc-error-wrap {
|
|
638
|
+
text-align: center;
|
|
639
|
+
padding: 30px 20px 16px;
|
|
640
|
+
animation: mc-fade-in 0.3s ease;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
.mc-error-icon {
|
|
644
|
+
width: 56px;
|
|
645
|
+
height: 56px;
|
|
646
|
+
border-radius: 50%;
|
|
647
|
+
background: rgba(239, 68, 68, 0.15);
|
|
648
|
+
border: 2px solid rgba(239, 68, 68, 0.4);
|
|
649
|
+
display: flex;
|
|
650
|
+
align-items: center;
|
|
651
|
+
justify-content: center;
|
|
652
|
+
margin: 0 auto 16px;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
.mc-error-icon svg {
|
|
656
|
+
width: 28px;
|
|
657
|
+
height: 28px;
|
|
658
|
+
color: #ef4444;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
.mc-error-title {
|
|
662
|
+
font-size: 20px;
|
|
663
|
+
font-weight: 700;
|
|
664
|
+
color: #f8fafc;
|
|
665
|
+
margin: 0 0 8px;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
.mc-error-detail {
|
|
669
|
+
font-size: 13px;
|
|
670
|
+
color: #fca5a5;
|
|
671
|
+
background: rgba(239, 68, 68, 0.1);
|
|
672
|
+
border: 1px solid rgba(239, 68, 68, 0.25);
|
|
673
|
+
border-radius: 8px;
|
|
674
|
+
padding: 10px 14px;
|
|
675
|
+
margin: 0 0 12px;
|
|
676
|
+
word-break: break-word;
|
|
677
|
+
line-height: 1.45;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
.mc-error-sub {
|
|
681
|
+
font-size: 13px;
|
|
682
|
+
color: #94a3b8;
|
|
683
|
+
margin: 0 0 20px;
|
|
684
|
+
line-height: 1.4;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
.mc-dismiss-btn {
|
|
688
|
+
display: inline-flex;
|
|
689
|
+
align-items: center;
|
|
690
|
+
justify-content: center;
|
|
691
|
+
padding: 11px 36px;
|
|
692
|
+
background: #ef4444;
|
|
693
|
+
color: #fff;
|
|
694
|
+
border: none;
|
|
695
|
+
border-radius: 8px;
|
|
696
|
+
font-size: 14px;
|
|
697
|
+
font-weight: 600;
|
|
698
|
+
cursor: pointer;
|
|
699
|
+
font-family: inherit;
|
|
700
|
+
transition: all 0.2s ease;
|
|
701
|
+
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.35);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
.mc-dismiss-btn:hover {
|
|
705
|
+
background: #dc2626;
|
|
706
|
+
transform: translateY(-1px);
|
|
707
|
+
}
|
|
708
|
+
|
|
588
709
|
/* ── Success state ─────────────────────────────────────────────────────────── */
|
|
589
710
|
.mc-success-wrap {
|
|
590
711
|
text-align: center;
|
|
@@ -744,6 +865,37 @@ const y = `
|
|
|
744
865
|
color: #dc2626;
|
|
745
866
|
}
|
|
746
867
|
|
|
868
|
+
[data-theme='light'] .mc-error-icon {
|
|
869
|
+
background: #fef2f2;
|
|
870
|
+
border-color: #fecaca;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
[data-theme='light'] .mc-error-icon svg {
|
|
874
|
+
color: #dc2626;
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
[data-theme='light'] .mc-error-title {
|
|
878
|
+
color: #0f172a;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
[data-theme='light'] .mc-error-detail {
|
|
882
|
+
background: #fff5f5;
|
|
883
|
+
border-color: #fed7d7;
|
|
884
|
+
color: #c53030;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
[data-theme='light'] .mc-error-sub {
|
|
888
|
+
color: #64748b;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
[data-theme='light'] .mc-dismiss-btn {
|
|
892
|
+
background: #dc2626;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
[data-theme='light'] .mc-dismiss-btn:hover {
|
|
896
|
+
background: #b91c1c;
|
|
897
|
+
}
|
|
898
|
+
|
|
747
899
|
[data-theme='light'] .mc-success-icon {
|
|
748
900
|
background: #f0fdf4;
|
|
749
901
|
border-color: #bbf7d0;
|
|
@@ -772,19 +924,19 @@ const y = `
|
|
|
772
924
|
[data-theme='light'] .mc-powered-by a:hover {
|
|
773
925
|
color: #4f46e5;
|
|
774
926
|
}
|
|
775
|
-
`,
|
|
776
|
-
dev: "http://
|
|
777
|
-
// ←
|
|
927
|
+
`, g = {
|
|
928
|
+
dev: "http://localhost:5000",
|
|
929
|
+
// ← Remote dev server (uncomment to use)
|
|
778
930
|
demo: "https://qa-payments.mygigsters.com.au",
|
|
779
931
|
// QA Demo environment
|
|
780
932
|
prod: "https://prod-payments.mygigsters.com.au"
|
|
781
933
|
// Production environment
|
|
782
|
-
},
|
|
934
|
+
}, v = {
|
|
783
935
|
dev: "demo",
|
|
784
936
|
demo: "demo",
|
|
785
937
|
prod: "prod"
|
|
786
938
|
};
|
|
787
|
-
class
|
|
939
|
+
class f {
|
|
788
940
|
constructor() {
|
|
789
941
|
this.config = null, this.api = null, this.renderer = null, this.host = null, this.shadowRoot = null, this.cardElement = null, this.clientSecret = null, this.authToken = null, this.tokenExpiresAt = null, this.airwallexClientId = null, this.isInitialized = !1, this.isSubmitting = !1, this._handleKeyDown = this._handleKeyDown.bind(this);
|
|
790
942
|
}
|
|
@@ -792,91 +944,113 @@ class m {
|
|
|
792
944
|
// PUBLIC: init
|
|
793
945
|
// ─────────────────────────────────────────────────────────────────────────
|
|
794
946
|
/**
|
|
795
|
-
* Initialize the Card SDK.
|
|
947
|
+
* Initialize the Card SDK using a pre-fetched token from GET /api/v1/client/me.
|
|
948
|
+
*
|
|
949
|
+
* ── FLOW B — Pre-fetched Token ─────────────────────────────────────────────
|
|
950
|
+
* 1. Call GET /api/v1/client/me on your server or frontend using Basic Auth:
|
|
951
|
+
* Authorization: Basic base64(apiKey:apiSecret)
|
|
952
|
+
* 2. Extract `token` and `myGigsterId` from the response `data`.
|
|
953
|
+
* 3. Pass `token` and `myGigsterId` directly to `MygigstersCard.init()`.
|
|
954
|
+
*
|
|
955
|
+
* try {
|
|
956
|
+
* await MygigstersCard.init({
|
|
957
|
+
* containerId: 'card-mount',
|
|
958
|
+
* token: resolvedToken, // ← from /client/me (no apiKey/apiSecret here!)
|
|
959
|
+
* customerId: customerId,
|
|
960
|
+
* myGigsterId: myGigsterId,
|
|
961
|
+
* env: env,
|
|
962
|
+
* mode: 'dark',
|
|
963
|
+
* onSuccess: (res) => console.log('Card saved!', res),
|
|
964
|
+
* });
|
|
965
|
+
* } catch (err) {
|
|
966
|
+
* console.error(err);
|
|
967
|
+
* }
|
|
796
968
|
*
|
|
797
|
-
* @param {object}
|
|
798
|
-
* @param {string}
|
|
799
|
-
* @param {string}
|
|
800
|
-
* @param {string}
|
|
801
|
-
* @param {string}
|
|
802
|
-
* @param {string}
|
|
803
|
-
* @param {
|
|
804
|
-
* @param {
|
|
805
|
-
* @param {
|
|
806
|
-
* @param {
|
|
807
|
-
* @param {function}
|
|
808
|
-
* @param {function}
|
|
809
|
-
* @param {function} [config.onClose] - Callback when modal closes
|
|
969
|
+
* @param {object} config
|
|
970
|
+
* @param {string} config.token - Pre-fetched Bearer token from GET /client/me (required)
|
|
971
|
+
* @param {string} config.customerId - Airwallex Customer ID (required)
|
|
972
|
+
* @param {string} [config.myGigsterId] - MyGigster ID from /client/me (required)
|
|
973
|
+
* @param {string} [config.accountId] - Alias for myGigsterId
|
|
974
|
+
* @param {string} config.containerId - ID of the DOM container (required)
|
|
975
|
+
* @param {'dev'|'demo'|'prod'} [config.env='demo'] - Environment
|
|
976
|
+
* @param {string} [config.baseUrl] - Custom base URL override
|
|
977
|
+
* @param {'light'|'dark'} [config.mode='dark'] - UI theme
|
|
978
|
+
* @param {function} [config.onSuccess] - Callback on successful card save
|
|
979
|
+
* @param {function} [config.onError] - Callback on error
|
|
980
|
+
* @param {function} [config.onClose] - Callback when modal closes
|
|
810
981
|
*/
|
|
811
982
|
async init(t) {
|
|
812
983
|
try {
|
|
813
984
|
if (!t)
|
|
814
985
|
throw new Error("Configuration object is required for MygigstersCard.init()");
|
|
815
|
-
if (!t.
|
|
816
|
-
|
|
986
|
+
if (!t.token)
|
|
987
|
+
throw new Error(
|
|
988
|
+
"token is required. Call GET /api/v1/client/me first to get the token, then pass it to MygigstersCard.init()"
|
|
989
|
+
);
|
|
817
990
|
if (!t.customerId) throw new Error("customerId is required");
|
|
818
|
-
const e = t.myGigsterId || t.accountId;
|
|
819
|
-
if (!e) throw new Error("myGigsterId is required");
|
|
820
991
|
if (!t.containerId) throw new Error("containerId is required");
|
|
992
|
+
const e = t.myGigsterId || t.accountId;
|
|
993
|
+
if (!e)
|
|
994
|
+
throw new Error(
|
|
995
|
+
"myGigsterId is required. Pass myGigsterId returned by GET /api/v1/client/me"
|
|
996
|
+
);
|
|
821
997
|
if (!document.getElementById(t.containerId))
|
|
822
998
|
throw new Error(`Container element with ID '${t.containerId}' not found`);
|
|
823
|
-
let
|
|
824
|
-
if (
|
|
825
|
-
throw new Error(`Unknown env "${
|
|
826
|
-
const
|
|
827
|
-
this.config = {
|
|
999
|
+
let r = t.env || "demo";
|
|
1000
|
+
if (r === "qa" && (r = "demo"), !g[r])
|
|
1001
|
+
throw new Error(`Unknown env "${r}". Use "dev", "demo", or "prod".`);
|
|
1002
|
+
const i = t.baseUrl || g[r];
|
|
1003
|
+
this.api = new y(i), this.authToken = t.token, this.config = {
|
|
828
1004
|
mode: "dark",
|
|
829
1005
|
onSuccess: () => {
|
|
830
1006
|
},
|
|
831
|
-
onError: (
|
|
1007
|
+
onError: (h) => console.error("MyGigsters Card SDK Error:", h),
|
|
832
1008
|
onClose: () => {
|
|
833
1009
|
},
|
|
834
1010
|
...t,
|
|
835
1011
|
myGigsterId: e,
|
|
836
1012
|
accountId: e,
|
|
837
|
-
env:
|
|
838
|
-
baseUrl:
|
|
1013
|
+
env: r,
|
|
1014
|
+
baseUrl: i
|
|
839
1015
|
};
|
|
840
|
-
const
|
|
841
|
-
|
|
842
|
-
const
|
|
843
|
-
|
|
844
|
-
const
|
|
845
|
-
|
|
1016
|
+
const s = document.getElementById("mygigsters-card-sdk-host");
|
|
1017
|
+
s?.parentNode && s.parentNode.removeChild(s), this.host = document.createElement("div"), this.host.id = "mygigsters-card-sdk-host", this.host.style.cssText = "display:block;position:fixed;top:0;left:0;right:0;bottom:0;width:100vw;height:100vh;z-index:2147483647;pointer-events:auto;", this.shadowRoot = this.host.attachShadow({ mode: "closed" });
|
|
1018
|
+
const n = document.createElement("style");
|
|
1019
|
+
n.textContent = x, this.shadowRoot.appendChild(n);
|
|
1020
|
+
const a = document.createElement("div");
|
|
1021
|
+
a.id = "mc-theme-container", a.setAttribute(
|
|
846
1022
|
"data-theme",
|
|
847
1023
|
this.config.mode === "light" ? "light" : "dark"
|
|
848
|
-
), this.shadowRoot.appendChild(
|
|
1024
|
+
), this.shadowRoot.appendChild(a), document.body.appendChild(this.host), this.renderer = new w(this.shadowRoot, {
|
|
849
1025
|
onClose: () => this.handleClose(),
|
|
850
1026
|
onSubmit: () => this.handleSubmit()
|
|
851
|
-
}), this.renderer.render(), this.isInitialized = !0, this.show(), window.addEventListener("keydown", this._handleKeyDown)
|
|
1027
|
+
}), this.renderer.render(), this.isInitialized = !0, this.show(), window.addEventListener("keydown", this._handleKeyDown);
|
|
1028
|
+
const l = this.authToken ? `Bearer ${this.authToken}` : null;
|
|
1029
|
+
console.info("[MyGigsters Card SDK] Verifying myGigsterId via profileDetails API...");
|
|
1030
|
+
const u = (await this.api.profileDetails(l))?.myGigsterId;
|
|
1031
|
+
if (!u)
|
|
1032
|
+
throw new Error(
|
|
1033
|
+
"Verification failed: Profile API response did not contain a valid myGigsterId."
|
|
1034
|
+
);
|
|
1035
|
+
if (String(u).trim() !== String(e).trim()) {
|
|
1036
|
+
const h = new Error(
|
|
1037
|
+
"Security check failed: The provided myGigsterId does not match the authenticated client profile. SDK initialization blocked."
|
|
1038
|
+
);
|
|
1039
|
+
throw h.code = "MYGIGSTER_ID_MISMATCH", h;
|
|
1040
|
+
}
|
|
1041
|
+
console.info("[MyGigsters Card SDK] myGigsterId verified successfully."), console.info(`[MyGigsters Card SDK] Initialized in "${r}" mode → ${i}`), await this._initializeCard();
|
|
852
1042
|
} catch (e) {
|
|
853
|
-
|
|
1043
|
+
if (this.renderer) {
|
|
1044
|
+
const o = e.code === "MYGIGSTER_ID_MISMATCH" ? "Security Verification Failed" : "Initialization Failed";
|
|
1045
|
+
this.renderer.showInitError(e.message || "Initialization failed", o);
|
|
1046
|
+
}
|
|
1047
|
+
throw this.config?.onError && this.config.onError(e), e;
|
|
854
1048
|
}
|
|
855
1049
|
}
|
|
856
1050
|
_handleKeyDown(t) {
|
|
857
1051
|
t.key === "Escape" && this.isInitialized && this.handleClose();
|
|
858
1052
|
}
|
|
859
1053
|
// ─────────────────────────────────────────────────────────────────────────
|
|
860
|
-
// PUBLIC: authenticate
|
|
861
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
862
|
-
async authenticate() {
|
|
863
|
-
try {
|
|
864
|
-
console.info(
|
|
865
|
-
`[MyGigsters Card SDK] Authenticating in "${this.config.env}" mode → ${this.config.baseUrl}`
|
|
866
|
-
);
|
|
867
|
-
const t = await this.api.authenticate(
|
|
868
|
-
this.config.apiKey,
|
|
869
|
-
this.config.apiSecret
|
|
870
|
-
);
|
|
871
|
-
this.authToken = t.token, this.airwallexClientId = t.clientId, t.expiresIn && (this.tokenExpiresAt = Date.now() + t.expiresIn * 1e3), console.info("[MyGigsters Card SDK] Authentication successful."), (this.config.env === "demo" || this.config.env === "dev") && console.info(`ℹ️ Connected to [${this.config.env.toUpperCase()}] backend.`);
|
|
872
|
-
} catch (t) {
|
|
873
|
-
const e = t.message || "";
|
|
874
|
-
throw e.includes("401") || e.toLowerCase().includes("unauthorized") ? new Error(
|
|
875
|
-
"Invalid API credentials: Please check your apiKey and apiSecret."
|
|
876
|
-
) : new Error(`Authentication failed: ${e}`);
|
|
877
|
-
}
|
|
878
|
-
}
|
|
879
|
-
// ─────────────────────────────────────────────────────────────────────────
|
|
880
1054
|
// HANDLERS
|
|
881
1055
|
// ─────────────────────────────────────────────────────────────────────────
|
|
882
1056
|
handleClose() {
|
|
@@ -892,7 +1066,7 @@ class m {
|
|
|
892
1066
|
if (!this.isSubmitting && this.renderer.state.cardComplete) {
|
|
893
1067
|
this.isSubmitting = !0, this.renderer.setSubmitting(!0), this.renderer.setError("");
|
|
894
1068
|
try {
|
|
895
|
-
const e = (await
|
|
1069
|
+
const e = (await m.createPaymentConsent({
|
|
896
1070
|
customer_id: this.config.customerId,
|
|
897
1071
|
client_secret: this.clientSecret,
|
|
898
1072
|
currency: "AUD",
|
|
@@ -902,11 +1076,11 @@ class m {
|
|
|
902
1076
|
})).payment_consent_id;
|
|
903
1077
|
if (!e)
|
|
904
1078
|
throw new Error("Failed to create payment consent from card element");
|
|
905
|
-
const
|
|
1079
|
+
const o = this.authToken ? `Bearer ${this.authToken}` : null, i = {
|
|
906
1080
|
...await this.api.saveConsent(
|
|
907
1081
|
this.config.customerId,
|
|
908
1082
|
e,
|
|
909
|
-
|
|
1083
|
+
o
|
|
910
1084
|
),
|
|
911
1085
|
paymentConsentId: e,
|
|
912
1086
|
customerId: this.config.customerId,
|
|
@@ -915,7 +1089,7 @@ class m {
|
|
|
915
1089
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
916
1090
|
event: "card.saved"
|
|
917
1091
|
};
|
|
918
|
-
this.renderer.showSuccess(
|
|
1092
|
+
this.renderer.showSuccess(i), this.config?.onSuccess && this.config.onSuccess(i);
|
|
919
1093
|
} catch (t) {
|
|
920
1094
|
const e = t.message || "Payment could not be completed. Please check your card details and try again.";
|
|
921
1095
|
this.renderer.setError(e), this.renderer.setSubmitting(!1), this.config?.onError && this.config.onError(t);
|
|
@@ -929,38 +1103,38 @@ class m {
|
|
|
929
1103
|
// ─────────────────────────────────────────────────────────────────────────
|
|
930
1104
|
async _initializeCard() {
|
|
931
1105
|
try {
|
|
932
|
-
const t = this.
|
|
1106
|
+
const t = this.authToken ? `Bearer ${this.authToken}` : null, { clientSecret: e } = await this.api.getClientSecret(
|
|
933
1107
|
this.config.customerId,
|
|
934
1108
|
this.config.accountId,
|
|
935
1109
|
t
|
|
936
1110
|
);
|
|
937
|
-
this.clientSecret = e, await
|
|
938
|
-
const
|
|
939
|
-
env:
|
|
1111
|
+
this.clientSecret = e, await m.loadAirwallex();
|
|
1112
|
+
const o = {
|
|
1113
|
+
env: v[this.config.env] || "demo",
|
|
940
1114
|
origin: window.location.origin
|
|
941
1115
|
};
|
|
942
|
-
this.airwallexClientId && (
|
|
943
|
-
const
|
|
944
|
-
color:
|
|
1116
|
+
this.airwallexClientId && (o.clientId = this.airwallexClientId), m.init(o);
|
|
1117
|
+
const r = this.config.mode === "light", i = {
|
|
1118
|
+
color: r ? "#0f172a" : "#ffffff",
|
|
945
1119
|
fontSize: "15px",
|
|
946
1120
|
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
947
1121
|
"::placeholder": {
|
|
948
|
-
color:
|
|
1122
|
+
color: r ? "#94a3b8" : "rgba(255, 255, 255, 0.45)",
|
|
949
1123
|
fontSize: "15px"
|
|
950
1124
|
}
|
|
951
|
-
},
|
|
952
|
-
style: { base:
|
|
1125
|
+
}, s = m.createElement("card", {
|
|
1126
|
+
style: { base: i }
|
|
953
1127
|
});
|
|
954
|
-
this.cardElement =
|
|
955
|
-
const
|
|
956
|
-
if (!
|
|
1128
|
+
this.cardElement = s, this.renderer.setCardLoaded(), await new Promise((a) => setTimeout(a, 0));
|
|
1129
|
+
const n = this.shadowRoot.querySelector("#card-root");
|
|
1130
|
+
if (!n) throw new Error("Card mount point not found");
|
|
957
1131
|
try {
|
|
958
|
-
|
|
1132
|
+
s.mount(n);
|
|
959
1133
|
} catch {
|
|
960
|
-
|
|
1134
|
+
s.mount("#card-root");
|
|
961
1135
|
}
|
|
962
|
-
this.renderer.markCardMounted(),
|
|
963
|
-
const l =
|
|
1136
|
+
this.renderer.markCardMounted(), s.on("change", (a) => {
|
|
1137
|
+
const l = a?.detail?.complete || !1;
|
|
964
1138
|
this.renderer.setCardComplete(l);
|
|
965
1139
|
});
|
|
966
1140
|
} catch (t) {
|
|
@@ -999,24 +1173,24 @@ class m {
|
|
|
999
1173
|
t?.parentNode && t.parentNode.removeChild(t), this.isInitialized = !1, this.isSubmitting = !1, this.config = null, this.api = null, this.renderer = null, this.host = null, this.shadowRoot = null, this.cardElement = null, this.clientSecret = null, this.authToken = null, this.tokenExpiresAt = null, this.airwallexClientId = null;
|
|
1000
1174
|
}
|
|
1001
1175
|
}
|
|
1002
|
-
let
|
|
1003
|
-
const
|
|
1004
|
-
init: async (d) => (
|
|
1176
|
+
let c = null;
|
|
1177
|
+
const b = {
|
|
1178
|
+
init: async (d) => (c && (c.destroy(), c = null), c = new f(), await c.init(d), c),
|
|
1005
1179
|
show: () => {
|
|
1006
|
-
|
|
1180
|
+
c?.show();
|
|
1007
1181
|
},
|
|
1008
1182
|
hide: () => {
|
|
1009
|
-
|
|
1183
|
+
c?.hide();
|
|
1010
1184
|
},
|
|
1011
1185
|
destroy: () => {
|
|
1012
|
-
|
|
1186
|
+
c && (c.destroy(), c = null);
|
|
1013
1187
|
},
|
|
1014
1188
|
setTheme: (d) => {
|
|
1015
|
-
|
|
1189
|
+
c?.setTheme(d);
|
|
1016
1190
|
},
|
|
1017
|
-
getInstance: () =>
|
|
1191
|
+
getInstance: () => c
|
|
1018
1192
|
};
|
|
1019
|
-
typeof window < "u" && (window.MygigstersCard =
|
|
1193
|
+
typeof window < "u" && (window.MygigstersCard = b, window.MygigstersCardSDK = f, document.addEventListener("DOMContentLoaded", () => {
|
|
1020
1194
|
const d = document.querySelector(
|
|
1021
1195
|
'script[data-mygigsters-config][src*="card-sdk"]'
|
|
1022
1196
|
);
|
|
@@ -1030,9 +1204,9 @@ typeof window < "u" && (window.MygigstersCard = u, window.MygigstersCardSDK = m,
|
|
|
1030
1204
|
console.error("Failed to parse MyGigsters Card SDK config:", t);
|
|
1031
1205
|
}
|
|
1032
1206
|
}));
|
|
1033
|
-
typeof window < "u" && (window.MygigstersCard =
|
|
1207
|
+
typeof window < "u" && (window.MygigstersCard = b, window.MygigstersCardSDK = f);
|
|
1034
1208
|
export {
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1209
|
+
b as MygigstersCard,
|
|
1210
|
+
f as MygigstersCardSDK,
|
|
1211
|
+
f as default
|
|
1038
1212
|
};
|