@fire-spark/payments-clisitef-client 0.7.0 → 0.7.1

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.
@@ -47,6 +47,8 @@ internal class BoundedRunner {
47
47
  Lane.ABORT -> abort
48
48
  Lane.RECOVERY -> recovery
49
49
  }
50
+ // TODO(debug-clisitef): remove.
51
+ Log.d(DEBUG_TAG, "runner.$operation › queued lane=$lane timeoutMs=$timeoutMs")
50
52
  val timer = if (timeoutMs > 0) {
51
53
  watchdog.schedule({
52
54
  if (settled.compareAndSet(false, true)) {
@@ -62,6 +64,8 @@ internal class BoundedRunner {
62
64
  }
63
65
  try {
64
66
  target.execute {
67
+ // TODO(debug-clisitef): remove.
68
+ Log.d(DEBUG_TAG, "runner.$operation › running thread=${Thread.currentThread().name} waited=${System.currentTimeMillis() - startedAt}ms")
65
69
  val outcome = runCatching(block)
66
70
  timer?.cancel(false)
67
71
  if (!settled.compareAndSet(false, true)) {
@@ -70,6 +74,8 @@ internal class BoundedRunner {
70
74
  }
71
75
  lastCallOperation = operation
72
76
  lastCallDurationMs = System.currentTimeMillis() - startedAt
77
+ // TODO(debug-clisitef): remove.
78
+ Log.d(DEBUG_TAG, "runner.$operation › settled ok=${outcome.isSuccess} value=${outcome.getOrNull()} error=${outcome.exceptionOrNull()?.message} ms=$lastCallDurationMs")
73
79
  outcome
74
80
  .onSuccess {
75
81
  consecutiveTimeouts.set(0)
@@ -122,6 +128,8 @@ internal class BoundedRunner {
122
128
 
123
129
  private companion object {
124
130
  const val TAG = "CliSiTef"
131
+ // TODO(debug-clisitef): remove.
132
+ const val DEBUG_TAG = "CLISITEF_DEBUG"
125
133
 
126
134
  fun daemon(name: String): ThreadFactory {
127
135
  val sequence = AtomicInteger(0)
@@ -17,14 +17,37 @@ internal class CliSiTefBridge(
17
17
  private val listener = object : ICliSiTefListener {
18
18
  override fun onData(stage: Int, command: Int, fieldId: Int, minLength: Int, maxLength: Int, input: ByteArray?) {
19
19
  val text = input?.let { String(it) } ?: ""
20
+ // TODO(debug-clisitef): remove.
21
+ debug("listener.onData", "stage=$stage command=$command fieldId=$fieldId min=$minLength max=$maxLength input=\"$text\"")
20
22
  onData(DataEvent(stage, command, fieldId, minLength, maxLength, text))
21
23
  }
22
24
 
23
25
  override fun onTransactionResult(stage: Int, resultCode: Int) {
26
+ // TODO(debug-clisitef): remove.
27
+ debug("listener.onTransactionResult", "stage=$stage resultCode=$resultCode")
24
28
  onResult(stage, resultCode)
25
29
  }
26
30
  }
27
31
 
32
+ // TODO(debug-clisitef): remove with every `traced` / `debug` call.
33
+ private fun debug(scope: String, message: String) {
34
+ Log.d(DEBUG_TAG, "$scope › $message [thread=${Thread.currentThread().name}]")
35
+ }
36
+
37
+ // TODO(debug-clisitef): remove; call `block()` directly.
38
+ private fun <T> traced(scope: String, input: String, block: () -> T): T {
39
+ val startedAt = System.currentTimeMillis()
40
+ debug(scope, "→ jar in $input")
41
+ try {
42
+ val output = block()
43
+ debug(scope, "← jar out $output (${System.currentTimeMillis() - startedAt}ms)")
44
+ return output
45
+ } catch (error: Throwable) {
46
+ debug(scope, "✕ jar error ${error.javaClass.simpleName}: ${error.message} (${System.currentTimeMillis() - startedAt}ms)")
47
+ throw error
48
+ }
49
+ }
50
+
28
51
  init {
29
52
  // CliSiTef copies CLSIT on instantiation but does not overwrite an existing copy, and files/
30
53
  // survives app updates; sync first so this APK's CLSIT wins.
@@ -37,47 +60,66 @@ internal class CliSiTefBridge(
37
60
 
38
61
  fun configure(params: ConfigureParams): Int {
39
62
  val additional = withAppDir(params.additionalParams, appDirectory)
40
- return clisitef.configure(params.serverIp, params.storeCode, params.terminalNumber, additional)
63
+ val input = "serverIp=${params.serverIp} storeCode=${params.storeCode} terminal=${params.terminalNumber} additional=$additional"
64
+ return traced("configure", input) {
65
+ clisitef.configure(params.serverIp, params.storeCode, params.terminalNumber, additional)
66
+ }
41
67
  }
42
68
 
43
- fun start(params: StartParams): Int = clisitef.startTransaction(
44
- listener,
45
- params.modality,
46
- params.value,
47
- params.fiscalCoupon,
48
- params.fiscalDate,
49
- params.time,
50
- params.operator,
51
- params.restrictions
52
- )
69
+ fun start(params: StartParams): Int {
70
+ val input = "modality=${params.modality} value=${params.value} coupon=${params.fiscalCoupon} " +
71
+ "date=${params.fiscalDate} time=${params.time} operator=${params.operator} restrictions=${params.restrictions}"
72
+ return traced("startTransaction", input) {
73
+ clisitef.startTransaction(
74
+ listener,
75
+ params.modality,
76
+ params.value,
77
+ params.fiscalCoupon,
78
+ params.fiscalDate,
79
+ params.time,
80
+ params.operator,
81
+ params.restrictions
82
+ )
83
+ }
84
+ }
53
85
 
54
- fun continueTransaction(data: String): Int = clisitef.continueTransaction(data)
86
+ fun continueTransaction(data: String): Int =
87
+ traced("continueTransaction", "data=\"$data\"") { clisitef.continueTransaction(data) }
55
88
 
56
89
  /** Without coupon, date and time the library reuses the ones from `startTransaction`. */
57
90
  fun finish(params: FinishParams): Int {
58
91
  val full = params.fiscalCoupon.isNotEmpty() && params.fiscalDate.isNotEmpty() && params.time.isNotEmpty()
59
- if (!full) return clisitef.finishTransaction(params.confirm)
60
- return clisitef.finishTransaction(params.confirm, params.fiscalCoupon, params.fiscalDate, params.time, params.additionalParams)
92
+ val input = "confirm=${params.confirm} full=$full coupon=${params.fiscalCoupon} " +
93
+ "date=${params.fiscalDate} time=${params.time} additional=${params.additionalParams}"
94
+ return traced("finishTransaction", input) {
95
+ if (!full) return@traced clisitef.finishTransaction(params.confirm)
96
+ clisitef.finishTransaction(params.confirm, params.fiscalCoupon, params.fiscalDate, params.time, params.additionalParams)
97
+ }
61
98
  }
62
99
 
63
- fun abort(continueFlag: Int): Int = clisitef.abortTransaction(continueFlag)
100
+ fun abort(continueFlag: Int): Int =
101
+ traced("abortTransaction", "continueFlag=$continueFlag") { clisitef.abortTransaction(continueFlag) }
64
102
 
65
103
  fun pendingCount(fiscalDate: String, fiscalCoupon: String): Int =
66
- clisitef.getQttPendingTransactions(fiscalDate, fiscalCoupon)
104
+ traced("getQttPendingTransactions", "date=$fiscalDate coupon=$fiscalCoupon") {
105
+ clisitef.getQttPendingTransactions(fiscalDate, fiscalCoupon)
106
+ }
67
107
 
68
- fun submitPending(): Int = clisitef.submitPendingMessages()
108
+ fun submitPending(): Int = traced("submitPendingMessages", "") { clisitef.submitPendingMessages() }
69
109
 
70
- fun version(): String = clisitef.getVersion()
110
+ fun version(): String = traced("getVersion", "") { clisitef.getVersion() }
71
111
 
72
- fun closePinPad(): Int = clisitef.closePinPad()
112
+ fun closePinPad(): Int = traced("closePinPad", "") { clisitef.closePinPad() }
73
113
 
74
114
  fun disconnectSitef(): Int {
75
- clisitef.disconnectSitef()
115
+ traced("disconnectSitef", "") { clisitef.disconnectSitef() }
76
116
  return 0
77
117
  }
78
118
 
79
119
  companion object {
80
120
  private const val TAG = "CliSiTef"
121
+ // TODO(debug-clisitef): remove.
122
+ private const val DEBUG_TAG = "CLISITEF_DEBUG"
81
123
  private const val CLSIT = "CLSIT"
82
124
  private val APP_DIR = Regex("DiretorioApp=[^;\\]]*")
83
125
 
@@ -101,6 +143,8 @@ internal class CliSiTefBridge(
101
143
  val temp = File(context.filesDir, "$CLSIT.new")
102
144
  try {
103
145
  val shipped = context.assets.open(CLSIT).use { it.readBytes() }
146
+ // TODO(debug-clisitef): remove.
147
+ Log.d(DEBUG_TAG, "syncClsit › shipped=${shipped.size}B target=${target.absolutePath} exists=${target.isFile}")
104
148
  if (shipped.isEmpty()) return
105
149
  if (target.isFile && target.readBytes().contentEquals(shipped)) return
106
150
  temp.outputStream().use { out ->
@@ -12,6 +12,8 @@ class CliSiTefModule : Module() {
12
12
  private fun bridge(): CliSiTefBridge = synchronized(this) {
13
13
  bridge?.let { return it }
14
14
  val context = appContext.reactContext?.applicationContext ?: throw Exceptions.ReactContextLost()
15
+ // TODO(debug-clisitef): remove.
16
+ android.util.Log.d("CLISITEF_DEBUG", "module › creating bridge dataDir=${context.applicationInfo.dataDir}")
15
17
  CliSiTefBridge(
16
18
  context,
17
19
  onData = { event -> sendEvent("onData", event.toMap()) },
package/dist/index.d.mts CHANGED
@@ -137,6 +137,12 @@ declare const CLISITEF_JS_GRACE_MS = 5000;
137
137
  declare const CLISITEF_DEFAULT_TIME_ZONE = "America/Sao_Paulo";
138
138
  declare const CLISITEF_DEFAULT_PINPAD = "ANDROID_USB";
139
139
  //#endregion
140
+ //#region src/debug.d.ts
141
+ /** Logs one step: `[CLISITEF_DEBUG] scope › event { data }`. */
142
+ declare const clisitefDebug: (scope: string, event: string, data?: unknown) => void;
143
+ /** Logs the input, the output or the error, and the duration of a call. */
144
+ declare const clisitefDebugCall: <T>(scope: string, input: unknown, call: () => Promise<T>) => Promise<T>;
145
+ //#endregion
140
146
  //#region src/errors.d.ts
141
147
  declare class ClisitefUnavailableError extends Error {
142
148
  readonly code = "CLISITEF_UNAVAILABLE";
@@ -190,4 +196,4 @@ declare const parseClisitefMenu: (input: string) => ClisitefMenuOption[];
190
196
  /** Shared client bound to the device's native module; one per app. */
191
197
  declare const clisitef: ClisitefClient;
192
198
  //#endregion
193
- export { CLISITEF_ABORT_FLAG, CLISITEF_CONFIRM, CLISITEF_DEFAULT_PINPAD, CLISITEF_DEFAULT_TIME_ZONE, CLISITEF_JS_GRACE_MS, CLISITEF_MODALITY, CLISITEF_RESULT, CLISITEF_TIMEOUT_CODE, CLISITEF_TIMEOUT_MS, ClisitefAdditionalParamsInput, ClisitefClient, ClisitefClientOptions, ClisitefConfigureParams, ClisitefDataEvent, ClisitefEvents, ClisitefFinishParams, ClisitefFiscalDateTime, ClisitefHealth, ClisitefMenuOption, ClisitefNativeModule, ClisitefOperation, ClisitefResultEvent, ClisitefStartParams, ClisitefSubscription, ClisitefTimeoutError, ClisitefTimeoutInfo, ClisitefUnavailableError, clisitef, clisitefAdditionalParams, clisitefConfigSignature, clisitefFiscalDateTime, createClisitefClient, formatClisitefAmount, parseClisitefMenu, toClisitefTimeout };
199
+ export { CLISITEF_ABORT_FLAG, CLISITEF_CONFIRM, CLISITEF_DEFAULT_PINPAD, CLISITEF_DEFAULT_TIME_ZONE, CLISITEF_JS_GRACE_MS, CLISITEF_MODALITY, CLISITEF_RESULT, CLISITEF_TIMEOUT_CODE, CLISITEF_TIMEOUT_MS, ClisitefAdditionalParamsInput, ClisitefClient, ClisitefClientOptions, ClisitefConfigureParams, ClisitefDataEvent, ClisitefEvents, ClisitefFinishParams, ClisitefFiscalDateTime, ClisitefHealth, ClisitefMenuOption, ClisitefNativeModule, ClisitefOperation, ClisitefResultEvent, ClisitefStartParams, ClisitefSubscription, ClisitefTimeoutError, ClisitefTimeoutInfo, ClisitefUnavailableError, clisitef, clisitefAdditionalParams, clisitefConfigSignature, clisitefDebug, clisitefDebugCall, clisitefFiscalDateTime, createClisitefClient, formatClisitefAmount, parseClisitefMenu, toClisitefTimeout };
package/dist/index.mjs CHANGED
@@ -36,6 +36,49 @@ const CLISITEF_JS_GRACE_MS = 5e3;
36
36
  const CLISITEF_DEFAULT_TIME_ZONE = "America/Sao_Paulo";
37
37
  const CLISITEF_DEFAULT_PINPAD = "ANDROID_USB";
38
38
  //#endregion
39
+ //#region src/debug.ts
40
+ const TAG = "[CLISITEF_DEBUG]";
41
+ const elapsed = (startedAt) => Date.now() - startedAt;
42
+ const safe = (value) => {
43
+ if (value instanceof Map) return Object.fromEntries(value);
44
+ if (value instanceof Error) {
45
+ const code = value.code;
46
+ return {
47
+ name: value.name,
48
+ message: value.message,
49
+ code
50
+ };
51
+ }
52
+ return value;
53
+ };
54
+ /** Logs one step: `[CLISITEF_DEBUG] scope › event { data }`. */
55
+ const clisitefDebug = (scope, event, data) => {
56
+ const time = (/* @__PURE__ */ new Date()).toISOString();
57
+ if (data === void 0) return console.log(TAG, time, `${scope} › ${event}`);
58
+ console.log(TAG, time, `${scope} › ${event}`, safe(data));
59
+ };
60
+ /** Logs the input, the output or the error, and the duration of a call. */
61
+ const clisitefDebugCall = async (scope, input, call) => {
62
+ const startedAt = Date.now();
63
+ clisitefDebug(scope, "→ in", input);
64
+ try {
65
+ const output = await call();
66
+ const ms = elapsed(startedAt);
67
+ clisitefDebug(scope, "← out", {
68
+ output: safe(output),
69
+ ms
70
+ });
71
+ return output;
72
+ } catch (error) {
73
+ const ms = elapsed(startedAt);
74
+ clisitefDebug(scope, "✕ error", {
75
+ error: safe(error),
76
+ ms
77
+ });
78
+ throw error;
79
+ }
80
+ };
81
+ //#endregion
39
82
  //#region src/errors.ts
40
83
  var ClisitefUnavailableError = class extends Error {
41
84
  code = "CLISITEF_UNAVAILABLE";
@@ -160,7 +203,10 @@ const createClisitefClient = (native, options = {}) => {
160
203
  if (!native) throw new ClisitefUnavailableError();
161
204
  return native;
162
205
  };
163
- const bounded = async (operation, call) => {
206
+ const bounded = async (operation, call, input) => {
207
+ return clisitefDebugCall(`client.${operation}`, input, () => boundedCall(operation, call));
208
+ };
209
+ const boundedCall = async (operation, call) => {
164
210
  const module = requireNative();
165
211
  const ceiling = CLISITEF_TIMEOUT_MS[operation];
166
212
  let timer;
@@ -181,6 +227,7 @@ const createClisitefClient = (native, options = {}) => {
181
227
  } catch (error) {
182
228
  const timeout = error instanceof ClisitefTimeoutError ? error : toClisitefTimeout(error);
183
229
  if (!timeout) throw error;
230
+ clisitefDebug(`client.${operation}`, "timeout", timeout.info);
184
231
  signature = null;
185
232
  throw timeout;
186
233
  } finally {
@@ -189,28 +236,58 @@ const createClisitefClient = (native, options = {}) => {
189
236
  };
190
237
  const configure = async (config) => {
191
238
  const next = clisitefConfigSignature(config);
192
- if (next === signature) return 0;
193
- const code = await bounded("configure", (module) => module.configure(config));
239
+ if (next === signature) {
240
+ clisitefDebug("client.configure", "skipped (same config)", config);
241
+ return 0;
242
+ }
243
+ const call = (module) => module.configure(config);
244
+ const code = await bounded("configure", call, config);
194
245
  signature = code === 0 ? next : null;
195
246
  return code;
196
247
  };
248
+ const listen = (event, listener) => {
249
+ const logged = ((payload) => {
250
+ clisitefDebug(`client.event.${event}`, "← native", payload);
251
+ listener(payload);
252
+ });
253
+ clisitefDebug(`client.event.${event}`, "subscribe");
254
+ const subscription = requireNative().addListener(event, logged);
255
+ return { remove: () => {
256
+ clisitefDebug(`client.event.${event}`, "unsubscribe");
257
+ subscription.remove();
258
+ } };
259
+ };
197
260
  return {
198
261
  available: native !== null,
199
262
  configure,
200
263
  invalidate: () => {
264
+ clisitefDebug("client.invalidate", "signature cleared");
201
265
  signature = null;
202
266
  },
203
- startTransaction: (params) => bounded("startTransaction", (module) => module.startTransaction(params)),
267
+ startTransaction: (params) => {
268
+ const call = (m) => m.startTransaction(params);
269
+ return bounded("startTransaction", call, params);
270
+ },
204
271
  continueTransaction: (data) => {
205
- return bounded("continueTransaction", (m) => m.continueTransaction(data));
272
+ const call = (m) => m.continueTransaction(data);
273
+ return bounded("continueTransaction", call, { data });
274
+ },
275
+ finishTransaction: (params) => {
276
+ const call = (m) => m.finishTransaction(params);
277
+ return bounded("finishTransaction", call, params);
278
+ },
279
+ abortTransaction: (flag) => {
280
+ const call = (m) => m.abortTransaction(flag);
281
+ return bounded("abortTransaction", call, { continueFlag: flag });
206
282
  },
207
- finishTransaction: (params) => bounded("finishTransaction", (module) => module.finishTransaction(params)),
208
- abortTransaction: (flag) => bounded("abortTransaction", (module) => module.abortTransaction(flag)),
209
283
  getQttPendingTransactions: (date, coupon) => {
210
284
  const call = (m) => {
211
285
  return m.getQttPendingTransactions(date, coupon);
212
286
  };
213
- return bounded("getQttPendingTransactions", call);
287
+ return bounded("getQttPendingTransactions", call, {
288
+ fiscalDate: date,
289
+ fiscalCoupon: coupon
290
+ });
214
291
  },
215
292
  submitPendingMessages: () => {
216
293
  return bounded("submitPendingMessages", (m) => m.submitPendingMessages());
@@ -218,9 +295,13 @@ const createClisitefClient = (native, options = {}) => {
218
295
  getVersion: () => bounded("getVersion", (module) => module.getVersion()),
219
296
  closePinPad: () => bounded("closePinPad", (module) => module.closePinPad()),
220
297
  disconnectSitef: () => bounded("disconnectSitef", (module) => module.disconnectSitef()),
221
- getHealth: () => native?.getHealth() ?? null,
222
- onData: (listener) => requireNative().addListener("onData", listener),
223
- onTransactionResult: (listener) => requireNative().addListener("onTransactionResult", listener)
298
+ getHealth: () => {
299
+ const health = native?.getHealth() ?? null;
300
+ clisitefDebug("client.getHealth", "← out", health);
301
+ return health;
302
+ },
303
+ onData: (listener) => listen("onData", listener),
304
+ onTransactionResult: (listener) => listen("onTransactionResult", listener)
224
305
  };
225
306
  };
226
307
  //#endregion
@@ -228,4 +309,4 @@ const createClisitefClient = (native, options = {}) => {
228
309
  /** Shared client bound to the device's native module; one per app. */
229
310
  const clisitef = createClisitefClient(requireOptionalNativeModule("CliSiTef"));
230
311
  //#endregion
231
- export { CLISITEF_ABORT_FLAG, CLISITEF_CONFIRM, CLISITEF_DEFAULT_PINPAD, CLISITEF_DEFAULT_TIME_ZONE, CLISITEF_JS_GRACE_MS, CLISITEF_MODALITY, CLISITEF_RESULT, CLISITEF_TIMEOUT_CODE, CLISITEF_TIMEOUT_MS, ClisitefTimeoutError, ClisitefUnavailableError, clisitef, clisitefAdditionalParams, clisitefConfigSignature, clisitefFiscalDateTime, createClisitefClient, formatClisitefAmount, parseClisitefMenu, toClisitefTimeout };
312
+ export { CLISITEF_ABORT_FLAG, CLISITEF_CONFIRM, CLISITEF_DEFAULT_PINPAD, CLISITEF_DEFAULT_TIME_ZONE, CLISITEF_JS_GRACE_MS, CLISITEF_MODALITY, CLISITEF_RESULT, CLISITEF_TIMEOUT_CODE, CLISITEF_TIMEOUT_MS, ClisitefTimeoutError, ClisitefUnavailableError, clisitef, clisitefAdditionalParams, clisitefConfigSignature, clisitefDebug, clisitefDebugCall, clisitefFiscalDateTime, createClisitefClient, formatClisitefAmount, parseClisitefMenu, toClisitefTimeout };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fire-spark/payments-clisitef-client",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "description": "Fire Spark native client for CliSiTef (Software Express) on Android: SiTef server and pinpad bridge shared by the CliSiTef adapters",
5
5
  "sideEffects": false,
6
6
  "files": [