@fire-spark/payments-clisitef-pinpad-rn 0.8.2 → 0.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -17,6 +17,8 @@ type ClisitefPinPadOptions = {
17
17
  timeZone?: string;
18
18
  now?: () => Date;
19
19
  confirmationTimeoutMs?: number;
20
+ /** Wait for the `stage 1` of a released leftover routine; defaults to 15 s. */
21
+ releaseTimeoutMs?: number;
20
22
  /**
21
23
  * Payment deadline (`policy.timeout_ms`); defaults to 180 s. A
22
24
  * `runtime.timeout_ms` in the payment or method config overrides it.
@@ -66,13 +68,11 @@ declare const CLISITEF_COMMAND: {
66
68
  /** `continueTransaction` answers to `CONFIRM` (20). */
67
69
  declare const CLISITEF_YES = "0";
68
70
  declare const CLISITEF_NO = "1";
69
- /** `continueTransaction` answer to `CHECK_ABORT` (23): interrupt. */
70
- declare const CLISITEF_INTERRUPT = "1";
71
71
  /**
72
- * Commands answered after `abortTransaction(-1)` without `stage 1`; then the
73
- * adapter stops answering and leaves the record open (legacy criterion).
72
+ * Wait for the `stage 1` of a routine a previous charge left open, after
73
+ * answering it with `abortTransaction(-1)`; then the charge starts anyway.
74
74
  */
75
- declare const CLISITEF_DRAIN_LIMIT = 10;
75
+ declare const CLISITEF_RELEASE_TIMEOUT_MS = 15000;
76
76
  /** `TipoCampo` ids stored with `STORE_FIELD` (Fiserv "Tabela TipoCampo"). */
77
77
  declare const CLISITEF_FIELD: {
78
78
  readonly TRANSACTION_DATE: 105;
@@ -350,4 +350,4 @@ type ClisitefTransactionInput = {
350
350
  */
351
351
  declare const runClisitefTransaction: (input: ClisitefTransactionInput) => Promise<ClisitefOutcome>;
352
352
  //#endregion
353
- export { CLISITEF_CANCEL_CODES, CLISITEF_CAPTURE_FIELDS, CLISITEF_COMMAND, CLISITEF_CONFIRMATION_TIMEOUT_MS, CLISITEF_DEFAULT_OPERATOR, CLISITEF_DRAIN_LIMIT, CLISITEF_FIELD, CLISITEF_INCONCLUSIVE_CODES, CLISITEF_INTERRUPT, CLISITEF_MODALITIES, CLISITEF_NO, CLISITEF_PAYMENT_TIMEOUT_MS, CLISITEF_PINPAD_TYPES, CLISITEF_REFUNDS_KEY, CLISITEF_REFUND_STORAGE_KEY, CLISITEF_REVERSALS_KEY, CLISITEF_STORAGE_KEY, CLISITEF_VOID_FIELD, CLISITEF_YES, ClisitefFields, ClisitefMenuDecision, ClisitefMenuPurpose, ClisitefModality, ClisitefOutcome, ClisitefOutcomeStatus, ClisitefPendingReversal, ClisitefPinPadOptions, ClisitefPinPadSettings, ClisitefProviderMetadata, ClisitefTransactionInput, ClisitefTransactionRecord, ClisitefTransactionState, ClisitefVoidContext, ClisitefVoidDeps, clisitefDetails, clisitefLast4, clisitefMetadata, clisitefModalityOf, clisitefPinPadCapabilities, clisitefPinPadDeviceFields, clisitefPinPadErrors, clisitefPinPadPayments, clisitefVoidMetadata, dropReversal, isClisitefModality, isOpen, queueReversal, readClisitefPinPadSettings, readPendingReversals, readRefund, readTransaction, redactSecrets, resolveClisitefMenu, runClisitefTransaction, runClisitefVoid, writeRefund, writeTransaction };
353
+ export { CLISITEF_CANCEL_CODES, CLISITEF_CAPTURE_FIELDS, CLISITEF_COMMAND, CLISITEF_CONFIRMATION_TIMEOUT_MS, CLISITEF_DEFAULT_OPERATOR, CLISITEF_FIELD, CLISITEF_INCONCLUSIVE_CODES, CLISITEF_MODALITIES, CLISITEF_NO, CLISITEF_PAYMENT_TIMEOUT_MS, CLISITEF_PINPAD_TYPES, CLISITEF_REFUNDS_KEY, CLISITEF_REFUND_STORAGE_KEY, CLISITEF_RELEASE_TIMEOUT_MS, CLISITEF_REVERSALS_KEY, CLISITEF_STORAGE_KEY, CLISITEF_VOID_FIELD, CLISITEF_YES, ClisitefFields, ClisitefMenuDecision, ClisitefMenuPurpose, ClisitefModality, ClisitefOutcome, ClisitefOutcomeStatus, ClisitefPendingReversal, ClisitefPinPadOptions, ClisitefPinPadSettings, ClisitefProviderMetadata, ClisitefTransactionInput, ClisitefTransactionRecord, ClisitefTransactionState, ClisitefVoidContext, ClisitefVoidDeps, clisitefDetails, clisitefLast4, clisitefMetadata, clisitefModalityOf, clisitefPinPadCapabilities, clisitefPinPadDeviceFields, clisitefPinPadErrors, clisitefPinPadPayments, clisitefVoidMetadata, dropReversal, isClisitefModality, isOpen, queueReversal, readClisitefPinPadSettings, readPendingReversals, readRefund, readTransaction, redactSecrets, resolveClisitefMenu, runClisitefTransaction, runClisitefVoid, writeRefund, writeTransaction };
package/dist/index.js CHANGED
@@ -34,13 +34,11 @@ const CLISITEF_COMMAND = {
34
34
  /** `continueTransaction` answers to `CONFIRM` (20). */
35
35
  const CLISITEF_YES = "0";
36
36
  const CLISITEF_NO = "1";
37
- /** `continueTransaction` answer to `CHECK_ABORT` (23): interrupt. */
38
- const CLISITEF_INTERRUPT = "1";
39
37
  /**
40
- * Commands answered after `abortTransaction(-1)` without `stage 1`; then the
41
- * adapter stops answering and leaves the record open (legacy criterion).
38
+ * Wait for the `stage 1` of a routine a previous charge left open, after
39
+ * answering it with `abortTransaction(-1)`; then the charge starts anyway.
42
40
  */
43
- const CLISITEF_DRAIN_LIMIT = 10;
41
+ const CLISITEF_RELEASE_TIMEOUT_MS = 15e3;
44
42
  /** `TipoCampo` ids stored with `STORE_FIELD` (Fiserv "Tabela TipoCampo"). */
45
43
  const CLISITEF_FIELD = {
46
44
  TRANSACTION_DATE: 105,
@@ -468,7 +466,6 @@ const runClisitefTransaction = (input) => {
468
466
  let aborted = false;
469
467
  /** Why the adapter asked to interrupt; `stage 1` has not arrived yet. */
470
468
  let givingUp = null;
471
- let drained = 0;
472
469
  let watchdog;
473
470
  let queue = Promise.resolve();
474
471
  let resolve = () => void 0;
@@ -517,20 +514,22 @@ const runClisitefTransaction = (input) => {
517
514
  });
518
515
  }
519
516
  };
520
- /** Stops answering and leaves the record open: `stage 1` never came. */
517
+ /**
518
+ * Stops waiting: `stage 1` never came. The routine may still be open in
519
+ * CliSiTef; the next charge releases it (`open` outcome).
520
+ */
521
521
  const abandon = () => {
522
- log("abandon", {
523
- drained,
524
- skipped: closing
525
- });
522
+ log("abandon", { skipped: closing });
526
523
  if (closing || !givingUp) return;
527
524
  closing = true;
528
525
  settle("open", givingUp);
529
526
  };
530
527
  /**
531
- * Asks CliSiTef to interrupt (`Continua = -1`) and keeps the loop running
532
- * until `stage 1`: dropping it earlier leaves the routine open (`-12`). An
533
- * approval already confirmed with `finishTransaction(1)` is undone instead.
528
+ * Marks the transaction to interrupt. CliSiTef only takes `Continua = -1`
529
+ * as the answer to an `onData` (Interface Simplificada §5.3), so nothing is
530
+ * sent here: the pending or next event is answered with
531
+ * `abortTransaction(-1)` until `stage 1`. An approval already confirmed
532
+ * with `finishTransaction(1)` is undone instead.
534
533
  */
535
534
  const giveUp = (reason) => {
536
535
  log("give up", reason);
@@ -542,7 +541,6 @@ const runClisitefTransaction = (input) => {
542
541
  }
543
542
  givingUp = reason;
544
543
  watchdog = setTimeout(abandon, input.confirmationTimeoutMs);
545
- client.abortTransaction(CLISITEF_ABORT_FLAG).catch(() => 0);
546
544
  };
547
545
  function onAbort() {
548
546
  log("signal aborted (payment deadline)");
@@ -573,19 +571,21 @@ const runClisitefTransaction = (input) => {
573
571
  expiresAt: null
574
572
  });
575
573
  };
576
- /** Answers without the UI while CliSiTef winds down after the abort. */
574
+ /**
575
+ * Answers every event after giving up with `Continua = -1`, without the UI
576
+ * and without a count: a `continueTransaction` here would tell CliSiTef to
577
+ * keep going (PIX keeps polling the server for minutes).
578
+ */
577
579
  const drain = async (event) => {
578
- drained += 1;
579
580
  log("drain", {
580
581
  command: event.command,
581
- drained
582
+ skipped: closing
582
583
  });
583
- if (drained > 10) return abandon();
584
584
  const { command } = event;
585
585
  if (command === CLISITEF_COMMAND.STORE_FIELD) store(event);
586
586
  if (command === CLISITEF_COMMAND.QR_HIDE) clearQr();
587
- const interrupt = command === CLISITEF_COMMAND.CHECK_ABORT;
588
- return reply(interrupt ? "1" : "");
587
+ if (closing) return;
588
+ await client.abortTransaction(CLISITEF_ABORT_FLAG);
589
589
  };
590
590
  /** A UI answer that arrives after giving up is not sent. */
591
591
  const respond = (event, data) => {
@@ -1378,7 +1378,10 @@ const clisitefPinPadPayments = (options = {}) => {
1378
1378
  const now = options.now ?? (() => /* @__PURE__ */ new Date());
1379
1379
  const confirmationMs = options.confirmationTimeoutMs ?? 6e4;
1380
1380
  const timeoutMs = options.timeoutMs ?? 18e4;
1381
+ const releaseMs = options.releaseTimeoutMs ?? 15e3;
1381
1382
  const lock = createLock();
1383
+ /** A charge of this process gave up without `stage 1` (`open`). */
1384
+ let leftOpen = false;
1382
1385
  const storageOf = (ctx) => {
1383
1386
  return options.storage ?? ctx.storage;
1384
1387
  };
@@ -1455,12 +1458,38 @@ const clisitefPinPadPayments = (options = {}) => {
1455
1458
  return error?.provider_code === "-12";
1456
1459
  };
1457
1460
  /**
1458
- * Interrupts a leftover native session so `configure` can run again. No
1459
- * `submitPendingMessages` or `closePinPad` here: right after an abort they
1460
- * left the native thread hung in production.
1461
+ * Ends an iterative routine left open in this process (`-12`). CliSiTef
1462
+ * takes `Continua = -1` only as the answer to an `onData`, so the pending
1463
+ * event and every new one get `abortTransaction(-1)` until the routine's
1464
+ * `stage 1`, which is dropped. No `submitPendingMessages` or `closePinPad`
1465
+ * here: right after an abort they left the native thread hung in production.
1461
1466
  */
1462
- const releaseNative = async () => {
1463
- await client.abortTransaction(CLISITEF_ABORT_FLAG).catch(() => 0);
1467
+ const releaseRoutine = () => {
1468
+ clisitefDebug("pinpad.adapter.release", "routine", { releaseMs });
1469
+ leftOpen = false;
1470
+ const subscriptions = [];
1471
+ let timer;
1472
+ const abort = () => {
1473
+ client.abortTransaction(CLISITEF_ABORT_FLAG).catch(() => 0);
1474
+ };
1475
+ return new Promise((resolve) => {
1476
+ const done = () => {
1477
+ clearTimeout(timer);
1478
+ for (const subscription of subscriptions) subscription.remove();
1479
+ client.invalidate();
1480
+ resolve();
1481
+ };
1482
+ subscriptions.push(client.onData(abort));
1483
+ subscriptions.push(client.onTransactionResult((event) => {
1484
+ if (event.stage === 1) done();
1485
+ }));
1486
+ timer = setTimeout(done, releaseMs);
1487
+ abort();
1488
+ });
1489
+ };
1490
+ /** `startTransaction` refused: a previous routine is still open. */
1491
+ const isStartBusy = (outcome) => {
1492
+ return outcome.status === "declined" && outcome.code === -12 && !outcome.aborted && outcome.fields.size === 0;
1464
1493
  };
1465
1494
  /** Open, or flagged for review with the coupon maybe still pending. */
1466
1495
  const recoverable = (record) => {
@@ -1469,10 +1498,11 @@ const clisitefPinPadPayments = (options = {}) => {
1469
1498
  /** Configures and resolves the open records of the device, if any. */
1470
1499
  const prepare = async (ctx) => {
1471
1500
  const settings = readClisitefPinPadSettings(ctx.config, ctx.deviceSettings);
1501
+ if (leftOpen) await releaseRoutine();
1472
1502
  let error = await configure(settings);
1473
1503
  if (isConfigureBusy(error)) {
1474
1504
  clisitefDebug("pinpad.adapter.release", "busy", {});
1475
- await releaseNative();
1505
+ await releaseRoutine();
1476
1506
  error = await configure(settings);
1477
1507
  }
1478
1508
  const storage = storageOf(ctx);
@@ -1598,11 +1628,17 @@ const clisitefPinPadPayments = (options = {}) => {
1598
1628
  modality: chosen,
1599
1629
  purpose: "charge"
1600
1630
  };
1601
- const outcome = await runClisitefTransaction({
1631
+ const run = () => runClisitefTransaction({
1602
1632
  ...t1,
1603
1633
  ...t2,
1604
1634
  ...t3
1605
1635
  });
1636
+ let outcome = await run();
1637
+ if (isStartBusy(outcome)) {
1638
+ await releaseRoutine();
1639
+ if (!await configure(settings)) outcome = await run();
1640
+ }
1641
+ if (outcome.status === "open") leftOpen = true;
1606
1642
  const fields = Object.fromEntries(outcome.fields);
1607
1643
  clisitefDebug("pinpad.adapter.charge", "outcome", {
1608
1644
  ...outcome,
@@ -1822,4 +1858,4 @@ const clisitefPinPadPayments = (options = {}) => {
1822
1858
  };
1823
1859
  };
1824
1860
  //#endregion
1825
- export { CLISITEF_CANCEL_CODES, CLISITEF_CAPTURE_FIELDS, CLISITEF_COMMAND, CLISITEF_CONFIRMATION_TIMEOUT_MS, CLISITEF_DEFAULT_OPERATOR, CLISITEF_DRAIN_LIMIT, CLISITEF_FIELD, CLISITEF_INCONCLUSIVE_CODES, CLISITEF_INTERRUPT, CLISITEF_MODALITIES, CLISITEF_NO, CLISITEF_PAYMENT_TIMEOUT_MS, CLISITEF_PINPAD_TYPES, CLISITEF_REFUNDS_KEY, CLISITEF_REFUND_STORAGE_KEY, CLISITEF_REVERSALS_KEY, CLISITEF_STORAGE_KEY, CLISITEF_VOID_FIELD, CLISITEF_YES, clisitefDetails, clisitefLast4, clisitefMetadata, clisitefModalityOf, clisitefPinPadCapabilities, clisitefPinPadDeviceFields, clisitefPinPadErrors, clisitefPinPadPayments, clisitefVoidMetadata, dropReversal, isClisitefModality, isOpen, queueReversal, readClisitefPinPadSettings, readPendingReversals, readRefund, readTransaction, redactSecrets, resolveClisitefMenu, runClisitefTransaction, runClisitefVoid, writeRefund, writeTransaction };
1861
+ export { CLISITEF_CANCEL_CODES, CLISITEF_CAPTURE_FIELDS, CLISITEF_COMMAND, CLISITEF_CONFIRMATION_TIMEOUT_MS, CLISITEF_DEFAULT_OPERATOR, CLISITEF_FIELD, CLISITEF_INCONCLUSIVE_CODES, CLISITEF_MODALITIES, CLISITEF_NO, CLISITEF_PAYMENT_TIMEOUT_MS, CLISITEF_PINPAD_TYPES, CLISITEF_REFUNDS_KEY, CLISITEF_REFUND_STORAGE_KEY, CLISITEF_RELEASE_TIMEOUT_MS, CLISITEF_REVERSALS_KEY, CLISITEF_STORAGE_KEY, CLISITEF_VOID_FIELD, CLISITEF_YES, clisitefDetails, clisitefLast4, clisitefMetadata, clisitefModalityOf, clisitefPinPadCapabilities, clisitefPinPadDeviceFields, clisitefPinPadErrors, clisitefPinPadPayments, clisitefVoidMetadata, dropReversal, isClisitefModality, isOpen, queueReversal, readClisitefPinPadSettings, readPendingReversals, readRefund, readTransaction, redactSecrets, resolveClisitefMenu, runClisitefTransaction, runClisitefVoid, writeRefund, writeTransaction };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fire-spark/payments-clisitef-pinpad-rn",
3
- "version": "0.8.2",
3
+ "version": "0.8.3",
4
4
  "description": "Fire Spark CliSiTef (Software Express) pinpad adapter for Brazil: debit, credit, voucher and Pix with automatic voids 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.8.2",
21
- "@fire-spark/payments-clisitef-client": "^0.8.2",
22
- "@fire-spark/types": "^0.8.2"
20
+ "@fire-spark/payments": "^0.8.3",
21
+ "@fire-spark/payments-clisitef-client": "^0.8.3",
22
+ "@fire-spark/types": "^0.8.3"
23
23
  },
24
24
  "devDependencies": {
25
25
  "tsdown": "^0.22.14",