Author: Jose Flores Date: 2026-01-05 Status: Proposed Module: Credits Module App
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.
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.
- Improve user experience by reducing unnecessary navigation steps
- Maintain proper handling for
In Reviewcases (manual review required) - Keep error handling intact for
Declinedcases
┌─────────────────────┐
│ 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 │
└─────────────────────────────────────────┘
| 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 |
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;
}┌─────────────────────┐
│ 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" │
└──────────────────┘
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,
},
);Changes:
- Add new properties to receive
productIdandclientId:
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,
);
}
// ...
}- 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;
}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,
);When redirecting directly to LoanProductDocumentsScreen, the navigation stack will be:
Home → LoanDetail → GeneralForm → References → Verification → Documents
The back button behavior should be considered. Options:
- Keep current stack - User can navigate back through all screens
- Clear stack to LoanDetail - Use
pushNamedAndRemoveUntilto clean up intermediate screens
Recommendation: Keep current stack for now. Users may want to go back and modify their information.
| 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 |
This change assumes that when Didit returns Approved, the backend has already:
- Updated the user's KYC status
- Set
CurrentStepTypetoACCEPT_TERMS - Made loan documents available for retrieval
Action Required: Verify with backend team that these conditions are met synchronously upon Didit approval.
-
VerificationScreencorrectly parses new arguments - Navigation functions pass correct parameters
- Approved status → navigates to
LoanProductDocumentsScreen - In Review status → navigates to
MessageScreenwith success state - Declined status → navigates to
MessageScreenwith error state - Back navigation works correctly from Documents screen
- 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
- Phase 1: Implement changes in development branch
- Phase 2: QA testing with Didit sandbox environment
- Phase 3: Deploy to staging for UAT
- Phase 4: Gradual rollout to production
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(...);
}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% |
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:
navigator_routes.dart- Add parameters to navigation functionverification_screen.dart- Receive parameters and implement conditional redirectreferences_screen.dart- Pass parameters when navigating to verification
Estimated effort: Low complexity, ~2-4 hours including testing