07 - ApiService

The single class that talks to the backend. ~1100 lines, ~60 methods, grouped by stage. Every network call in the app goes through this file.

Overview

ApiService is instantiated with a StorageService dependency. It exposes one public method per backend endpoint. Internally, all methods delegate to four generic helpers: _post, _get, _put, _delete.

flutter_app/lib/services/api_service.dartDartclass header + constructor
class ApiService {
  final StorageService _storage;

  ApiService({required StorageService storage}) : _storage = storage;
}

Headers Injection

Every request gets standard headers automatically. Auth bearer token, X-Lead-Id, X-Session-Id are read from StorageService.

flutter_app/lib/services/api_service.dartDart_headers getter
Map<String, String> get _headers {
  final headers = <String, String>{
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  };

  final token = _storage.token;
  if (token != null) headers['Authorization'] = 'Bearer $token';

  final leadId = _storage.leadId;
  if (leadId != null) headers['X-Lead-Id'] = leadId;

  final sessionId = _storage.sessionId;
  if (sessionId != null) headers['X-Session-Id'] = sessionId;

  return headers;
}

Generic _post Helper

All POST methods use this. It handles timeout, auth, leadId injection into body, response parsing, error handling, and network exceptions.

flutter_app/lib/services/api_service.dartDart_post<T>
Future<ApiResponse<T>> _post<T>(
  String url,
  Map<String, dynamic> body, {
  required T Function(Map<String, dynamic>) fromJson,
}) async {
  // Auto-inject leadId if missing from body
  body['leadId'] ??= _storage.leadId;

  try {
    final response = await http.post(
      Uri.parse(url),
      headers: _headers,
      body: jsonEncode(body),
    ).timeout(ApiConfig.apiTimeout);

    await _storage.updateActivity();   // reset session timer

    final json = jsonDecode(response.body) as Map<String, dynamic>;
    if (response.statusCode == 200 && json['success'] == true) {
      return ApiResponse.ok(
        fromJson(json['data'] as Map<String, dynamic>),
        json['message'] as String?,
      );
    }
    return ApiResponse.fail(
      json['message'] as String? ?? 'Unknown error',
      json['errorCode'] as String?,
    );
  } on SocketException {
    return ApiResponse.fail('No internet connection');
  } on TimeoutException {
    return ApiResponse.fail('Request timed out');
  } catch (e) {
    debugPrint('API error: $e');
    return ApiResponse.fail('Something went wrong');
  }
}

Cross-Cutting Concerns

Every call goes through _post/_get, so all of these are handled in one place:

ConcernHow
Auth_headers adds Authorization: Bearer <token>
Lead tracking_headers adds X-Lead-Id, and body['leadId'] ??= ... in _post
Session activity_storage.updateActivity() resets the session timer
Timeout.timeout(ApiConfig.apiTimeout) = 30 seconds
Network errorsSocketException → "No internet connection"
Response wrappingJSON parsed and wrapped in ApiResponse<T>
Stage progressionIndividual methods call _storage.setCurrentStage(N) on success

API Method Reference (Grouped by Stage)

Below is every public method in ApiService, what it does, and the backend handler it hits. Use this as a quick lookup when you need to find where an endpoint is defined.

Stage 0 - Arrival

MethodHTTPBackend Controller
journeyInitGET /api/v1/journey/initCommon/ArrivalController.cs
appVersionCheckPOST /api/v1/app/version-checkCommon/JourneyController.cs
getJourneyStatusGET /api/v1/journey/statusCommon/JourneyController.cs

Stage 1 - Registration

MethodHTTPBackend Controller
registrationSignupPOST /api/v1/registration/signupStage1/RegistrationController.cs
getResumeStateGET /api/v1/registration/resume/{leadId}Stage1/RegistrationController.cs

Stage 2 - Mobile OTP

MethodHTTPBackend Controller
verifyMobileOtpPOST /api/v1/registration/otp/verifyStage2/OtpVerificationController.cs
resendMobileOtpPOST /api/v1/registration/otp/resendStage2/OtpVerificationController.cs

Stage 3 - Email

MethodHTTPBackend Controller
getEmailDetailsGET /api/v1/emailStage3/EmailController.cs
generateEmailOtpPOST /api/v1/email/otp/generateStage3/EmailController.cs
verifyEmailOtpPOST /api/v1/email/otp/verifyStage3/EmailController.cs
resendEmailOtpPOST /api/v1/email/otp/resendStage3/EmailController.cs

Stage 4 - PAN

MethodHTTPBackend Controller
getPanDetailsGET /api/v1/pan/detailsStage4/PanController.cs
validatePanPOST /api/v1/pan/validateStage4/PanController.cs
confirmPanPOST /api/v1/pan/verifyStage4/PanController.cs

Stage 5 - Aadhaar / DigiLocker

MethodHTTPBackend Controller
getAadhaarDetailsGET /api/v1/aadhaar/detailsStage5/AadhaarController.cs
initiateDigilockerPOST /api/v1/aadhaar/digilocker/initiateStage5/AadhaarController.cs
digilockerCallbackPOST /api/v1/aadhaar/digilocker/callbackStage5/AadhaarController.cs
uploadAadhaarPOST /api/v1/aadhaar/uploadStage5/AadhaarController.cs

Stage 6 - Bank

MethodHTTPBackend Controller
getBankDetailsGET /api/v1/bank/detailsStage6/BankController.cs
submitBankAccountPOST /api/v1/bank/accountStage6/BankController.cs
initiateReversePennyPOST /api/v1/bank/reverse-penny/initiateStage6/BankController.cs
completeReversePennyPOST /api/v1/bank/reverse-penny/completeStage6/BankController.cs
getBankByIfscGET /api/v1/bank/list/{ifsc}Stage6/BankController.cs

Stage 7 - Liveness

MethodHTTPBackend Controller
initiateLivenessPOST /api/v1/selfie/liveness/initiateStage7/LivenessController.cs
livenessCallbackPOST /api/v1/selfie/liveness/callbackStage7/LivenessController.cs

Stages 8-15 (abbreviated)

StageKey methodsBackend Controller
8 SignaturegetSignatureDetails, uploadSignatureStage8/SignatureController.cs
9 PersonalgetPersonalDetails, savePersonalDetails, addNominee, updateNominee, deleteNominee, verifyNomineePan, declareNoNominee, uploadIncomeProofStage9/PersonalDetailsController.cs, NomineeController.cs
11 ValidationrunFinalValidationStage11/FinalValidationController.cs
12 Doc GengenerateAof, getDocumentPdfStage12/DocumentGenerationController.cs
13 eSigngetEsignDetails, initiateEsign, esignCallbackStage13/EsignController.cs
14 AccountcreateAccount, getAccountStatusStage14/AccountCreationController.cs
15 Activation + PaymentactivateAccount, getPaymentDefaults, initiatePayment, paymentCallbackStage15/ActivationController.cs, PaymentController.cs
Backend route constants
  • backend/src/MO.Ekyc.Shared/Constants/ApiRoutes.cs - source of truth for all route strings
  • Backend docs (pending) - placeholder

How to Add a New API Method

Example: adding a method getStageConfig that GETs /api/v1/config/stage/{n}.

Step 1 - Add the URL to ApiConfig

flutter_app/lib/config/api_config.dartDart
static String stageConfig(int stage) => '$_base/config/stage/$stage';

Step 2 - Create a Model (if new response shape)

flutter_app/lib/models/stage_config.dartDart
class StageConfig {
  final int stageNumber;
  final String displayName;
  final int progressPercent;

  StageConfig({required this.stageNumber, required this.displayName, required this.progressPercent});

  factory StageConfig.fromJson(Map<String, dynamic> json) => StageConfig(
    stageNumber: json['stageNumber'] as int,
    displayName: json['displayName'] as String,
    progressPercent: json['progressPercent'] as int,
  );
}

Step 3 - Add the Method to ApiService

flutter_app/lib/services/api_service.dartDart
// Under the appropriate stage section
Future<ApiResponse<StageConfig>> getStageConfig(int stage) async {
  return _get<StageConfig>(
    ApiConfig.stageConfig(stage),
    fromJson: StageConfig.fromJson,
  );
}

Step 4 - Call from a Screen

Any screen fileDart
final result = await api.getStageConfig(6);
if (result.success) {
  debugPrint('Stage 6: ${result.data!.displayName}');
}

That's it - four steps, no new files beyond the model.

StorageService Reference

ApiService depends on StorageService for auth/session state. Here are its main getters and setters.

flutter_app/lib/services/storage_service.dartDart
class StorageService {
  // Singleton
  static Future<StorageService> getInstance();

  // Session identifiers
  String? get leadId;
  String? get sessionId;
  String? get token;
  int get currentStage;

  // Setters (all async, persist immediately)
  Future<void> setLeadId(String id);
  Future<void> setSessionId(String id);
  Future<void> setToken(String t);
  Future<void> setCurrentStage(int stage);

  // Derived
  bool get hasActiveLead => leadId != null;

  // Activity tracking (for session timer)
  Future<void> updateActivity();
  DateTime? get lastActivity;

  // Clear all (used on journey exit)
  Future<void> clearAll();
}