npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

rn-cr-cedula-reader

v1.0.0

Published

React Native module for scanning Costa Rica Cédulas de Identidad. Detects PDF417 and QR codes from the device camera, decrypts PDF417 binary payloads with the official 17-byte XOR key, and returns structured identity fields.

Readme

rn-cr-cedula-reader

npm version license platform

React Native module for scanning Costa Rica Cédulas de Identidad.

Detects whether the barcode is a PDF417 (back of cédula) or a QR code (front), then applies the correct resolution strategy:

| Side | Format | Strategy | |------|--------|----------| | Back | PDF417 | XOR-decrypt binary payload locally with 17-byte rotating key → structured fields | | Front | QR Code | Validated TSE URL → fetch() to consulta.tse.go.cr → parse response → structured fields |

Both paths return the same CedulaData type.

PDF417 decryption algorithm ported from:

lateraluz/Python-Datos-Cedula-Costa-Rica


Installation

npm install rn-cr-cedula-reader

Peer dependency

npm install react-native-vision-camera@^4

Only needed if you use CedulaCamera or useCedulaScanner from the /camera subpath. The resolveId resolver in the main entry has no native dependencies.

iOS

cd ios && pod install

Add to Info.plist:

<key>NSCameraUsageDescription</key>
<string>Camera access is required to scan your Cédula de Identidad.</string>

Android

Add to AndroidManifest.xml:

<uses-permission android:name="android.permission.CAMERA" />

Package structure

This package ships two entry points to keep the resolver bundle-size friendly:

| Entry | Import | Contents | |-------|--------|----------| | rn-cr-cedula-reader | main | resolveId, CedulaData, low-level decoder utilities — no native deps | | rn-cr-cedula-reader/camera | subpath | CedulaCamera, useCedulaScanner — requires react-native-vision-camera |


Quick Start

Option A — Async resolver (BYO camera)

Use your existing barcode scanner to get the raw value, then call resolveId:

import { resolveId } from 'rn-cr-cedula-reader';
import type { CedulaData } from 'rn-cr-cedula-reader';

// code.value + code.type come from react-native-vision-camera's useCodeScanner,
// or any other barcode SDK

const barcodeType = code.type === 'pdf-417' ? 'PDF417' : 'QR_CODE';

try {
  const data: CedulaData = await resolveId(code.value, barcodeType);
  console.log(data.cedula, data.nombres, data.primerApellido);
} catch (err) {
  console.error('Scan failed', err);
}

Option B — Drop-in camera component

import { CedulaCamera } from 'rn-cr-cedula-reader/camera';
import type { CedulaData } from 'rn-cr-cedula-reader';

export function ScanScreen({ navigation }) {
  function handleResult(data: CedulaData) {
    console.log(data);
    navigation.goBack();
  }

  return (
    <CedulaCamera
      onResult={handleResult}
      onCancel={() => navigation.goBack()}
    />
  );
}

Option C — Hook (custom camera UI)

import { useCedulaScanner } from 'rn-cr-cedula-reader/camera';
import { Camera } from 'react-native-vision-camera';

export function CustomScanScreen() {
  const { status, data, device, codeScanner, reset } = useCedulaScanner({
    onResult: (data) => console.log(data),
    rescanDelay: 2000,
  });

  if (!device) return null;

  return (
    <Camera
      style={{ flex: 1 }}
      device={device}
      isActive={status === 'scanning' || status === 'resolving'}
      codeScanner={codeScanner}
    />
  );
}

API Reference

resolveId(raw, type) — main entry

import { resolveId } from 'rn-cr-cedula-reader';

async function resolveId(raw: string, type: BarcodeType): Promise<CedulaData>

| Param | Type | Description | |-------|------|-------------| | raw | string | Raw barcode value from the scanner | | type | 'PDF417' \| 'QR_CODE' | Barcode format |

  • PDF417 → XOR-decrypts locally, no network. Returns immediately.
  • QR_CODE → Validates the TSE signed URL, fetches consulta.tse.go.cr, parses the HTML table.

Throws on network errors, invalid URLs, or unsupported barcode types.


CedulaData

interface CedulaData {
  cedula: string;           // 9-digit national ID number
  nombres: string;          // Given name(s)
  primerApellido: string;   // First surname
  segundoApellido?: string; // Second surname
  fechaNacimiento?: string; // DD/MM/YYYY (QR) or YYYY-MM-DD (PDF417)
  sexo?: string;            // 'M' | 'F' — PDF417 only
  conocidoComo?: string;    // Nickname / known-as — QR/TSE only
  fechaExpiracion?: string; // DD/MM/YYYY (QR) or YYYY-MM-DD (PDF417)
  source: 'QR_TSE' | 'PDF417';
}

<CedulaCamera />/camera subpath

Drop-in full-screen camera component with permission handling.

| Prop | Type | Default | Description | |------|------|---------|-------------| | onResult | (data: CedulaData) => void | required | Called on successful scan + resolve | | onCancel | () => void | — | Called when user taps Cancel | | style | ViewStyle | — | Container style override |


useCedulaScanner(options?)/camera subpath

import { useCedulaScanner } from 'rn-cr-cedula-reader/camera';

const { status, data, error, device, codeScanner, reset } = useCedulaScanner({
  rescanDelay: 2000,          // ms cooldown between scans (default: 2000)
  onResult: (data) => {},     // called on each successful resolve
});

| Return field | Type | Description | |-------------|------|-------------| | status | ScannerStatus | 'idle' \| 'requesting_permission' \| 'permission_denied' \| 'scanning' \| 'resolving' \| 'success' \| 'error' | | data | CedulaData \| null | Resolved identity data when status === 'success' | | error | string \| null | Error message when status === 'error' | | device | CameraDevice | Pass to <Camera device={device} /> | | codeScanner | CodeScanner | Pass to <Camera codeScanner={codeScanner} /> | | reset | () => void | Returns to 'scanning' state |


Low-level utilities — main entry

import { decodeScan, decryptPDF417Payload, parseCedulaFields, hexStringToBytes } from 'rn-cr-cedula-reader';

// XOR-decrypt a byte array with the 17-byte rotating CR cédula key
decryptPDF417Payload(dataBytes: number[]): string

// Parse the decrypted string into structured fields
parseCedulaFields(decrypted: string): CedulaFields

// Convert "27 30 04 A0" or "273004A0" → [0x27, 0x30, 0x04, 0xA0]
hexStringToBytes(hex: string): number[]

// Route by type: PDF417 → XOR decrypt, QR_CODE → raw passthrough
decodeScan(rawValue: string, type: BarcodeType): ScanResult

How PDF417 decryption works

The PDF417 on the back of the CR cédula is XOR-encrypted with a 17-byte rotating key:

KEY = [0x27, 0x30, 0x04, 0xA0, 0x00, 0x0F, 0x93, 0x12,
       0xA0, 0xD1, 0x22, 0xE0, 0x03, 0xD0, 0x00, 0xDF, 0x00]

For each byte b at position i:

decrypted[i] = b XOR KEY[i % 17]

Printable ASCII (0x20–0x7E) and Latin-1 extended characters (0xA0–0xFF, covering accented letters like Á É Í Ó Ú Ñ) are preserved. Control characters become spaces. The decrypted string is then tokenised to extract identity fields.


How QR resolution works

The QR code on the front of the CR cédula encodes a signed TSE URL:

https://www.consulta.tse.go.cr/consultacedula/Cedula?cedula=XXXXXXXXX&sol=...&sign=...

resolveId validates the hostname, extracts the cedula parameter, fetches the page, and parses the Bootstrap <th>/<td> table for: Nombre, Primer Apellido, Segundo Apellido, Fecha de Nacimiento, Conocido Como, Fecha de Vencimiento.


Known Limitations

PDF417 field parsing is heuristic. Token layout can vary between cédula print generations. Validate against real samples in your QA environment. Call parseCedulaFields directly if you need custom extraction logic.

QR resolution requires network. The TSE endpoint must be reachable. Offline or slow connections will produce a rejected promise.


Contributing

Issues and pull requests are welcome at github.com/Quality-XP-Development-SESSA/rn-cr-cedula-reader.


License

MIT — Copyright (c) 2026 QXD — Quality XP Development

Decryption algorithm derived from lateraluz/Python-Datos-Cedula-Costa-Rica (GPL-3.0).