Developer Guide
Backend Developer Guide:
Introduction
This technical documentation outlines the core features and functionalities of the system, providing developers with an overview and detailed guidance for implementation. The system is designed to offer seamless user management, data security, and efficient, Benefit exploring and connection with the UBI network(ONSET) and application handling.
Architecture
Key Features
Data storage
Postgres 17 stores user data, user documents, user applications, user consent, and user roles.
User authentication & authorization
Keyclock is used for IAM along with user authentication & authorization. Keyclock is also used for the registration & login process.
Import documents from E-Wallet Documents can be imported from e-wallet.
Eligibility criteria filter Users are shown benefits they are eligible to based on their profile.
Apply to benefits Users can apply for benefits provided by different providers. The application process runs through the ONEST network.
Application tracking The status of each user application is tracked through ONEST APIs.
Data privacy & security Sensitive information such as aadhaar number is stored in encrypted format. User documents are also stored in encrypted format.
Modules
1. Auth: For user registration, login and logout APIs 2. OTP: Contains APIs for sending OTP and verifying OTP 3. Users: Contains APIs required for user experience
Services
1. Keycloak: Contains all the functions related to keycloak such as CRUD users in keycloak, get keycloak admin token, get user token etc.
2. Proxy: For calling ONEST-related APIs
3. Hasura:
APIs
1. register_with_password: Register a new user on ‘username’-’password’ basis.
curl --location 'https://your-beneficary-api-domain.com/api/auth/register_with_password' \
--header 'Accept: application/json, text/plain, */*' \
--header 'Accept-Language: en-GB,en-US;q=0.9,en;q=0.8' \
--header 'Connection: keep-alive' \
--header 'Content-Type: application/json' \
--header 'Origin: http://localhost:5173' \
--header 'Referer: http://localhost:5173/' \
--header 'Sec-Fetch-Dest: empty' \
--header 'Sec-Fetch-Mode: cors' \
--header 'Sec-Fetch-Site: cross-site' \
--header 'User-Agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36' \
--header 'sec-ch-ua: "Chromium";v="128", "Not;A=Brand";v="24", "Google Chrome";v="128"' \
--header 'sec-ch-ua-mobile: ?1' \
--header 'sec-ch-ua-platform: "Android"' \
--data '{
"firstName": "Yash",
"lastName": "Gavai",
"phoneNumber": "1324567890",
"password": "123456"
}'
2. login: Login to the beneficiary app
curl --location 'https://your-beneficary-api-domain.com/api/auth/login' \
--header 'Accept: application/json, text/plain, */*' \
--header 'Accept-Language: en-GB,en-US;q=0.9,en;q=0.8' \
--header 'Connection: keep-alive' \
--header 'Content-Type: application/json' \
--header 'Origin: http://localhost:5173' \
--header 'Referer: http://localhost:5173/' \
--header 'Sec-Fetch-Dest: empty' \
--header 'Sec-Fetch-Mode: cors' \
--header 'Sec-Fetch-Site: cross-site' \
--header 'User-Agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36' \
--header 'sec-ch-ua: "Chromium";v="128", "Not;A=Brand";v="24", "Google Chrome";v="128"' \
--header 'sec-ch-ua-mobile: ?1' \
--header 'sec-ch-ua-platform: "Android"' \
--data '{"username":"","password":""}'
3. logout: logout of app
curl --location 'https://your-beneficary-api-domain.com/api/auth/logout' \
--header 'Accept: application/json, text/plain, */*' \
--header 'Accept-Language: en-GB,en-US;q=0.9,en;q=0.8' \
--header 'Connection: keep-alive' \
--header 'Content-Type: application/json' \
--header 'Origin: http://localhost:5173' \
--header 'Referer: http://localhost:5173/' \
--header 'Sec-Fetch-Dest: empty' \
--header 'Sec-Fetch-Mode: cors' \
--header 'Sec-Fetch-Site: cross-site' \
--header 'User-Agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36' \
--header 'sec-ch-ua: "Chromium";v="128", "Not;A=Brand";v="24", "Google Chrome";v="128"' \
--header 'sec-ch-ua-mobile: ?1' \
--header 'sec-ch-ua-platform: "Android"' \
--data '{"access_token":<access_token>,"refresh_token":<refresh_token>}'
4. get_my_consents:
curl --location 'https://your-beneficary-api-domain.com/api/users/get_my_consents' \
--header 'Accept: */*' \
--header 'Accept-Language: en-GB,en-US;q=0.9,en;q=0.8' \
--header 'Authorization: Bearer <token>' \
--header 'Connection: keep-alive' \
--header 'If-None-Match: W/"1ed-1j9BkBZ1ZAkJwId4k8fcQL34/JM"' \
--header 'Origin: http://localhost:5173' \
--header 'Referer: http://localhost:5173/' \
--header 'Sec-Fetch-Dest: empty' \
--header 'Sec-Fetch-Mode: cors' \
--header 'Sec-Fetch-Site: cross-site' \
--header 'User-Agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36' \
--header 'sec-ch-ua: "Chromium";v="128", "Not;A=Brand";v="24", "Google Chrome";v="128"' \
--header 'sec-ch-ua-mobile: ?1' \
--header 'sec-ch-ua-platform: "Android"'
5. consent:
curl --location 'https://your-beneficary-api-domain.com/api/users/consent' \
--header 'Accept: */*' \
--header 'Accept-Language: en-GB,en-US;q=0.9,en;q=0.8' \
--header 'Authorization: Bearer <token>' \
--header 'Connection: keep-alive' \
--header 'Content-Type: application/json' \
--header 'Origin: http://localhost:5173' \
--header 'Referer: http://localhost:5173/' \
--header 'Sec-Fetch-Dest: empty' \
--header 'Sec-Fetch-Mode: cors' \
--header 'Sec-Fetch-Site: cross-site' \
--header 'User-Agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36' \
--header 'sec-ch-ua: "Chromium";v="128", "Not;A=Brand";v="24", "Google Chrome";v="128"' \
--header 'sec-ch-ua-mobile: ?1' \
--header 'sec-ch-ua-platform: "Android"' \
--data '{"user_id":"4d37-9f87-217f828afc43","purpose":"sign_up_tnc","purpose_text":"sign_up_tnc","accepted":true}'
6. get_one:
curl --location 'https://your-beneficary-api-domain.com/api/users/get_one/?decryptData=true' \
--header 'Accept: application/json' \
--header 'Accept-Language: en-GB,en-US;q=0.9,en;q=0.8' \
--header 'Authorization: Bearer <token>' \
--header 'Connection: keep-alive' \
--header 'If-None-Match: W/"43c-WpEjzpeZc92dbz32ki5lJAM1tfU"' \
--header 'Origin: http://localhost:5173' \
--header 'Referer: http://localhost:5173/' \
--header 'Sec-Fetch-Dest: empty' \
--header 'Sec-Fetch-Mode: cors' \
--header 'Sec-Fetch-Site: cross-site' \
--header 'User-Agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36' \
--header 'sec-ch-ua: "Chromium";v="128", "Not;A=Brand";v="24", "Google Chrome";v="128"' \
--header 'sec-ch-ua-mobile: ?1' \
--header 'sec-ch-ua-platform: "Android"'
7. user_docs:
This API is used when importing documents from the
wallet. User documents are passed in the request body as an array. Each
document must contain doc_type, doc_subtype, doc_name, doc_data,
imported_from and doc_datatype. doc_data is encrypted before saving
to the database. After documents are saved successfully, user profile
fields are updated based on respective documents.
curl --location 'https://your-beneficary-api-domain.com/api/users/wallet/user_docs' \
--header 'Accept: application/json, text/plain, */*' \
--header 'Accept-Language: en-GB,en-US;q=0.9,en;q=0.8' \
--header 'Authorization: Bearer <token>' \
--header 'Connection: keep-alive' \
--header 'Content-Type: application/json' \
--header 'Origin: http://localhost:5173' \
--header 'Referer: http://localhost:5173/' \
--header 'Sec-Fetch-Dest: empty' \
--header 'Sec-Fetch-Mode: cors' \
--header 'Sec-Fetch-Site: cross-site' \
--header 'User-Agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36' \
--header 'sec-ch-ua: "Chromium";v="128", "Not;A=Brand";v="24", "Google Chrome";v="128"' \
--header 'sec-ch-ua-mobile: ?1' \
--header 'sec-ch-ua-platform: "Android"' \
--data-raw '[
{
"doc_name": "Aadhaar Card",
"doc_type": "idProof",
"doc_subtype": "aadhaar",
"doc_data": {
<Adhaar_Data>
},
"uploaded_at": "2024-12-03T12:57:45.557Z",
"imported_from": "e-wallet",
"doc_datatype": "Application/JSON"
}
]'
8. user_applications_list:
curl --location 'https://your-beneficary-api-domain.com/api/users/user_applications_list' \
--header 'Accept: application/json' \
--header 'Accept-Language: en-GB,en-US;q=0.9,en;q=0.8' \
--header 'Authorization: Bearer <token>' \
--header 'Connection: keep-alive' \
--header 'Content-Type: application/json' \
--header 'Origin: http://localhost:5173' \
--header 'Referer: http://localhost:5173/' \
--header 'Sec-Fetch-Dest: empty' \
--header 'Sec-Fetch-Mode: cors' \
--header 'Sec-Fetch-Site: cross-site' \
--header 'User-Agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36' \
--header 'sec-ch-ua: "Chromium";v="128", "Not;A=Brand";v="24", "Google Chrome";v="128"' \
--header 'sec-ch-ua-mobile: ?1' \
--header 'sec-ch-ua-platform: "Android"' \
--data '{"filters":{"user_id":"605bb1ee-e6d4-400c-b52e-0ff17da12bd3","benefit_id":"PB-BTR-2024-12-02-000726"}}'
9. user_application (Get method):
curl --location 'https://your-beneficary-api-domain.com/api/users/user_application/be0e44f7-fc60-4fad-b7ae-e603eb8b2ef2' \
--header 'Accept: */*' \
--header 'Accept-Language: en-GB,en-US;q=0.9,en;q=0.8' \
--header 'Authorization: Bearer <token>' \
--header 'Connection: keep-alive' \
--header 'If-None-Match: W/"e2aa6-4oHe1n4lnN+LC4qsdUGjed4HlFs"' \
--header 'Origin: http://localhost:5173' \
--header 'Referer: http://localhost:5173/' \
--header 'Sec-Fetch-Dest: empty' \
--header 'Sec-Fetch-Mode: cors' \
--header 'Sec-Fetch-Site: cross-site' \
--header 'User-Agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36' \
--header 'sec-ch-ua: "Chromium";v="128", "Not;A=Brand";v="24", "Google Chrome";v="128"' \
--header 'sec-ch-ua-mobile: ?1' \
--header 'sec-ch-ua-platform: "Android"'
10. user_application (Post method):
curl --location 'https://your-beneficary-api-domain.com/api/users/user_application' \
--header 'Accept: application/json, text/plain, */*' \
--header 'Accept-Language: en-GB,en-US;q=0.9,en;q=0.8' \
--header 'Authorization: Bearer <token>' \
--header 'Connection: keep-alive' \
--header 'Content-Type: application/json' \
--header 'Origin: http://localhost:5173' \
--header 'Referer: http://localhost:5173/' \
--header 'Sec-Fetch-Dest: empty' \
--header 'Sec-Fetch-Mode: cors' \
--header 'Sec-Fetch-Site: cross-site' \
--header 'User-Agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36' \
--header 'sec-ch-ua: "Chromium";v="128", "Not;A=Brand";v="24", "Google Chrome";v="128"' \
--header 'sec-ch-ua-mobile: ?1' \
--header 'sec-ch-ua-platform: "Android"' \
--data '{
<Application_Data>
}'
ONEST-related APIs:
1. Search benefits 2. Select Benefit
3. Apply to benefit 4. Confirm application 5. Track application status
For the above 5 APIs understanding of ONEST APIs is required, refer to the following document:
Tech Documentation for ONEST Network
12. document_list:
curl --location 'https://your-beneficary-api-domain.com/api/content/documents_list' \
--header 'Accept: */*' \
--header 'Accept-Language: en-GB,en-US;q=0.9,en;q=0.8' \
--header 'Authorization: Bearer <token>' \
--header 'Connection: keep-alive' \
--header 'If-None-Match: W/"2de-nf2hEgiRb8d1mSVxED0TIUOO+qo"' \
--header 'Origin: http://localhost:5173' \
--header 'Referer: http://localhost:5173/' \
--header 'Sec-Fetch-Dest: empty' \
--header 'Sec-Fetch-Mode: cors' \
--header 'Sec-Fetch-Site: cross-site' \
--header 'User-Agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36' \
--header 'sec-ch-ua: "Chromium";v="128", "Not;A=Brand";v="24", "Google Chrome";v="128"' \
--header 'sec-ch-ua-mobile: ?1' \
--header 'sec-ch-ua-platform: "Android"'
13. search:
curl --location 'https://your-beneficary-api-domain.com/api/content/search' \
--header 'Accept: application/json, text/plain, */*' \
--header 'Accept-Language: en-GB,en-US;q=0.9,en;q=0.8' \
--header 'Connection: keep-alive' \
--header 'Content-Type: application/json' \
--header 'Origin: http://localhost:5173' \
--header 'Referer: http://localhost:5173/' \
--header 'Sec-Fetch-Dest: empty' \
--header 'Sec-Fetch-Mode: cors' \
--header 'Sec-Fetch-Site: cross-site' \
--header 'User-Agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36' \
--header 'sec-ch-ua: "Chromium";v="128", "Not;A=Brand";v="24", "Google Chrome";v="128"' \
--header 'sec-ch-ua-mobile: ?1' \
--header 'sec-ch-ua-platform: "Android"' \
--data '{"filters":{"caste":"","annualIncome":""},"search":""}'
14. send_otp: This API is used to send OTP to a given phone number.
This API expects phone number with country code, for example ‘+91-9876543210’. A random 6-digit OTP is generated with 5 minutes
expiry time. The expiry time is in unix format. An irreversible hash of
OTP and phone numbers are generated using the ‘sha256’ algorithm. An
encrypted token of hash and expiry time is generated. API sends a status code, success message & token in response. Alongside this, the API also sends the generated OTP to a given phone number via
SMS using a third party service.
curl --location 'https://your-beneficary-api-domain.com/api/otp/send_otp' \
--header 'Content-Type: application/json' \
--data '{
"phoneNumber" : "+91-9373607160"
}'
15. verify_otp: This API is used to verify the OTP sent to a phone
number. Token sent as response in ‘send_otp’ API, OTP sent to SMS
and phone number with country code is expected in the request body.
This token is decrypted to get hash and expiry time (in unix format). A
new hash is generated using otp and phone number. This hash is
Compared with hash decrypted from token to check similarity of OTP. Expiry time from the token is also compared with current time.
curl --location 'https://your-beneficary-api-domain.com/api/otp/verify_otp' \
--header 'Content-Type: application/json' \
--data '{
"phoneNumber" : "+91-43983232435",
"otp": 170549,
"token": "3876f0e5395845bc6"
}'
CRON Jobs
1. Profile Updating CRON:- This cron job updates the values of user profile
fields based on available documents for a user. A configurable file is
maintained which consists of a list of document names which can be used
to get the value of each profile field. A folder contains JSON files. Each
file is named after a document and contains JSON paths of attributes that are correlated with profile fields. For example, ‘name’ attribute in aadhaar can be used for firstName,
lastName and middleName(fatherName), or ‘totalAnnualIncome’ from income certificate can be used for income field. This CRON job takes 10 users at a time each 5 minutes.
Users are
sorted primarily by: fieldsVerified being NULL (first),
Then false with a fieldsVerifiedAt timestamp,
Then all other cases (fieldsVerified is true or irrelevant). Within each primary group: If fieldsVerifiedAt is NULL, updated_at determines the order.
Otherwise, fieldsVerifiedAt determines the order. This CRON job also takes into consideration, the types and formats of values present in documents before updating in the database. All the major functions used in this CRON job are maintained in a separate file. These same functions are called when the documents are imported from wallet.
2. Application Status Updating CRON:- This cron job updates the status of user applications (applications to a benefit). Each user application is differentiated based on ‘external_application_id’. This attribute is also used to get the status from ONEST ‘on_status’ API (refer to document mentioned previously). This cron job takes all the applications from the database which are not yet in a final state (status which is either
‘AMOUNT RECEIVED’ or ‘REJECTED’, calls the ONEST API and updates the value. The frequency of this cron job is each 30 minutes.
Installation and Deployment
Pre-requisites
UBI Network setup
Nodejs ( version 16+)
PostgreSQL(Version 14+)
Hasura
Local Installation
Fork and Clone the Repository:
Clone the repository: GitHub Repo
Check out the main branch.
Install Dependencies:
Run: npm install
Add Environment Variables:
Create a .env file in the root directory with the necessary configurations from the example.env file.
Start the Application:
For development: npm run start:Dev
Deployment
Dockerfile:
A Dockerfile to containerize the application: Dockerfile
Build the Docker Image:
Run: docker build -t beneficiary.
Run the Container:
Run: docker run -p 3399:3399 beneficiary
GitHub Actions Deployment: Deployment file
Troubleshooting
Registration Issues: Step 1: Login to keycloak. Step 2: Search for the user using firstName or lastName. Step 3: If a user exists, note down its ID in the ‘Details’ tab. Else go to step 8. Step 4: Delete the user from keycloak. Step 5: Login to pgAdmin to access the database. Step 6: Search for a user with ‘sso_id’ equal to id noted in step 3. Step 7: If a user exists, then delete the user record from the database. Note: There might be records in other tables which are bound to the user. Delete these records first with caution. Else, go to step 8. Step 8: Try to register the user again.
Login Issues: Step 1: Login to keycloak. Step 2: Search for the user using firstName or lastName. Step 3: If a user exists, try to reset the password in case the password is Forgotten, and again try to log in. Else, register a new user.
The issue in importing documents:
Step 1: Make sure that the documents being imported are available in the wallet. Login to pgAdmin to access the wallet database and check for documents associated with the user. Step 2: If documents exist in the wallet database as well as in the beneficiary database, then delete and try to re-upload in a wallet and then import in the beneficiary app.
Step 3: Make sure that values in all documents are in valid format.
Future Enhancements
Audit logs implementation
RBAC + Ownership implementation
More dynamicity with respect to schemas of VCs
Frontend Developer Guide:
Architecture
The Beneficiary App is a web-based application designed for seekers to apply for eligible benefits.
Seekers need their user credentials if not available need to register. After successfully logged in need to upload the required documents and explore the available benefits. Seeker can apply for benefits available in lists and get scholarships.
Framework: React (with TypeScript)
Key Responsibilities:
User Interface (UI) rendering.
Form submissions and user interaction handling
Structure:
Components:
Login and Registration
User profile
Upload documents from wallet
Explore benefits
See details of selected benefit
Apply for benefit
See seeker details in my application
State Management:
React Query for server state management (API calls, caching).
Routing: React Router for navigation (e.g., Dashboard, Benefits, Applicants).
UI Framework: Chakra UI for consistent styling and responsiveness.
Key Features
Authentication System
Login: Secure login with username and password.
Registration:
Fields: First Name, Last Name, Phone Number, Password, Confirm Password.
On success: Display username and redirect to the login page.
Beneficiary Profile
Document Import: Import required documents from E-Wallet.
Auto-Update Profile: Fetch and update profile details using fetched documents
Explore Benefits
Dynamic Listing: Display benefits based on income, caste, and vendor details.
Filters & Search: Search benefits by caste, income range, etc.
Application Process: Auto-filled application form user adds bank details and applies.
The application form is coming from the provider side.
When seekers click the 'Proceed to Apply' button, they will see the application form only if all criteria or eligibility requirements are met.
The form will be generated according to benefit selection. For now two benefits added one checks Sports documents and another one checks income range depending on these fields are displayed.
Seekers' available data from profile get prefilled and document gets selected automatically.Only seekers need to add their bank details and submit form.
After successful submission of form seekers get a confirmation message with order id and navigate to My Application page.
My Application
Tracking: Monitor application status (e.g., Pending, Approved).
Details View: View submitted application details.
4. Technical Details
Frontend
Framework and Libraries:
React (TypeScript) type safety and code maintainability
UI Framework:
Chakra UI for responsive and consistent UI components
Build Tool:
Vite: Faster development and optimized builds for production,
Programming Language:
TypeScript: Ensures type safety, better code quality, and maintainability
API Specification:
A. RESTful APIs:
{{base_url}}/auth/register_with_password
{{base_url}}/auth/login
{{base_url}}/auth/logout
{{base_url}}/users/get_my_consents
{{base_url}}/users/consent
{{base_url}}/users/get_one/?decryptData=true
{{base_url}}/users/wallet/user_docs
{{base_url}}/users/user_applications_list
{{base_url}}/users/user_application/{id}
{{base_url}}/users/user_application
{{base_url}}/select
{{base_url}}/init
{{base_url}}/confirm
{{base_url}}/content/documents_list
{{base_url}}/content/search
Environment Management:
Environment variables managed via .env files
B. Tools and Integrations:
DE: Visual Studio Code
Linting: ESLint and Prettier for code formatting and quality
Installation and Deployment
Prerequisite
Node JS
NPM
GIT
Installation
Project Setup with Vite
Step 1: Clone the Repository
Step 1: Navigate to the Project Directory
Step 1: Create .env
Step 4: Add Environment Variables
VITE_API_BASE_URL=
VITE_API_BASE_ID=
#Keycloak-related configs
VITE_KEYCLOAK_URL=
VITE_KEYCLOAK_REALM=
VITE_KEYCLOAK_CLIENT_ID=
# Wallet related configs
VITE_EWALLET_ORIGIN=
VITE_EWALLET_IFRAME_SRC=
# Onest network related configs
VITE_BPP_URL=
VITE_BPP_ID=
VITE_BAP_URL=
VITE_PROVIDER_URL=
Step 2: Install Dependencies
Step 3: Start the Development Server-npm run dev
Step 4: Build for Production-npm run build
Last updated
Was this helpful?