@fire-spark/payments-clisitef-pinpad-rn 0.7.0 → 0.7.2
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/dist/index.d.ts +25 -8
- package/dist/index.js +231 -77
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -17,12 +17,18 @@ type ClisitefPinPadOptions = {
|
|
|
17
17
|
timeZone?: string;
|
|
18
18
|
now?: () => Date;
|
|
19
19
|
confirmationTimeoutMs?: number;
|
|
20
|
+
/**
|
|
21
|
+
* Payment deadline (`policy.timeout_ms`); defaults to 180 s. A
|
|
22
|
+
* `runtime.timeout_ms` in the payment or method config overrides it.
|
|
23
|
+
*/
|
|
24
|
+
timeoutMs?: number;
|
|
20
25
|
};
|
|
21
26
|
/**
|
|
22
|
-
* CliSiTef (Software Express) on the kiosk pinpad, Brazil.
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
27
|
+
* CliSiTef (Software Express) on the kiosk pinpad, Brazil. The payment lists
|
|
28
|
+
* its `modalities` (DEBIT / CREDIT / VOUCHER); with more than one the
|
|
29
|
+
* customer picks it before the pinpad starts. The pinpad waits on the
|
|
30
|
+
* customer, so the deadline is `timeoutMs` instead of the 30 s library
|
|
31
|
+
* default.
|
|
26
32
|
*/
|
|
27
33
|
declare const clisitefPinPadPayments: (options?: ClisitefPinPadOptions) => PaymentAdapter;
|
|
28
34
|
//#endregion
|
|
@@ -80,6 +86,11 @@ declare const CLISITEF_CANCEL_CODES: ReadonlySet<number>;
|
|
|
80
86
|
declare const CLISITEF_INCONCLUSIVE_CODES: ReadonlySet<number>;
|
|
81
87
|
/** Approved (`stage 1`) without `stage 2`: undo (legacy `CONFIRMATION_TIMEOUT_MS`). */
|
|
82
88
|
declare const CLISITEF_CONFIRMATION_TIMEOUT_MS = 60000;
|
|
89
|
+
/**
|
|
90
|
+
* Payment deadline: covers the pinpad table load (`OBTENDO REGISTRO`), which
|
|
91
|
+
* can take minutes, plus card and PIN entry.
|
|
92
|
+
*/
|
|
93
|
+
declare const CLISITEF_PAYMENT_TIMEOUT_MS = 180000;
|
|
83
94
|
/** Legacy operator when the merchant config has none. */
|
|
84
95
|
declare const CLISITEF_DEFAULT_OPERATOR = "KIOSKO_BR";
|
|
85
96
|
/** Device-wide record of the last transaction, to undo it after a crash. */
|
|
@@ -117,14 +128,20 @@ declare const clisitefPinPadErrors: Readonly<Record<string, PaymentErrorEntry>>
|
|
|
117
128
|
//#endregion
|
|
118
129
|
//#region src/settings.d.ts
|
|
119
130
|
declare const CLISITEF_PINPAD_TYPES: readonly ["ANDROID_USB", "ANDROID_BT", "ANDROID_AUTO"];
|
|
120
|
-
/**
|
|
131
|
+
/**
|
|
132
|
+
* Ficha §6: filled per kiosk in the device settings. SiTef IP, store code
|
|
133
|
+
* and store CNPJ come from the payment's store settings (`config.stores`);
|
|
134
|
+
* the device values only override them.
|
|
135
|
+
*/
|
|
121
136
|
declare const clisitefPinPadDeviceFields: DeviceField[];
|
|
122
137
|
type ClisitefModality = keyof typeof CLISITEF_MODALITY;
|
|
138
|
+
type ClisitefCardModality = Exclude<ClisitefModality, "PIX">;
|
|
123
139
|
type ClisitefPinPadSettings = {
|
|
124
140
|
configure: ClisitefConfigureParams;
|
|
125
|
-
|
|
141
|
+
/** Asked to the customer when there is more than one. */
|
|
142
|
+
modalities: ClisitefCardModality[];
|
|
126
143
|
operator: string;
|
|
127
|
-
restrictions: string
|
|
144
|
+
restrictions: Partial<Record<ClisitefCardModality, string>>;
|
|
128
145
|
terminalNumber: string;
|
|
129
146
|
};
|
|
130
147
|
/** `payments.config` (merchant) + device settings; throws when incomplete. */
|
|
@@ -204,4 +221,4 @@ type ClisitefTransactionInput = {
|
|
|
204
221
|
*/
|
|
205
222
|
declare const runClisitefTransaction: (input: ClisitefTransactionInput) => Promise<ClisitefOutcome>;
|
|
206
223
|
//#endregion
|
|
207
|
-
export { CLISITEF_CANCEL_CODES, CLISITEF_CAPTURE_FIELDS, CLISITEF_COMMAND, CLISITEF_CONFIRMATION_TIMEOUT_MS, CLISITEF_DEFAULT_OPERATOR, CLISITEF_FIELD, CLISITEF_INCONCLUSIVE_CODES, CLISITEF_NO, CLISITEF_PINPAD_TYPES, CLISITEF_STORAGE_KEY, CLISITEF_YES, ClisitefFields, ClisitefModality, ClisitefOutcome, ClisitefOutcomeStatus, ClisitefPinPadOptions, ClisitefPinPadSettings, ClisitefTransactionInput, ClisitefTransactionRecord, ClisitefTransactionState, clisitefDetails, clisitefLast4, clisitefMetadata, clisitefPinPadCapabilities, clisitefPinPadDeviceFields, clisitefPinPadErrors, clisitefPinPadPayments, isOpen, readClisitefPinPadSettings, readTransaction, runClisitefTransaction, writeTransaction };
|
|
224
|
+
export { CLISITEF_CANCEL_CODES, CLISITEF_CAPTURE_FIELDS, CLISITEF_COMMAND, CLISITEF_CONFIRMATION_TIMEOUT_MS, CLISITEF_DEFAULT_OPERATOR, CLISITEF_FIELD, CLISITEF_INCONCLUSIVE_CODES, CLISITEF_NO, CLISITEF_PAYMENT_TIMEOUT_MS, CLISITEF_PINPAD_TYPES, CLISITEF_STORAGE_KEY, CLISITEF_YES, ClisitefCardModality, ClisitefFields, ClisitefModality, ClisitefOutcome, ClisitefOutcomeStatus, ClisitefPinPadOptions, ClisitefPinPadSettings, ClisitefTransactionInput, ClisitefTransactionRecord, ClisitefTransactionState, clisitefDetails, clisitefLast4, clisitefMetadata, clisitefPinPadCapabilities, clisitefPinPadDeviceFields, clisitefPinPadErrors, clisitefPinPadPayments, isOpen, readClisitefPinPadSettings, readTransaction, runClisitefTransaction, writeTransaction };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PaymentError, createPaymentError, definePaymentErrors, toPaymentError } from "@fire-spark/payments";
|
|
2
|
-
import { CLISITEF_ABORT_FLAG, CLISITEF_CONFIRM, CLISITEF_MODALITY, CLISITEF_RESULT, CLISITEF_TIMEOUT_CODE, clisitef, clisitefAdditionalParams, clisitefFiscalDateTime, formatClisitefAmount, parseClisitefMenu } from "@fire-spark/payments-clisitef-client";
|
|
2
|
+
import { CLISITEF_ABORT_FLAG, CLISITEF_CONFIRM, CLISITEF_MODALITY, CLISITEF_RESULT, CLISITEF_TIMEOUT_CODE, clisitef, clisitefAdditionalParams, clisitefDebug, clisitefDebugCall, clisitefFiscalDateTime, formatClisitefAmount, parseClisitefMenu } from "@fire-spark/payments-clisitef-client";
|
|
3
3
|
//#region src/constants.ts
|
|
4
4
|
/**
|
|
5
5
|
* `onData` commands handled by the adapter (Fiserv "Tabela de códigos de
|
|
@@ -71,6 +71,11 @@ const CLISITEF_INCONCLUSIVE_CODES = /* @__PURE__ */ new Set([
|
|
|
71
71
|
]);
|
|
72
72
|
/** Approved (`stage 1`) without `stage 2`: undo (legacy `CONFIRMATION_TIMEOUT_MS`). */
|
|
73
73
|
const CLISITEF_CONFIRMATION_TIMEOUT_MS = 6e4;
|
|
74
|
+
/**
|
|
75
|
+
* Payment deadline: covers the pinpad table load (`OBTENDO REGISTRO`), which
|
|
76
|
+
* can take minutes, plus card and PIN entry.
|
|
77
|
+
*/
|
|
78
|
+
const CLISITEF_PAYMENT_TIMEOUT_MS = 18e4;
|
|
74
79
|
/** Legacy operator when the merchant config has none. */
|
|
75
80
|
const CLISITEF_DEFAULT_OPERATOR = "KIOSKO_BR";
|
|
76
81
|
/** Device-wide record of the last transaction, to undo it after a crash. */
|
|
@@ -226,40 +231,29 @@ const CLISITEF_PINPAD_TYPES = [
|
|
|
226
231
|
"ANDROID_BT",
|
|
227
232
|
"ANDROID_AUTO"
|
|
228
233
|
];
|
|
229
|
-
/**
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
type: "text",
|
|
253
|
-
required: true
|
|
254
|
-
},
|
|
255
|
-
{
|
|
256
|
-
key: "pinpad_type",
|
|
257
|
-
label: "Pinpad connection",
|
|
258
|
-
type: "select",
|
|
259
|
-
required: true,
|
|
260
|
-
options: [...CLISITEF_PINPAD_TYPES]
|
|
261
|
-
}
|
|
262
|
-
];
|
|
234
|
+
/**
|
|
235
|
+
* Ficha §6: filled per kiosk in the device settings. SiTef IP, store code
|
|
236
|
+
* and store CNPJ come from the payment's store settings (`config.stores`);
|
|
237
|
+
* the device values only override them.
|
|
238
|
+
*/
|
|
239
|
+
const clisitefPinPadDeviceFields = [{
|
|
240
|
+
key: "terminal_number",
|
|
241
|
+
label: "SiTef terminal number",
|
|
242
|
+
type: "text",
|
|
243
|
+
required: true
|
|
244
|
+
}, {
|
|
245
|
+
key: "pinpad_type",
|
|
246
|
+
label: "Pinpad connection",
|
|
247
|
+
type: "select",
|
|
248
|
+
options: [...CLISITEF_PINPAD_TYPES]
|
|
249
|
+
}];
|
|
250
|
+
/** The API only sends the entry of the kiosk's store. */
|
|
251
|
+
const storeSettingsOf = (config) => {
|
|
252
|
+
const stores = config.stores;
|
|
253
|
+
if (!stores || typeof stores !== "object") return {};
|
|
254
|
+
const [first] = Object.values(stores);
|
|
255
|
+
return first && typeof first === "object" ? first : {};
|
|
256
|
+
};
|
|
263
257
|
const text = (value) => {
|
|
264
258
|
return typeof value === "string" ? value.trim() : "";
|
|
265
259
|
};
|
|
@@ -269,17 +263,33 @@ const notConfigured = (missing) => {
|
|
|
269
263
|
const isModality = (value) => {
|
|
270
264
|
return value === "DEBIT" || value === "CREDIT" || value === "VOUCHER";
|
|
271
265
|
};
|
|
266
|
+
/** `modalities`, or the legacy single `modality` of older configs. */
|
|
267
|
+
const listModalities = (config) => {
|
|
268
|
+
const listed = Array.isArray(config.modalities) ? config.modalities : [];
|
|
269
|
+
return (listed.length ? listed : [config.modality]).map(text).filter(Boolean);
|
|
270
|
+
};
|
|
271
|
+
/** Per modality; the legacy `restrictions` applies to all of them. */
|
|
272
|
+
const readRestrictions = (config, modalities) => {
|
|
273
|
+
const raw = config.restrictions_by_modality;
|
|
274
|
+
const byModality = raw && typeof raw === "object" ? raw : {};
|
|
275
|
+
const legacy = text(config.restrictions);
|
|
276
|
+
const entries = modalities.map((modality) => {
|
|
277
|
+
return [modality, text(byModality[modality]) || legacy];
|
|
278
|
+
});
|
|
279
|
+
return Object.fromEntries(entries.filter(([, value]) => value));
|
|
280
|
+
};
|
|
272
281
|
/** `payments.config` (merchant) + device settings; throws when incomplete. */
|
|
273
282
|
const readClisitefPinPadSettings = (config, device) => {
|
|
274
|
-
const
|
|
283
|
+
const listed = listModalities(config);
|
|
275
284
|
const cnpjEnterprise = text(config.cnpj_enterprise);
|
|
276
|
-
const
|
|
277
|
-
const
|
|
285
|
+
const store = storeSettingsOf(config);
|
|
286
|
+
const serverIp = text(device.sitef_ip) || text(store.sitef_ip);
|
|
287
|
+
const storeCode = text(device.store_code) || text(store.store_code);
|
|
278
288
|
const terminalNumber = text(device.terminal_number);
|
|
279
|
-
const cnpjStore = text(device.cnpj_store).replace(/\D/g, "");
|
|
289
|
+
const cnpjStore = (text(device.cnpj_store) || text(store.cnpj_store)).replace(/\D/g, "");
|
|
280
290
|
const pinpadType = text(device.pinpad_type) || void 0;
|
|
281
291
|
const required = {
|
|
282
|
-
|
|
292
|
+
modalities: listed.join(","),
|
|
283
293
|
cnpj_enterprise: cnpjEnterprise
|
|
284
294
|
};
|
|
285
295
|
const fromDevice = {
|
|
@@ -296,22 +306,30 @@ const readClisitefPinPadSettings = (config, device) => {
|
|
|
296
306
|
};
|
|
297
307
|
Object.assign(values, more);
|
|
298
308
|
const missing = Object.keys(values).filter((key) => !values[key]);
|
|
309
|
+
clisitefDebug("pinpad.settings", "read", {
|
|
310
|
+
config,
|
|
311
|
+
device,
|
|
312
|
+
missing
|
|
313
|
+
});
|
|
299
314
|
if (missing.length) throw notConfigured(missing);
|
|
300
|
-
if (!isModality
|
|
315
|
+
if (!listed.every(isModality)) throw notConfigured(["a valid modality"]);
|
|
316
|
+
const configure = {
|
|
317
|
+
serverIp,
|
|
318
|
+
storeCode,
|
|
319
|
+
terminalNumber,
|
|
320
|
+
additionalParams: clisitefAdditionalParams({
|
|
321
|
+
cnpjStore,
|
|
322
|
+
cnpjEnterprise,
|
|
323
|
+
pinpadType
|
|
324
|
+
})
|
|
325
|
+
};
|
|
326
|
+
const operator = text(config.operator) || "KIOSKO_BR";
|
|
327
|
+
const unique = [...new Set(listed)];
|
|
301
328
|
return {
|
|
302
|
-
configure
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
additionalParams: clisitefAdditionalParams({
|
|
307
|
-
cnpjStore,
|
|
308
|
-
cnpjEnterprise,
|
|
309
|
-
pinpadType
|
|
310
|
-
})
|
|
311
|
-
},
|
|
312
|
-
modality,
|
|
313
|
-
operator: text(config.operator) || "KIOSKO_BR",
|
|
314
|
-
restrictions: text(config.restrictions),
|
|
329
|
+
configure,
|
|
330
|
+
modalities: unique,
|
|
331
|
+
operator,
|
|
332
|
+
restrictions: readRestrictions(config, unique),
|
|
315
333
|
terminalNumber
|
|
316
334
|
};
|
|
317
335
|
};
|
|
@@ -324,6 +342,7 @@ const isRecord = (value) => {
|
|
|
324
342
|
};
|
|
325
343
|
const readTransaction = async (storage) => {
|
|
326
344
|
const raw = await storage.get(CLISITEF_STORAGE_KEY);
|
|
345
|
+
clisitefDebug("pinpad.store", "read", { raw });
|
|
327
346
|
if (!raw) return null;
|
|
328
347
|
try {
|
|
329
348
|
const parsed = JSON.parse(raw);
|
|
@@ -333,6 +352,7 @@ const readTransaction = async (storage) => {
|
|
|
333
352
|
}
|
|
334
353
|
};
|
|
335
354
|
const writeTransaction = async (storage, record) => {
|
|
355
|
+
clisitefDebug("pinpad.store", `write ${record.state}`, record);
|
|
336
356
|
await storage.set(CLISITEF_STORAGE_KEY, JSON.stringify(record));
|
|
337
357
|
};
|
|
338
358
|
const isOpen = (record) => {
|
|
@@ -391,7 +411,23 @@ const runClisitefTransaction = (input) => {
|
|
|
391
411
|
let queue = Promise.resolve();
|
|
392
412
|
let resolve = () => void 0;
|
|
393
413
|
const done = new Promise((r) => resolve = r);
|
|
414
|
+
const log = (event, data) => {
|
|
415
|
+
const flags = {
|
|
416
|
+
captured,
|
|
417
|
+
approved,
|
|
418
|
+
closing,
|
|
419
|
+
aborted
|
|
420
|
+
};
|
|
421
|
+
clisitefDebug("pinpad.transaction", event, {
|
|
422
|
+
...data && typeof data === "object" ? data : { value: data },
|
|
423
|
+
flags
|
|
424
|
+
});
|
|
425
|
+
};
|
|
394
426
|
const settle = (status, reason) => {
|
|
427
|
+
log(`settle ${status}`, {
|
|
428
|
+
...reason,
|
|
429
|
+
fields: Object.fromEntries(fields)
|
|
430
|
+
});
|
|
395
431
|
clearTimeout(watchdog);
|
|
396
432
|
for (const subscription of subscriptions) subscription.remove();
|
|
397
433
|
signal.removeEventListener("abort", onAbort);
|
|
@@ -403,6 +439,7 @@ const runClisitefTransaction = (input) => {
|
|
|
403
439
|
});
|
|
404
440
|
};
|
|
405
441
|
const undo = async (reason) => {
|
|
442
|
+
log("undo (finishTransaction 0)", reason);
|
|
406
443
|
try {
|
|
407
444
|
const p1 = {
|
|
408
445
|
confirm: CLISITEF_CONFIRM.UNDO,
|
|
@@ -418,6 +455,7 @@ const runClisitefTransaction = (input) => {
|
|
|
418
455
|
}
|
|
419
456
|
};
|
|
420
457
|
const close = async (reason) => {
|
|
458
|
+
log("close", reason);
|
|
421
459
|
if (closing) return;
|
|
422
460
|
closing = true;
|
|
423
461
|
if (!approved) await client.abortTransaction(CLISITEF_ABORT_FLAG).catch(() => 0);
|
|
@@ -425,6 +463,7 @@ const runClisitefTransaction = (input) => {
|
|
|
425
463
|
settle("declined", reason);
|
|
426
464
|
};
|
|
427
465
|
function onAbort() {
|
|
466
|
+
log("signal aborted (payment deadline)");
|
|
428
467
|
aborted = true;
|
|
429
468
|
close({
|
|
430
469
|
code: null,
|
|
@@ -432,10 +471,18 @@ const runClisitefTransaction = (input) => {
|
|
|
432
471
|
});
|
|
433
472
|
}
|
|
434
473
|
const reply = async (data) => {
|
|
474
|
+
log("reply", {
|
|
475
|
+
data,
|
|
476
|
+
skipped: closing
|
|
477
|
+
});
|
|
435
478
|
if (closing) return;
|
|
436
479
|
await client.continueTransaction(data);
|
|
437
480
|
};
|
|
438
481
|
const onData = async (event) => {
|
|
482
|
+
log("onData", {
|
|
483
|
+
...event,
|
|
484
|
+
title
|
|
485
|
+
});
|
|
439
486
|
const { command, input: text } = event;
|
|
440
487
|
if (command === CLISITEF_COMMAND.STORE_FIELD) {
|
|
441
488
|
fields.set(event.fieldId, text);
|
|
@@ -459,16 +506,24 @@ const runClisitefTransaction = (input) => {
|
|
|
459
506
|
}
|
|
460
507
|
if (SILENT.has(command)) return reply("");
|
|
461
508
|
if (command === CLISITEF_COMMAND.CONFIRM) {
|
|
509
|
+
log("ui.confirm → in", { text: text.trim() || title });
|
|
462
510
|
const yes = await ui.confirm({ text: text.trim() || title });
|
|
511
|
+
log("ui.confirm ← out", { yes });
|
|
463
512
|
return reply(yes ? "0" : "1");
|
|
464
513
|
}
|
|
465
514
|
if (command === CLISITEF_COMMAND.MENU) {
|
|
466
515
|
const options = parseClisitefMenu(text);
|
|
467
516
|
const label = title || "Selecione uma opção";
|
|
468
|
-
|
|
517
|
+
log("ui.select → in", {
|
|
469
518
|
title: label,
|
|
470
519
|
options
|
|
471
|
-
})
|
|
520
|
+
});
|
|
521
|
+
const picked = await ui.select({
|
|
522
|
+
title: label,
|
|
523
|
+
options
|
|
524
|
+
});
|
|
525
|
+
log("ui.select ← out", { picked });
|
|
526
|
+
return reply(picked);
|
|
472
527
|
}
|
|
473
528
|
if (READS.has(command)) {
|
|
474
529
|
const label = text.trim() || title || "Informe o dado";
|
|
@@ -478,7 +533,10 @@ const runClisitefTransaction = (input) => {
|
|
|
478
533
|
type: inputType(command),
|
|
479
534
|
maxLength: max
|
|
480
535
|
};
|
|
481
|
-
|
|
536
|
+
log("ui.input → in", p1);
|
|
537
|
+
const value = await ui.input(p1);
|
|
538
|
+
log("ui.input ← out", { value });
|
|
539
|
+
return reply(value);
|
|
482
540
|
}
|
|
483
541
|
const message = `Unsupported CliSiTef command ${command}`;
|
|
484
542
|
return close({
|
|
@@ -489,6 +547,7 @@ const runClisitefTransaction = (input) => {
|
|
|
489
547
|
const confirm = () => {
|
|
490
548
|
const message = "CliSiTef did not close the confirmation";
|
|
491
549
|
const expire = () => {
|
|
550
|
+
log("confirmation watchdog expired", { skipped: closing });
|
|
492
551
|
if (closing) return;
|
|
493
552
|
closing = true;
|
|
494
553
|
undo({
|
|
@@ -501,6 +560,10 @@ const runClisitefTransaction = (input) => {
|
|
|
501
560
|
confirm: CLISITEF_CONFIRM.CONFIRM,
|
|
502
561
|
...input.finish
|
|
503
562
|
};
|
|
563
|
+
log("confirm (finishTransaction 1)", {
|
|
564
|
+
...p1,
|
|
565
|
+
watchdogMs: input.confirmationTimeoutMs
|
|
566
|
+
});
|
|
504
567
|
client.finishTransaction(p1).catch((error) => {
|
|
505
568
|
if (closing) return;
|
|
506
569
|
closing = true;
|
|
@@ -513,6 +576,11 @@ const runClisitefTransaction = (input) => {
|
|
|
513
576
|
};
|
|
514
577
|
const onResult = async (event) => {
|
|
515
578
|
const { stage, resultCode: code } = event;
|
|
579
|
+
log("onTransactionResult", {
|
|
580
|
+
stage,
|
|
581
|
+
code,
|
|
582
|
+
skipped: closing
|
|
583
|
+
});
|
|
516
584
|
if (closing) return;
|
|
517
585
|
if (stage === 1 && code === CLISITEF_RESULT.OK) {
|
|
518
586
|
approved = true;
|
|
@@ -552,6 +620,10 @@ const runClisitefTransaction = (input) => {
|
|
|
552
620
|
code: errorCode(error),
|
|
553
621
|
message: errorMessage(error)
|
|
554
622
|
};
|
|
623
|
+
log("handler threw", {
|
|
624
|
+
...reason,
|
|
625
|
+
error
|
|
626
|
+
});
|
|
555
627
|
if (closing) return;
|
|
556
628
|
await close({
|
|
557
629
|
...reason,
|
|
@@ -560,6 +632,10 @@ const runClisitefTransaction = (input) => {
|
|
|
560
632
|
});
|
|
561
633
|
};
|
|
562
634
|
const begin = async () => {
|
|
635
|
+
log("begin", {
|
|
636
|
+
start: input.start,
|
|
637
|
+
finish: input.finish
|
|
638
|
+
});
|
|
563
639
|
if (signal.aborted) {
|
|
564
640
|
aborted = true;
|
|
565
641
|
return settle("declined", {
|
|
@@ -665,6 +741,36 @@ const outcomeError = (outcome, attempt) => {
|
|
|
665
741
|
fallback
|
|
666
742
|
});
|
|
667
743
|
};
|
|
744
|
+
const MODALITY_LABELS = {
|
|
745
|
+
DEBIT: "Débito",
|
|
746
|
+
CREDIT: "Crédito",
|
|
747
|
+
VOUCHER: "Voucher"
|
|
748
|
+
};
|
|
749
|
+
/** The only modality, or the one the customer picks. */
|
|
750
|
+
const chooseModality = async (ui, modalities) => {
|
|
751
|
+
const [first] = modalities;
|
|
752
|
+
if (modalities.length === 1 && first) return first;
|
|
753
|
+
const options = modalities.map((value) => {
|
|
754
|
+
return {
|
|
755
|
+
label: MODALITY_LABELS[value],
|
|
756
|
+
value
|
|
757
|
+
};
|
|
758
|
+
});
|
|
759
|
+
return ui.select({
|
|
760
|
+
title: "Selecione a forma de pagamento",
|
|
761
|
+
options
|
|
762
|
+
});
|
|
763
|
+
};
|
|
764
|
+
const debugContext = (ctx) => {
|
|
765
|
+
const { attemptReference, attempt, amount, config, deviceSettings } = ctx;
|
|
766
|
+
return {
|
|
767
|
+
attemptReference,
|
|
768
|
+
attempt,
|
|
769
|
+
amount,
|
|
770
|
+
config,
|
|
771
|
+
deviceSettings
|
|
772
|
+
};
|
|
773
|
+
};
|
|
668
774
|
const terminalError = (error) => {
|
|
669
775
|
const message = error instanceof Error ? error.message : String(error);
|
|
670
776
|
return {
|
|
@@ -674,21 +780,27 @@ const terminalError = (error) => {
|
|
|
674
780
|
};
|
|
675
781
|
};
|
|
676
782
|
/**
|
|
677
|
-
* CliSiTef (Software Express) on the kiosk pinpad, Brazil.
|
|
678
|
-
*
|
|
679
|
-
*
|
|
680
|
-
*
|
|
783
|
+
* CliSiTef (Software Express) on the kiosk pinpad, Brazil. The payment lists
|
|
784
|
+
* its `modalities` (DEBIT / CREDIT / VOUCHER); with more than one the
|
|
785
|
+
* customer picks it before the pinpad starts. The pinpad waits on the
|
|
786
|
+
* customer, so the deadline is `timeoutMs` instead of the 30 s library
|
|
787
|
+
* default.
|
|
681
788
|
*/
|
|
682
789
|
const clisitefPinPadPayments = (options = {}) => {
|
|
683
790
|
const client = options.client ?? clisitef;
|
|
684
791
|
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
685
792
|
const confirmationMs = options.confirmationTimeoutMs ?? 6e4;
|
|
793
|
+
const timeoutMs = options.timeoutMs ?? 18e4;
|
|
686
794
|
const lock = createLock();
|
|
687
795
|
const storageOf = (ctx) => {
|
|
688
796
|
return options.storage ?? ctx.storage;
|
|
689
797
|
};
|
|
690
798
|
const configure = async (settings) => {
|
|
691
799
|
const code = await client.configure(settings.configure);
|
|
800
|
+
clisitefDebug("pinpad.adapter.configure", "result", {
|
|
801
|
+
code,
|
|
802
|
+
settings
|
|
803
|
+
});
|
|
692
804
|
if (code === 0) return null;
|
|
693
805
|
client.invalidate();
|
|
694
806
|
return toPaymentError(clisitefPinPadErrors, {
|
|
@@ -701,6 +813,10 @@ const clisitefPinPadPayments = (options = {}) => {
|
|
|
701
813
|
const { fiscalCoupon, fiscalDate, time } = record;
|
|
702
814
|
const count = client.getQttPendingTransactions;
|
|
703
815
|
const pending = await count(fiscalDate, fiscalCoupon).catch(() => null);
|
|
816
|
+
clisitefDebug("pinpad.adapter.recover", "pending", {
|
|
817
|
+
pending,
|
|
818
|
+
record
|
|
819
|
+
});
|
|
704
820
|
if (pending === null) return record;
|
|
705
821
|
let next;
|
|
706
822
|
if (pending > 0) {
|
|
@@ -740,6 +856,7 @@ const clisitefPinPadPayments = (options = {}) => {
|
|
|
740
856
|
error
|
|
741
857
|
};
|
|
742
858
|
}
|
|
859
|
+
clisitefDebug("pinpad.adapter.recover", `→ ${next.state}`, next);
|
|
743
860
|
await writeTransaction(storage, next);
|
|
744
861
|
return next;
|
|
745
862
|
};
|
|
@@ -750,6 +867,10 @@ const clisitefPinPadPayments = (options = {}) => {
|
|
|
750
867
|
const storage = storageOf(ctx);
|
|
751
868
|
let record = await readTransaction(storage);
|
|
752
869
|
if (!error && record && isOpen(record)) record = await recover(storage, record);
|
|
870
|
+
clisitefDebug("pinpad.adapter.prepare", "done", {
|
|
871
|
+
error,
|
|
872
|
+
record
|
|
873
|
+
});
|
|
753
874
|
return {
|
|
754
875
|
settings,
|
|
755
876
|
error,
|
|
@@ -787,6 +908,7 @@ const clisitefPinPadPayments = (options = {}) => {
|
|
|
787
908
|
const error = createPaymentError("TERMINAL_BUSY", { attempt: ctx.attempt });
|
|
788
909
|
return failed(empty, reference, error);
|
|
789
910
|
}
|
|
911
|
+
const chosen = await chooseModality(ctx.ui, settings.modalities);
|
|
790
912
|
const date = now();
|
|
791
913
|
const fiscal = clisitefFiscalDateTime(date, options.timeZone);
|
|
792
914
|
const fiscalCoupon = String(date.getTime());
|
|
@@ -803,18 +925,18 @@ const clisitefPinPadPayments = (options = {}) => {
|
|
|
803
925
|
};
|
|
804
926
|
await writeTransaction(storage, record);
|
|
805
927
|
const s1 = {
|
|
806
|
-
modality: CLISITEF_MODALITY[
|
|
928
|
+
modality: CLISITEF_MODALITY[chosen],
|
|
807
929
|
value: formatClisitefAmount(amount),
|
|
808
930
|
...finish
|
|
809
931
|
};
|
|
810
|
-
const
|
|
932
|
+
const restrictions = settings.restrictions[chosen] ?? "";
|
|
811
933
|
const start = {
|
|
812
934
|
...s1,
|
|
813
|
-
|
|
814
|
-
restrictions
|
|
935
|
+
operator: settings.operator,
|
|
936
|
+
restrictions
|
|
815
937
|
};
|
|
816
938
|
const meta = {
|
|
817
|
-
modality:
|
|
939
|
+
modality: chosen,
|
|
818
940
|
fiscalCoupon
|
|
819
941
|
};
|
|
820
942
|
const m1 = {
|
|
@@ -855,6 +977,11 @@ const clisitefPinPadPayments = (options = {}) => {
|
|
|
855
977
|
...t1,
|
|
856
978
|
...t2
|
|
857
979
|
});
|
|
980
|
+
const fields = Object.fromEntries(outcome.fields);
|
|
981
|
+
clisitefDebug("pinpad.adapter.charge", "outcome", {
|
|
982
|
+
...outcome,
|
|
983
|
+
fields
|
|
984
|
+
});
|
|
858
985
|
const metadata = metadataOf(outcome.fields);
|
|
859
986
|
if (outcome.status === "confirmed") {
|
|
860
987
|
const details = clisitefDetails(metadata);
|
|
@@ -916,8 +1043,14 @@ const clisitefPinPadPayments = (options = {}) => {
|
|
|
916
1043
|
error: void 0
|
|
917
1044
|
};
|
|
918
1045
|
};
|
|
919
|
-
const operation = (run) => {
|
|
1046
|
+
const operation = (run, name = "terminal") => {
|
|
920
1047
|
return (ctx) => {
|
|
1048
|
+
const scope = `pinpad.terminal.${name}`;
|
|
1049
|
+
const input = {
|
|
1050
|
+
config: ctx.config,
|
|
1051
|
+
deviceSettings: ctx.deviceSettings
|
|
1052
|
+
};
|
|
1053
|
+
const traced = () => clisitefDebugCall(scope, input, task);
|
|
921
1054
|
const task = async () => {
|
|
922
1055
|
try {
|
|
923
1056
|
const data = await run(ctx);
|
|
@@ -930,7 +1063,7 @@ const clisitefPinPadPayments = (options = {}) => {
|
|
|
930
1063
|
return terminalError(error);
|
|
931
1064
|
}
|
|
932
1065
|
};
|
|
933
|
-
return lock(
|
|
1066
|
+
return lock(traced);
|
|
934
1067
|
};
|
|
935
1068
|
};
|
|
936
1069
|
const terminal = {
|
|
@@ -940,15 +1073,17 @@ const clisitefPinPadPayments = (options = {}) => {
|
|
|
940
1073
|
const error = await configure(s1);
|
|
941
1074
|
if (error) throw new Error(error.message);
|
|
942
1075
|
return "CliSiTef configured";
|
|
943
|
-
}),
|
|
944
|
-
test: operation(async () =>
|
|
945
|
-
|
|
1076
|
+
}, "init"),
|
|
1077
|
+
test: operation(async () => {
|
|
1078
|
+
return `CliSiTef ${await client.getVersion()}`;
|
|
1079
|
+
}, "test"),
|
|
1080
|
+
status: operation(async () => client.getHealth() ?? {}, "status"),
|
|
946
1081
|
extra: [{
|
|
947
1082
|
key: "submit_pending_messages",
|
|
948
1083
|
label: "Send pending messages",
|
|
949
1084
|
run: operation(async () => {
|
|
950
1085
|
return `Pending messages sent (${await client.submitPendingMessages()})`;
|
|
951
|
-
})
|
|
1086
|
+
}, "submit_pending_messages")
|
|
952
1087
|
}, {
|
|
953
1088
|
key: "release_pinpad",
|
|
954
1089
|
label: "Release pinpad",
|
|
@@ -957,7 +1092,7 @@ const clisitefPinPadPayments = (options = {}) => {
|
|
|
957
1092
|
await client.disconnectSitef();
|
|
958
1093
|
client.invalidate();
|
|
959
1094
|
return "Pinpad released";
|
|
960
|
-
})
|
|
1095
|
+
}, "release_pinpad")
|
|
961
1096
|
}]
|
|
962
1097
|
};
|
|
963
1098
|
return {
|
|
@@ -966,12 +1101,31 @@ const clisitefPinPadPayments = (options = {}) => {
|
|
|
966
1101
|
deviceFields: clisitefPinPadDeviceFields,
|
|
967
1102
|
errors: clisitefPinPadErrors,
|
|
968
1103
|
capabilities: clisitefPinPadCapabilities,
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
1104
|
+
policy: { timeout_ms: timeoutMs },
|
|
1105
|
+
start: (ctx) => {
|
|
1106
|
+
const run = () => charge(ctx);
|
|
1107
|
+
const traced = () => clisitefDebugCall("pinpad.start", debugContext(ctx), run);
|
|
1108
|
+
return lock(traced);
|
|
1109
|
+
},
|
|
1110
|
+
query: (ctx, result) => {
|
|
1111
|
+
const input = {
|
|
1112
|
+
ctx: debugContext(ctx),
|
|
1113
|
+
result
|
|
1114
|
+
};
|
|
1115
|
+
const run = () => query(ctx, result);
|
|
1116
|
+
return lock(() => clisitefDebugCall("pinpad.query", input, run));
|
|
1117
|
+
},
|
|
1118
|
+
cancel: (ctx, result) => {
|
|
1119
|
+
const input = {
|
|
1120
|
+
ctx: debugContext(ctx),
|
|
1121
|
+
result
|
|
1122
|
+
};
|
|
1123
|
+
const run = () => cancel(ctx, result);
|
|
1124
|
+
return lock(() => clisitefDebugCall("pinpad.cancel", input, run));
|
|
1125
|
+
},
|
|
972
1126
|
reversalFor: (result) => result.status === "PAID" ? "manual" : "cancel",
|
|
973
1127
|
terminal
|
|
974
1128
|
};
|
|
975
1129
|
};
|
|
976
1130
|
//#endregion
|
|
977
|
-
export { CLISITEF_CANCEL_CODES, CLISITEF_CAPTURE_FIELDS, CLISITEF_COMMAND, CLISITEF_CONFIRMATION_TIMEOUT_MS, CLISITEF_DEFAULT_OPERATOR, CLISITEF_FIELD, CLISITEF_INCONCLUSIVE_CODES, CLISITEF_NO, CLISITEF_PINPAD_TYPES, CLISITEF_STORAGE_KEY, CLISITEF_YES, clisitefDetails, clisitefLast4, clisitefMetadata, clisitefPinPadCapabilities, clisitefPinPadDeviceFields, clisitefPinPadErrors, clisitefPinPadPayments, isOpen, readClisitefPinPadSettings, readTransaction, runClisitefTransaction, writeTransaction };
|
|
1131
|
+
export { CLISITEF_CANCEL_CODES, CLISITEF_CAPTURE_FIELDS, CLISITEF_COMMAND, CLISITEF_CONFIRMATION_TIMEOUT_MS, CLISITEF_DEFAULT_OPERATOR, CLISITEF_FIELD, CLISITEF_INCONCLUSIVE_CODES, CLISITEF_NO, CLISITEF_PAYMENT_TIMEOUT_MS, CLISITEF_PINPAD_TYPES, CLISITEF_STORAGE_KEY, CLISITEF_YES, clisitefDetails, clisitefLast4, clisitefMetadata, clisitefPinPadCapabilities, clisitefPinPadDeviceFields, clisitefPinPadErrors, clisitefPinPadPayments, isOpen, readClisitefPinPadSettings, readTransaction, runClisitefTransaction, writeTransaction };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fire-spark/payments-clisitef-pinpad-rn",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"description": "Fire Spark CliSiTef (Software Express) pinpad adapter for Brazil: debit, credit and voucher on Android kiosks",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
}
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@fire-spark/payments": "^0.7.
|
|
21
|
-
"@fire-spark/payments-clisitef-client": "^0.7.
|
|
22
|
-
"@fire-spark/types": "^0.7.
|
|
20
|
+
"@fire-spark/payments": "^0.7.2",
|
|
21
|
+
"@fire-spark/payments-clisitef-client": "^0.7.2",
|
|
22
|
+
"@fire-spark/types": "^0.7.2"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"tsdown": "^0.22.14",
|