Skip to content

Instantly share code, notes, and snippets.

@jluisflo
Created January 5, 2026 20:00
Show Gist options
  • Select an option

  • Save jluisflo/991976d13358e100362fe736a2480c0b to your computer and use it in GitHub Desktop.

Select an option

Save jluisflo/991976d13358e100362fe736a2480c0b to your computer and use it in GitHub Desktop.
Design Doc: KYC Approval Direct Redirect to Document Acceptance - Credits Module

Design Document: KYC Approval Direct Redirect to Document Acceptance

Author: Jose Flores Date: 2026-01-05 Status: Proposed Module: Credits Module App


1. Overview

1.1 Problem Statement

Currently, when Didit approves a user's KYC verification, the application redirects to a generic "Request in Progress" message screen (MessageScreen). The user must then navigate back and tap "Continue" again to reach the document acceptance screen (LoanProductDocumentsScreen).

This creates unnecessary friction in the credit application flow.

1.2 Proposed Solution

Modify the verification flow to redirect users directly to the document acceptance screen when Didit returns an Approved status, while maintaining the current behavior for In Review and Declined statuses.

1.3 Goals

  • Improve user experience by reducing unnecessary navigation steps
  • Maintain proper handling for In Review cases (manual review required)
  • Keep error handling intact for Declined cases

2. Current Architecture

2.1 Flow Diagram (Current)

┌─────────────────────┐
│  ReferencesScreen   │
│  (has clientId,     │
│   productId)        │
└─────────┬───────────┘
          │ navigateToVerificationScreen(url)
          ▼
┌─────────────────────┐
│ VerificationScreen  │
│  (only has url)     │
│  [Didit WebView]    │
└─────────┬───────────┘
          │ Didit returns status
          ▼
┌─────────────────────────────────────────┐
│            Status Check                  │
├──────────┬──────────────┬───────────────┤
│ Approved │  In Review   │   Declined    │
└────┬─────┴──────┬───────┴───────┬───────┘
     │            │               │
     ▼            ▼               ▼
┌─────────────────────────────────────────┐
│           MessageScreen                  │
│     "Request in Progress" / Error        │
└─────────────────┬───────────────────────┘
                  │ User navigates back
                  ▼
┌─────────────────────────────────────────┐
│   MerchantAvailableLoanDetailScreen     │
│   (CurrentStepType = ACCEPT_TERMS)      │
└─────────────────┬───────────────────────┘
                  │ User taps "Continue"
                  ▼
┌─────────────────────────────────────────┐
│      LoanProductDocumentsScreen         │
└─────────────────────────────────────────┘

2.2 Key Files

File Purpose
lib/src/ui/pages/kyc/verification/verification_screen.dart WebView for Didit KYC, handles redirect logic
lib/src/ui/pages/kyc/references/references_screen.dart Triggers verification, has access to clientId & productId
lib/src/config/navigation/navigator_routes.dart Navigation helper functions
lib/src/config/constants/constants.dart KYC status constants

2.3 Current Code (verification_screen.dart:81-107)

shouldOverrideUrlLoading: (controller, navigationAction) async {
  final status = navigationAction.request.url?.queryParameters['status'];

  if (status == STATUS_KYC_APPROVED ||
      status == STATUS_KYC_IN_REVIEW ||
      status == STATUS_KYC_DECLINED) {

    // Both APPROVED and IN_REVIEW treated as success
    final bool isSuccess = status == STATUS_KYC_APPROVED ||
        status == STATUS_KYC_IN_REVIEW;

    final screenTitle = isSuccess
        ? KYC_REQUEST_IN_PROGRESS.i18n()
        : KYC_ERROR_IDENTITY_KYC.i18n();

    final screenMessage = isSuccess
        ? KYC_MESSAGE_REQUEST_IN_PROGRESS.i18n()
        : KYC_ERROR_IDENTITY_MESSAGE.i18n();

    // Always goes to MessageScreen
    navigateToMessageScreen(
      context,
      success: isSuccess,
      title: screenTitle,
      message: screenMessage,
    );

    return NavigationActionPolicy.CANCEL;
  }
  return NavigationActionPolicy.ALLOW;
}

3. Proposed Architecture

3.1 Flow Diagram (Proposed)

┌─────────────────────┐
│  ReferencesScreen   │
│  (has clientId,     │
│   productId)        │
└─────────┬───────────┘
          │ navigateToVerificationScreen(url, productId, clientId)
          ▼
┌─────────────────────┐
│ VerificationScreen  │
│  (has url, productId│
│   clientId)         │
│  [Didit WebView]    │
└─────────┬───────────┘
          │ Didit returns status
          ▼
┌─────────────────────────────────────────┐
│            Status Check                  │
├──────────┬──────────────┬───────────────┤
│ Approved │  In Review   │   Declined    │
└────┬─────┴──────┬───────┴───────┬───────┘
     │            │               │
     ▼            ▼               ▼
┌──────────────┐  │    ┌──────────────────┐
│LoanProduct   │  │    │  MessageScreen   │
│Documents     │  │    │  (Error)         │
│Screen        │  │    └──────────────────┘
└──────────────┘  │
                  ▼
          ┌──────────────────┐
          │  MessageScreen   │
          │  "In Review"     │
          └──────────────────┘

3.2 Changes Required

3.2.1 navigator_routes.dart

Current:

Future<dynamic> navigateToVerificationScreen(
  BuildContext context, {
  required String url,
}) =>
    Navigator.pushNamed(
      context,
      CreditsRoutes.verificationScreen,
      arguments: {
        URL_KEY: url,
      },
    );

Proposed:

Future<dynamic> navigateToVerificationScreen(
  BuildContext context, {
  required String url,
  required String productId,
  required String clientId,
}) =>
    Navigator.pushNamed(
      context,
      CreditsRoutes.verificationScreen,
      arguments: {
        URL_KEY: url,
        LOAN_PRODUCT_ID_KEY: productId,
        CLIENT_ID_KEY: clientId,
      },
    );

3.2.2 verification_screen.dart

Changes:

  1. Add new properties to receive productId and clientId:
class VerificationScreen extends StatefulWidget {
  const VerificationScreen({
    super.key,
    required this.sessionUrl,
    required this.productId,  // New
    required this.clientId,   // New
  });

  final String sessionUrl;
  final String productId;
  final String clientId;

  static Widget create(
    BuildContext context, {
    Map<Object, dynamic> args = const {},
  }) {
    final sessionUrl = args[URL_KEY] as String;
    final productId = args[LOAN_PRODUCT_ID_KEY] as String;  // New
    final clientId = args[CLIENT_ID_KEY] as String;         // New

    return VerificationScreen(
      sessionUrl: sessionUrl,
      productId: productId,
      clientId: clientId,
    );
  }
  // ...
}
  1. Update redirect logic in shouldOverrideUrlLoading:
shouldOverrideUrlLoading: (controller, navigationAction) async {
  final status = navigationAction.request.url?.queryParameters['status'];

  if (status == STATUS_KYC_APPROVED) {
    // Direct redirect to document acceptance
    navigateToLoanDocumentsList(
      context,
      productId: widget.productId,
      clientId: widget.clientId,
    );
    return NavigationActionPolicy.CANCEL;
  }

  if (status == STATUS_KYC_IN_REVIEW) {
    // Manual review required - show waiting message
    navigateToMessageScreen(
      context,
      success: true,
      title: KYC_REQUEST_IN_PROGRESS.i18n(),
      message: KYC_MESSAGE_REQUEST_IN_PROGRESS.i18n(),
    );
    return NavigationActionPolicy.CANCEL;
  }

  if (status == STATUS_KYC_DECLINED) {
    // KYC rejected - show error
    navigateToMessageScreen(
      context,
      success: false,
      title: KYC_ERROR_IDENTITY_KYC.i18n(),
      message: KYC_ERROR_IDENTITY_MESSAGE.i18n(),
    );
    return NavigationActionPolicy.CANCEL;
  }

  return NavigationActionPolicy.ALLOW;
}

3.2.3 references_screen.dart

Current (line 91-96):

navigateToVerificationScreen(
  context,
  url: state.clientIsEligible.clientIsEligibleData
          ?.prerequisiteStep.kycRedirectUrl ?? '',
);

Proposed:

navigateToVerificationScreen(
  context,
  url: state.clientIsEligible.clientIsEligibleData
          ?.prerequisiteStep.kycRedirectUrl ?? '',
  productId: loanDetails.productId,
  clientId: clientId,
);

4. Considerations

4.1 Navigation Stack

When redirecting directly to LoanProductDocumentsScreen, the navigation stack will be:

Home → LoanDetail → GeneralForm → References → Verification → Documents

The back button behavior should be considered. Options:

  1. Keep current stack - User can navigate back through all screens
  2. Clear stack to LoanDetail - Use pushNamedAndRemoveUntil to clean up intermediate screens

Recommendation: Keep current stack for now. Users may want to go back and modify their information.

4.2 Edge Cases

Scenario Handling
User presses back from Documents Returns to Verification (empty WebView) - may need to handle
Network error during redirect Let existing error handling manage it
Missing productId/clientId Add null checks with fallback to MessageScreen

4.3 Backend Dependency

This change assumes that when Didit returns Approved, the backend has already:

  1. Updated the user's KYC status
  2. Set CurrentStepType to ACCEPT_TERMS
  3. Made loan documents available for retrieval

Action Required: Verify with backend team that these conditions are met synchronously upon Didit approval.


5. Testing Plan

5.1 Unit Tests

  • VerificationScreen correctly parses new arguments
  • Navigation functions pass correct parameters

5.2 Integration Tests

  • Approved status → navigates to LoanProductDocumentsScreen
  • In Review status → navigates to MessageScreen with success state
  • Declined status → navigates to MessageScreen with error state
  • Back navigation works correctly from Documents screen

5.3 Manual Testing

  • Complete full KYC flow with Didit sandbox (Approved)
  • Complete full KYC flow with Didit sandbox (In Review)
  • Complete full KYC flow with Didit sandbox (Declined)
  • Verify documents load correctly after direct redirect

6. Rollout Plan

  1. Phase 1: Implement changes in development branch
  2. Phase 2: QA testing with Didit sandbox environment
  3. Phase 3: Deploy to staging for UAT
  4. Phase 4: Gradual rollout to production

6.1 Feature Flag (Optional)

Consider adding a feature flag to enable/disable direct redirect:

if (FeatureFlags.directKycRedirect && status == STATUS_KYC_APPROVED) {
  navigateToLoanDocumentsList(...);
} else if (status == STATUS_KYC_APPROVED || status == STATUS_KYC_IN_REVIEW) {
  navigateToMessageScreen(...);
}

7. Metrics

Track the following metrics to measure success:

Metric Current Target
Time from KYC approval to document acceptance ~15-30s (manual navigation) <3s (automatic)
Drop-off rate after KYC approval TBD Reduce by 20%
Credit application completion rate TBD Increase by 10%

8. Summary

This change improves the user experience by eliminating an unnecessary navigation step when Didit approves the KYC verification. The implementation requires modifications to three files and maintains backward compatibility for In Review and Declined scenarios.

Files to modify:

  1. navigator_routes.dart - Add parameters to navigation function
  2. verification_screen.dart - Receive parameters and implement conditional redirect
  3. references_screen.dart - Pass parameters when navigating to verification

Estimated effort: Low complexity, ~2-4 hours including testing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment