verifyNfcDocument method

  1. @override
Future<Map<String, dynamic>> verifyNfcDocument(
  1. String sessionId,
  2. Map<String, dynamic> verificationData
)
override

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');
  }
}