Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ apply plugin: "com.facebook.react"
apply plugin: "org.jetbrains.kotlin.android"
apply plugin: 'kotlin-android'
apply plugin: 'com.google.gms.google-services'
apply from: "../../../node_modules/@sentry/react-native/sentry.gradle"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Guard Sentry Gradle script to avoid CI breakage and accidental symbol/sourcemap uploads

Apply only if the script exists and DSN is set. This prevents noisy tasks on debug/PR builds and failures when node_modules aren’t present.

-apply from: "../../../node_modules/@sentry/react-native/sentry.gradle"
+def sentryGradle = file("../../../node_modules/@sentry/react-native/sentry.gradle")
+if (sentryGradle.exists() && System.getenv("SENTRY_DSN")) {
+  apply from: sentryGradle
+}

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "PWD: $(pwd)"
echo "---- app/android/app/build.gradle (first 120 lines) ----"
if [ -f app/android/app/build.gradle ]; then
  sed -n '1,120p' app/android/app/build.gradle || true
else
  echo "MISSING: app/android/app/build.gradle"
fi

echo
echo "---- grep for sentry.gradle references ----"
rg -n --hidden -S "@sentry/react-native/sentry.gradle" -g '!*node_modules*' -C3 || true

echo
echo "---- node_modules/@sentry/react-native/sentry.gradle exists? ----"
if [ -f node_modules/@sentry/react-native/sentry.gradle ]; then
  echo "FOUND: node_modules/@sentry/react-native/sentry.gradle"
  sed -n '1,80p' node_modules/@sentry/react-native/sentry.gradle || true
else
  echo "NOT FOUND: node_modules/@sentry/react-native/sentry.gradle"
fi

echo
echo "---- android/sentry.properties exists? ----"
if [ -f android/sentry.properties ]; then
  echo "FOUND: android/sentry.properties"
  sed -n '1,200p' android/sentry.properties || true
else
  echo "NOT FOUND: android/sentry.properties"
fi

echo
echo "---- search for SENTRY_* env var usages in repo (exclude node_modules) ----"
rg -n --hidden -S 'SENTRY_AUTH_TOKEN|SENTRY_DSN|SENTRY_PROPERTIES|SENTRY_DISABLE_AUTO_UPLOAD' -g '!*node_modules*' -C2 || true

echo
echo "---- search for Sentry.init in JS/TS (exclude node_modules) ----"
rg -n --hidden -S 'Sentry\\.init' -g '!*node_modules*' -C2 || true

echo
echo "---- search for any sentry.properties filenames in repo (exclude node_modules) ----"
rg -n --hidden -S 'sentry.properties' -g '!*node_modules*' -C2 || true

echo
echo "Done."

Length of output: 5852


Guard Sentry Gradle script to avoid CI breakage and accidental symbol/sourcemap uploads

app/android/app/build.gradle applies "../../../node_modules/@sentry/react-native/sentry.gradle" unguarded; node_modules may be missing in CI/PRs and this will fail the Gradle build — apply only when the script exists and SENTRY_DSN is set.

File: app/android/app/build.gradle (around line 6)

-apply from: "../../../node_modules/@sentry/react-native/sentry.gradle"
+def sentryGradle = file("../../../node_modules/@sentry/react-native/sentry.gradle")
+if (sentryGradle.exists() && System.getenv("SENTRY_DSN")) {
+  apply from: sentryGradle
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
apply from: "../../../node_modules/@sentry/react-native/sentry.gradle"
def sentryGradle = file("../../../node_modules/@sentry/react-native/sentry.gradle")
if (sentryGradle.exists() && System.getenv("SENTRY_DSN")) {
apply from: sentryGradle
}
🤖 Prompt for AI Agents
In app/android/app/build.gradle around line 6, the unguarded apply of
"../../../node_modules/@sentry/react-native/sentry.gradle" can fail in CI/PRs
when node_modules is absent or SENTRY is not configured; modify the file to
conditionally apply the Sentry Gradle script only when the script file exists
and the SENTRY_DSN (or equivalent Sentry env var) is set, i.e., check for the
existence of the node_modules sentry.gradle path and verify
System.getenv("SENTRY_DSN") (or project property) is non-empty before calling
apply from.




/**
Expand Down
2 changes: 2 additions & 0 deletions app/android/react-native-passport-reader/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,6 @@ dependencies {
implementation 'commons-io:commons-io:2.8.0'
implementation 'com.squareup.okhttp3:okhttp:4.9.0'
implementation 'com.facebook.react:react-native:+'
implementation "io.sentry:sentry-android:8.20.0"

Comment on lines +45 to +46
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Avoid double-packaging Sentry; switch to compileOnly to prevent classpath/version conflicts

@sentry/react-native already pulls in io.sentry:sentry-android. Declaring it here as implementation risks duplicate classes or version skew. Use compileOnly so the app supplies the runtime.

-    implementation "io.sentry:sentry-android:8.20.0"
+    compileOnly "io.sentry:sentry-android:8.20.0"

Run to verify duplicates and RN Sentry versions:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "Gradle refs to sentry-android:"
rg -nP '\bio\.sentry:sentry-android\b' -g '**/*.gradle'
echo
echo "package.json refs to @sentry/react-native:"
rg -n '"@sentry/react-native"' -g '**/package.json' -C2

Length of output: 511


Change Sentry Android dependency to compileOnly to avoid double‑packaging/version skew

app/android/react-native-passport-reader/android/build.gradle:45 currently has implementation "io.sentry:sentry-android:8.20.0" while package.json lists @sentry/[email protected] — @sentry/react-native ships the native Android SDK, so keeping an implementation dependency can duplicate classes or produce runtime version conflicts; switch to compileOnly (or remove the explicit dependency and align versions / adjust Sentry AGP autoInstallation). (github.com)

-    implementation "io.sentry:sentry-android:8.20.0"
+    compileOnly "io.sentry:sentry-android:8.20.0"

File: app/android/react-native-passport-reader/android/build.gradle:45

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
implementation "io.sentry:sentry-android:8.20.0"
compileOnly "io.sentry:sentry-android:8.20.0"
🤖 Prompt for AI Agents
In app/android/react-native-passport-reader/android/build.gradle around lines
45-46, the dependency implementation "io.sentry:sentry-android:8.20.0" conflicts
with @sentry/[email protected] (which already ships the native SDK);
change that implementation to compileOnly "io.sentry:sentry-android:8.20.0" (or
remove the explicit dependency entirely) to avoid double-packaging and version
skew, and ensure the project relies on the native SDK provided by
@sentry/react-native (or align versions / disable Sentry AGP auto-installation
if you choose to keep an explicit native dependency).

}
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,13 @@ import com.facebook.react.bridge.Arguments
import com.facebook.react.modules.core.DeviceEventManagerModule
import com.facebook.react.bridge.LifecycleEventListener
import com.facebook.react.bridge.Callback
import io.sentry.Breadcrumb
import io.sentry.Sentry
import io.sentry.SentryLevel

object Messages {
const val SCANNING = "Scanning....."
const val STOP_MOVING = "Stop moving....."
const val STOP_MOVING = "Stop moving....."
const val AUTH = "Auth....."
const val COMPARING = "Comparing....."
const val COMPLETED = "Scanning completed"
Expand Down Expand Up @@ -159,7 +162,7 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)
private var opts: ReadableMap? = null
private val apduLogger = APDULogger()
private var currentSessionId: String? = null

data class Data(val id: String, val digest: String, val signature: String, val publicKey: String)

data class PassportData(
Expand All @@ -171,7 +174,7 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)
interface DataCallback {
fun onDataReceived(data: String)
}

init {
instance = this
reactContext.addLifecycleEventListener(this)
Expand Down Expand Up @@ -200,29 +203,33 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)

@ReactMethod
fun scan(opts: ReadableMap, promise: Promise) {
currentSessionId = generateSessionId()
currentSessionId = if (opts.hasKey("sessionId")) opts.getString("sessionId") else generateSessionId()

apduLogger.setContext("session_id", currentSessionId!!)

// Log scan start
logAnalyticsEvent("nfc_scan_started", mapOf(
"use_can" to (opts.getBoolean(PARAM_USE_CAN) ?: false),
"has_document_number" to (!opts.getString(PARAM_DOC_NUM).isNullOrEmpty()),
"has_can_number" to (!opts.getString(PARAM_CAN).isNullOrEmpty()),
"platform" to "android"
))


logNfc(SentryLevel.INFO, "scan_start", "start")
Comment on lines +206 to +218
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Prevent NPE on sessionId and ensure accurate scan_type in initial breadcrumb

currentSessionId can be null if sessionId key exists but value is null, and the first logNfc call computes scan_type from this.opts before it's assigned, defaulting to "mrz" even when useCan=true.

Apply this reordering and null-safe assignment:

 @ReactMethod
 fun scan(opts: ReadableMap, promise: Promise) {
-        currentSessionId = if (opts.hasKey("sessionId")) opts.getString("sessionId") else generateSessionId()
-
-        apduLogger.setContext("session_id", currentSessionId!!)
+        // Store opts early so logNfc resolves scan_type correctly
+        this.opts = opts
+        val sessionId = (if (opts.hasKey("sessionId")) opts.getString("sessionId") else null) ?: generateSessionId()
+        currentSessionId = sessionId
+        apduLogger.setContext("session_id", sessionId)

         // Log scan start
         logAnalyticsEvent("nfc_scan_started", mapOf(
-            "use_can" to (opts.getBoolean(PARAM_USE_CAN) ?: false),
+            "use_can" to (if (opts.hasKey(PARAM_USE_CAN)) opts.getBoolean(PARAM_USE_CAN) else false),
             "has_document_number" to (!opts.getString(PARAM_DOC_NUM).isNullOrEmpty()),
             "has_can_number" to (!opts.getString(PARAM_CAN).isNullOrEmpty()),
             "platform" to "android"
         ))
 
         logNfc(SentryLevel.INFO, "scan_start", "start")
@@
-        this.opts = opts
         this.scanPromise = promise

Also applies to: 243-246

🤖 Prompt for AI Agents
In
app/android/react-native-passport-reader/android/src/main/java/io/tradle/nfc/RNPassportReaderModule.kt
around lines 206-218 (and similarly adjust lines 243-246), currentSessionId is
assigned unsafely which can be null if the "sessionId" key exists but its value
is null, and logNfc is called before currentSessionId is set causing scan_type
to be derived from opts incorrectly; fix by first computing a null-safe
sessionId: if opts.hasKey("sessionId") get the string and fall back to
generateSessionId() when null, assign to currentSessionId, set
apduLogger.setContext("session_id", currentSessionId!!), then compute scan_type
using the updated opts/use_can state and only after that call logAnalyticsEvent
and logNfc; apply the same null-safe assignment and call-order reorder at lines
243-246.


eventMessageEmitter(Messages.SCANNING)
val mNfcAdapter = NfcAdapter.getDefaultAdapter(reactApplicationContext)
// val mNfcAdapter = NfcAdapter.getDefaultAdapter(this.reactContext)
if (mNfcAdapter == null) {
logAnalyticsError("nfc_not_supported", "NFC chip reading not supported")
logNfc(SentryLevel.WARNING, "nfc_not_supported", "check")
promise.reject("E_NOT_SUPPORTED", "NFC chip reading not supported")
return
}

if (!mNfcAdapter.isEnabled) {
logAnalyticsError("nfc_not_enabled", "NFC chip reading not enabled")
logNfc(SentryLevel.WARNING, "nfc_not_enabled", "check")
promise.reject("E_NOT_ENABLED", "NFC chip reading not enabled")
return
}
Expand Down Expand Up @@ -290,7 +297,7 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)
}
}


private fun toBase64(bitmap: Bitmap, quality: Int): String {
val byteArrayOutputStream = ByteArrayOutputStream()
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, byteArrayOutputStream)
Expand Down Expand Up @@ -348,9 +355,9 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)
false,
)
Log.e("MY_LOGS", "service gotten")

service.addAPDUListener(apduLogger)

service.open()
Log.e("MY_LOGS", "service opened")
logAnalyticsEvent("nfc_passport_service_opened")
Expand All @@ -368,7 +375,7 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)
eventMessageEmitter(Messages.PACE_STARTED)
apduLogger.setContext("operation", "pace_authentication")
apduLogger.setContext("auth_key_type", authKey.javaClass.simpleName)

// Determine proper PACE key: use CAN key if provided; otherwise derive PACE MRZ key from BAC
val paceKeyToUse: PACEKeySpec? = when (authKey) {
is PACEKeySpec -> authKey
Expand Down Expand Up @@ -410,10 +417,10 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)
val maxAttempts = 3

eventMessageEmitter(Messages.BAC_STARTED)

apduLogger.setContext("operation", "bac_authentication")
apduLogger.setContext("auth_key_type", authKey.javaClass.simpleName)

while (!bacSucceeded && attempts < maxAttempts) {
try {
attempts++
Expand Down Expand Up @@ -486,11 +493,11 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)


logAnalyticsEvent("nfc_reading_data_groups")

apduLogger.setContext("operation", "reading_data_groups")
apduLogger.setContext("pace_succeeded", paceSucceeded)
apduLogger.setContext("bac_succeeded", bacSucceeded)

eventMessageEmitter(Messages.READING_DG1)
logAnalyticsEvent("nfc_reading_dg1_started")
val dg1In = service.getInputStream(PassportService.EF_DG1)
Expand Down Expand Up @@ -570,7 +577,7 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)
private fun doChipAuth(service: PassportService) {
try {
apduLogger.setContext("operation", "chip_authentication")

logAnalyticsEvent("nfc_reading_dg14_started")
eventMessageEmitter(Messages.READING_DG14)
val dg14In = service.getInputStream(PassportService.EF_DG14)
Expand Down Expand Up @@ -602,19 +609,19 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)
try {
apduLogger.setContext("operation", "passive_authentication")
apduLogger.setContext("chip_auth_succeeded", chipAuthSucceeded)

logAnalyticsEvent("nfc_passive_auth_started")
Log.d(TAG, "Starting passive authentication...")
val digest = MessageDigest.getInstance(sodFile.digestAlgorithm)
Log.d(TAG, "Using digest algorithm: ${sodFile.digestAlgorithm}")


val dataHashes = sodFile.dataGroupHashes

val dg14Hash = if (chipAuthSucceeded) digest.digest(dg14Encoded) else ByteArray(0)
val dg1Hash = digest.digest(dg1File.encoded)
val dg2Hash = digest.digest(dg2File.encoded)

Comment on lines 621 to +624
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Crash risk: dg2File is never initialized but its hash is computed.

DG2 read is commented out above, yet dg2Hash is computed from dg2File.encoded. This will throw UninitializedPropertyAccessException at runtime.

Minimal fix:

-                val dg2Hash = digest.digest(dg2File.encoded)
+                val dg2Hash = if (::dg2File.isInitialized) digest.digest(dg2File.encoded) else ByteArray(0)

Optionally reinstate DG2 reading if you intend to validate it.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
val dg14Hash = if (chipAuthSucceeded) digest.digest(dg14Encoded) else ByteArray(0)
val dg1Hash = digest.digest(dg1File.encoded)
val dg2Hash = digest.digest(dg2File.encoded)
val dg14Hash = if (chipAuthSucceeded) digest.digest(dg14Encoded) else ByteArray(0)
val dg1Hash = digest.digest(dg1File.encoded)
val dg2Hash = if (::dg2File.isInitialized) digest.digest(dg2File.encoded) else ByteArray(0)
🤖 Prompt for AI Agents
In
app/android/react-native-passport-reader/android/src/main/java/io/tradle/nfc/RNPassportReaderModule.kt
around lines 621-624, the code computes dg2Hash from dg2File.encoded even though
dg2File may never be initialized (DG2 read was commented out), which will throw
UninitializedPropertyAccessException; fix by guarding access to dg2File (only
compute digest if dg2File was successfully read/initialized) or set dg2Hash to
an empty ByteArray/default value when DG2 is absent, or alternatively re-enable
the DG2 read logic above so dg2File is always initialized before computing its
hash. Ensure any branch you add keeps types consistent and preserves
chipAuthSucceeded logic for DG14.

// val gson = Gson()
// Log.d(TAG, "dataHashes " + gson.toJson(dataHashes))
// val hexMap = sodFile.dataGroupHashes.mapValues { (_, value) ->
Expand Down Expand Up @@ -741,7 +748,7 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)
}

apduLogger.clearContext()

resetState()
return
}
Expand All @@ -757,12 +764,12 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)

// val signedDataField = SODFile::class.java.getDeclaredField("signedData")
// signedDataField.isAccessible = true

// val signedData = signedDataField.get(sodFile) as SignedData

val eContentAsn1InputStream = ASN1InputStream(sodFile.eContent.inputStream())
// val eContentDecomposed: ASN1Primitive = eContentAsn1InputStream.readObject()

val passport = Arguments.createMap()
passport.putString("mrz", mrzInfo.toString())
passport.putString("signatureAlgorithm", sodFile.docSigningCertificate.sigAlgName) // this one is new
Expand All @@ -772,7 +779,7 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)
val certificateBytes = certificate.encoded
val certificateBase64 = Base64.encodeToString(certificateBytes, Base64.DEFAULT)
Log.d(TAG, "certificateBase64: ${certificateBase64}")


passport.putString("documentSigningCertificate", certificateBase64)

Expand All @@ -781,10 +788,10 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)
passport.putString("modulus", publicKey.modulus.toString())
} else if (publicKey is ECPublicKey) {
// Handle the elliptic curve public key case

// val w = publicKey.getW()
// passport.putString("publicKeyW", w.toString())

// val ecParams = publicKey.getParams()
// passport.putInt("cofactor", ecParams.getCofactor())
// passport.putString("curve", ecParams.getCurve().toString())
Expand All @@ -793,7 +800,7 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)
// if (ecParams is ECNamedCurveSpec) {
// passport.putString("curveName", ecParams.getName())
// }

// Old one, probably wrong:
// passport.putString("curveName", (publicKey.parameters as ECNamedCurveSpec).name)
// passport.putString("curveName", (publicKey.parameters.algorithm)) or maybe this
Expand Down Expand Up @@ -831,15 +838,15 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)
// passport.putString("getDocSigningCertificate", gson.toJson(sodFile.getDocSigningCertificate))
// passport.putString("getIssuerX500Principal", gson.toJson(sodFile.getIssuerX500Principal))
// passport.putString("getSerialNumber", gson.toJson(sodFile.getSerialNumber))
// Another way to get signing time is to get into signedData.signerInfos, then search for the ICO identifier 1.2.840.113549.1.9.5


// Another way to get signing time is to get into signedData.signerInfos, then search for the ICO identifier 1.2.840.113549.1.9.5
// passport.putString("signerInfos", gson.toJson(signedData.signerInfos))

// Log.d(TAG, "signedData.digestAlgorithms: ${gson.toJson(signedData.digestAlgorithms)}")
// Log.d(TAG, "signedData.signerInfos: ${gson.toJson(signedData.signerInfos)}")
// Log.d(TAG, "signedData.certificates: ${gson.toJson(signedData.certificates)}")

// var quality = 100
// val base64 = bitmap?.let { toBase64(it, quality) }
// val photo = Arguments.createMap()
Expand All @@ -848,13 +855,13 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)
// photo.putInt("height", bitmap?.height ?: 0)
// passport.putMap("photo", photo)
// passport.putString("dg2File", gson.toJson(dg2File))

eventMessageEmitter(Messages.COMPLETED)
scanPromise?.resolve(passport)
eventMessageEmitter(Messages.RESET)

apduLogger.clearContext()

resetState()
}
}
Expand Down Expand Up @@ -890,10 +897,10 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)
if (params.isNotEmpty()) {
logData.put("data", JSONObject(Gson().toJson(params)))
}

// Send to React Native via logEvent emission using the same working approach
emitLogEvent(logData.toString())

// Also log to Android logs for debugging
Log.d(TAG, "Analytics event: $eventName with params: $params")
} catch (e: Exception) {
Expand All @@ -911,9 +918,9 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)
put("event", eventName)
put("error_description", message)
})

emitLogEvent(logData.toString())

Log.e(TAG, "Analytics error: $eventName - $message")
} catch (e: Exception) {
Log.e(TAG, "Error logging analytics error", e)
Expand All @@ -934,17 +941,52 @@ class RNPassportReaderModule(private val reactContext: ReactApplicationContext)
fun reset() {
logAnalyticsEvent("nfc_scan_reset")
apduLogger.clearContext()

resetState()
}

/**
* Generate a unique session ID for tracking passport reading sessions
*/
private fun generateSessionId(): String {
return "nfc_${System.currentTimeMillis()}_${UUID.randomUUID().toString().take(8)}"
}

private fun logNfc(level: SentryLevel, message: String, stage: String, extras: Map<String, Any?> = emptyMap()) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated kotlin logic to send capture message on error but to send breadcrumbs during other calls

val data = mutableMapOf<String, Any?>().apply {
currentSessionId?.let { put("session_id", it) }
put("platform", "android")
put("scan_type", if (opts?.getBoolean(PARAM_USE_CAN) == true) "can" else "mrz")
put("stage", stage)
putAll(extras)
}

if (level == SentryLevel.ERROR) {
// For errors, capture a message (this will include all previous breadcrumbs)
Sentry.withScope { scope ->
scope.level = level
currentSessionId?.let { scope.setTag("session_id", it) }
scope.setTag("platform", "android")
scope.setTag("scan_type", if (opts?.getBoolean(PARAM_USE_CAN) == true) "can" else "mrz")
scope.setTag("stage", stage)
for ((k, v) in extras) {
scope.setExtra(k, v?.toString())
}
Sentry.captureMessage(message)
}
} else {
// For info/warn, add as breadcrumb only
Sentry.addBreadcrumb(
Breadcrumb().apply {
this.message = message
this.level = level
this.category = "nfc"
data.forEach { (key, value) -> this.data[key] = value?.toString() ?: "" }
}
)
}
}

companion object {
private val TAG = RNPassportReaderModule::class.java.simpleName
private const val PARAM_DOC_NUM = "documentNumber";
Expand Down
1 change: 1 addition & 0 deletions app/ios/PassportReader.m
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ @interface RCT_EXTERN_MODULE(PassportReader, NSObject)
skipCA:(NSNumber * _Nonnull)skipCA
extendedMode:(NSNumber * _Nonnull)extendedMode
usePacePolling:(NSNumber * _Nonnull)usePacePolling
sessionId:(NSString *)sessionId
resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject)

Expand Down
Loading
Loading