# NPCI (UPI) Integration - Merchant Onboarding & API Flow Documentation

## Overview
This document details how merchant onboarding and API interactions work with the external NPCI (National Payments Corporation of India) UPI system. The system maintains a dual-layer architecture:
- **Internal Database**: Local merchant records (Groups, Brands, Stores, Terminals)
- **External NPCI API**: Partner merchant management system at `demo.ctrmv.com`

---

## Architecture & Configuration

### External API Endpoints
The system uses two different base URLs for NPCI API calls:

| Purpose | Base URL | Config Key |
|---------|----------|-----------|
| **Registration** | `http://demo.ctrmv.com:4041/api/v1` | `:base_url` |
| **Updates/Queries** | `http://demo.ctrmv.com:4040/api/v1` | `:update_base_url` |

### Configuration (config/dev.exs or runtime.exs)
```elixir
config :da_product_app, :npci_external_api,
  base_url: "http://demo.ctrmv.com:4041/api/v1",
  update_base_url: "http://demo.ctrmv.com:4040/api/v1",
  partner_id: "UAE_PARTNER_001",
  api_key: "jfx2ozaJQpszIuek2b5NHagH93Z61sNVk_lYTWyVAlk",
  timeout: 30_000
```

---

## 1. Merchant Onboarding Flow

### Endpoint
```
POST /api/npci/merchant/onboard
```

### Request Payload
```json
{
  "merchant_code": "MRC_TEST_005",
  "brand_name": "Alice Store v5",
  "legal_name": "Alice Retail Pvt Ltd",
  "merchant_vpa": "alice5@upi",
  "contact_email": "alice@example.com",
  "contact_phone": "9876543210",
  "mid": "MID999999",
  "tid": "TID1234",
  "address": "123 Main St",
  "city": "Dubai",
  "state": "Dubai",
  "pincode": "400001",
  "business_type": "RETAIL",
  "merchant_type": "SMALL",
  "merchant_genre": "RETAIL",
  "corridor": "DOMESTIC",
  "settlement_frequency": "T+1",
  "settlement_account_number": "1234567890",
  "settlement_account_ifsc": "SBIN0001234",
  "max_transaction_limit": "50000.00",
  "daily_transaction_limit": "100000.00"
}
```

### Processing Flow

```
1. VALIDATION
   ├─ Check required fields: merchant_code, brand_name, merchant_vpa
   └─ Verify merchant_code is unique OR exists but not yet NPCI-registered

2. DATABASE CREATION (if new merchant)
   ├─ Create Group record (represents merchant chain)
   ├─ Create Brand record (under the Group)
   └─ Create Store record (under the Brand)
   
3. NPCI REGISTRATION
   ├─ Transform merchant data to external API format
   ├─ POST to external NPCI: /partners/{PARTNER_ID}/merchants
   │  Headers: Authorization: Bearer {API_KEY}, Content-Type: application/json
   └─ Receive merchant registration response from NPCI

4. RESPONSE STORAGE
   ├─ Save NPCI response in Group.params["npci_registration"]
   │  Structure:
   │  {
   │    "status": "success/failed",
   │    "response": {...NPCI_RESPONSE...},
   │    "registeredAt": "2026-05-12T12:33:48Z"
   │  }
   └─ Mark Group as npci_registered: true

5. RETURN RESPONSE
   └─ Return external NPCI API response to client
```

### Controller Implementation
**File**: `lib/da_product_app_web/controllers/npci_controller.ex:onboard_merchant/2`

Key steps:
1. Validates onboarding parameters (merchant_code, brand_name, merchant_vpa required)
2. Checks if merchant_code is unique or previously attempted
3. Creates Group, Brand, Store records in transaction
4. Calls `NpciRegistration.register/1`
5. Returns NPCI API response or error

### Example Response (Success)
```json
{
  "status": "success",
  "data": {
    "merchant_code": "MRC_TEST_005",
    "merchant_vpa": "alice5@upi",
    "brand_name": "Alice Store v5",
    "mid": "MID999999",
    "contact_email": "alice@example.com",
    "compliance_status": "PENDING"
  },
  "message": "Merchant registered successfully"
}
```

---

## 2. Merchant Update Flow (Partial Updates)

### Endpoint
```
PUT /api/npci/merchants/:merchant_code
```

### Request Payload (Example - updating compliance status)
```json
{
  "merchant_code": "MRC_TEST_005",
  "compliance_status": "PENDING"
}
```

Or multiple fields:
```json
{
  "merchant_code": "MRC_TEST_005",
  "brand_name": "Updated Alice Store v5",
  "contact_email": "newemail@alicestore.com",
  "contact_phone": "9999999999",
  "compliance_status": "VERIFIED"
}
```

### Processing Flow

```
1. VALIDATION
   ├─ Extract merchant_code from URL parameter
   └─ Check if merchant exists in local Group table

2. DATA TRANSFORMATION
   ├─ Extract only updatable fields from request:
   │  - brand_name
   │  - merchant_vpa
   │  - contact_email
   │  - contact_phone
   │  - address, city, state, pincode
   │  - max_transaction_limit
   │  - daily_transaction_limit
   │  - settlement_frequency
   │  - settlement_account_number
   │  - settlement_account_ifsc
   │  - compliance_status (NEW - now included)
   └─ Create flat payload for external API

3. EXTERNAL API CALL
   ├─ PUT to: {UPDATE_BASE_URL}/partners/{PARTNER_ID}/merchants/{MERCHANT_CODE}
   ├─ Headers:
   │  - Authorization: Bearer {API_KEY}
   │  - Content-Type: application/json
   │  - Accept: application/json
   └─ Send payload with only non-empty fields

4. RESPONSE HANDLING
   ├─ Parse external NPCI response
   └─ Save to Group.params["npci_update"]:
      {
        "status": "success/failed",
        "response": {...NPCI_RESPONSE...},
        "updatedAt": "2026-05-12T12:33:48.374278Z"
      }

5. RETURN RESPONSE
   └─ Return parsed NPCI response to client
```

### Controller Implementation
**File**: `lib/da_product_app_web/controllers/npci_controller.ex:update_merchant_details/2`

**Business Logic**: `lib/da_product_app/merchant_registration/npci_registration.ex:update_merchant/1`

**Field Transformation**: `lib/da_product_app/merchant_registration/npci_registration.ex:transform_merchant_update_format/1`

### Example CURL Request
```bash
curl --location --request PUT 'http://demo.ctrmv.com:6092/api/npci/merchants/MRC_TEST_005' \
--header 'Content-Type: application/json' \
--data '{
    "compliance_status": "PENDING"
}'
```

### HTTP Call to External NPCI
```bash
PUT http://demo.ctrmv.com:4040/api/v1/partners/UAE_PARTNER_001/merchants/MRC_TEST_005
Authorization: Bearer jfx2ozaJQpszIuek2b5NHagH93Z61sNVk_lYTWyVAlk
Content-Type: application/json

{
    "compliance_status": "PENDING"
}
```

### Response (Success)
```json
{
  "status": "success",
  "data": {
    "merchant_code": "MRC_TEST_005",
    "compliance_status": "PENDING",
    "updated_at": "2026-05-12T11:56:42Z"
  },
  "message": "Merchant updated successfully"
}
```

---

## 3. Merchant Status Update Flow

### Endpoint
```
PATCH /api/npci/merchants/:merchant_code/status
```

### Request Payload
```json
{
  "merchant_code": "MRC_TEST_005",
  "status": "ACTIVE"
}
```

### Processing Flow

```
1. VALIDATION
   ├─ Extract merchant_code from URL
   ├─ Check if merchant exists locally
   └─ Validate status field is present and not empty

2. STATUS PAYLOAD CONSTRUCTION
   └─ Create payload: { "status": "ACTIVE" }

3. EXTERNAL API CALL
   ├─ PATCH to: {UPDATE_BASE_URL}/partners/{PARTNER_ID}/merchants/{MERCHANT_CODE}/status
   ├─ Headers: Authorization, Content-Type
   └─ Send status payload

4. RESPONSE STORAGE
   └─ Save to Group.params["npci_status_update"]

5. RETURN RESPONSE
```

### HTTP Call to External NPCI
```bash
PATCH http://demo.ctrmv.com:4040/api/v1/partners/UAE_PARTNER_001/merchants/MRC_TEST_005/status
Authorization: Bearer {API_KEY}
Content-Type: application/json

{
    "status": "ACTIVE"
}
```

**Controller**: `lib/da_product_app_web/controllers/npci_controller.ex:update_merchant_status/2`

**Business Logic**: `lib/da_product_app/merchant_registration/npci_registration.ex:update_merchant_status/1`

---

## 4. Query APIs

### 4.1 Get Store Details
```
GET /api/npci/storeDetails?merchantRefId=MRC_TEST_005
```

**Flow**: Query local database for stores under merchant code
- Joins: Group → Brand → Store
- Returns store details with IDs and metadata

### 4.2 Get Device Details
```
GET /api/npci/deviceDetails?merchantRefId=MRC_TEST_005
```

**Flow**: Query devices (POS Terminals) under merchant's stores
- Joins: Group → Brand → Store → PosTerminal
- Returns all terminals assigned to merchant

### 4.3 Get Today's Transactions
```
GET /api/npci/todayTransactions?merchantRefId=MRC_TEST_005
```

**Flow**: Fetch transactions from today
- Date range: 00:00:00 - 23:59:59 of current day
- Includes settlement info
- Calculates total amounts

### 4.4 Get Total Transactions (with date range)
```
POST /api/npci/transactions
Content-Type: application/json

{
  "merchantRefId": "MRC_TEST_005",
  "start_date": "2026-05-01",
  "end_date": "2026-05-12"
}
```

**Flow**: Fetch transactions within date range
- Default: Month-to-date if no dates provided
- Returns settlement amounts and transaction details

### 4.5 Get Merchant Hierarchy
```
GET /api/npci/merchant/hierarchy?merchantRefId=MRC_TEST_005
```

**Flow**: Return full hierarchy
```
Chain (Group)
├── Brands
│   ├── Brand 1
│   ├── Brand 2
├── Stores
│   ├── Store 1
│   │   └── Terminals
│   └── Store 2
```

---

## 5. Data Storage Schema

### Group Table (Merchants)
```elixir
%Group{
  id: integer,
  name: string,                    # "Updated Alice Store v5"
  code: string,                    # "MRC_TEST_005" (merchant_code)
  description: string,             # legal_name
  phone_number: string,
  status: string,                  # "active", "inactive"
  npci_registered: boolean,        # Set after successful onboarding
  params: map,                     # Contains:
    %{
      "merchant_vpa" => "alice5@upi",
      "mid" => "MID999999",
      "tid" => "TID1234",
      ...
      "npci_registration" => %{
        "status" => "success",
        "response" => {...},
        "registeredAt" => "2026-05-12T..."
      },
      "npci_update" => %{
        "status" => "success",
        "response" => {...},
        "updatedAt" => "2026-05-12T..."
      },
      "npci_status_update" => %{
        "status" => "success",
        "response" => {...},
        "updatedAt" => "2026-05-12T..."
      }
    }
}
```

### Related Tables
- **Brand**: Represents business brand under merchant
- **Store**: Physical/logical store under brand
- **PosTerminal**: Payment terminal/device at store
- **Transaction**: Individual transactions

---

## 6. Error Handling

### Common Error Scenarios

| Scenario | HTTP Status | Response |
|----------|-------------|----------|
| Merchant not found | 404 | `{status: "error", message: "Merchant not found"}` |
| Missing required fields | 400 | `{status: "error", errors: {...}}` |
| Merchant code already exists | 409 | `{status: "error", message: "Merchant already exists"}` |
| External API failure | 502 | `{status: "error", message: "External NPCI update failed"}` |
| Network/Timeout error | 502 | `{status: "error", error: "Network error"}` |

### Logging
All operations are logged with:
- Request parameters
- External API URLs and headers (redacted API key in debug)
- Response status and body
- Database operations
- Error details

---

## 7. Complete Request-Response Flow Diagram

```
CLIENT
  │
  ├─ POST /api/npci/merchant/onboard
  │    │
  │    └─→ NpciController.onboard_merchant
  │         │
  │         ├─ Validate params
  │         ├─ Create Group, Brand, Store (local DB)
  │         │
  │         └─ Call NpciRegistration.register
  │              │
  │              ├─ Transform data
  │              │
  │              └─ POST http://demo.ctrmv.com:4041/api/v1/partners/UAE_PARTNER_001/merchants
  │                   │
  │                   └─→ NPCI External API
  │
  └─ PUT /api/npci/merchants/:merchant_code
       │
       └─→ NpciController.update_merchant_details
            │
            ├─ Validate merchant exists (local DB)
            │
            └─ Call NpciRegistration.update_merchant
                 │
                 ├─ Transform update payload
                 │
                 └─ PUT http://demo.ctrmv.com:4040/api/v1/partners/UAE_PARTNER_001/merchants/{code}
                      │
                      └─→ NPCI External API
                           │
                           └─ Response saved to Group.params["npci_update"]
```

---

## 8. Key Implementation Details

### HTTP Client
- **Library**: HTTPoison
- **Methods Used**: `post/4`, `put/4`, `patch/4`, `get/2`
- **Timeout**: 30 seconds (configurable)

### Request Headers
```elixir
headers = [
  {"Content-Type", "application/json"},
  {"Authorization", "Bearer #{api_key}"},
  {"Accept", "application/json"}
]
```

### Response Handling
- JSON parsing with Jason
- Status code validation (200-299 = success)
- Error responses saved for audit trail
- Raw responses preserved for non-JSON responses

### Data Transformation
The `transform_merchant_update_format/1` function ensures:
- Only non-empty fields are included
- Field names match external API expectations
- Optional fields are skipped if not provided
- New field `compliance_status` is now supported

---

## 9. Testing the Flow

### Test Onboarding
```bash
curl -X POST http://localhost:4000/api/npci/merchant/onboard \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_code": "TEST_MRC_001",
    "brand_name": "Test Store",
    "merchant_vpa": "test@upi",
    "contact_email": "test@test.com",
    "contact_phone": "9876543210"
  }'
```

### Test Update
```bash
curl -X PUT http://localhost:4000/api/npci/merchants/TEST_MRC_001 \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_code": "TEST_MRC_001",
    "compliance_status": "VERIFIED"
  }'
```

### Test Status Update
```bash
curl -X PATCH http://localhost:4000/api/npci/merchants/TEST_MRC_001/status \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_code": "TEST_MRC_001",
    "status": "ACTIVE"
  }'
```

---

## 10. Recent Changes

### Compliance Status Support
**Date**: 2026-05-12

**Change**: Added `compliance_status` field to merchant update transformation

**File Modified**: `lib/da_product_app/merchant_registration/npci_registration.ex`

**Details**: 
- The `transform_merchant_update_format/1` function now includes compliance_status mapping
- Allows updating merchant compliance status via PUT /api/npci/merchants/:merchant_code
- External NPCI API receives compliance_status in the payload
- Useful for updating merchant verification/compliance state

**Before**:
```elixir
# compliance_status was NOT mapped - would be dropped
```

**After**:
```elixir
payload =
  if Map.has_key?(params, "compliance_status"),
    do: Map.put(payload, "compliance_status", params["compliance_status"]),
    else: payload
```

---

## References

- **NpciRegistration Module**: `lib/da_product_app/merchant_registration/npci_registration.ex`
- **NpciController**: `lib/da_product_app_web/controllers/npci_controller.ex`
- **Routes**: `lib/da_product_app_web/router.ex` (scope `/api/npci`)
- **External API Docs**: Partner NPCI documentation at `demo.ctrmv.com:4040` & `4041`
