Initial commit

This commit is contained in:
Yaser
2026-08-13 19:56:46 +03:30
commit de1d57a67b
474 changed files with 43185 additions and 0 deletions

View File

@@ -0,0 +1,95 @@
# KYC: Access mobile
###### Sprint: 3
### Outcome:
User should enter his number, after the validation app sends a sms with an otp code, the user must enter the code to prove to us that he has access to the entered number.
#### Endpoints:
POST {$domain}/api/v2/barong/resource/mobiles
POST {$domain}/api/v2/barong/resource/mobiles/send_code
POST {$domain}/api/v2/barong/resource/mobiles/verify
#### File destination:
{$Dalan_Path}/app/api/v2/resource/phones.rb
#### Commits:
bd7f980596
695b0b56e2
6dd40a9c42
e04a58b012
36167eda10
2089690fe0
90cbc48649
17a5bce849
63722fe22a
63977c330d
bff2746686
75490dcb22
f13ee3f4d5
2c05522f77
46c053b435
551e7cdba2
77d7b9a9b3
2da71b1097
#### What did we implement:
First of all we added a new service for our sms&call third party service(kavenegar).it needs its own initializer for private keys.in this service we also handle caching data(for storing keys and codes with expiration time) and creating OTP code by vault.
We created a new api for phones with our rules.
after verification every number, app create a new label(access_phone) if there were not that label.
note:
if user wants to enter new number:
1.if user has pending number --> legacy number will be update with new number
2.If user has verified access label --> new number must get access label to create in db and legacy one replaced
3.If user has verified ownership label --> new number must get both labels to create in db and legacy one replaced
```mermaid
sequenceDiagram
title: access phone
User->>Ranj: enter phone number with +98
Ranj-->>Dalan: post /mobiles params
Dalan->>Ranj: 400, message: 'Required params are empty'
Dalan->>Ranj: 401, message: 'Invalid bearer token'
Dalan->>Ranj: 404, message: 'Record is not found'
Dalan->>Ranj: 422, message: 'Validation errors'
Dalan->>Ranj: 422, message: 'code_doesnt_expired'(there is an active process for addding new mobile)
Dalan->>Ranj: 200, message: 'New phone was added'
note left of Dalan: if there is a pending number it will be replaced with new one
Dalan->>KaveNegar: send sms to User in case of success
KaveNegar->>User: SMS with code
Ranj->>User: show result
note right of User: if didnt catch sms
User->>Ranj: resend code
Ranj->>Dalan: Post mobiles/send_code params
Dalan->>Ranj: 400, message: 'Required params are empty'
Dalan->>Ranj: 401, message: 'Invalid bearer token'
Dalan->>Ranj: 404, message: 'Record is not found', 'phone.doesnt_exist'
Dalan->>Ranj: 422, message: 'Validation errors'
Dalan->>Ranj: 422, message: 'code_doesnt_expired' (expiration time doesnt finish)
Dalan->>KaveNegar: send sms to User in case of success
KaveNegar->>User: SMS with code
Ranj->>User: show result
User->>Ranj:enter code
Ranj->>Dalan: Post mobiles/verify params
Dalan->>Ranj: 401, message: 'Invalid bearer token'
Dalan->>Ranj: 404, message: 'Record is not found', 'phone.doesnt_exist'
Dalan->>Ranj: 422, message: 'Validation errors'
Dalan->>Ranj: 400, message: phone.verification_invalid
note right of Ranj: if user wants to replace a new verified phone and the legacy phone has owner_phone label, we should check ownership. if it doesnt legacy phone labels all changes will be rollback.
Dalan->>Ranj: success 2xx
Ranj->>User: show result
```

View File

@@ -0,0 +1,45 @@
# KYC: Bank information
### Outcome:
we checking ownership of card-numbers and ibans.
### Implementation description:
#### Endpoints:
Post {$domain}/api/v2/barong/resource/profiles/treasury
#### File destination:
- {$Dalan_Path}/app/api/v2/resource/profiles.rb
- {$Dalan_Path}/app/workers/kyc/local/treasury_worker.rb
- {$Dalan_Path}/app/services/jibit_service.rb
#### Commits:
- 40177a4f
- 816fb00e
- 9e17677b
- 63977c33
- c76f72fe
- 90cbc486
#### What did we implement:
We implemented a new model and table to keep users bank information and called it treasury.<br />
After users entered their card_number or IBAN, we are sending this information to the Jibit service,<br />
and then the Jibit Service returns the first name and last name of the real owner.<br />
Now we can check this returned information to users' profile information to find out everything is ok.<br />
It is worth mentioning that the steps of getting information from the Jibit and checking it with the profile are done in the background.
```mermaid
sequenceDiagram
Title: bank information (treasury)
note over User,Dalan: Users enter their bank information
note over User,Dalan: `data` is card-number or iban value, `kind` is 'card' or 'iban'
User->>Dalan:data, kind, title
note over Dalan,Jibit: Dalan send request to Jibbit service (as worker)
Dalan->>Jibit:GET: iban value or card value (data)
Jibit->>Dalan:Response: first-name and last-name of real owner
Dalan->>Dalan: checking returned information with users profile data
Dalan->>User: add a confirmed label if the names matched, otherwise rejected label was created.
```

View File

@@ -0,0 +1,38 @@
# KYC: Personal info
###### Sprint: 3
### Outcome:
Users add the below information to their profile
### Implementation description:
#### Endpoints:
POST {$domain}/api/v2/barong/resource/profiles
#### File destination:
{$Dalan_Path}/app/api/v2/resource/profiles.rb
#### Commits:
9f1cde76
2422f5d1
#### What did we implement:
In the Opendax, users can create their profiles by personal info and Address info in one step, also in this step there is no way to upload identity card
So In Zagros, we separate the step of adding personal info from adding address info step, and also we add upload identity card features
```mermaid
sequenceDiagram
Title: KYC - Profile Step
note over User,Ranj:**upload** is a picture of front identity card
User->>Ranj:first_name, last_name, dob, national_code, upload
Ranj->>Dalan:Post: after client side checking
Dalan->>Ranj:4xx if any required params are empty
Dalan->>Ranj:4xx if Profile model validity fails
Dalan->>Ranj:4xx if Document model validity fails
Dalan->>Ranj:2xx the profile create successfully
Ranj->>User:notify user that profile created
```

View File

@@ -0,0 +1,91 @@
# KYC: landline
###### Sprint: 3
### Outcome:
User should enter his number, after the validation app sends a call with an otp code, the user must enter the code to prove to us that he has access to the entered number.
#### Endpoints:
POST {$domain}/api/v2/barong/resource/phones
POST {$domain}/api/v2/barong/resource/phones/send_code
POST {$domain}/api/v2/barong/resource/phones/verify
#### File destination:
{$Dalan_Path}/app/api/v2/resource/phones.rb
#### Commits:
bd7f980596
695b0b56e2
6dd40a9c42
e04a58b012
36167eda10
2089690fe0
90cbc48649
17a5bce849
63722fe22a
63977c330d
bff2746686
75490dcb22
f13ee3f4d5
2c05522f77
46c053b435
551e7cdba2
77d7b9a9b3
2da71b1097
#### What did we implement:
in access mobile told about KaveNegarService
user entered landline number then system send call by kave negar with otp code
user must enter send code to prove us that has access.
if user wants to enter new number:
1.if user have verified phone: response error
2.if user has pending number: update that number with new one
3.if user doesnt have number in our system: create new one
```mermaid
sequenceDiagram
title: access landline
User->>Ranj: enter landline number with +98
Ranj-->>Dalan: post /phones params
Dalan->>Ranj: 400, message: 'Required params are empty'
Dalan->>Ranj: 401, message: 'Invalid bearer token'
Dalan->>Ranj: 404, message: 'Record is not found'
Dalan->>Ranj: 422, message: 'Validation errors'
Dalan->>Ranj: 422, message: 'code_doesnt_expired'(there is an active process for addding new landline) and phone.exists(has verified number in system)
Dalan->>Ranj: 200, message: 'New phone was added'
note left of Dalan: if there is a pending number it will be replaced with new one
Dalan->>KaveNegar: send call to User in case of success
KaveNegar->>User: call with code
Ranj->>User: show result
note right of User: if didnt catch call
User->>Ranj: resend code
Ranj->>Dalan: Post phones/send_code params
Dalan->>Ranj: 400, message: 'Required params are empty'
Dalan->>Ranj: 401, message: 'Invalid bearer token'
Dalan->>Ranj: 404, message: 'Record is not found', 'phone.doesnt_exist'
Dalan->>Ranj: 422, message: 'Validation errors'
Dalan->>Ranj: 422, message: 'code_doesnt_expired' (expiration time doesnt finish)
Dalan->>KaveNegar: send call to User in case of success
KaveNegar->>User: call with code
Ranj->>User: show result
User->>Ranj:enter code
Ranj->>Dalan: Post phones/verify params
Dalan->>Ranj: 401, message: 'Invalid bearer token'
Dalan->>Ranj: 404, message: 'Record is not found', 'phone.doesnt_exist', 'verification_invalid
Dalan->>Ranj: 422, message: 'Validation errors'
Dalan->>Ranj: success 2xx
Ranj->>User: show result
```

View File

@@ -0,0 +1,42 @@
# KYC: mobile ownership
### Outcome:
we checking ownership of mobile.
### Implementation description:
#### Endpoints:
PUT {$domain}/api/v2/barong/admin/profiles/label
#### File destination:
- {$Dalan_Path}/app/workers/kyc/local/owner_mobile_worker.rb
- {$Dalan_Path}/app/models/phone.rb
- {$Dalan_Path}/app/services/kyc_service.rb
- {$Dalan_Path}/app/api/v2/admin/profiles.rb
- {$Dalan_Path}/app/services/jibit_service.rb
#### Commits:
- 7f06a2e3
- 816fb00e
- 2c05522f
- 90cbc486
- 91e75604
- bdfc578d
#### What did we implement:
Ownership of mobile is done in background (worker) after last step (selfie confirmation).<br />
The national code of verified profile with phone number , is sent to Jibit Service for matching.<br />
The label key in the `labels` table is **owner_phone**
```mermaid
sequenceDiagram
Title: mobile ownership
note over Admin,Dalan: admin user confirm users selfie
Admin->>PUT Dalan:user_uid, label_key, label_value
note over Dalan,Jibit: Dalan send request to Jibbit service (as worker)
Dalan->>Jibit:GET: natonal_code, phone_number
Jibit->>Dalan:Response: matched, false or true
Dalan->>Dalan: create ownership label with reject label or verified label
```

View File

@@ -0,0 +1,72 @@
# KYC: Residence
###### Sprint: ?
### Outcome:
User can add address with complete information(city and province and etc..)
#### Endpoints:
POST {$domain}/api/v2/barong/resource/profiles/address
#### File destination:
{$Dalan_Path}/app/api/v2/resource/profiles.rb
#### Commits:
f7820d8
3e0fff4
67c41b3
f420fa4
bff2746
63977c3
#### What did we implement:
Opendax just implements address as a string but we need more information like city and province and document for residency.
So first of all we implemented city and province as below steps:
##### 1.create migrations:
20210420111532_create_provinces.rb → have just a name
20210420111629_create_cities.rb → have name and must connect to province(foreign key) references :province, foreign_key: true
##### 2.create models
Province model:
* Create validation for name: only allows letters, digits "-", "\'", and space. length: 1..255
* Mention to rails that this model has many cities
City model:
* Mention to rails that this model belongs to province and this column is required.
* Create validation for name: only allows letters, digits "-", "\'", and space. length: 1..255
we need seeds to fill city and province tables. So we used a JSON file from git and create a seed. data of the seed was put in config/seeds.yml file and the functions are in lib/barong/seed.rb.(seed_provinces and seed_cities) they check the existence of the data(city or province) then create it in db.
After these steps, we were ready to create a new API. we check the existence of the city and province that the user has been sent and create poa label. if user submitted another doc for poa and it has not been accepted, the new document will be replaced.
```mermaid
sequenceDiagram
title: residence
User->>Ranj: residence information
Ranj-->>Dalan: post /profiles/address params
Dalan->>Ranj: 400, message: 'Required params are empty'
Dalan->>Ranj: 401, message: 'Invalid bearer token'
Dalan->>Ranj: 404, message: 'needed data doesnt exist'
Dalan->>Ranj: 422, message: 'Validation errors',(address.already_exist,province_doesnt_exist)
Dalan->>Ranj: 422, message: 'code_doesnt_expired'(there is an active process for addding new mobile)
Dalan->>Ranj: 200, message: 'New phone was added'
note left of Dalan: if there is a pending address it will be replaced with new one
```

View File

@@ -0,0 +1,38 @@
# KYC: Selfie
###### Sprint: 3
### Outcome:
Users can upload image for certification (the image includes selfie and signed text of the agreement )
### Implementation description:
#### Endpoints:
POST{$domain}/api/v2/barong/resource/profiles/selfie
#### File destination:
{$Dalan_Path}/app/api/v2/resource/profiles.rb
#### Commits:
ac083d21
ea79c211
#### What did we implement:
Users can upload images for the Selfie KYC step. In the first place that image is in a `pending` state.
In the Admin panel, this image must be change state from `pending` to `confirm` or `reject`
```mermaid
sequenceDiagram
Title: KYC - Selfie
note over User,Ranj: **upload** must be valid format of image (jpg, jpeg, png)
User->>Ranj: upload
Ranj->>Dalan:Post: after client side checking
Dalan->>Ranj:4xx if does not exist verified profile for the current user
Dalan->>Ranj:4xx if before exist verified selfie image for the current user
Dalan->>Ranj:2xx update (if exist unconfirmed ) or create new selfie
Ranj->>User:notify user that password changed
```

View File

@@ -0,0 +1,78 @@
# KYC level logic
###### Sprint: 3
### Outcome:
Users can upgrade their level by doing KYC processes
### Implementation description:
#### File destination:
{$Dalan_Path}/app/models/user.rb<br />
{$Dalan_Path}/app/models/document.rb<br />
{$Dalan_Path}/app/models/label.rb<br />
{$Dalan_Path}/app/models/level.rb<br />
{$Dalan_Path}/app/models/treasury.rb<br />
{$Dalan_Path}/app/models/profile.rb<br />
{$Dalan_Path}/app/services/kyc_service.rb
#### Commits:
2422f5d1
40177a4f
ac083d21
7f06a2e3
816fb00e
2c05522f
9e17677b
bff27466
63977c33
c76f72fe
90cbc486
6dd40a9c
695b0b56
b3b5a08d
bd7f9805
#### What did we implement:
in the Opendax, KYC level handled by labels that id of them (label) shows user level,
but not support feature that per-user level has own steps
(each label is database record as Label model)
obviously per step has its own label too and the label has a unique id itself.
so by considering this rule in the Opendax, we kept it but change some things to support multi-steps.<br />
we implement some kind of giving-points system.
users can achieve a new level If the sum of their label IDs reaches a certain limit that defines in the code.
as you know, each step is a kind of record database that controlling by model so:<br />
bank information step is `Treasury` model
selfie and address step is `Document` model (for uploading)
mobile and landline steps are `Telephone` model
profile is `Profile` model
so if one of the above kind records, is updated or created, we will create or update the corresponding label recording,
and if the label is created or updated we will change the users level by calculating their points (points are the sum of label ids)
also, we keep levels (steps for KYC) in the Level model with the below data that was entered by seed
levels:
- key: email, id: 1 ,value: verified, description: "User enter a valid code after registration"
- key: profile, id: 2, value: verified, description: "User personal documents have been verified"
- key: access_phone, id: 3, value: verified, description: "User entered a valid code from sms"
- key: card, id: 4, value: verified, description: "User card number have been verified"
- key: iban, id: 5, value: verified, description: "User iban have been verified"
- key: poa id: 6, value: verified, description: "User card bank have been verified"
- key: telephone id: 7, value: verified, description: "User entered a valid code from ring call"
- key: selfie id: 8, value: verified, description: "User selfie documents have been verified"
- key: owner_phone id: 9, value: verified description: "User owner mobile have been verified"
- key: vip id: 10, value: verified, description: "User Vip have been verified"
### TODO
now if the label being confirmed accidental in the admin panel, they can not delete it,<br />
so must implement new codes to handle this stuff