@achyutlabsau/vue-payment-gateway 0.3.4 → 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { defineComponent, openBlock, createElementBlock, createElementVNode, createVNode, unref, Fragment, renderList, toDisplayString } from "vue";
1
+ import { defineComponent, createElementBlock, openBlock, createElementVNode, createVNode, unref, Fragment, renderList, toDisplayString } from "vue";
2
2
  import { QIcon, QBtn } from "quasar";
3
3
  const mdiBarcode = "M2,6H4V18H2V6M5,6H6V18H5V6M7,6H10V18H7V6M11,6H12V18H11V6M14,6H16V18H14V6M17,6H20V18H17V6M21,6H22V18H21V6Z";
4
4
  const mdiCheckCircle = "M12 2C6.5 2 2 6.5 2 12S6.5 22 12 22 22 17.5 22 12 17.5 2 12 2M10 17L5 12L6.41 10.59L10 14.17L17.59 6.58L19 8L10 17Z";
@@ -74,8 +74,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
74
74
  });
75
75
  export {
76
76
  _sfc_main as _,
77
- mdiChevronLeft as a,
78
- mdiRefresh as b,
77
+ mdiRefresh as a,
78
+ mdiChevronLeft as b,
79
79
  mdiLink as c,
80
80
  mdiBarcode as d,
81
81
  mdiCheckCircle as m
@@ -0,0 +1,673 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+ import { defineComponent, useModel, ref, resolveDirective, createBlock, openBlock, unref, withCtx, createVNode, withDirectives, renderSlot, createTextVNode, createElementVNode, onMounted, createElementBlock, toDisplayString } from "vue";
5
+ import { Dialog, QDialog, QCard, QToolbar, QToolbarTitle, QIcon, QCardSection, QInput, QCardActions, QBtn, Loading, Notify, QForm } from "quasar";
6
+ import { i as isNetworkError, a as isServerError, d as delay, g as generateTransactionId } from "./index-e4lpzJsL.js";
7
+ import axios, { isAxiosError, HttpStatusCode } from "axios";
8
+ import { v7 } from "uuid";
9
+ import { R as ReceiptAutoPrint, T as TransactionTypes, d as dialogDefaultOpts, C as CurrencyCodes, a as ResponseCodes, u as updateDialog, b as TransactionOutcome } from "./utils-Bp5B61Zb.js";
10
+ import { e as environment, s as state } from "./state-Bwi0Em_K.js";
11
+ import { m as mdiCheckCircle, b as mdiChevronLeft, a as mdiRefresh, _ as _sfc_main$2, c as mdiLink } from "./PairInstructions.vue_vue_type_script_setup_true_lang-CLCmTRYn.js";
12
+ import { useLocalStorage } from "@vueuse/core";
13
+ const LINKLY_CONSTANTS = {
14
+ PATHS: {
15
+ TRANSACTION: "transaction",
16
+ PAIRING: "pairing/cloudpos",
17
+ TOKENS: "tokens/cloudpos",
18
+ REPRINT_RECEIPT: "reprintreceipt"
19
+ },
20
+ STORAGE_KEYS: {
21
+ PAIRING_SECRET: "___linklyPairingSecret",
22
+ REFRESH_TOKEN: "___linklyRefreshToken",
23
+ TOKEN_EXPIRY: "___linklyRefreshTokenExpiry",
24
+ LAST_TRANSACTION_ID: "___linklyLastTransactionId"
25
+ },
26
+ URLS: {
27
+ DEVELOPMENT: {
28
+ AUTH_BASE: "https://auth.sandbox.cloud.pceftpos.com/v1/",
29
+ BASE: "https://rest.pos.sandbox.cloud.pceftpos.com/v1/sessions/"
30
+ },
31
+ PRODUCTION: {
32
+ AUTH_BASE: "https://auth.cloud.pceftpos.com/v1/",
33
+ BASE: "https://rest.pos.cloud.pceftpos.com/v1/sessions/"
34
+ }
35
+ }
36
+ };
37
+ const DEFAULT_REQUEST_PARAMS = {
38
+ async: false
39
+ };
40
+ const MAX_INTERVAL_TIME = 18e4;
41
+ const BASE_INTERVAL = 2e3;
42
+ const INTERVAL_INCREMENT = 1e3;
43
+ const { PATHS, STORAGE_KEYS } = LINKLY_CONSTANTS;
44
+ class LinklyPaymentGateway {
45
+ constructor(config) {
46
+ __publicField(this, "_api");
47
+ __publicField(this, "_authApi");
48
+ __publicField(this, "token", null);
49
+ __publicField(this, "tokenExpiry", null);
50
+ __publicField(this, "pairingSecret", null);
51
+ __publicField(this, "_config");
52
+ const { environment: environment2 = "development", ...linklyConfig } = config;
53
+ const urlConfig = environment2 === "production" ? LINKLY_CONSTANTS.URLS.PRODUCTION : LINKLY_CONSTANTS.URLS.DEVELOPMENT;
54
+ this._config = {
55
+ ...linklyConfig
56
+ };
57
+ this._api = axios.create({
58
+ baseURL: urlConfig.BASE,
59
+ headers: { Accept: "application/json" }
60
+ });
61
+ this._authApi = axios.create({
62
+ baseURL: urlConfig.AUTH_BASE,
63
+ headers: { Accept: "application/json" }
64
+ });
65
+ this._setupInterceptors();
66
+ this._loadSavedCredentials();
67
+ }
68
+ _loadSavedCredentials() {
69
+ const { STORAGE_KEYS: STORAGE_KEYS2 } = LINKLY_CONSTANTS;
70
+ this.pairingSecret = localStorage.getItem(STORAGE_KEYS2.PAIRING_SECRET);
71
+ this.token = localStorage.getItem(STORAGE_KEYS2.REFRESH_TOKEN);
72
+ const savedTokenExpiry = localStorage.getItem(STORAGE_KEYS2.TOKEN_EXPIRY);
73
+ if (savedTokenExpiry) {
74
+ this.tokenExpiry = parseInt(savedTokenExpiry, 10);
75
+ }
76
+ }
77
+ _setupInterceptors() {
78
+ this._api.interceptors.request.use((config) => {
79
+ if (this.token) {
80
+ config.headers.Authorization = `Bearer ${this.token}`;
81
+ }
82
+ return config;
83
+ });
84
+ }
85
+ _getErrorMessage(error, defaultMessage) {
86
+ var _a, _b, _c;
87
+ let message = defaultMessage ?? "An unexpected error occurred.";
88
+ if (isAxiosError(error)) {
89
+ const status = (_a = error.response) == null ? void 0 : _a.status;
90
+ switch (status) {
91
+ case HttpStatusCode.Unauthorized:
92
+ message = "Looks like the username, password or pair code is no longer valid, or your account has been disabled. Try re-pairing the PIN pad or resetting the password.";
93
+ break;
94
+ case HttpStatusCode.BadRequest:
95
+ const errorMessage = (_c = (_b = error.response) == null ? void 0 : _b.data[0]) == null ? void 0 : _c.errorMessage;
96
+ message = errorMessage ?? "Invalid request! Correct the request and try again.";
97
+ break;
98
+ case HttpStatusCode.RequestTimeout:
99
+ message = "Request Timeout. This should be rare: a transient error has occurred, possibly due to server overloading.";
100
+ break;
101
+ default:
102
+ if (isServerError(status)) {
103
+ message = "A server error has occurred. Wait a few seconds and attempt the request again. If the problem persists, contact Linkly Support.";
104
+ }
105
+ }
106
+ if (isNetworkError(error)) {
107
+ message = "You are not connected to the internet";
108
+ }
109
+ }
110
+ return message;
111
+ }
112
+ // New method to check and renew token if needed
113
+ async _ensureValidToken() {
114
+ if (!this.token || !this.tokenExpiry || this.tokenExpiry - Date.now() < 60 * 60 * 1e3) {
115
+ try {
116
+ if (this.pairingSecret) {
117
+ await this.getLinklyAuthToken();
118
+ } else {
119
+ throw new Error("No valid authentication method available");
120
+ }
121
+ } catch (error) {
122
+ throw new Error("Failed to renew authentication token. Please check your Linkly account credentials.");
123
+ }
124
+ }
125
+ }
126
+ async pairPinpad(pairingData) {
127
+ try {
128
+ const response = await this._authApi.post(PATHS.PAIRING, pairingData);
129
+ if (response.data.secret) {
130
+ this._savePairingSecret(response.data.secret);
131
+ await this.getLinklyAuthToken();
132
+ }
133
+ return {
134
+ success: true,
135
+ secret: response.data.secret,
136
+ message: "Pinpad Paired successfully."
137
+ };
138
+ } catch (error) {
139
+ const message = this._getErrorMessage(error, "An unexpected error occurred during pinpad pairing.");
140
+ return {
141
+ success: false,
142
+ message,
143
+ secret: ""
144
+ };
145
+ }
146
+ }
147
+ _savePairingSecret(secret) {
148
+ this.pairingSecret = secret;
149
+ localStorage.setItem(STORAGE_KEYS.PAIRING_SECRET, secret);
150
+ }
151
+ _clearCredentials() {
152
+ const { STORAGE_KEYS: STORAGE_KEYS2 } = LINKLY_CONSTANTS;
153
+ this.pairingSecret = null;
154
+ this.token = null;
155
+ this.tokenExpiry = null;
156
+ localStorage.removeItem(STORAGE_KEYS2.PAIRING_SECRET);
157
+ localStorage.removeItem(STORAGE_KEYS2.REFRESH_TOKEN);
158
+ localStorage.removeItem(STORAGE_KEYS2.TOKEN_EXPIRY);
159
+ }
160
+ unpairPinpad() {
161
+ this._clearCredentials();
162
+ return true;
163
+ }
164
+ async getLinklyAuthToken() {
165
+ var _a;
166
+ const { STORAGE_KEYS: STORAGE_KEYS2 } = LINKLY_CONSTANTS;
167
+ try {
168
+ const tokenData = {
169
+ secret: this.pairingSecret,
170
+ ...this._config
171
+ };
172
+ const response = await this._authApi.post(PATHS.TOKENS, tokenData);
173
+ this.token = response.data.token;
174
+ this.tokenExpiry = Date.now() + response.data.expirySeconds * 1e3;
175
+ localStorage.setItem(STORAGE_KEYS2.REFRESH_TOKEN, response.data.token);
176
+ localStorage.setItem(STORAGE_KEYS2.TOKEN_EXPIRY, String(this.tokenExpiry));
177
+ return response.data;
178
+ } catch (error) {
179
+ console.error("Get token error:", error);
180
+ if (axios.isAxiosError(error) && ((_a = error.response) == null ? void 0 : _a.status) === HttpStatusCode.Unauthorized) {
181
+ this._clearCredentials();
182
+ throw new Error("Authentication failed. Pairing may have been reset or credentials changed.");
183
+ }
184
+ throw error;
185
+ }
186
+ }
187
+ async sendTransactionRequest(data) {
188
+ await this._ensureValidToken();
189
+ const sessionId = v7();
190
+ localStorage.setItem(STORAGE_KEYS.LAST_TRANSACTION_ID, sessionId);
191
+ try {
192
+ const response = await this._api.post(`${sessionId}/${PATHS.TRANSACTION}`, data, {
193
+ params: DEFAULT_REQUEST_PARAMS
194
+ });
195
+ if (response.status === HttpStatusCode.Accepted) {
196
+ return this.getTransactionStatus(sessionId);
197
+ }
198
+ return response.data;
199
+ } catch (error) {
200
+ if (axios.isAxiosError(error) && error.response) {
201
+ const status = error.response.status;
202
+ if (status === HttpStatusCode.Unauthorized) {
203
+ try {
204
+ await this.getLinklyAuthToken();
205
+ return this.sendTransactionRequest(data);
206
+ } catch {
207
+ throw new Error("Failed to authenticate. Please check your Linkly account credentials.");
208
+ }
209
+ }
210
+ if (status === HttpStatusCode.RequestTimeout || isServerError(error)) {
211
+ return this.getTransactionStatus(sessionId);
212
+ }
213
+ }
214
+ throw error;
215
+ }
216
+ }
217
+ async getTransactionStatus(transactionId) {
218
+ let interval = BASE_INTERVAL;
219
+ const doRequest = async () => {
220
+ var _a;
221
+ try {
222
+ const response = await this._api.get(`${transactionId}/${PATHS.TRANSACTION}`, {
223
+ params: DEFAULT_REQUEST_PARAMS
224
+ });
225
+ if (response.status === HttpStatusCode.Accepted) {
226
+ return doRequest();
227
+ }
228
+ return response.data;
229
+ } catch (error) {
230
+ if (axios.isAxiosError(error)) {
231
+ const status = ((_a = error.response) == null ? void 0 : _a.status) ?? 0;
232
+ if (status === HttpStatusCode.RequestTimeout || isServerError(error)) {
233
+ if (interval < MAX_INTERVAL_TIME) {
234
+ await delay(BASE_INTERVAL);
235
+ interval = Math.min(interval + INTERVAL_INCREMENT, MAX_INTERVAL_TIME);
236
+ return doRequest();
237
+ }
238
+ throw new Error("Unable to get the transaction details. Please contact service provider!");
239
+ } else if (status === HttpStatusCode.Unauthorized) {
240
+ await this.getLinklyAuthToken();
241
+ return doRequest();
242
+ }
243
+ }
244
+ throw error;
245
+ }
246
+ };
247
+ return doRequest();
248
+ }
249
+ async getLastTransactionStatus() {
250
+ const txnId = localStorage.getItem(STORAGE_KEYS.LAST_TRANSACTION_ID);
251
+ if (!txnId) {
252
+ throw new Error("No transaction found.");
253
+ }
254
+ return this.getTransactionStatus(txnId);
255
+ }
256
+ async reprintReceipt(data) {
257
+ var _a, _b, _c, _d;
258
+ const uuid = v7();
259
+ try {
260
+ await this._ensureValidToken();
261
+ const res = await this._api.post(`${uuid}/${PATHS.REPRINT_RECEIPT}`, data, {
262
+ params: DEFAULT_REQUEST_PARAMS
263
+ });
264
+ const isSuccess = ((_b = (_a = res.data) == null ? void 0 : _a.response) == null ? void 0 : _b.success) ?? false;
265
+ const message = isSuccess ? "Receipt printed successfully." : "Failed to print receipt: " + ((_d = (_c = res.data) == null ? void 0 : _c.response) == null ? void 0 : _d.responseText);
266
+ return {
267
+ success: isSuccess,
268
+ message,
269
+ data: res.data
270
+ };
271
+ } catch (error) {
272
+ if (axios.isAxiosError(error) && error.response) {
273
+ const status = error.response.status;
274
+ if (status === HttpStatusCode.Unauthorized) {
275
+ try {
276
+ await this.getLinklyAuthToken();
277
+ return this.reprintReceipt(data);
278
+ } catch {
279
+ return {
280
+ success: false,
281
+ message: "Failed to authenticate. Please check your Linkly account credentials.",
282
+ data: null
283
+ };
284
+ }
285
+ }
286
+ if (status === HttpStatusCode.RequestTimeout || isServerError(error)) {
287
+ await delay();
288
+ return this.reprintReceipt(data);
289
+ }
290
+ }
291
+ return {
292
+ success: false,
293
+ message: "Failed to print receipt",
294
+ data: null
295
+ };
296
+ }
297
+ }
298
+ }
299
+ const useLinkly = () => {
300
+ const linklyAPI = new LinklyPaymentGateway({
301
+ posId: state.posId,
302
+ posName: state.productName,
303
+ posVendorId: state.productVendorName,
304
+ posVersion: state.productVersion,
305
+ environment: environment.value
306
+ });
307
+ const pairPinpad = async (pairData) => {
308
+ const dialog = Dialog.create({ ...dialogDefaultOpts, message: "Pairing with the terminal..." });
309
+ const res = await linklyAPI.pairPinpad(pairData);
310
+ dialog.update({
311
+ title: res.success ? "Success!" : "Failed!",
312
+ class: res.success ? "text-green-500" : "text-red-500",
313
+ message: res.message,
314
+ progress: false
315
+ });
316
+ return res;
317
+ };
318
+ const _initiateTransaction = async (data, TxnType) => {
319
+ var _a, _b, _c;
320
+ const payload = {
321
+ Request: {
322
+ Merchant: "00",
323
+ Application: "00",
324
+ TxnRef: generateTransactionId(),
325
+ TxnType,
326
+ CurrencyCode: CurrencyCodes.AustralianDollar,
327
+ CutReceipt: data.CutReceipt,
328
+ AmtPurchase: data.AmtPurchase,
329
+ ReceiptAutoPrint: data.ReceiptAutoPrint
330
+ }
331
+ };
332
+ const dialog = Dialog.create({ ...dialogDefaultOpts });
333
+ try {
334
+ const res = await linklyAPI.sendTransactionRequest(payload);
335
+ if (((_a = res == null ? void 0 : res.Response) == null ? void 0 : _a.ResponseCode) === ResponseCodes.APPROVED || ((_b = res == null ? void 0 : res.Response) == null ? void 0 : _b.ResponseCode) === ResponseCodes.Approved) {
336
+ updateDialog(dialog, TransactionOutcome.APPROVED);
337
+ return {
338
+ result: TransactionOutcome.APPROVED,
339
+ data: res.Response
340
+ };
341
+ }
342
+ if (((_c = res == null ? void 0 : res.Response) == null ? void 0 : _c.ResponseCode) === ResponseCodes.GENERAL_DECLINE) {
343
+ updateDialog(dialog, TransactionOutcome.DECLINED);
344
+ return {
345
+ result: TransactionOutcome.DECLINED,
346
+ data: res.Response
347
+ };
348
+ }
349
+ updateDialog(dialog, TransactionOutcome.UNKNOWN);
350
+ return {
351
+ result: TransactionOutcome.UNKNOWN,
352
+ data: res.Response
353
+ };
354
+ } catch (error) {
355
+ updateDialog(dialog, TransactionOutcome.UNKNOWN);
356
+ throw error;
357
+ }
358
+ };
359
+ const initiatePurchase = async (data) => {
360
+ return _initiateTransaction(data, TransactionTypes.Purchase);
361
+ };
362
+ const initiateRefund = async (data) => {
363
+ return _initiateTransaction(data, TransactionTypes.Refund);
364
+ };
365
+ const reprintReceipt = async (txnRef) => {
366
+ const reprintPayload = {
367
+ Request: {
368
+ Merchant: "00",
369
+ Application: "00",
370
+ ReceiptAutoPrint: ReceiptAutoPrint.ReturnToPOS,
371
+ ReprintType: "2",
372
+ OriginalTxnRef: txnRef
373
+ }
374
+ };
375
+ return linklyAPI.reprintReceipt(reprintPayload);
376
+ };
377
+ const unpairPinpad = () => {
378
+ return linklyAPI.unpairPinpad();
379
+ };
380
+ return {
381
+ linklyAPI,
382
+ pairPinpad,
383
+ unpairPinpad,
384
+ initiatePurchase,
385
+ initiateRefund,
386
+ reprintReceipt,
387
+ getTransactionBySessionId: linklyAPI.getTransactionStatus,
388
+ getLastTransaction: linklyAPI.getLastTransactionStatus
389
+ };
390
+ };
391
+ const _hoisted_1$1 = { class: "" };
392
+ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
393
+ __name: "ReprintReceiptDialog",
394
+ props: {
395
+ "modelValue": { type: Boolean },
396
+ "modelModifiers": {}
397
+ },
398
+ emits: ["update:modelValue"],
399
+ setup(__props) {
400
+ const linkly = useLinkly();
401
+ const modelValue = useModel(__props, "modelValue");
402
+ const txnNumber = ref("");
403
+ const reprintReceipt = async () => {
404
+ try {
405
+ Loading.show({ group: "reprintReceipt" });
406
+ const res = await linkly.reprintReceipt(txnNumber.value || void 0);
407
+ if (res.success) {
408
+ modelValue.value = false;
409
+ Notify.create("Receipt printed successfully");
410
+ } else {
411
+ Notify.create({ type: "negative", message: res.message });
412
+ }
413
+ } catch (error) {
414
+ const errorMessage = error instanceof Error ? error.message : "Failed to print receipts";
415
+ Notify.create({ type: "negative", message: errorMessage });
416
+ } finally {
417
+ Loading.hide("reprintReceipt");
418
+ }
419
+ };
420
+ return (_ctx, _cache) => {
421
+ const _directive_close_popup = resolveDirective("close-popup");
422
+ return openBlock(), createBlock(unref(QDialog), {
423
+ modelValue: modelValue.value,
424
+ "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => modelValue.value = $event)
425
+ }, {
426
+ default: withCtx(() => [
427
+ createVNode(unref(QCard), { class: "w-full" }, {
428
+ default: withCtx(() => [
429
+ createVNode(unref(QToolbar), { class: "bg-primary text-white" }, {
430
+ default: withCtx(() => [
431
+ createVNode(unref(QToolbarTitle), null, {
432
+ default: withCtx(() => [
433
+ renderSlot(_ctx.$slots, "title", {}, () => [
434
+ _cache[2] || (_cache[2] = createTextVNode("Reprint Receipt"))
435
+ ])
436
+ ]),
437
+ _: 3
438
+ }),
439
+ withDirectives(createVNode(unref(QIcon), {
440
+ class: "float-right cursor-pointer rounded p-2 transition-colors",
441
+ color: "gray",
442
+ name: "close",
443
+ size: "md"
444
+ }, null, 512), [
445
+ [_directive_close_popup]
446
+ ])
447
+ ]),
448
+ _: 3
449
+ }),
450
+ createVNode(unref(QCardSection), null, {
451
+ default: withCtx(() => [
452
+ createElementVNode("div", _hoisted_1$1, [
453
+ _cache[3] || (_cache[3] = createTextVNode(" Txn Number: ")),
454
+ createVNode(unref(QInput), {
455
+ modelValue: txnNumber.value,
456
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => txnNumber.value = $event),
457
+ class: "q-mb-sm",
458
+ type: "text",
459
+ outlined: "",
460
+ dense: "",
461
+ placeholder: "Enter txn number"
462
+ }, null, 8, ["modelValue"])
463
+ ])
464
+ ]),
465
+ _: 1
466
+ }),
467
+ createVNode(unref(QCardActions), {
468
+ class: "!p-4",
469
+ align: "right"
470
+ }, {
471
+ default: withCtx(() => [
472
+ withDirectives(createVNode(unref(QBtn), {
473
+ label: "Cancel",
474
+ "no-caps": "",
475
+ unelevated: "",
476
+ flat: ""
477
+ }, null, 512), [
478
+ [_directive_close_popup]
479
+ ]),
480
+ createVNode(unref(QBtn), {
481
+ label: "Re-print Receipt",
482
+ "no-caps": "",
483
+ unelevated: "",
484
+ color: "primary",
485
+ onClick: reprintReceipt
486
+ })
487
+ ]),
488
+ _: 1
489
+ })
490
+ ]),
491
+ _: 3
492
+ })
493
+ ]),
494
+ _: 3
495
+ }, 8, ["modelValue"]);
496
+ };
497
+ }
498
+ });
499
+ const _hoisted_1 = { class: "" };
500
+ const _hoisted_2 = {
501
+ key: "paired",
502
+ class: "flex items-center justify-center rounded-lg bg-white p-8 shadow-lg"
503
+ };
504
+ const _hoisted_3 = { class: "text-center" };
505
+ const _hoisted_4 = { class: "mb-6" };
506
+ const _hoisted_5 = { class: "mb-6 text-sm text-gray-500" };
507
+ const _hoisted_6 = { class: "!space-x-3" };
508
+ const _hoisted_7 = { key: 1 };
509
+ const _hoisted_8 = { class: "flex items-center justify-end gap-3" };
510
+ const _sfc_main = /* @__PURE__ */ defineComponent({
511
+ __name: "PairLinkly",
512
+ emits: ["pairing-success"],
513
+ setup(__props, { emit: __emit }) {
514
+ const SETUP_STEPS = [
515
+ "Plug the PIN pad in and connect to the Internet via cable or Wi-fi depending upon the EFTPOS hardware.",
516
+ "Set the PIN pad up to connect to the Linkly cloud. Each bank may have different configuration settings.",
517
+ "Once connected, the PIN pad will display a temporary 'pair code'",
518
+ "Enter the username, password, and PIN pad 'pair code' from the PIN pad in the form on next screen."
519
+ ];
520
+ const fieldValidation = (fieldName) => {
521
+ return [
522
+ (v) => !!v || `${fieldName} is required`,
523
+ (v) => v.length <= 16 || `${fieldName} must be less than 16 characters.`
524
+ ];
525
+ };
526
+ const emit = __emit;
527
+ const linkly = useLinkly();
528
+ const pairConfig = useLocalStorage("linkly.pairConfig", {
529
+ isPaired: false,
530
+ lastPairedDate: null
531
+ });
532
+ const reprintReceiptModel = ref(false);
533
+ const pairingForm = ref({
534
+ username: "",
535
+ password: "",
536
+ pairCode: ""
537
+ });
538
+ const showInstructions = ref(!pairConfig.value.isPaired);
539
+ const handleNext = () => {
540
+ showInstructions.value = false;
541
+ };
542
+ const doPairing = async () => {
543
+ const res = await linkly.pairPinpad(pairingForm.value);
544
+ if (res.success) {
545
+ pairConfig.value.isPaired = true;
546
+ pairConfig.value.lastPairedDate = /* @__PURE__ */ new Date();
547
+ emit("pairing-success", res);
548
+ }
549
+ };
550
+ const handleRepair = () => {
551
+ linkly.unpairPinpad();
552
+ pairConfig.value.isPaired = false;
553
+ pairConfig.value.lastPairedDate = null;
554
+ };
555
+ onMounted(() => {
556
+ });
557
+ return (_ctx, _cache) => {
558
+ return openBlock(), createElementBlock("div", _hoisted_1, [
559
+ unref(pairConfig).isPaired ? (openBlock(), createElementBlock("div", _hoisted_2, [
560
+ createElementVNode("div", _hoisted_3, [
561
+ createElementVNode("div", _hoisted_4, [
562
+ createVNode(unref(QIcon), {
563
+ name: unref(mdiCheckCircle),
564
+ size: "4rem",
565
+ class: "text-green-500"
566
+ }, null, 8, ["name"])
567
+ ]),
568
+ _cache[6] || (_cache[6] = createElementVNode("h2", { class: "mb-4 text-3xl font-bold text-gray-800" }, "Terminal Connected", -1)),
569
+ createElementVNode("p", _hoisted_5, " Last paired: " + toDisplayString(new Date(unref(pairConfig).lastPairedDate).toLocaleString()), 1),
570
+ createElementVNode("div", _hoisted_6, [
571
+ createVNode(unref(QBtn), {
572
+ color: "green",
573
+ "no-caps": "",
574
+ outline: "",
575
+ unelevated: "",
576
+ label: "Reprint Receipt",
577
+ "icon-left": unref(mdiChevronLeft),
578
+ onClick: _cache[0] || (_cache[0] = ($event) => reprintReceiptModel.value = true)
579
+ }, null, 8, ["icon-left"]),
580
+ createVNode(unref(QBtn), {
581
+ color: "primary",
582
+ unelevated: "",
583
+ "no-caps": "",
584
+ "icon-left": unref(mdiRefresh),
585
+ label: "Re-pair Terminal",
586
+ onClick: handleRepair
587
+ }, null, 8, ["icon-left"])
588
+ ])
589
+ ])
590
+ ])) : (openBlock(), createElementBlock("section", _hoisted_7, [
591
+ showInstructions.value ? (openBlock(), createBlock(_sfc_main$2, {
592
+ key: 0,
593
+ onNext: handleNext,
594
+ steps: SETUP_STEPS
595
+ })) : (openBlock(), createBlock(unref(QCard), {
596
+ key: 1,
597
+ flat: "",
598
+ bordered: "",
599
+ class: "w-full rounded-md p-6"
600
+ }, {
601
+ default: withCtx(() => [
602
+ _cache[7] || (_cache[7] = createElementVNode("div", { class: "text-lg font-semibold text-gray-600" }, "Pairing configuration", -1)),
603
+ createVNode(unref(QForm), {
604
+ onSubmit: doPairing,
605
+ class: "mt-6 space-y-4"
606
+ }, {
607
+ default: withCtx(() => [
608
+ createVNode(unref(QInput), {
609
+ modelValue: pairingForm.value.username,
610
+ "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => pairingForm.value.username = $event),
611
+ rules: fieldValidation("Username"),
612
+ label: "Username",
613
+ hint: "Cloud EFT Client Username",
614
+ type: "text",
615
+ outlined: "",
616
+ dense: ""
617
+ }, null, 8, ["modelValue", "rules"]),
618
+ createVNode(unref(QInput), {
619
+ modelValue: pairingForm.value.password,
620
+ "onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => pairingForm.value.password = $event),
621
+ rules: fieldValidation("Password"),
622
+ label: "Password",
623
+ type: "password",
624
+ outlined: "",
625
+ hint: "Enter Cloud EFT Client Password",
626
+ dense: ""
627
+ }, null, 8, ["modelValue", "rules"]),
628
+ createVNode(unref(QInput), {
629
+ modelValue: pairingForm.value.pairCode,
630
+ "onUpdate:modelValue": _cache[3] || (_cache[3] = ($event) => pairingForm.value.pairCode = $event),
631
+ rules: fieldValidation("Pairing Code"),
632
+ label: "Pairing Code",
633
+ type: "text",
634
+ outlined: "",
635
+ hint: "Enter Pair code from terminal screen",
636
+ dense: ""
637
+ }, null, 8, ["modelValue", "rules"]),
638
+ createElementVNode("div", _hoisted_8, [
639
+ createVNode(unref(QBtn), {
640
+ flat: "",
641
+ "no-caps": "",
642
+ label: "Back",
643
+ "icon-left": unref(mdiChevronLeft),
644
+ onClick: _cache[4] || (_cache[4] = ($event) => showInstructions.value = true)
645
+ }, null, 8, ["icon-left"]),
646
+ createVNode(unref(QBtn), {
647
+ type: "submit",
648
+ color: "primary",
649
+ unelevated: "",
650
+ "no-caps": "",
651
+ "icon-right": unref(mdiLink),
652
+ label: "Pair Device"
653
+ }, null, 8, ["icon-right"])
654
+ ])
655
+ ]),
656
+ _: 1
657
+ })
658
+ ]),
659
+ _: 1
660
+ }))
661
+ ])),
662
+ createVNode(_sfc_main$1, {
663
+ modelValue: reprintReceiptModel.value,
664
+ "onUpdate:modelValue": _cache[5] || (_cache[5] = ($event) => reprintReceiptModel.value = $event)
665
+ }, null, 8, ["modelValue"])
666
+ ]);
667
+ };
668
+ }
669
+ });
670
+ export {
671
+ _sfc_main as _,
672
+ useLinkly as u
673
+ };