django-camera-kit 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- django_camera_kit-0.1.0/LICENSE +21 -0
- django_camera_kit-0.1.0/MANIFEST.in +4 -0
- django_camera_kit-0.1.0/PKG-INFO +129 -0
- django_camera_kit-0.1.0/README.md +80 -0
- django_camera_kit-0.1.0/django_camera_kit/__init__.py +1 -0
- django_camera_kit-0.1.0/django_camera_kit/apps.py +7 -0
- django_camera_kit-0.1.0/django_camera_kit/kyc/__init__.py +0 -0
- django_camera_kit-0.1.0/django_camera_kit/kyc/apps.py +8 -0
- django_camera_kit-0.1.0/django_camera_kit/kyc/conf.py +24 -0
- django_camera_kit-0.1.0/django_camera_kit/kyc/embeddings.py +39 -0
- django_camera_kit-0.1.0/django_camera_kit/kyc/migrations/0001_initial.py +85 -0
- django_camera_kit-0.1.0/django_camera_kit/kyc/migrations/__init__.py +0 -0
- django_camera_kit-0.1.0/django_camera_kit/kyc/models.py +67 -0
- django_camera_kit-0.1.0/django_camera_kit/kyc/serializers.py +23 -0
- django_camera_kit-0.1.0/django_camera_kit/kyc/static/django_camera_kit/kyc/css/kyc_capture.css +59 -0
- django_camera_kit-0.1.0/django_camera_kit/kyc/static/django_camera_kit/kyc/js/face_kit.js +116 -0
- django_camera_kit-0.1.0/django_camera_kit/kyc/static/django_camera_kit/kyc/js/kyc_capture.js +347 -0
- django_camera_kit-0.1.0/django_camera_kit/kyc/static/django_camera_kit/kyc/vendor/face_detection_yunet_2023mar.onnx +0 -0
- django_camera_kit-0.1.0/django_camera_kit/kyc/templates/django_camera_kit/kyc/widgets/kyc_verification.html +7 -0
- django_camera_kit-0.1.0/django_camera_kit/kyc/throttling.py +20 -0
- django_camera_kit-0.1.0/django_camera_kit/kyc/urls.py +9 -0
- django_camera_kit-0.1.0/django_camera_kit/kyc/views.py +65 -0
- django_camera_kit-0.1.0/django_camera_kit/kyc/widgets.py +39 -0
- django_camera_kit-0.1.0/django_camera_kit/static/django_camera_kit/css/camera_kit.css +97 -0
- django_camera_kit-0.1.0/django_camera_kit/static/django_camera_kit/js/camera_kit.js +213 -0
- django_camera_kit-0.1.0/django_camera_kit/static/django_camera_kit/js/doc_scan.js +367 -0
- django_camera_kit-0.1.0/django_camera_kit/static/django_camera_kit/vendor/README.md +6 -0
- django_camera_kit-0.1.0/django_camera_kit/static/django_camera_kit/vendor/jspdf.umd.min.js +373 -0
- django_camera_kit-0.1.0/django_camera_kit/static/django_camera_kit/vendor/opencv.js +0 -0
- django_camera_kit-0.1.0/django_camera_kit/templates/django_camera_kit/widgets/document_scanner.html +6 -0
- django_camera_kit-0.1.0/django_camera_kit/widgets.py +25 -0
- django_camera_kit-0.1.0/django_camera_kit.egg-info/PKG-INFO +129 -0
- django_camera_kit-0.1.0/django_camera_kit.egg-info/SOURCES.txt +37 -0
- django_camera_kit-0.1.0/django_camera_kit.egg-info/dependency_links.txt +1 -0
- django_camera_kit-0.1.0/django_camera_kit.egg-info/requires.txt +19 -0
- django_camera_kit-0.1.0/django_camera_kit.egg-info/top_level.txt +1 -0
- django_camera_kit-0.1.0/pyproject.toml +76 -0
- django_camera_kit-0.1.0/setup.cfg +4 -0
- django_camera_kit-0.1.0/tests/test_widgets.py +15 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 AltiusAO
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: django-camera-kit
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Document scanning and KYC face verification widgets for Django
|
|
5
|
+
Author: Altius Academy SNC
|
|
6
|
+
Author-email: Paul Guindo <paulguindo@altius-group.ch>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/Altius-Academy-SNC/django-camera-kit
|
|
9
|
+
Project-URL: Documentation, https://altius-academy-snc.github.io/django-camera-kit
|
|
10
|
+
Project-URL: Repository, https://github.com/Altius-Academy-SNC/django-camera-kit
|
|
11
|
+
Project-URL: Issues, https://github.com/Altius-Academy-SNC/django-camera-kit/issues
|
|
12
|
+
Keywords: django,camera,document-scanner,kyc,identity-verification
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Framework :: Django
|
|
15
|
+
Classifier: Framework :: Django :: 4.2
|
|
16
|
+
Classifier: Framework :: Django :: 5.0
|
|
17
|
+
Classifier: Framework :: Django :: 5.1
|
|
18
|
+
Classifier: Framework :: Django :: 6.0
|
|
19
|
+
Classifier: Intended Audience :: Developers
|
|
20
|
+
Classifier: Programming Language :: Python :: 3
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
25
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
26
|
+
Classifier: Topic :: Multimedia :: Graphics :: Capture
|
|
27
|
+
Classifier: Topic :: Security
|
|
28
|
+
Requires-Python: >=3.10
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
License-File: LICENSE
|
|
31
|
+
Requires-Dist: django>=4.2
|
|
32
|
+
Provides-Extra: kyc
|
|
33
|
+
Requires-Dist: djangorestframework>=3.14; extra == "kyc"
|
|
34
|
+
Requires-Dist: pgvector>=0.2; extra == "kyc"
|
|
35
|
+
Requires-Dist: psycopg[binary]>=3.1; extra == "kyc"
|
|
36
|
+
Requires-Dist: insightface>=0.7; extra == "kyc"
|
|
37
|
+
Requires-Dist: onnxruntime>=1.16; extra == "kyc"
|
|
38
|
+
Requires-Dist: pillow>=10.0; extra == "kyc"
|
|
39
|
+
Provides-Extra: dev
|
|
40
|
+
Requires-Dist: djangorestframework>=3.14; extra == "dev"
|
|
41
|
+
Requires-Dist: pgvector>=0.2; extra == "dev"
|
|
42
|
+
Requires-Dist: psycopg[binary]>=3.1; extra == "dev"
|
|
43
|
+
Requires-Dist: insightface>=0.7; extra == "dev"
|
|
44
|
+
Requires-Dist: onnxruntime>=1.16; extra == "dev"
|
|
45
|
+
Requires-Dist: pillow>=10.0; extra == "dev"
|
|
46
|
+
Requires-Dist: pytest; extra == "dev"
|
|
47
|
+
Requires-Dist: pytest-django; extra == "dev"
|
|
48
|
+
Dynamic: license-file
|
|
49
|
+
|
|
50
|
+
# Django Camera Kit
|
|
51
|
+
|
|
52
|
+
Document scanning and KYC face verification widgets for Django. Drop a real document scanner (live edge detection, manual corner adjustment, multi-page → PDF) into any existing `FileField`, no new models or endpoints required. Add live selfie/ID face verification with a server-side matching endpoint.
|
|
53
|
+
|
|
54
|
+
[](https://github.com/Altius-Academy-SNC/django-camera-kit/actions/workflows/tests.yml)
|
|
55
|
+
[](https://pypi.org/project/django-camera-kit/)
|
|
56
|
+
[](LICENSE)
|
|
57
|
+
|
|
58
|
+
**[Documentation](https://altius-academy-snc.github.io/django-camera-kit)** | **[PyPI](https://pypi.org/project/django-camera-kit/)** | **[Source](https://github.com/Altius-Academy-SNC/django-camera-kit)**
|
|
59
|
+
|
|
60
|
+
## Status
|
|
61
|
+
|
|
62
|
+
Document scanning (this package's base install) and KYC identity verification (the `kyc` extra) are both implemented and tested. See the [documentation](https://altius-academy-snc.github.io/django-camera-kit) for the full guide, including the KYC module's security model and known MVP limitations.
|
|
63
|
+
|
|
64
|
+
## Quick example
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
# forms.py — any existing ModelForm, any existing FileField/ImageField
|
|
68
|
+
from django import forms
|
|
69
|
+
from django_camera_kit.widgets import DocumentScannerWidget
|
|
70
|
+
|
|
71
|
+
from .models import Contract
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class ContractForm(forms.ModelForm):
|
|
75
|
+
class Meta:
|
|
76
|
+
model = Contract
|
|
77
|
+
fields = ["signed_document"]
|
|
78
|
+
widgets = {"signed_document": DocumentScannerWidget()}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
```html
|
|
82
|
+
<!-- template -->
|
|
83
|
+
{{ form.media }}
|
|
84
|
+
<form method="post" enctype="multipart/form-data">
|
|
85
|
+
{% csrf_token %}
|
|
86
|
+
{{ form }}
|
|
87
|
+
<button type="submit">Enregistrer</button>
|
|
88
|
+
</form>
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The widget replaces the plain file input with a "Scanner un document" button. Clicking it opens the camera, detects the document's edges live (OpenCV.js), lets the user drag the corners if detection misses, and supports adding several pages before assembling everything into a single PDF — written back into the same file input via the `DataTransfer` API. From the form's point of view, it's a normal file upload.
|
|
92
|
+
|
|
93
|
+
## KYC quick example
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
pip install django-camera-kit[kyc]
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
# forms.py
|
|
101
|
+
from django import forms
|
|
102
|
+
from django_camera_kit.kyc.widgets import KYCVerificationWidget
|
|
103
|
+
|
|
104
|
+
class SignupForm(forms.Form):
|
|
105
|
+
kyc_verification_id = forms.IntegerField(
|
|
106
|
+
widget=KYCVerificationWidget(verify_url="/kyc/verify/")
|
|
107
|
+
)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Requires PostgreSQL with the `pgvector` extension. Matching (face embeddings via YuNet + insightface) only ever happens server-side. See [KYC & Identity Verification](https://altius-academy-snc.github.io/django-camera-kit/guides/kyc/) for the full setup and security model.
|
|
111
|
+
|
|
112
|
+
## Setup
|
|
113
|
+
|
|
114
|
+
Two vendor JS files are required for the scan widget and not committed to this repo (binary build artifacts, pinned separately) — see [`django_camera_kit/static/django_camera_kit/vendor/README.md`](django_camera_kit/static/django_camera_kit/vendor/README.md) for exact files and versions to download.
|
|
115
|
+
|
|
116
|
+
Requires Bootstrap (or Bootstrap Native) CSS/JS to already be loaded on the host page for button styling — `django-camera-kit` only ships the structural/positioning CSS for the scanner overlays themselves.
|
|
117
|
+
|
|
118
|
+
## Roadmap
|
|
119
|
+
|
|
120
|
+
1. ~~Scan MVP~~ — single page, manual corner adjustment, perspective warp
|
|
121
|
+
2. Auto contour detection refinements
|
|
122
|
+
3. Multi-page flow polish
|
|
123
|
+
4. ~~KYC MVP~~ — selfie + ID capture, embedding comparison, head-movement liveness challenge
|
|
124
|
+
5. Anti-deepfake hardening — server-verified liveness, passive scoring, multiple challenge types
|
|
125
|
+
6. ~~Docs site~~ + PyPI release
|
|
126
|
+
|
|
127
|
+
## License
|
|
128
|
+
|
|
129
|
+
MIT
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Django Camera Kit
|
|
2
|
+
|
|
3
|
+
Document scanning and KYC face verification widgets for Django. Drop a real document scanner (live edge detection, manual corner adjustment, multi-page → PDF) into any existing `FileField`, no new models or endpoints required. Add live selfie/ID face verification with a server-side matching endpoint.
|
|
4
|
+
|
|
5
|
+
[](https://github.com/Altius-Academy-SNC/django-camera-kit/actions/workflows/tests.yml)
|
|
6
|
+
[](https://pypi.org/project/django-camera-kit/)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
|
|
9
|
+
**[Documentation](https://altius-academy-snc.github.io/django-camera-kit)** | **[PyPI](https://pypi.org/project/django-camera-kit/)** | **[Source](https://github.com/Altius-Academy-SNC/django-camera-kit)**
|
|
10
|
+
|
|
11
|
+
## Status
|
|
12
|
+
|
|
13
|
+
Document scanning (this package's base install) and KYC identity verification (the `kyc` extra) are both implemented and tested. See the [documentation](https://altius-academy-snc.github.io/django-camera-kit) for the full guide, including the KYC module's security model and known MVP limitations.
|
|
14
|
+
|
|
15
|
+
## Quick example
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
# forms.py — any existing ModelForm, any existing FileField/ImageField
|
|
19
|
+
from django import forms
|
|
20
|
+
from django_camera_kit.widgets import DocumentScannerWidget
|
|
21
|
+
|
|
22
|
+
from .models import Contract
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ContractForm(forms.ModelForm):
|
|
26
|
+
class Meta:
|
|
27
|
+
model = Contract
|
|
28
|
+
fields = ["signed_document"]
|
|
29
|
+
widgets = {"signed_document": DocumentScannerWidget()}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
```html
|
|
33
|
+
<!-- template -->
|
|
34
|
+
{{ form.media }}
|
|
35
|
+
<form method="post" enctype="multipart/form-data">
|
|
36
|
+
{% csrf_token %}
|
|
37
|
+
{{ form }}
|
|
38
|
+
<button type="submit">Enregistrer</button>
|
|
39
|
+
</form>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The widget replaces the plain file input with a "Scanner un document" button. Clicking it opens the camera, detects the document's edges live (OpenCV.js), lets the user drag the corners if detection misses, and supports adding several pages before assembling everything into a single PDF — written back into the same file input via the `DataTransfer` API. From the form's point of view, it's a normal file upload.
|
|
43
|
+
|
|
44
|
+
## KYC quick example
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pip install django-camera-kit[kyc]
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
# forms.py
|
|
52
|
+
from django import forms
|
|
53
|
+
from django_camera_kit.kyc.widgets import KYCVerificationWidget
|
|
54
|
+
|
|
55
|
+
class SignupForm(forms.Form):
|
|
56
|
+
kyc_verification_id = forms.IntegerField(
|
|
57
|
+
widget=KYCVerificationWidget(verify_url="/kyc/verify/")
|
|
58
|
+
)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Requires PostgreSQL with the `pgvector` extension. Matching (face embeddings via YuNet + insightface) only ever happens server-side. See [KYC & Identity Verification](https://altius-academy-snc.github.io/django-camera-kit/guides/kyc/) for the full setup and security model.
|
|
62
|
+
|
|
63
|
+
## Setup
|
|
64
|
+
|
|
65
|
+
Two vendor JS files are required for the scan widget and not committed to this repo (binary build artifacts, pinned separately) — see [`django_camera_kit/static/django_camera_kit/vendor/README.md`](django_camera_kit/static/django_camera_kit/vendor/README.md) for exact files and versions to download.
|
|
66
|
+
|
|
67
|
+
Requires Bootstrap (or Bootstrap Native) CSS/JS to already be loaded on the host page for button styling — `django-camera-kit` only ships the structural/positioning CSS for the scanner overlays themselves.
|
|
68
|
+
|
|
69
|
+
## Roadmap
|
|
70
|
+
|
|
71
|
+
1. ~~Scan MVP~~ — single page, manual corner adjustment, perspective warp
|
|
72
|
+
2. Auto contour detection refinements
|
|
73
|
+
3. Multi-page flow polish
|
|
74
|
+
4. ~~KYC MVP~~ — selfie + ID capture, embedding comparison, head-movement liveness challenge
|
|
75
|
+
5. Anti-deepfake hardening — server-verified liveness, passive scoring, multiple challenge types
|
|
76
|
+
6. ~~Docs site~~ + PyPI release
|
|
77
|
+
|
|
78
|
+
## License
|
|
79
|
+
|
|
80
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
|
File without changes
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from django.conf import settings
|
|
2
|
+
|
|
3
|
+
DEFAULTS = {
|
|
4
|
+
# insightface model pack — see embeddings.py. Changing this to a model
|
|
5
|
+
# with a different embedding size requires a migration (see models.py).
|
|
6
|
+
"EMBEDDING_MODEL": "buffalo_sc",
|
|
7
|
+
# Cosine similarity threshold above which a selfie/ID pair is considered
|
|
8
|
+
# a match. This default is a starting point, not a calibrated value —
|
|
9
|
+
# tune it against real data before relying on it in production.
|
|
10
|
+
"MATCH_THRESHOLD": 0.45,
|
|
11
|
+
# Max requests per authenticated user against KYCVerifyView. Each request
|
|
12
|
+
# runs a full face-embedding inference pass — without a limit the
|
|
13
|
+
# endpoint is a cheap DoS/probing vector. DRF throttle-rate syntax
|
|
14
|
+
# ("N/period" — second/minute/hour/day).
|
|
15
|
+
"THROTTLE_RATE": "10/hour",
|
|
16
|
+
# Reject selfie/id_document uploads larger than this, before they're
|
|
17
|
+
# saved or processed.
|
|
18
|
+
"MAX_UPLOAD_SIZE_MB": 8,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get_setting(name):
|
|
23
|
+
user_settings = getattr(settings, "DJANGO_CAMERA_KIT_KYC", {})
|
|
24
|
+
return user_settings.get(name, DEFAULTS[name])
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from . import conf
|
|
4
|
+
|
|
5
|
+
_face_app = None
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _get_face_app():
|
|
9
|
+
global _face_app
|
|
10
|
+
if _face_app is None:
|
|
11
|
+
from insightface.app import FaceAnalysis
|
|
12
|
+
|
|
13
|
+
_face_app = FaceAnalysis(
|
|
14
|
+
name=conf.get_setting("EMBEDDING_MODEL"),
|
|
15
|
+
providers=["CPUExecutionProvider"],
|
|
16
|
+
)
|
|
17
|
+
_face_app.prepare(ctx_id=-1, det_size=(320, 320))
|
|
18
|
+
return _face_app
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def extract_face_embedding(image_bgr):
|
|
22
|
+
"""Return the normalized embedding of the largest face in the image,
|
|
23
|
+
or None if no face was detected — also if `image_bgr` is None, which
|
|
24
|
+
is what cv2.imdecode() returns for corrupt/malformed image data that
|
|
25
|
+
nonetheless passed DRF's ImageField validation."""
|
|
26
|
+
if image_bgr is None:
|
|
27
|
+
return None
|
|
28
|
+
faces = _get_face_app().get(image_bgr)
|
|
29
|
+
if not faces:
|
|
30
|
+
return None
|
|
31
|
+
largest = max(
|
|
32
|
+
faces, key=lambda f: (f.bbox[2] - f.bbox[0]) * (f.bbox[3] - f.bbox[1])
|
|
33
|
+
)
|
|
34
|
+
return largest.normed_embedding
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def compare_embeddings(embedding_a, embedding_b):
|
|
38
|
+
"""Cosine similarity between two normalized embeddings, in [-1, 1]."""
|
|
39
|
+
return float(np.dot(embedding_a, embedding_b))
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Generated by Django 5.0.14 on 2026-07-10 10:22
|
|
2
|
+
|
|
3
|
+
import django.db.models.deletion
|
|
4
|
+
import pgvector.django.indexes
|
|
5
|
+
import pgvector.django.vector
|
|
6
|
+
from django.conf import settings
|
|
7
|
+
from django.db import migrations, models
|
|
8
|
+
from pgvector.django import VectorExtension
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Migration(migrations.Migration):
|
|
12
|
+
initial = True
|
|
13
|
+
|
|
14
|
+
dependencies = [
|
|
15
|
+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
operations = [
|
|
19
|
+
VectorExtension(),
|
|
20
|
+
migrations.CreateModel(
|
|
21
|
+
name="KYCVerification",
|
|
22
|
+
fields=[
|
|
23
|
+
(
|
|
24
|
+
"id",
|
|
25
|
+
models.BigAutoField(
|
|
26
|
+
auto_created=True,
|
|
27
|
+
primary_key=True,
|
|
28
|
+
serialize=False,
|
|
29
|
+
verbose_name="ID",
|
|
30
|
+
),
|
|
31
|
+
),
|
|
32
|
+
("selfie", models.ImageField(upload_to="camera_kit/kyc/selfies/")),
|
|
33
|
+
(
|
|
34
|
+
"id_document",
|
|
35
|
+
models.ImageField(upload_to="camera_kit/kyc/id_documents/"),
|
|
36
|
+
),
|
|
37
|
+
(
|
|
38
|
+
"selfie_embedding",
|
|
39
|
+
pgvector.django.vector.VectorField(
|
|
40
|
+
blank=True, dimensions=512, null=True
|
|
41
|
+
),
|
|
42
|
+
),
|
|
43
|
+
(
|
|
44
|
+
"id_face_embedding",
|
|
45
|
+
pgvector.django.vector.VectorField(
|
|
46
|
+
blank=True, dimensions=512, null=True
|
|
47
|
+
),
|
|
48
|
+
),
|
|
49
|
+
("match_score", models.FloatField(blank=True, null=True)),
|
|
50
|
+
("liveness_passed", models.BooleanField(default=False)),
|
|
51
|
+
(
|
|
52
|
+
"status",
|
|
53
|
+
models.CharField(
|
|
54
|
+
choices=[
|
|
55
|
+
("pending", "En attente"),
|
|
56
|
+
("verified", "Vérifié"),
|
|
57
|
+
("rejected", "Rejeté"),
|
|
58
|
+
],
|
|
59
|
+
default="pending",
|
|
60
|
+
max_length=16,
|
|
61
|
+
),
|
|
62
|
+
),
|
|
63
|
+
("created_at", models.DateTimeField(auto_now_add=True)),
|
|
64
|
+
(
|
|
65
|
+
"user",
|
|
66
|
+
models.ForeignKey(
|
|
67
|
+
on_delete=django.db.models.deletion.CASCADE,
|
|
68
|
+
related_name="kyc_verifications",
|
|
69
|
+
to=settings.AUTH_USER_MODEL,
|
|
70
|
+
),
|
|
71
|
+
),
|
|
72
|
+
],
|
|
73
|
+
options={
|
|
74
|
+
"indexes": [
|
|
75
|
+
pgvector.django.indexes.HnswIndex(
|
|
76
|
+
ef_construction=64,
|
|
77
|
+
fields=["selfie_embedding"],
|
|
78
|
+
m=16,
|
|
79
|
+
name="camera_kit_kyc_selfie_hnsw",
|
|
80
|
+
opclasses=["vector_cosine_ops"],
|
|
81
|
+
)
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
),
|
|
85
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
from django.conf import settings
|
|
2
|
+
from django.db import models
|
|
3
|
+
from pgvector.django import HnswIndex, VectorField
|
|
4
|
+
|
|
5
|
+
# Matches buffalo_sc's w600k_mbf recognition output (see embeddings.py).
|
|
6
|
+
# Changing the embedding model to one with a different output size requires
|
|
7
|
+
# a new migration.
|
|
8
|
+
EMBEDDING_DIMENSIONS = 512
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class KYCVerification(models.Model):
|
|
12
|
+
class Status(models.TextChoices):
|
|
13
|
+
PENDING = "pending", "En attente"
|
|
14
|
+
VERIFIED = "verified", "Vérifié"
|
|
15
|
+
REJECTED = "rejected", "Rejeté"
|
|
16
|
+
|
|
17
|
+
# Every verification is bound to the authenticated user who submitted
|
|
18
|
+
# it (KYCVerifyView requires authentication) — a verification result
|
|
19
|
+
# is meaningless without knowing whose identity it claims to confirm.
|
|
20
|
+
user = models.ForeignKey(
|
|
21
|
+
settings.AUTH_USER_MODEL,
|
|
22
|
+
on_delete=models.CASCADE,
|
|
23
|
+
related_name="kyc_verifications",
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
# selfie/id_document contain biometric + government-ID imagery. The
|
|
27
|
+
# consuming project MUST point Django's storage backend at a private,
|
|
28
|
+
# access-controlled location for these — never a publicly readable
|
|
29
|
+
# MEDIA_URL. django-camera-kit doesn't enforce this itself since
|
|
30
|
+
# storage config is the consuming project's responsibility, but the
|
|
31
|
+
# API response (KYCVerificationSerializer) deliberately never exposes
|
|
32
|
+
# these files' URLs.
|
|
33
|
+
selfie = models.ImageField(upload_to="camera_kit/kyc/selfies/")
|
|
34
|
+
id_document = models.ImageField(upload_to="camera_kit/kyc/id_documents/")
|
|
35
|
+
|
|
36
|
+
selfie_embedding = VectorField(
|
|
37
|
+
dimensions=EMBEDDING_DIMENSIONS, null=True, blank=True
|
|
38
|
+
)
|
|
39
|
+
id_face_embedding = VectorField(
|
|
40
|
+
dimensions=EMBEDDING_DIMENSIONS, null=True, blank=True
|
|
41
|
+
)
|
|
42
|
+
match_score = models.FloatField(null=True, blank=True)
|
|
43
|
+
|
|
44
|
+
# MVP liveness only: client-asserted result of the in-browser
|
|
45
|
+
# head-movement challenge. Not a server-verified anti-spoofing signal —
|
|
46
|
+
# a malicious client could send True without ever performing it. Real
|
|
47
|
+
# server-side liveness verification is a later phase.
|
|
48
|
+
liveness_passed = models.BooleanField(default=False)
|
|
49
|
+
|
|
50
|
+
status = models.CharField(
|
|
51
|
+
max_length=16, choices=Status.choices, default=Status.PENDING
|
|
52
|
+
)
|
|
53
|
+
created_at = models.DateTimeField(auto_now_add=True)
|
|
54
|
+
|
|
55
|
+
class Meta:
|
|
56
|
+
indexes = [
|
|
57
|
+
HnswIndex(
|
|
58
|
+
name="camera_kit_kyc_selfie_hnsw",
|
|
59
|
+
fields=["selfie_embedding"],
|
|
60
|
+
m=16,
|
|
61
|
+
ef_construction=64,
|
|
62
|
+
opclasses=["vector_cosine_ops"],
|
|
63
|
+
),
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
def __str__(self):
|
|
67
|
+
return f"KYCVerification #{self.pk} ({self.status})"
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from rest_framework import serializers
|
|
2
|
+
|
|
3
|
+
from . import conf
|
|
4
|
+
from .models import KYCVerification
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def validate_upload_size(file):
|
|
8
|
+
max_mb = conf.get_setting("MAX_UPLOAD_SIZE_MB")
|
|
9
|
+
if file.size > max_mb * 1024 * 1024:
|
|
10
|
+
raise serializers.ValidationError(f"Fichier trop volumineux (max {max_mb} Mo).")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class KYCSubmissionSerializer(serializers.Serializer):
|
|
14
|
+
selfie = serializers.ImageField(validators=[validate_upload_size])
|
|
15
|
+
id_document = serializers.ImageField(validators=[validate_upload_size])
|
|
16
|
+
liveness_passed = serializers.BooleanField(default=False)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class KYCVerificationSerializer(serializers.ModelSerializer):
|
|
20
|
+
class Meta:
|
|
21
|
+
model = KYCVerification
|
|
22
|
+
fields = ["id", "status", "match_score", "liveness_passed", "created_at"]
|
|
23
|
+
read_only_fields = fields
|
django_camera_kit-0.1.0/django_camera_kit/kyc/static/django_camera_kit/kyc/css/kyc_capture.css
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
.camera-kit-kyc {
|
|
2
|
+
display: flex;
|
|
3
|
+
align-items: center;
|
|
4
|
+
gap: 0.5rem;
|
|
5
|
+
flex-wrap: wrap;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/* ISO/IEC 7810 ID-1 format (85.60 x 53.98mm) — the size of virtually every
|
|
9
|
+
national ID card, driving license and payment card. The ratio is fixed
|
|
10
|
+
via aspect-ratio so it holds regardless of the camera's own aspect
|
|
11
|
+
ratio, instead of an arbitrary box that doesn't match a real card. */
|
|
12
|
+
.camera-kit-kyc-id-guide {
|
|
13
|
+
position: absolute;
|
|
14
|
+
top: 50%;
|
|
15
|
+
left: 50%;
|
|
16
|
+
width: 75%;
|
|
17
|
+
aspect-ratio: 85.6 / 53.98;
|
|
18
|
+
transform: translate(-50%, -50%);
|
|
19
|
+
border: 3px dashed rgba(255, 255, 255, 0.8);
|
|
20
|
+
border-radius: 0.5rem;
|
|
21
|
+
pointer-events: none;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.camera-kit-kyc-face-guide {
|
|
25
|
+
position: absolute;
|
|
26
|
+
top: 12%;
|
|
27
|
+
left: 30%;
|
|
28
|
+
right: 30%;
|
|
29
|
+
bottom: 12%;
|
|
30
|
+
border: 3px dashed rgba(255, 255, 255, 0.8);
|
|
31
|
+
border-radius: 50%;
|
|
32
|
+
pointer-events: none;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.camera-kit-kyc-hint {
|
|
36
|
+
text-align: center;
|
|
37
|
+
padding: 0.5rem 1rem;
|
|
38
|
+
margin: 0;
|
|
39
|
+
font-weight: 600;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.camera-kit-kyc-review {
|
|
43
|
+
display: flex;
|
|
44
|
+
gap: 1rem;
|
|
45
|
+
padding: 1rem;
|
|
46
|
+
justify-content: center;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.camera-kit-kyc-review img {
|
|
50
|
+
max-height: 180px;
|
|
51
|
+
border-radius: 0.25rem;
|
|
52
|
+
border: 1px solid #dee2e6;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.camera-kit-kyc-review-status,
|
|
56
|
+
.camera-kit-kyc-result {
|
|
57
|
+
text-align: center;
|
|
58
|
+
padding: 0 1rem;
|
|
59
|
+
}
|