# Pirimid Banking Analysis Flow --- ## Overview This document outlines the end-to-end flow of bank statement processing via the Pirimid integration. It covers how a bank statement is uploaded, how it's sent to Pirimid for analysis, the asynchronous webhook handling, data extraction, and the final business rules engine (BRE) evaluation. It also provides a brief overview of the alternative Account Aggregator (AA) flow. --- ## 1. Bank Statement Upload Flow ### A. Document Upload (`uploadConsumerDocuments`) The process begins when a user uploads bank statements through the admin UI. - **Location**: `app/Http/Controllers/Admin/ConsumerApplicationController.php` -> `uploadConsumerDocuments()` - **Action**: The method collects all uploaded bank statement files and their associated passwords into a collection (`$bankStatementCollection`). - It then passes the collection to `processAndSubmitBankStatements($id, $type, $bankStatementCollection, $app)` to handle processing. ### B. Processing and Submission (`processAndSubmitBankStatements`) - **File Handling**: If multiple files are uploaded for a single applicant (e.g., multiple months of statements), the method zips them together using `createZipFromBankStatements()`. Single files are processed directly. - **Payload Preparation**: A data array (`$pirimidData`) is prepared containing the file(s), passwords, applicant type, and employment type. - **Triggering Pirimid**: The method instantiates the `Pirimid` class and calls `uploadBankStatement()`. ### C. Sending to Pirimid (`Pirimid::uploadBankStatement`) - **Location**: `app/Pirimid.php` -> `uploadBankStatement()` - **Action**: - The method prepares the request payload using the `postData()` helper. - It makes a `POST` cURL request to Pirimid's `/bank-statement/process-work-order` API with the file(s). - **Database Entry**: Upon a successful API call, Pirimid returns a response containing a `trackingId`. The method then calls `saveToDB()` which creates a new entry in the `pirimid_bank_analysis` table. This entry stores the `application_id`, `tracking_id`, `applicant_type`, and the initial API response. --- ## 2. Asynchronous Webhook & Analytics Retrieval Because bank statement analysis takes time, Pirimid processes the statement asynchronously and fires a webhook to the CreditFair system upon completion. ### A. Webhook Handler (`Pirimid::processWebhook`) - **Location**: `app/Pirimid.php` -> `processWebhook()` - **Action**: - The webhook endpoint receives the Pirimid payload containing the `trackingId`. - It validates the `trackingId` against the `pirimid_bank_analysis` table. - It checks if the analysis is fully complete: `$data['message']['reportStatus'] == "COMPLETED"`. ### B. Fetching Analytics (`Pirimid::fetchAnalytics`) Once the report is flagged as completed, the webhook triggers the retrieval of the resulting analytics files from Pirimid's `/analytics/{type}/download` API: 1. **Excel File**: - Calls `fetchAnalytics($trackingId, 'excel')`. - Saves the Excel stream to the S3 bucket (`consumer/{id}/pirimid_analysis_excel_{trackingId}.xls`). - Logs the file metadata in the `consumer_application_files` table. 2. **JSON File**: - Calls `fetchAnalytics($trackingId, 'json')`. - Saves the JSON stream to S3 (`consumer/{id}/pirimid_analysis_json{trackingId}.json`). - Logs the file metadata in `consumer_application_files`. ### C. Data Extraction & Storage After fetching the JSON file, the webhook decodes it to extract specific aggregate data to be used in the internal database: - **`adjusted_abb`**: Extracted from `$parsedJson['analytics']['overallAnalysis']['customAbbs']['adjustedAbb2']['averageOfMonthlyAverages']`. - **`duration_in_months`**: Calculated by counting the elements in the `$parsedJson['analytics']['overallAnalysis']['customAbbs']['adjustedAbb2']['monthlyData']` array. - These two values are saved to the existing `pirimid_bank_analysis` database row, and the row's `analytics_fetch` flag is updated to `1`. ### D. Auto-Decision / BRE Execution Once the files and extracted data are safely saved, the webhook initiates the business rules engine (BRE): - **Auto Rejection**: - If the applicant is a co-applicant (`applicant_type == 2`), it runs `autoRejectCoApplicantJSONBRE()`. - Otherwise, it runs `autoReject()` for the primary applicant. - **State Updates**: If the BRE determines the application should be rejected, the application state is updated to `rejected` and a status log is created. - **Auto Decisioning**: If the application is not rejected, and the merchant has specific banking approval rules configured (e.g., `emi_multiplier_on_abb`), it runs `ConsumerApplicationController::runAutoDecision(..., 'banking_decision_webhook')` to evaluate if the application can be auto-approved based on the newly available ABB data. ## 3. Account Aggregator (AA) Flow There is an alternative, paperless flow for fetching bank analytics using the Account Aggregator (AA) framework. This allows users to grant consent digitally via OTP without uploading physical PDF bank statements. This entire process is encapsulated in `app/AccountAggregator.php`. ### A. Initiation (`initiate`) The flow begins when the user opts for the Account Aggregator method. 1. **Validation & Deduplication (`isAlreadyInitiated`)**: - The system checks the `account_aggregator` table. If an AA request was initiated for this application within the last 24 hours, it retrieves the existing `redirectionUrl` and simply re-triggers the SMS to the user rather than creating a duplicate request with Pirimid. - If the request is older than 24 hours, it marks it as expired and generates a new request. 2. **Database Tracking**: A new `AccountAggregatorModel` entry is saved with `is_monitoring` set based on the required templates (typically `[UNDERWRITING, MONITORING]`). 3. **Pirimid API Request (`process`)**: A `POST` request is sent to Pirimid's `/web/multi-consent/initiate/phone-number` endpoint with the user's phone number, tracking ID (format: `{request_id}_{application_id}`), and the template types. 4. **Customer Communication (`sendCommunication`)**: Upon a successful API response, the system receives a `redirectionUrl`. It shortens the URL using `Shortio` and sends an SMS to the user containing the link to complete the consent journey. Slack alerts are generated for both successful payloads and errors. ### B. Webhook & Fetching Analytics (`fetchAnalytics`) Once the user completes the OTP and consent journey via the AA link, Pirimid fires a webhook to the system. 1. **Updating Identifiers (`updateReferenceAndTrackingId`)**: The webhook payload contains consent tracking details. The system parses these to update the `trackingId` and `referenceId` in the `account_aggregator` table, distinguishing between `UNDERWRITING` and `MONITORING` templates. 2. **Sequential File Retrieval**: The system makes three separate `GET` requests to Pirimid using the received `trackingId` and `referenceId`: - **JSON Format**: Fetched from `/web/fetch/analytics/json`. - **Excel Format**: Fetched from `/web/fetch/analytics/excel`. - **PDF Format**: Fetched from `/web/fetch/pdf`. 3. **Storage (`saveAnalyticsFile`)**: For each format fetched, the payload is saved to the S3 bucket (`{application_id}/{request_id}_account_aggregator.{ext}`) and logged inside the `consumer_application_files` table. 4. **Mandatory Document Removal**: Because the AA journey provides verified bank statements digitally, the system automatically removes `3_month_bank_statement` from the application's `mandatory_document_list` in `ConsumerApplicationElement`. 5. **Triggering BRE**: Once the files are successfully saved, the flow triggers `runBankingBre()`. ### C. Auto-Decision / BRE Execution (`runBankingBre`) This method mirrors the final step of the manual upload flow but relies on the AA data. 1. **Pre-check**: Aborts if the application is already approved (`status_logs`). 2. **Auto Rejection**: Calls `AutoRejection::autoReject(false, $id, 'banking', '', $accountAggregatorId)`. Note that it passes the `$accountAggregatorId` down to the BRE engine instead of a Pirimid tracking ID, ensuring the engine queries the AA dataset. - If rejected, the application state changes to `rejected` and a status log is recorded. 3. **Auto Decisioning**: If the application is not rejected, it checks if the merchant has an active `emi_multiplier_on_abb` rule. - If the rule exists and the application is in a valid state (e.g., `in-process`, `waiting for decision`), it runs `ConsumerApplicationController::runAutoDecision(..., 'banking_decision_webhook', $accountAggregatorId)`. - If auto-approval conditions are not fully met, the application is gracefully moved to `waiting for decision`. ### D. Ongoing FI Monitoring (`processMonitoringRequest`) Because AA allows for recurring consent (`MONITORING` template), the system supports ongoing background checks (e.g., fetching updated statements every few days). - **Background Jobs**: The system can enqueue background tasks (`Jobs()` table) via `createMonitoringRequest()`. These jobs run automatically after a configured frequency (e.g., `auto_fetch_frequency`, defaulting to 7 days). - **FI API Call**: When the job processes, `processMonitoringRequest()` calls Pirimid's `/web/fi-request` endpoint with a dynamic date range (typically the last 3 months up to the current date). - **Dynamic Updates**: Monitoring fetch dates and statuses can be manually adjusted (`updateAutoFetch`) or paused based on external business events, such as a subsidy being received (`updateSubsidyStatus`).