# 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
ApacuanaAPIErrorconNOT_INITIALIZED_ERROR. - Si
customerIdestá 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
customerIdválido (ver_checkSdk); el README indica per-método si requiere customerId. - Los métodos devuelven
ApacuanaSuccess<T>en éxito o lanzanApacuanaAPIErroren fallo; algunos métodos devuelvendynamicporque delegan a APIs que pueden retornar Map o wrapper — se recomienda capturar/normalizar.
- 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.
- init(Map<String,dynamic> config)
Future<ApacuanaSuccess<Map<String,dynamic>>> res = await apacuana.init(config);
- Descripción: Inicializa el SDK; si
customerIdestá presente, obtiene token y userData. - Retorno: ApacuanaSuccess<Map<String,dynamic>> con keys informativas.
- close()
apacuana.close();
- Descripción: Cierra/cancela recursos del SDK.
--- Revocaciones ---
- requestRevocation({ required int reasonCode })
Future<dynamic> result = await apacuana.requestRevocation({'reasonCode': 3});
- Requiere: customerId
- Parámetros:
- data Map con key
reasonCode(int) — obligatorio.
- data Map con key
- Retorno: dynamic (delegado a revocationsApi). Puede ser ApacuanaSuccess
- getRevocationReasons()
final res = await apacuana.getRevocationReasons();
- Requiere: customerId
- Retorno: dynamic (lista o wrapper con motivos disponibles).
--- Certificados ---
- 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);
- 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>>
- 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.
- 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 ---
- 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>>
- deleteSignatureVariant()
final res = await apacuana.deleteSignatureVariant();
- Requiere: customerId
- Retorno: ApacuanaSuccess<Map<String,dynamic>>
- 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'].
- 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.
- 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).
- 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>>
- 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 ---
- getCustomer()
final res = await apacuana.getCustomer();
- Requiere: customerId
- Retorno: ApacuanaSuccess
- createFaceLivenessSession()
final res = await apacuana.createFaceLivenessSession();
- Requiere: customerId
- Retorno: dynamic — normalmente ApacuanaSuccess
- 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.
- 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) ydoc(String o num). - Si envías archivos, pásalos como
MultipartFile(de Dio) endata['files']o en keys esperadas por backend.
- data (Map<String, dynamic>) — campos del usuario. Mínimos obligatorios:
- 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,createApacuanaUsercon archivos). - Si llamas desde Flutter/mobile puedes convertir
dart:io FileaMultipartFile:
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) ---
- 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
- 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;
- 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);
- 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
encryptionKeyen 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
datapara 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.