verifyNfcDocument method
Verify NFC document
Implementation
@override
Future<Map<String, dynamic>> verifyNfcDocument(String sessionId, Map<String, dynamic> verificationData) async {
try {
print('=== PREPARING NFC VERIFICATION REQUEST ===');
print('Session ID: $sessionId');
print('Verification Data Keys: ${verificationData.keys.toList()}');
// Validate required fields
if (!verificationData.containsKey('document_type') ||
!verificationData.containsKey('document_country') ||
!verificationData.containsKey('face_video') ||
!verificationData.containsKey('document_face_image') ||
!verificationData.containsKey('document_infos')) {
throw ArgumentError('Missing required fields for NFC verification');
}
final dio = Dio(BaseOptions(
connectTimeout: const Duration(seconds: 30),
receiveTimeout: const Duration(minutes: 2),
sendTimeout: const Duration(minutes: 1),
));
// Add bearer token if available
if (_hasValidBearerToken) {
dio.options.headers['Authorization'] = 'Bearer $_bearerToken';
print('Bearer token added to request');
} else {
print('Warning: No bearer token available');
}
// Prepare face video file
final File faceVideoFile = verificationData['face_video'] as File;
if (!await faceVideoFile.exists()) {
throw ArgumentError('Face video file does not exist: ${faceVideoFile.path}');
}
// Prepare document face image
final String imageBase64 = verificationData['document_face_image'] as String;
if (imageBase64.isEmpty) {
throw ArgumentError('Document face image base64 string is empty');
}
// Clean and decode base64 image with better error handling
String cleanBase64 = imageBase64
.trim()
.replaceAll('\n', '')
.replaceAll('\r', '')
.replaceAll(' ', '')
.replaceAll('\t', '');
// Remove data URL prefix if present (e.g., "data:image/jpeg;base64,")
if (cleanBase64.contains(',')) {
cleanBase64 = cleanBase64.split(',').last;
}
// Add padding if needed
while (cleanBase64.length % 4 != 0) {
cleanBase64 += '=';
}
Uint8List documentFaceImage;
try {
documentFaceImage = base64Decode(cleanBase64);
print('✅ Base64 image decoded successfully. Size: ${documentFaceImage.length} bytes');
// Validate that it's actually an image by checking the first few bytes
if (documentFaceImage.length < 4) {
throw ArgumentError('Image data too small to be valid');
}
// Check for common image format signatures
bool isValidImage = false;
if (documentFaceImage.length >= 2) {
// JPEG signature: FF D8
if (documentFaceImage[0] == 0xFF && documentFaceImage[1] == 0xD8) {
isValidImage = true;
print('✅ Detected JPEG format');
}
// PNG signature: 89 50 4E 47
else if (documentFaceImage.length >= 4 &&
documentFaceImage[0] == 0x89 &&
documentFaceImage[1] == 0x50 &&
documentFaceImage[2] == 0x4E &&
documentFaceImage[3] == 0x47) {
isValidImage = true;
print('✅ Detected PNG format');
}
}
if (!isValidImage) {
print('⚠️ Warning: Image format signature not recognized. First bytes: ${documentFaceImage.take(8).map((b) => b.toRadixString(16).padLeft(2, '0')).join(' ')}');
}
} catch (e) {
print('❌ Failed to decode base64 image: $e');
print('❌ Base64 string length: ${cleanBase64.length}');
print('❌ Base64 string preview: ${cleanBase64.substring(0, cleanBase64.length > 100 ? 100 : cleanBase64.length)}...');
throw ArgumentError('Failed to decode base64 image: $e');
}
// Prepare document infos
final Map<String, dynamic> documentInfos = verificationData['document_infos'] as Map<String, dynamic>;
final String documentType = verificationData['document_type'] as String;
final String documentCountry = verificationData['document_country'] as String;
// Build FormData
final formData = FormData.fromMap({
"face_video": await MultipartFile.fromFile(
faceVideoFile.path,
filename: "face_video.mp4",
contentType: MediaType("video", "mp4"),
),
"document_face_image": MultipartFile.fromBytes(
documentFaceImage,
filename: "face_image.jpg",
contentType: MediaType("image", "jpeg"),
),
"document_type": documentType.toLowerCase(),
"document_country": documentCountry,
"document_infos": MultipartFile.fromString(
jsonEncode(documentInfos),
contentType: MediaType("application", "json"),
),
});
final url = "$_baseAppUrl/verify/nfc/$sessionId";
print('=== SENDING NFC VERIFICATION REQUEST ===');
print('URL: $url');
print('Document Type: $documentType');
print('Document Country: $documentCountry');
print('Face Video Size: ${await faceVideoFile.length()} bytes');
print('Document Face Image Size: ${documentFaceImage.length} bytes');
print('Document Face Image First 32 bytes: ${documentFaceImage.take(32).map((b) => b.toRadixString(16).padLeft(2, '0')).join(' ')}');
print('Document Infos: ${jsonEncode(documentInfos)}');
final response = await dio.post(url, data: formData);
print('=== NFC VERIFICATION RESPONSE RECEIVED ===');
print('Status Code: ${response.statusCode}');
print('Response Data: ${response.data}');
if (response.statusCode == 200 || response.statusCode == 201) {
print('✅ NFC verification upload success: ${response.data}');
return response.data;
} else {
print('❌ NFC verification upload failed with status ${response.statusCode}');
throw Exception('NFC verification failed with status ${response.statusCode}: ${response.data}');
}
} catch (e) {
print('❌ NFC verification error: $e');
throw Exception('Error verifying NFC document: $e');
}
}