# apacuana_sdk_core_dart

Apacuana SDK Core (Dart) — cliente modular para interactuar con el backend de Apacuana.
Este paquete contiene la lógica "core" (sin UI) para:
- Inicializar y configurar el cliente.
- Gestionar certificados (generación, estado, tipos, requisitos).
- Flujos de firma y manipulación de documentos (digest, sign, signature variants).
- Gestión de usuarios y sesiones de face-liveness.
- Peticiones HTTP y manejo de errores específico del dominio.

Este README explica cómo instalar, inicializar y usar cada método público con tipos y ejemplos prácticos.

Badges
- Version on pub.dev — (apunta a la página del paquete en pub.dev una vez publicado)
- License — MIT

Requisitos
- Dart SDK >= 3.2.0
- Si vas a usar MultipartFile para subir ficheros: dependencia `dio` (o construir MultipartFile equivalente).

Contenido
- Instalación
- Inicialización
- Tipos importantes (ApacuanaSuccess, GenerateCertResult, ApacuanaAPIError)
- API pública (métodos detallados con parámetros, tipos y ejemplos)
- Manejo de archivos (MultipartFile)
- Ejemplos prácticos (flujos comunes)
- Buenas prácticas y manejo de errores
- Recursos: ejemplo, changelog y soporte

---

Instalación

En el pubspec.yaml de tu proyecto:

```yaml
dependencies:
  apacuana_sdk_core_dart: ^0.1.0

Luego:

dart pub get
# o en Flutter:
flutter pub get

Importa el package:

import 'package:apacuana_sdk_core_dart/apacuana_sdk_core_dart.dart';

Inicialización

Antes de llamar a la mayoría de métodos, inicializa el SDK con la configuración:

Parámetros esperados en config (Map<String, dynamic>)

  • apiUrl (String) — URL base de la API.
  • apiKey (String) — (opcional) clave de API si tu backend la requiere.
  • encryptionKey (String) — clave usada por utilidades internas de cifrado (si aplica).
  • customerId (String) — si ya existe sesión, incluir customerId para operaciones autenticadas.
  • token / userData — (optionales) valores que pueden añadirse en init al detectarse sesión.

Ejemplo de inicialización:

final config = {
  'apiUrl': 'https://api.tu-backend.com',
  'apiKey': 'TU_API_KEY',
  'encryptionKey': 'dRgUkXp2s5v8y/B?',
  // 'customerId': 'uuid-usuario' // opcional si tienes sesión
};

final res = await apacuana.init(config);
// apacuana.init devuelve ApacuanaSuccess<Map<String,dynamic>>
print(res.data); // { initialized: true, message: '...' }

Notas:

  • Si no inicializas, muchas llamadas lanzarán ApacuanaAPIError con NOT_INITIALIZED_ERROR.
  • Si customerId está presente en config y es válido, init intentará obtener token/userData y establecer autenticación.

Tipos importantes

ApacuanaSuccess

class ApacuanaSuccess<T> {
  final bool success = true; // siempre true
  final int statusCode; // por defecto 200
  final T data;         // payload principal
  const ApacuanaSuccess(this.data, {this.statusCode = 200});
}
  • Uso: la mayoría de operaciones exitosas retornan ApacuanaSuccess

GenerateCertResult

class GenerateCertResult {
  final String cert;        // certificado (ej. PEM/base64)
  final String certifiedId; // id del certificado en backend
}

ApacuanaAPIError

  • Excepción lanzada por el SDK cuando ocurre un error controlado (autenticación, validación, servidor).
  • Propiedades típicas: message, statusCode, errorCode, stackTrace, raw/error.

ApiResult (en capas superiores)

  • Algunos wrappers / capas de UI usan ApiResult para representar { success, message, data, code }.

API pública (métodos, parámetros y ejemplos)

Notas generales:

  • Todas las llamadas son asíncronas (Future).
  • Muchas funciones requieren que el SDK esté inicializado y un customerId válido (ver _checkSdk); el README indica per-método si requiere customerId.
  • Los métodos devuelven ApacuanaSuccess<T> en éxito o lanzan ApacuanaAPIError en fallo; algunos métodos devuelven dynamic porque delegan a APIs que pueden retornar Map o wrapper — se recomienda capturar/normalizar.
  1. getConfig()
/// Devuelve la configuración actual (SdkConfig u objeto equivalente).
final cfg = apacuana.getConfig();
  • Retorno: objeto SdkConfig (o Map-like) — inspecciona campos customerId, apiUrl, apiKey, encryptionKey, userData.
  1. init(Map<String,dynamic> config)
Future<ApacuanaSuccess<Map<String,dynamic>>> res = await apacuana.init(config);
  • Descripción: Inicializa el SDK; si customerId está presente, obtiene token y userData.
  • Retorno: ApacuanaSuccess<Map<String,dynamic>> con keys informativas.
  1. close()
apacuana.close();
  • Descripción: Cierra/cancela recursos del SDK.

--- Revocaciones ---

  1. requestRevocation({ required int reasonCode })
Future<dynamic> result = await apacuana.requestRevocation({'reasonCode': 3});
  • Requiere: customerId
  • Parámetros:
    • data Map con key reasonCode (int) — obligatorio.
  • Retorno: dynamic (delegado a revocationsApi). Puede ser ApacuanaSuccess
  1. getRevocationReasons()
final res = await apacuana.getRevocationReasons();
  • Requiere: customerId
  • Retorno: dynamic (lista o wrapper con motivos disponibles).

--- Certificados ---

  1. generateCert({ required String csr })
final ApacuanaSuccess<GenerateCertResult> res = await apacuana.generateCert(csr: csr);
  • Requiere: customerId
  • Parámetros:
    • csr (String) — CSR a enviar. En tu flujo móvil normalmente este CSR se cifra/transforma antes (el SDK asume que la capa que llama le pasa el CSR en el formato esperado por el backend).
  • Retorno: ApacuanaSuccess

Ejemplo:

final csr = '-----BEGIN CERTIFICATE REQUEST-----...'; // o CSR cifrado según backend
final res = await apacuana.generateCert(csr: csr);
print(res.data.certifiedId);
  1. getCertStatus(bool isCertificateInDevice = false)
final ApacuanaSuccess<Map<String,dynamic>> res = await apacuana.getCertStatus();
  • Requiere: customerId
  • Parámetros:
    • isCertificateInDevice (bool) — opcional (por defecto false). Indica si debe considerar el certificado almacenado localmente.
  • Retorno: ApacuanaSuccess<Map<String,dynamic>>
  1. getCertTypes()
final ApacuanaSuccess<Map<String,dynamic>> res = await apacuana.getCertTypes();
  • No requiere customerId (operación pública).
  • Retorno: lista/Map con tipos de certificados disponibles.
  1. getRequerimentsByTypeUser({ required int type })
final res = await apacuana.getRequerimentsByTypeUser(type: 1);
  • No requiere customerId.
  • Parámetros:
    • type (int) — tipo de usuario.
  • Retorno: ApacuanaSuccess<Map<String,dynamic>> con requisitos.

--- Firmas y documentos ---

  1. addSigner(Map<String,dynamic> data)
final res = await apacuana.addSigner({'name': 'Juan', 'doc': '12345678'});
  • Requiere: customerId
  • Parámetros: Map con campos del firmante (depende de la API backend).
  • Retorno: ApacuanaSuccess<Map<String,dynamic>>
  1. deleteSignatureVariant()
final res = await apacuana.deleteSignatureVariant();
  • Requiere: customerId
  • Retorno: ApacuanaSuccess<Map<String,dynamic>>
  1. getDigest(Map<String,dynamic> data)
// data example: {'cert': certBody, 'document': '<base64-document>'}
// or {'cert': certBody, 'signatureId': '<signature-id>'}
final res = await apacuana.getDigest({'cert': cert, 'signatureId': '...'});
  • Requiere: customerId
  • Retorno: ApacuanaSuccess<Map<String,dynamic>> con data['digest'].
  1. getDocs({ required int page, required int size, int? status })
final res = await apacuana.getDocs(page: 1, size: 20, status: null);
  • Requiere: customerId
  • Parámetros:
    • page (int), size (int), status (int?) — opcional filtro.
  • Retorno: ApacuanaSuccess<Map<String,dynamic>> con lista de documentos/paginación.
  1. getSignatureVariant()
final res = await apacuana.getSignatureVariant();
  • Requiere: customerId
  • Retorno: ApacuanaSuccess<Map<String,dynamic>> con la imagen/variant del usuario (base64 o URL según backend).
  1. signDocument(Map<String,dynamic> data)
final body = {
  'signature': {
    'id': 'signature-id',
    'positions': [{'page':1, 'x':100, 'y':200}]
  },
  'cert': certBody,
  'signedDigest': signedDigestBase64,
};
final res = await apacuana.signDocument(body);
  • Requiere: customerId
  • Parámetros (ejemplo):
    • signature (Map) con id y positions (List
    • cert (String) — certificado del firmante
    • signedDigest (String) — digest firmado en base64
  • Retorno: ApacuanaSuccess<Map<String,dynamic>>
  1. uploadSignatureVariant({ required MultipartFile file })
// Construir MultipartFile (ejemplo con Dio)
final mp = await MultipartFile.fromFile(filePath, filename: 'sig.png', contentType: MediaType('image','png'));
final res = await apacuana.uploadSignatureVariant(file: mp);
  • Requiere: customerId
  • Parámetro:
    • file: Dio MultipartFile (nota: el core espera MultipartFile; wrappers pueden aceptar dart:io File y convertirlo antes de llamar).
  • Retorno: ApacuanaSuccess<Map<String,dynamic>>

--- Usuarios y Face Liveness ---

  1. getCustomer()
final res = await apacuana.getCustomer();
  • Requiere: customerId
  • Retorno: ApacuanaSuccess
  1. createFaceLivenessSession()
final res = await apacuana.createFaceLivenessSession();
  • Requiere: customerId
  • Retorno: dynamic — normalmente ApacuanaSuccess
  1. validateFaceLiveness({ required String sessionId })
final res = await apacuana.validateFaceLiveness(sessionId: 'session-id-abc');
  • Requiere: customerId
  • Parámetro:
    • sessionId (String) — id obtenido al crear la sesión.
  • Retorno: ApacuanaSuccess<Map<String,dynamic>> con resultado de validación.
  1. createApacuanaUser(Map<String, dynamic> data)
// data minimal:
final data = {
  'kinddoc': 'V',
  'doc': '12345678',
  'email': 'usuario@correo.com',
  // ...otros campos opcionales...
  // 'files': {'idDoc': MultipartFile.fromFileSync('/path/..', filename: 'doc.jpg')}
};
final res = await apacuana.createApacuanaUser(data);
  • No requiere customerId (operación pública para registro).
  • Parámetros:
    • data (Map<String, dynamic>) — campos del usuario. Mínimos obligatorios: kinddoc (String) y doc (String o num).
    • Si envías archivos, pásalos como MultipartFile (de Dio) en data['files'] o en keys esperadas por backend.
  • Retorno: ApacuanaSuccess<Map<String,dynamic>>

--- Notas sobre tipos de parámetros y estructuras comunes ---

Map payloads

  • Muchas funciones esperan Map<String, dynamic> con claves específicas. Documenta las claves según tu API backend.
  • Para fechas se recomienda ISO-8601 "YYYY-MM-DD" como String.

MultipartFile / Archivos

  • El core espera MultipartFile (de Dio) para subir archivos (uploadSignatureVariant, createApacuanaUser con archivos).
  • Si llamas desde Flutter/mobile puedes convertir dart:io File a MultipartFile:
import 'package:dio/dio.dart';
final mp = await MultipartFile.fromFile(file.path, filename: 'doc.jpg');
await apacuana.uploadSignatureVariant(file: mp);

--- Ejemplos prácticos (flujos completos) ---

  1. Flujo simple: inicializar y obtener tipos de certificado
import 'package:apacuana_sdk_core_dart/apacuana_sdk_core_dart.dart';

final cfg = {
  'apiUrl': 'https://api.example.com',
  'apiKey': 'XXX',
  'encryptionKey': 'dRgUkXp2s5v8y/B?'
};

await apacuana.init(cfg);
final certTypes = await apacuana.getCertTypes();
print(certTypes.data); // según respuesta del backend
  1. Generar certificado (móvil): generar keypair local, crear CSR, enviar CSR al backend
// 1) Generas keypair y CSR en la capa cliente (ejemplos fuera del core)
// 2) Envías CSR tal y como el backend lo espera:
final csr = '<CSR-string>';
final res = await apacuana.generateCert(csr: csr);
final cert = res.data.cert;
final certId = res.data.certifiedId;
  1. Subir variant de firma (desde Flutter)
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:http_parser/http_parser.dart';

// file: dart:io File obtenido por picker
final file = File('/path/to/signature.png');
final mp = await MultipartFile.fromFile(file.path, filename: 'signature.png', contentType: MediaType('image', 'png'));

final res = await apacuana.uploadSignatureVariant(file: mp);
print(res.data);
  1. Firma de documento (ejemplo mínimo)
// 1) Obtener digest
final digestRes = await apacuana.getDigest({'cert': certBody, 'signatureId': 'id'});
final digestBase64 = digestRes.data['digest'] as String;

// 2) Firmar localmente el digest con la clave privada (fuera del core)
final signedDigestBase64 = signDigestLocally(digestBase64, privateKeyPem); // tu función

// 3) Enviar signDocument
final body = {
  'signature': { 'id': 'sigId', 'positions': [{'page':1,'x':100,'y':200}] },
  'cert': certBody,
  'signedDigest': signedDigestBase64,
};
final signRes = await apacuana.signDocument(body);
print(signRes.data);

--- Manejo de errores

  • La mayoría de errores controlados se lanzan como ApacuanaAPIError. Envuelve las llamadas en try/catch:
try {
  final res = await apacuana.generateCert(csr: csr);
  // usar res.data
} on ApacuanaAPIError catch (e) {
  print('API error: ${e.errorCode} ${e.message}');
} catch (e) {
  print('Unexpected error: $e');
}
  • Algunas funciones delegan a APIs que retornan dynamic (Map o wrapper). Si integras con UI, normaliza la respuesta antes de serializar a JSON.

--- Buenas prácticas

  • Inicializa el SDK una sola vez al inicio de tu aplicación.
  • Mantén encryptionKey en lugar seguro (no lo publiques).
  • En mobile, guarda claves privadas en Keychain / Keystore y no en texto plano.
  • Usa MultipartFile para archivos en el core; provee adaptadores en tus capas UI para convertir File -> MultipartFile.
  • Documenta la estructura específica de data para endpoints que dependan del backend (createApacuanaUser, addSigner, signDocument, getDigest).

--- Ejemplo de proyecto de demostración

El repositorio incluye una carpeta example/ con una app Flutter que muestra:

  • Inicialización
  • Generación de certificado
  • Flujos de liveness (modal)
  • Crear usuario de ejemplo
  • Subir / obtener / eliminar signature variant
  • Firmar documento

Revisa example/lib para ver implementaciones completas y un UI simple para demostrar la SDK.

--- Licencia

MIT — ver archivo LICENSE en la raíz del paquete.