@qrcommunication/viva-local-terminal-sdk 1.0.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/CHANGELOG.md +26 -0
- package/LICENSE +21 -0
- package/README.md +252 -0
- package/dist/index.cjs +422 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +593 -0
- package/dist/index.d.ts +593 -0
- package/dist/index.js +409 -0
- package/dist/index.js.map +1 -0
- package/package.json +85 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var undici = require('undici');
|
|
4
|
+
|
|
5
|
+
// src/config.ts
|
|
6
|
+
var VivaLocalTerminalConfig = class {
|
|
7
|
+
/** The normalized terminal base URL (no trailing slash). */
|
|
8
|
+
baseUrl;
|
|
9
|
+
verifyTls;
|
|
10
|
+
useIsvEndpoints;
|
|
11
|
+
timeout;
|
|
12
|
+
fetch;
|
|
13
|
+
dispatcher;
|
|
14
|
+
constructor(options) {
|
|
15
|
+
const trimmed = (options.terminalBaseUrl ?? "").trim().replace(/\/+$/, "");
|
|
16
|
+
if (trimmed === "") {
|
|
17
|
+
throw new Error(
|
|
18
|
+
"VivaLocalTerminalConfig: terminalBaseUrl cannot be empty."
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
if (!/^https?:\/\//i.test(trimmed)) {
|
|
22
|
+
throw new Error(
|
|
23
|
+
'VivaLocalTerminalConfig: terminalBaseUrl must include the scheme, e.g. "https://192.168.1.50:8080".'
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
this.baseUrl = trimmed;
|
|
27
|
+
this.verifyTls = options.verifyTls ?? true;
|
|
28
|
+
this.useIsvEndpoints = options.useIsvEndpoints ?? false;
|
|
29
|
+
this.timeout = options.timeout ?? 3e4;
|
|
30
|
+
this.fetch = options.fetch ?? globalThis.fetch;
|
|
31
|
+
this.dispatcher = options.dispatcher;
|
|
32
|
+
if (typeof this.fetch !== "function") {
|
|
33
|
+
throw new Error(
|
|
34
|
+
"VivaLocalTerminalConfig: no fetch implementation available. Provide one via `fetch` option or run on a platform with a global fetch (Node 18+, browsers, Deno)."
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Build the absolute URL for a `/pos/v1/...` endpoint.
|
|
40
|
+
*
|
|
41
|
+
* The ISV variants of the endpoints differ only by a trailing slash. When
|
|
42
|
+
* {@link useIsvEndpoints} is true, a trailing slash is appended to the path
|
|
43
|
+
* (unless one is already present), matching the spec's `(ISV)` paths.
|
|
44
|
+
*/
|
|
45
|
+
url(path) {
|
|
46
|
+
let normalized = `/${path.replace(/^\/+/, "")}`;
|
|
47
|
+
if (this.useIsvEndpoints && !normalized.endsWith("/")) {
|
|
48
|
+
normalized += "/";
|
|
49
|
+
}
|
|
50
|
+
return this.baseUrl + normalized;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// src/errors.ts
|
|
55
|
+
var VivaError = class extends Error {
|
|
56
|
+
constructor(message) {
|
|
57
|
+
super(message);
|
|
58
|
+
this.name = "VivaError";
|
|
59
|
+
if (typeof Error.captureStackTrace === "function") {
|
|
60
|
+
Error.captureStackTrace(this, this.constructor);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
var ApiError = class extends VivaError {
|
|
65
|
+
httpStatus;
|
|
66
|
+
responseBody;
|
|
67
|
+
constructor(message, httpStatus, responseBody = null) {
|
|
68
|
+
super(message);
|
|
69
|
+
this.name = "ApiError";
|
|
70
|
+
this.httpStatus = httpStatus;
|
|
71
|
+
this.responseBody = responseBody;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* The error text reported by the terminal, preferring a structured field and
|
|
75
|
+
* falling back to the raw string body. Returns `null` if nothing was sent.
|
|
76
|
+
*/
|
|
77
|
+
getErrorText() {
|
|
78
|
+
return this.responseBody?.message ?? this.responseBody?.error ?? this.responseBody?.raw ?? null;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// src/http.ts
|
|
83
|
+
var HttpClient = class {
|
|
84
|
+
constructor(config) {
|
|
85
|
+
this.config = config;
|
|
86
|
+
if (config.dispatcher) {
|
|
87
|
+
this.dispatcher = config.dispatcher;
|
|
88
|
+
} else if (config.verifyTls === false) {
|
|
89
|
+
this.dispatcher = new undici.Agent({ connect: { rejectUnauthorized: false } });
|
|
90
|
+
} else {
|
|
91
|
+
this.dispatcher = void 0;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
config;
|
|
95
|
+
dispatcher;
|
|
96
|
+
get(path, options = {}) {
|
|
97
|
+
return this.request("GET", path, options);
|
|
98
|
+
}
|
|
99
|
+
post(path, body = {}, options = {}) {
|
|
100
|
+
return this.request("POST", path, { ...options, body });
|
|
101
|
+
}
|
|
102
|
+
async request(method, path, options) {
|
|
103
|
+
const url = this.buildUrl(path, options.query);
|
|
104
|
+
const timeoutController = new AbortController();
|
|
105
|
+
const timeoutId = setTimeout(
|
|
106
|
+
() => timeoutController.abort(),
|
|
107
|
+
this.config.timeout
|
|
108
|
+
);
|
|
109
|
+
const signal = this.mergeSignals(options.signal, timeoutController.signal);
|
|
110
|
+
const init = {
|
|
111
|
+
method,
|
|
112
|
+
headers: {
|
|
113
|
+
// No Authorization header — the P2P protocol is unauthenticated.
|
|
114
|
+
Accept: "application/json",
|
|
115
|
+
...options.body !== void 0 ? { "Content-Type": "application/json" } : {}
|
|
116
|
+
},
|
|
117
|
+
body: options.body !== void 0 ? JSON.stringify(options.body) : void 0,
|
|
118
|
+
signal
|
|
119
|
+
};
|
|
120
|
+
if (this.dispatcher) {
|
|
121
|
+
init.dispatcher = this.dispatcher;
|
|
122
|
+
}
|
|
123
|
+
let response;
|
|
124
|
+
try {
|
|
125
|
+
response = await this.config.fetch(url, init);
|
|
126
|
+
} catch (err) {
|
|
127
|
+
clearTimeout(timeoutId);
|
|
128
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
129
|
+
throw new ApiError(
|
|
130
|
+
`Local Terminal request failed (is the terminal reachable on the LAN?): ${reason}`,
|
|
131
|
+
0,
|
|
132
|
+
null
|
|
133
|
+
);
|
|
134
|
+
} finally {
|
|
135
|
+
clearTimeout(timeoutId);
|
|
136
|
+
}
|
|
137
|
+
const rawBody = await response.text();
|
|
138
|
+
const decoded = this.decodeBody(rawBody);
|
|
139
|
+
if (!response.ok) {
|
|
140
|
+
throw this.buildException(response.status, rawBody, decoded);
|
|
141
|
+
}
|
|
142
|
+
return decoded;
|
|
143
|
+
}
|
|
144
|
+
buildUrl(path, query) {
|
|
145
|
+
const joined = this.config.url(path);
|
|
146
|
+
if (!query) return joined;
|
|
147
|
+
const params = new URLSearchParams();
|
|
148
|
+
for (const [key, value] of Object.entries(query)) {
|
|
149
|
+
if (value === void 0 || value === null) continue;
|
|
150
|
+
params.set(key, String(value));
|
|
151
|
+
}
|
|
152
|
+
const qs = params.toString();
|
|
153
|
+
return qs ? `${joined}?${qs}` : joined;
|
|
154
|
+
}
|
|
155
|
+
decodeBody(rawBody) {
|
|
156
|
+
if (rawBody === "") return {};
|
|
157
|
+
try {
|
|
158
|
+
return JSON.parse(rawBody);
|
|
159
|
+
} catch {
|
|
160
|
+
return { raw: rawBody };
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
buildException(statusCode, rawBody, decoded) {
|
|
164
|
+
const isObject = decoded !== null && typeof decoded === "object" && !Array.isArray(decoded);
|
|
165
|
+
const body = isObject ? decoded : null;
|
|
166
|
+
const message = (body && (typeof body.message === "string" ? body.message : typeof body.error === "string" ? body.error : typeof body.raw === "string" ? body.raw : void 0)) ?? (rawBody !== "" ? rawBody : `HTTP ${statusCode}`);
|
|
167
|
+
return new ApiError(message, statusCode, body ?? { raw: rawBody });
|
|
168
|
+
}
|
|
169
|
+
mergeSignals(external, internal) {
|
|
170
|
+
if (!external) return internal;
|
|
171
|
+
const anyFn = AbortSignal.any;
|
|
172
|
+
if (typeof anyFn === "function") {
|
|
173
|
+
return anyFn([external, internal]);
|
|
174
|
+
}
|
|
175
|
+
if (external.aborted) {
|
|
176
|
+
const controller2 = new AbortController();
|
|
177
|
+
controller2.abort(external.reason);
|
|
178
|
+
return controller2.signal;
|
|
179
|
+
}
|
|
180
|
+
const controller = new AbortController();
|
|
181
|
+
external.addEventListener(
|
|
182
|
+
"abort",
|
|
183
|
+
() => controller.abort(external.reason),
|
|
184
|
+
{
|
|
185
|
+
once: true
|
|
186
|
+
}
|
|
187
|
+
);
|
|
188
|
+
internal.addEventListener(
|
|
189
|
+
"abort",
|
|
190
|
+
() => controller.abort(internal.reason),
|
|
191
|
+
{
|
|
192
|
+
once: true
|
|
193
|
+
}
|
|
194
|
+
);
|
|
195
|
+
return controller.signal;
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
// src/resources/device.ts
|
|
200
|
+
var Device = class {
|
|
201
|
+
constructor(http) {
|
|
202
|
+
this.http = http;
|
|
203
|
+
}
|
|
204
|
+
http;
|
|
205
|
+
/**
|
|
206
|
+
* Control the device screen / sleep mode — POST /pos/v1/awake-lock.
|
|
207
|
+
*
|
|
208
|
+
* @param wakeUpLock When `true`, the device wakes from sleep and the screen
|
|
209
|
+
* stays active until the user unlocks it. When `false`, the device reverts
|
|
210
|
+
* to its normal sleep behavior.
|
|
211
|
+
*/
|
|
212
|
+
screenControl(wakeUpLock) {
|
|
213
|
+
return this.http.post("/pos/v1/awake-lock", { wakeUpLock });
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Adjust the terminal app brightness — POST /pos/v1/brightness.
|
|
217
|
+
*
|
|
218
|
+
* @param brightness Value in the range (0, 1], where 1.00 is maximum
|
|
219
|
+
* brightness. A value of 0 is NOT allowed.
|
|
220
|
+
* @throws {RangeError} If `brightness` is not within (0, 1].
|
|
221
|
+
*/
|
|
222
|
+
brightness(brightness) {
|
|
223
|
+
if (!(brightness > 0) || brightness > 1) {
|
|
224
|
+
throw new RangeError(
|
|
225
|
+
"brightness must be greater than 0 and at most 1.0 (0 is not allowed)."
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
return this.http.post("/pos/v1/brightness", { brightness });
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
// src/resources/sessions.ts
|
|
233
|
+
var Sessions = class {
|
|
234
|
+
constructor(http) {
|
|
235
|
+
this.http = http;
|
|
236
|
+
}
|
|
237
|
+
http;
|
|
238
|
+
/**
|
|
239
|
+
* Retrieve a session's full details by its id —
|
|
240
|
+
* GET /pos/v1/sessions/{sessionId}.
|
|
241
|
+
*/
|
|
242
|
+
get(sessionId) {
|
|
243
|
+
return this.http.get(
|
|
244
|
+
`/pos/v1/sessions/${encodeURIComponent(sessionId)}`
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
// src/resources/transactions.ts
|
|
250
|
+
function compact(obj) {
|
|
251
|
+
const out = {};
|
|
252
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
253
|
+
if (value !== void 0) out[key] = value;
|
|
254
|
+
}
|
|
255
|
+
return out;
|
|
256
|
+
}
|
|
257
|
+
var Transactions = class {
|
|
258
|
+
constructor(http) {
|
|
259
|
+
this.http = http;
|
|
260
|
+
}
|
|
261
|
+
http;
|
|
262
|
+
/**
|
|
263
|
+
* Initiate a sale request — POST /pos/v1/sale.
|
|
264
|
+
*
|
|
265
|
+
* The terminal displays the amount and waits for the card. Use
|
|
266
|
+
* `sessions.get()` with the returned `sessionId` to retrieve the final result.
|
|
267
|
+
*/
|
|
268
|
+
sale(params) {
|
|
269
|
+
return this.http.post(
|
|
270
|
+
"/pos/v1/sale",
|
|
271
|
+
compact({ ...params })
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Capture (complete) a previously pre-authorized sale —
|
|
276
|
+
* POST /pos/v1/preauth-completion.
|
|
277
|
+
*/
|
|
278
|
+
capturePreauth(params) {
|
|
279
|
+
return this.http.post(
|
|
280
|
+
"/pos/v1/preauth-completion",
|
|
281
|
+
compact({ ...params })
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Refund / cancel a transaction by its original transaction id —
|
|
286
|
+
* POST /pos/v1/refund.
|
|
287
|
+
*/
|
|
288
|
+
refund(params) {
|
|
289
|
+
return this.http.post(
|
|
290
|
+
"/pos/v1/refund",
|
|
291
|
+
compact({ ...params })
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Issue an unreferenced refund (not linked to an original transaction) —
|
|
296
|
+
* POST /pos/v1/unreferenced-refund.
|
|
297
|
+
*/
|
|
298
|
+
unreferencedRefund(params) {
|
|
299
|
+
return this.http.post(
|
|
300
|
+
"/pos/v1/unreferenced-refund",
|
|
301
|
+
compact({ ...params })
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Abort an in-progress SALE session — POST /pos/v1/abort.
|
|
306
|
+
*/
|
|
307
|
+
abort(sessionId) {
|
|
308
|
+
return this.http.post("/pos/v1/abort", { sessionId });
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Create an AADE FIM control action — POST /pos/v1/aade-fim-control.
|
|
312
|
+
*
|
|
313
|
+
* Creates the Echo Message (to fetch the Master Key) and the Control Message
|
|
314
|
+
* (to create the Session Key), validating the Master Key, KCV and encrypted
|
|
315
|
+
* Session Key (Greek AADE fiscalisation).
|
|
316
|
+
*
|
|
317
|
+
* @param token The FIMAS control token including the AADE token.
|
|
318
|
+
*/
|
|
319
|
+
aadeFimControl(token) {
|
|
320
|
+
return this.http.post("/pos/v1/aade-fim-control", {
|
|
321
|
+
token
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Retrieve historical transactions with optional filters —
|
|
326
|
+
* GET /pos/v1/transactions.
|
|
327
|
+
*
|
|
328
|
+
* `transactionTypes` may be a single id or a list. When an array is passed it
|
|
329
|
+
* is sent as a comma-separated value.
|
|
330
|
+
*/
|
|
331
|
+
retrieve(params = {}) {
|
|
332
|
+
const {
|
|
333
|
+
transactionTypes,
|
|
334
|
+
isAadeAutonomouslyOnly,
|
|
335
|
+
cardNumber,
|
|
336
|
+
orderCode,
|
|
337
|
+
transactionStatus,
|
|
338
|
+
startDate,
|
|
339
|
+
endDate
|
|
340
|
+
} = params;
|
|
341
|
+
const query = compact({
|
|
342
|
+
cardNumber,
|
|
343
|
+
orderCode,
|
|
344
|
+
transactionTypes: Array.isArray(transactionTypes) ? transactionTypes.join(",") : transactionTypes,
|
|
345
|
+
transactionStatus,
|
|
346
|
+
startDate,
|
|
347
|
+
endDate,
|
|
348
|
+
isAadeAutonomouslyOnly: isAadeAutonomouslyOnly === void 0 ? void 0 : isAadeAutonomouslyOnly ? "true" : "false"
|
|
349
|
+
});
|
|
350
|
+
return this.http.get("/pos/v1/transactions", {
|
|
351
|
+
query
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
// src/client.ts
|
|
357
|
+
var VivaLocalTerminalClient = class {
|
|
358
|
+
transactions;
|
|
359
|
+
sessions;
|
|
360
|
+
device;
|
|
361
|
+
config;
|
|
362
|
+
http;
|
|
363
|
+
constructor(options) {
|
|
364
|
+
this.config = new VivaLocalTerminalConfig(options);
|
|
365
|
+
this.http = new HttpClient(this.config);
|
|
366
|
+
this.transactions = new Transactions(this.http);
|
|
367
|
+
this.sessions = new Sessions(this.http);
|
|
368
|
+
this.device = new Device(this.http);
|
|
369
|
+
}
|
|
370
|
+
/** The resolved, immutable configuration in use by this client. */
|
|
371
|
+
getConfig() {
|
|
372
|
+
return this.config;
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
// src/enums.ts
|
|
377
|
+
var PaymentMethod = /* @__PURE__ */ ((PaymentMethod2) => {
|
|
378
|
+
PaymentMethod2["CARD_PRESENT"] = "CardPresent";
|
|
379
|
+
PaymentMethod2["IRIS"] = "Iris";
|
|
380
|
+
return PaymentMethod2;
|
|
381
|
+
})(PaymentMethod || {});
|
|
382
|
+
var SessionState = /* @__PURE__ */ ((SessionState2) => {
|
|
383
|
+
SessionState2["PROCESSING"] = "PROCESSING";
|
|
384
|
+
SessionState2["BUSY_ERROR"] = "BUSY_ERROR";
|
|
385
|
+
SessionState2["SERVER_ERROR"] = "SERVER_ERROR";
|
|
386
|
+
SessionState2["SESSION_NOT_FOUND_ERROR"] = "SESSION_NOT_FOUND_ERROR";
|
|
387
|
+
SessionState2["SUCCESS"] = "SUCCESS";
|
|
388
|
+
SessionState2["FAILURE"] = "FAILURE";
|
|
389
|
+
return SessionState2;
|
|
390
|
+
})(SessionState || {});
|
|
391
|
+
var SessionType = /* @__PURE__ */ ((SessionType2) => {
|
|
392
|
+
SessionType2["SALE"] = "SALE";
|
|
393
|
+
SessionType2["CAPTURE_PREAUTH"] = "CAPTURE_PREAUTH";
|
|
394
|
+
SessionType2["CANCEL"] = "CANCEL";
|
|
395
|
+
SessionType2["UNREFERENCED_REFUND"] = "UNREFERENCED_REFUND";
|
|
396
|
+
SessionType2["ABORT"] = "ABORT";
|
|
397
|
+
return SessionType2;
|
|
398
|
+
})(SessionType || {});
|
|
399
|
+
var TransactionTypeId = /* @__PURE__ */ ((TransactionTypeId2) => {
|
|
400
|
+
TransactionTypeId2[TransactionTypeId2["SALE"] = 5] = "SALE";
|
|
401
|
+
TransactionTypeId2[TransactionTypeId2["REFUND"] = 4] = "REFUND";
|
|
402
|
+
TransactionTypeId2[TransactionTypeId2["REVERSAL"] = 7] = "REVERSAL";
|
|
403
|
+
TransactionTypeId2[TransactionTypeId2["IRIS_SALE"] = 60] = "IRIS_SALE";
|
|
404
|
+
TransactionTypeId2[TransactionTypeId2["IRIS_REFUND"] = 61] = "IRIS_REFUND";
|
|
405
|
+
TransactionTypeId2[TransactionTypeId2["IRIS_VOID"] = 85] = "IRIS_VOID";
|
|
406
|
+
return TransactionTypeId2;
|
|
407
|
+
})(TransactionTypeId || {});
|
|
408
|
+
|
|
409
|
+
exports.ApiError = ApiError;
|
|
410
|
+
exports.Device = Device;
|
|
411
|
+
exports.HttpClient = HttpClient;
|
|
412
|
+
exports.PaymentMethod = PaymentMethod;
|
|
413
|
+
exports.SessionState = SessionState;
|
|
414
|
+
exports.SessionType = SessionType;
|
|
415
|
+
exports.Sessions = Sessions;
|
|
416
|
+
exports.TransactionTypeId = TransactionTypeId;
|
|
417
|
+
exports.Transactions = Transactions;
|
|
418
|
+
exports.VivaError = VivaError;
|
|
419
|
+
exports.VivaLocalTerminalClient = VivaLocalTerminalClient;
|
|
420
|
+
exports.VivaLocalTerminalConfig = VivaLocalTerminalConfig;
|
|
421
|
+
//# sourceMappingURL=index.cjs.map
|
|
422
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/config.ts","../src/errors.ts","../src/http.ts","../src/resources/device.ts","../src/resources/sessions.ts","../src/resources/transactions.ts","../src/client.ts","../src/enums.ts"],"names":["Agent","controller","PaymentMethod","SessionState","SessionType","TransactionTypeId"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Configuration for the Viva.com Local Terminal API.\n *\n * Unlike the Viva cloud APIs (api.vivapayments.com, OAuth2), the Local Terminal\n * API is a PEER-TO-PEER (P2P) protocol: the client talks DIRECTLY to the EFT POS\n * terminal over the local network. There is therefore:\n *\n * - NO cloud \"environment\" (demo/production) — the base URL is the terminal's\n * own IP address and port, e.g. `https://192.168.1.50:8080`.\n * - NO authentication. The official spec states verbatim:\n * \"In a closed network environment, authentication is not required for\n * peer-to-peer communication. [...] eliminating the need to include an\n * Authorization tag in the header.\"\n *\n * The terminal serves HTTPS, usually with a self-signed certificate. By default\n * this SDK VERIFIES TLS. On a closed LAN with a self-signed terminal cert you\n * typically pass `verifyTls: false` (accepting the LAN-only trust model).\n */\n\nimport type { Dispatcher } from \"undici\";\n\nexport interface VivaLocalTerminalConfigOptions {\n /**\n * The terminal's base URL: scheme + IP + port, e.g.\n * `https://192.168.1.50:8080`. A trailing slash, if present, is stripped.\n */\n terminalBaseUrl: string;\n\n /**\n * TLS certificate verification.\n *\n * `true` (default) verifies the terminal's certificate against the system CA\n * bundle. `false` disables verification, which is the common case for a\n * self-signed terminal certificate on a trusted closed LAN.\n */\n verifyTls?: boolean;\n\n /**\n * Target the ISV (trailing-slash) endpoint variants, e.g. `/pos/v1/sale/`.\n * The merchant (default) variants use no trailing slash.\n */\n useIsvEndpoints?: boolean;\n\n /**\n * Per-request timeout in milliseconds. Terminal interactions are async (they\n * return PROCESSING immediately), so a short timeout is fine for the\n * initiating call. Defaults to 30000 (30s).\n */\n timeout?: number;\n\n /**\n * Optional `fetch` implementation override (mainly for testing). Defaults to\n * the platform global `fetch` (Node 18+, browsers, Deno).\n */\n fetch?: typeof fetch;\n\n /**\n * Optional undici `Dispatcher` override. When omitted and `verifyTls` is\n * `false` on Node, an undici `Agent` with `connect.rejectUnauthorized = false`\n * is created automatically so the self-signed terminal cert is accepted.\n */\n dispatcher?: Dispatcher;\n}\n\n/**\n * Immutable runtime configuration for the Local Terminal client.\n */\nexport class VivaLocalTerminalConfig {\n /** The normalized terminal base URL (no trailing slash). */\n readonly baseUrl: string;\n readonly verifyTls: boolean;\n readonly useIsvEndpoints: boolean;\n readonly timeout: number;\n readonly fetch: typeof fetch;\n readonly dispatcher: Dispatcher | undefined;\n\n constructor(options: VivaLocalTerminalConfigOptions) {\n const trimmed = (options.terminalBaseUrl ?? \"\").trim().replace(/\\/+$/, \"\");\n\n if (trimmed === \"\") {\n throw new Error(\n \"VivaLocalTerminalConfig: terminalBaseUrl cannot be empty.\",\n );\n }\n\n if (!/^https?:\\/\\//i.test(trimmed)) {\n throw new Error(\n 'VivaLocalTerminalConfig: terminalBaseUrl must include the scheme, e.g. \"https://192.168.1.50:8080\".',\n );\n }\n\n this.baseUrl = trimmed;\n this.verifyTls = options.verifyTls ?? true;\n this.useIsvEndpoints = options.useIsvEndpoints ?? false;\n this.timeout = options.timeout ?? 30_000;\n this.fetch = options.fetch ?? globalThis.fetch;\n this.dispatcher = options.dispatcher;\n\n if (typeof this.fetch !== \"function\") {\n throw new Error(\n \"VivaLocalTerminalConfig: no fetch implementation available. Provide one via `fetch` option or run on a platform with a global fetch (Node 18+, browsers, Deno).\",\n );\n }\n }\n\n /**\n * Build the absolute URL for a `/pos/v1/...` endpoint.\n *\n * The ISV variants of the endpoints differ only by a trailing slash. When\n * {@link useIsvEndpoints} is true, a trailing slash is appended to the path\n * (unless one is already present), matching the spec's `(ISV)` paths.\n */\n url(path: string): string {\n let normalized = `/${path.replace(/^\\/+/, \"\")}`;\n\n if (this.useIsvEndpoints && !normalized.endsWith(\"/\")) {\n normalized += \"/\";\n }\n\n return this.baseUrl + normalized;\n }\n}\n","/**\n * Error model for the Viva.com Local Terminal (P2P) API.\n *\n * The Local Terminal API does NOT return structured error codes. On a 400 it\n * returns a PLAIN STRING body (e.g. \"ZeroconfException error message Amount\n * cannot be null\"). The raw body is always preserved so callers can inspect the\n * exact text the terminal sent back.\n */\n\n/**\n * Shape used to carry whatever the terminal returned on an error. When the body\n * was JSON it is spread here; when it was a plain string it is preserved under\n * `raw`.\n */\nexport interface VivaErrorBody {\n message?: string;\n error?: string;\n /** The raw, undecoded response body (set when the terminal returns a string). */\n raw?: string;\n [key: string]: unknown;\n}\n\n/**\n * Base error for every Viva Local Terminal SDK failure.\n */\nexport class VivaError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"VivaError\";\n if (\n typeof (Error as { captureStackTrace?: unknown }).captureStackTrace ===\n \"function\"\n ) {\n (\n Error as { captureStackTrace: (target: object, ctor: unknown) => void }\n ).captureStackTrace(this, this.constructor);\n }\n }\n}\n\n/**\n * Thrown when the terminal returns an HTTP error (4xx/5xx) or when the request\n * could not reach the terminal at all (httpStatus === 0).\n *\n * The Local Terminal API returns a plain string body on 400, so the human\n * message is that raw string verbatim. Use {@link getErrorText} to retrieve it.\n */\nexport class ApiError extends VivaError {\n public readonly httpStatus: number;\n public readonly responseBody: VivaErrorBody | null;\n\n constructor(\n message: string,\n httpStatus: number,\n responseBody: VivaErrorBody | null = null,\n ) {\n super(message);\n this.name = \"ApiError\";\n this.httpStatus = httpStatus;\n this.responseBody = responseBody;\n }\n\n /**\n * The error text reported by the terminal, preferring a structured field and\n * falling back to the raw string body. Returns `null` if nothing was sent.\n */\n getErrorText(): string | null {\n return (\n this.responseBody?.message ??\n this.responseBody?.error ??\n this.responseBody?.raw ??\n null\n );\n }\n}\n","import { Agent, type Dispatcher } from \"undici\";\nimport type { VivaLocalTerminalConfig } from \"./config.js\";\nimport { ApiError, type VivaErrorBody } from \"./errors.js\";\n\nexport interface HttpRequestOptions {\n query?: Record<string, string | number | boolean | undefined | null>;\n body?: unknown;\n signal?: AbortSignal;\n}\n\n/**\n * Low-level HTTP client for the Local Terminal (P2P) API.\n *\n * Transport model:\n * - Requests go DIRECTLY to the terminal on the LAN (base URL = terminal\n * IP:port). There is NO cloud endpoint.\n * - NO authentication: no Authorization header, no token exchange. The\n * terminal trusts any caller on the closed network (per the official spec).\n * - HTTPS is served by the terminal, often with a self-signed certificate. On\n * Node, when `verifyTls` is `false`, an undici `Agent` with\n * `connect.rejectUnauthorized = false` is used so the self-signed cert is\n * accepted (the standard `fetch` API has no per-call TLS toggle).\n *\n * Error model:\n * The API returns JSON on 2xx but a PLAIN STRING body on 400 (e.g.\n * \"ZeroconfException error message Amount cannot be null\"). This client\n * decodes JSON when possible and preserves the raw string under `raw`\n * otherwise. The thrown {@link ApiError}'s message is that raw text.\n */\nexport class HttpClient {\n private readonly dispatcher: Dispatcher | undefined;\n\n constructor(private readonly config: VivaLocalTerminalConfig) {\n if (config.dispatcher) {\n this.dispatcher = config.dispatcher;\n } else if (config.verifyTls === false) {\n // No user-supplied dispatcher and TLS verification disabled: build an\n // undici Agent that accepts the terminal's self-signed certificate.\n this.dispatcher = new Agent({ connect: { rejectUnauthorized: false } });\n } else {\n this.dispatcher = undefined;\n }\n }\n\n get<T = unknown>(\n path: string,\n options: Omit<HttpRequestOptions, \"body\"> = {},\n ): Promise<T> {\n return this.request<T>(\"GET\", path, options);\n }\n\n post<T = unknown>(\n path: string,\n body: unknown = {},\n options: Omit<HttpRequestOptions, \"body\"> = {},\n ): Promise<T> {\n return this.request<T>(\"POST\", path, { ...options, body });\n }\n\n private async request<T>(\n method: string,\n path: string,\n options: HttpRequestOptions,\n ): Promise<T> {\n const url = this.buildUrl(path, options.query);\n\n const timeoutController = new AbortController();\n const timeoutId = setTimeout(\n () => timeoutController.abort(),\n this.config.timeout,\n );\n const signal = this.mergeSignals(options.signal, timeoutController.signal);\n\n // undici reads `dispatcher` off the RequestInit; the global fetch supports\n // it on Node. We cast because the DOM lib's RequestInit doesn't type it.\n const init: RequestInit & { dispatcher?: Dispatcher } = {\n method,\n headers: {\n // No Authorization header — the P2P protocol is unauthenticated.\n Accept: \"application/json\",\n ...(options.body !== undefined\n ? { \"Content-Type\": \"application/json\" }\n : {}),\n },\n body:\n options.body !== undefined ? JSON.stringify(options.body) : undefined,\n signal,\n };\n if (this.dispatcher) {\n init.dispatcher = this.dispatcher;\n }\n\n let response: Response;\n try {\n response = await this.config.fetch(url, init);\n } catch (err) {\n clearTimeout(timeoutId);\n const reason = err instanceof Error ? err.message : String(err);\n throw new ApiError(\n `Local Terminal request failed (is the terminal reachable on the LAN?): ${reason}`,\n 0,\n null,\n );\n } finally {\n clearTimeout(timeoutId);\n }\n\n const rawBody = await response.text();\n const decoded = this.decodeBody(rawBody);\n\n if (!response.ok) {\n throw this.buildException(response.status, rawBody, decoded);\n }\n\n return decoded as T;\n }\n\n private buildUrl(path: string, query: HttpRequestOptions[\"query\"]): string {\n const joined = this.config.url(path);\n if (!query) return joined;\n\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(query)) {\n if (value === undefined || value === null) continue;\n params.set(key, String(value));\n }\n const qs = params.toString();\n return qs ? `${joined}?${qs}` : joined;\n }\n\n private decodeBody(rawBody: string): unknown {\n if (rawBody === \"\") return {};\n try {\n return JSON.parse(rawBody);\n } catch {\n // The Local Terminal API returns a plain string body on 400.\n return { raw: rawBody };\n }\n }\n\n private buildException(\n statusCode: number,\n rawBody: string,\n decoded: unknown,\n ): ApiError {\n const isObject =\n decoded !== null &&\n typeof decoded === \"object\" &&\n !Array.isArray(decoded);\n const body = isObject ? (decoded as VivaErrorBody) : null;\n\n // Prefer a structured field; otherwise use the raw string verbatim.\n const message =\n (body &&\n (typeof body.message === \"string\"\n ? body.message\n : typeof body.error === \"string\"\n ? body.error\n : typeof body.raw === \"string\"\n ? body.raw\n : undefined)) ??\n (rawBody !== \"\" ? rawBody : `HTTP ${statusCode}`);\n\n return new ApiError(message, statusCode, body ?? { raw: rawBody });\n }\n\n private mergeSignals(\n external: AbortSignal | undefined,\n internal: AbortSignal,\n ): AbortSignal {\n if (!external) return internal;\n const anyFn = (\n AbortSignal as { any?: (signals: AbortSignal[]) => AbortSignal }\n ).any;\n if (typeof anyFn === \"function\") {\n return anyFn([external, internal]);\n }\n if (external.aborted) {\n const controller = new AbortController();\n controller.abort(external.reason);\n return controller.signal;\n }\n const controller = new AbortController();\n external.addEventListener(\n \"abort\",\n () => controller.abort(external.reason),\n {\n once: true,\n },\n );\n internal.addEventListener(\n \"abort\",\n () => controller.abort(internal.reason),\n {\n once: true,\n },\n );\n return controller.signal;\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type { DeviceResponse } from \"../types/common.js\";\n\n/**\n * Device configuration operations on the Local Terminal (P2P) API.\n *\n * These control the physical terminal device directly: screen wake/sleep and\n * display brightness.\n *\n * @see /pos/v1/awake-lock, /pos/v1/brightness\n */\nexport class Device {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Control the device screen / sleep mode — POST /pos/v1/awake-lock.\n *\n * @param wakeUpLock When `true`, the device wakes from sleep and the screen\n * stays active until the user unlocks it. When `false`, the device reverts\n * to its normal sleep behavior.\n */\n screenControl(wakeUpLock: boolean): Promise<DeviceResponse> {\n return this.http.post<DeviceResponse>(\"/pos/v1/awake-lock\", { wakeUpLock });\n }\n\n /**\n * Adjust the terminal app brightness — POST /pos/v1/brightness.\n *\n * @param brightness Value in the range (0, 1], where 1.00 is maximum\n * brightness. A value of 0 is NOT allowed.\n * @throws {RangeError} If `brightness` is not within (0, 1].\n */\n brightness(brightness: number): Promise<DeviceResponse> {\n if (!(brightness > 0) || brightness > 1) {\n throw new RangeError(\n \"brightness must be greater than 0 and at most 1.0 (0 is not allowed).\",\n );\n }\n\n return this.http.post<DeviceResponse>(\"/pos/v1/brightness\", { brightness });\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type { SessionResponse } from \"../types/common.js\";\n\n/**\n * Session retrieval on the Local Terminal (P2P) API.\n *\n * After a sale/refund/preauth-completion returns `PROCESSING`, poll the session\n * to obtain the final outcome. The `payloadData` field is populated only once\n * the transaction has completed (with `state` SUCCESS or FAILURE).\n *\n * The ISV variant uses a trailing slash (`/pos/v1/sessions/{id}/`), handled\n * automatically when `useIsvEndpoints` is enabled.\n *\n * @see /pos/v1/sessions/{sessionId}\n */\nexport class Sessions {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Retrieve a session's full details by its id —\n * GET /pos/v1/sessions/{sessionId}.\n */\n get(sessionId: string): Promise<SessionResponse> {\n return this.http.get<SessionResponse>(\n `/pos/v1/sessions/${encodeURIComponent(sessionId)}`,\n );\n }\n}\n","import type { HttpClient } from \"../http.js\";\nimport type {\n AadeFimControlResponse,\n TransactionResponse,\n TransactionsListResponse,\n} from \"../types/common.js\";\nimport type {\n CapturePreauthParams,\n RefundParams,\n RetrieveTransactionsParams,\n SaleParams,\n UnreferencedRefundParams,\n} from \"../types/transactions.js\";\n\n/** Drop keys whose value is `undefined` so they aren't serialized. */\nfunction compact<T extends Record<string, unknown>>(obj: T): Partial<T> {\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n if (value !== undefined) out[key] = value;\n }\n return out as Partial<T>;\n}\n\n/**\n * Transaction operations on the Local Terminal (P2P) API.\n *\n * Every operation that starts a transaction returns IMMEDIATELY with a `state`\n * (typically `PROCESSING`) and a `sessionId`. The actual outcome is then polled\n * via `sessions.get()` once the cardholder has interacted with the terminal.\n *\n * Amounts are ALWAYS integers in the currency's minor unit (cents).\n *\n * Merchant vs ISV variants: the same methods serve both. Whether a request hits\n * the ISV trailing-slash path (e.g. `/pos/v1/sale/`) is controlled globally by\n * `useIsvEndpoints`. ISV sale/refund additionally accept `isvDetails`.\n *\n * @see /pos/v1/sale, /pos/v1/preauth-completion, /pos/v1/refund,\n * /pos/v1/unreferenced-refund, /pos/v1/abort, /pos/v1/aade-fim-control,\n * /pos/v1/transactions\n */\nexport class Transactions {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Initiate a sale request — POST /pos/v1/sale.\n *\n * The terminal displays the amount and waits for the card. Use\n * `sessions.get()` with the returned `sessionId` to retrieve the final result.\n */\n sale(params: SaleParams): Promise<TransactionResponse> {\n return this.http.post<TransactionResponse>(\n \"/pos/v1/sale\",\n compact({ ...params }),\n );\n }\n\n /**\n * Capture (complete) a previously pre-authorized sale —\n * POST /pos/v1/preauth-completion.\n */\n capturePreauth(params: CapturePreauthParams): Promise<TransactionResponse> {\n return this.http.post<TransactionResponse>(\n \"/pos/v1/preauth-completion\",\n compact({ ...params }),\n );\n }\n\n /**\n * Refund / cancel a transaction by its original transaction id —\n * POST /pos/v1/refund.\n */\n refund(params: RefundParams): Promise<TransactionResponse> {\n return this.http.post<TransactionResponse>(\n \"/pos/v1/refund\",\n compact({ ...params }),\n );\n }\n\n /**\n * Issue an unreferenced refund (not linked to an original transaction) —\n * POST /pos/v1/unreferenced-refund.\n */\n unreferencedRefund(\n params: UnreferencedRefundParams,\n ): Promise<TransactionResponse> {\n return this.http.post<TransactionResponse>(\n \"/pos/v1/unreferenced-refund\",\n compact({ ...params }),\n );\n }\n\n /**\n * Abort an in-progress SALE session — POST /pos/v1/abort.\n */\n abort(sessionId: string): Promise<TransactionResponse> {\n return this.http.post<TransactionResponse>(\"/pos/v1/abort\", { sessionId });\n }\n\n /**\n * Create an AADE FIM control action — POST /pos/v1/aade-fim-control.\n *\n * Creates the Echo Message (to fetch the Master Key) and the Control Message\n * (to create the Session Key), validating the Master Key, KCV and encrypted\n * Session Key (Greek AADE fiscalisation).\n *\n * @param token The FIMAS control token including the AADE token.\n */\n aadeFimControl(token: string): Promise<AadeFimControlResponse> {\n return this.http.post<AadeFimControlResponse>(\"/pos/v1/aade-fim-control\", {\n token,\n });\n }\n\n /**\n * Retrieve historical transactions with optional filters —\n * GET /pos/v1/transactions.\n *\n * `transactionTypes` may be a single id or a list. When an array is passed it\n * is sent as a comma-separated value.\n */\n retrieve(\n params: RetrieveTransactionsParams = {},\n ): Promise<TransactionsListResponse> {\n const {\n transactionTypes,\n isAadeAutonomouslyOnly,\n cardNumber,\n orderCode,\n transactionStatus,\n startDate,\n endDate,\n } = params;\n\n const query = compact({\n cardNumber,\n orderCode,\n transactionTypes: Array.isArray(transactionTypes)\n ? transactionTypes.join(\",\")\n : transactionTypes,\n transactionStatus,\n startDate,\n endDate,\n isAadeAutonomouslyOnly:\n isAadeAutonomouslyOnly === undefined\n ? undefined\n : isAadeAutonomouslyOnly\n ? \"true\"\n : \"false\",\n });\n\n return this.http.get<TransactionsListResponse>(\"/pos/v1/transactions\", {\n query,\n });\n }\n}\n","import {\n VivaLocalTerminalConfig,\n type VivaLocalTerminalConfigOptions,\n} from \"./config.js\";\nimport { HttpClient } from \"./http.js\";\nimport { Device } from \"./resources/device.js\";\nimport { Sessions } from \"./resources/sessions.js\";\nimport { Transactions } from \"./resources/transactions.js\";\n\nexport type VivaLocalTerminalClientOptions = VivaLocalTerminalConfigOptions;\n\n/**\n * Viva.com Local Terminal SDK — main entry point.\n *\n * The Local Terminal API is a PEER-TO-PEER (P2P) protocol: this client talks\n * DIRECTLY to an EFT POS terminal over the local network. There is no Viva cloud\n * endpoint and no authentication — you address the terminal's own IP and port.\n *\n * @example\n * ```ts\n * import { VivaLocalTerminalClient } from \"@qrcommunication/viva-local-terminal-sdk\";\n * import { randomUUID } from \"node:crypto\";\n *\n * const terminal = new VivaLocalTerminalClient({\n * terminalBaseUrl: \"https://192.168.1.50:8080\",\n * // self-signed terminal cert on a closed LAN:\n * verifyTls: false,\n * });\n *\n * // Start a sale (amount in cents)\n * const sessionId = randomUUID();\n * const res = await terminal.transactions.sale({\n * sessionId,\n * amount: 1170, // 11.70 EUR\n * currencyCode: 978, // EUR\n * merchantReference: \"order-123\",\n * });\n * // res.state === \"PROCESSING\"\n *\n * // Poll for the result\n * const session = await terminal.sessions.get(sessionId);\n * // session.state === \"SUCCESS\" once the cardholder has paid\n *\n * // Control the device\n * await terminal.device.screenControl(true);\n * await terminal.device.brightness(0.5);\n * ```\n */\nexport class VivaLocalTerminalClient {\n readonly transactions: Transactions;\n readonly sessions: Sessions;\n readonly device: Device;\n\n private readonly config: VivaLocalTerminalConfig;\n private readonly http: HttpClient;\n\n constructor(options: VivaLocalTerminalClientOptions) {\n this.config = new VivaLocalTerminalConfig(options);\n this.http = new HttpClient(this.config);\n\n this.transactions = new Transactions(this.http);\n this.sessions = new Sessions(this.http);\n this.device = new Device(this.http);\n }\n\n /** The resolved, immutable configuration in use by this client. */\n getConfig(): VivaLocalTerminalConfig {\n return this.config;\n }\n}\n","/**\n * Enumerations for the Viva.com Local Terminal API.\n *\n * These mirror the string/numeric values the terminal returns and accepts.\n */\n\n/**\n * The `paymentMethod` accepted by the sale request — the payment method\n * displayed to the user first by default. The user may still choose an\n * alternative method on the terminal.\n *\n * @see /pos/v1/sale\n */\nexport enum PaymentMethod {\n CARD_PRESENT = \"CardPresent\",\n IRIS = \"Iris\",\n}\n\n/**\n * The `state` value returned by the Local Terminal API.\n *\n * Returned immediately by sale/refund/abort/etc. (PROCESSING, BUSY_ERROR, ...)\n * and as the terminal/session outcome by GET /pos/v1/sessions/{sessionId}\n * (SUCCESS, FAILURE).\n *\n * @see /pos/v1/sale, /pos/v1/sessions/{sessionId}\n */\nexport enum SessionState {\n /** The operation is in progress on the terminal. */\n PROCESSING = \"PROCESSING\",\n /** Another transaction is already in progress on the terminal. */\n BUSY_ERROR = \"BUSY_ERROR\",\n /** The terminal reported a server-side error. */\n SERVER_ERROR = \"SERVER_ERROR\",\n /** The given session id could not be found (abort). */\n SESSION_NOT_FOUND_ERROR = \"SESSION_NOT_FOUND_ERROR\",\n /** The transaction completed successfully (sessions outcome). */\n SUCCESS = \"SUCCESS\",\n /** The transaction completed with an error (sessions outcome). */\n FAILURE = \"FAILURE\",\n}\n\n/**\n * The `sessionType` returned by the Local Terminal API, indicating which kind\n * of operation a session represents.\n *\n * @see /pos/v1/sale, /pos/v1/refund, /pos/v1/abort, /pos/v1/sessions/{sessionId}\n */\nexport enum SessionType {\n /** A sale transaction. */\n SALE = \"SALE\",\n /** A capture (completion) of a pre-authorized sale. */\n CAPTURE_PREAUTH = \"CAPTURE_PREAUTH\",\n /** A refund / cancellation referencing an original transaction. */\n CANCEL = \"CANCEL\",\n /** An unreferenced refund (not linked to an original transaction). */\n UNREFERENCED_REFUND = \"UNREFERENCED_REFUND\",\n /** An abort of an in-progress SALE session. */\n ABORT = \"ABORT\",\n}\n\n/**\n * Transaction type identifiers returned in transaction / session payloads\n * (`transactionTypeId`) and accepted as the `transactionTypes` query filter on\n * GET /pos/v1/transactions.\n *\n * @see /pos/v1/transactions, /pos/v1/sessions/{sessionId}\n */\nexport enum TransactionTypeId {\n SALE = 5,\n REFUND = 4,\n REVERSAL = 7,\n IRIS_SALE = 60,\n IRIS_REFUND = 61,\n IRIS_VOID = 85,\n}\n"]}
|