Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,12 @@ One way to configure these environment variables is to use an .env file. There a

```python
from yoti_python_sdk import Client
@app.route('/profile')


@app.route("/profile")
def auth():
client = Client(YOTI_CLIENT_SDK_ID, YOTI_KEY_FILE_PATH)
activity_details = client.get_activity_details(request.args['token'])
activity_details = client.get_activity_details(request.args["token"])
```

## Handling Users
Expand Down
48 changes: 48 additions & 0 deletions examples/doc_scan/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
RequestedSupplementaryDocTextExtractionTaskBuilder,
SdkConfigBuilder,
SessionSpecBuilder,
SessionOverviewPdfRequestBuilder,
)
from yoti_python_sdk.doc_scan.exception import DocScanException
from yoti_python_sdk.doc_scan.session.create.filter import (
Expand Down Expand Up @@ -174,6 +175,53 @@ def media():
)


@app.route("/session-pdf")
def session_pdf():
"""
Generates and returns the session overview PDF for the current session.
The PDF is streamed back to the browser as a downloadable file.
"""
doc_scan_client = DocScanClient(YOTI_CLIENT_SDK_ID, YOTI_KEY_FILE_PATH)

session_id = session.get("doc_scan_session_id", None)
if session_id is None:
return Response("No session ID available", status=404)

include_images = request.args.get("include_images", None)
include_extracted_text = request.args.get("include_extracted_text", None)
include_device_tracking = request.args.get("include_device_tracking", None)

pdf_request_builder = SessionOverviewPdfRequestBuilder()
if include_images is not None:
pdf_request_builder.with_include_images(include_images.lower() == "true")
if include_extracted_text is not None:
pdf_request_builder.with_include_extracted_text(
include_extracted_text.lower() == "true"
)
if include_device_tracking is not None:
pdf_request_builder.with_include_device_tracking(
include_device_tracking.lower() == "true"
)

pdf_request = pdf_request_builder.build()

try:
pdf_bytes = doc_scan_client.create_session_overview_pdf(session_id, pdf_request)
except DocScanException as e:
return render_template("error.html", error=e.message)

return Response(
pdf_bytes,
content_type="application/pdf",
status=200,
headers={
"Content-Disposition": "attachment; filename=session-overview-{}.pdf".format(
session_id
)
},
)


@app.route("/privacy-policy")
def privacy_policy():
return render_template("privacy.html")
Expand Down
12 changes: 12 additions & 0 deletions examples/doc_scan/templates/success.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,18 @@ <h1>Get Session Result</h1>
<td>{{ session_result.biometric_consent_timestamp }}</td>
</tr>
{% endif %}
<tr>
<td>Session Overview PDF</td>
<td>
<a href="/session-pdf" class="btn btn-sm btn-primary">
Download PDF
</a>
&nbsp;
<a href="/session-pdf?include_images=true&include_extracted_text=true&include_device_tracking=true" class="btn btn-sm btn-secondary">
Download PDF (all flags)
</a>
</td>
</tr>
</tbody>
</table>
</div>
Expand Down
1 change: 1 addition & 0 deletions examples/yoti_example_django/yoti_example/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
1. Import the include() function: from django.conf.urls import url, include
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
"""

from django.urls import re_path
from django.contrib import admin

Expand Down
4 changes: 0 additions & 4 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@
author_email="websdk@yoti.com",
python_requires=">=3.9",
install_requires=[


"asn1==2.2.0", # still pinned due to enum34 issue
"cryptography>=44.0.1",
"protobuf>=4.25.8,<6",
Expand All @@ -30,7 +28,6 @@
"pytz>=2025.2",
"iso8601>=1.1.0",
"deprecated>=1.2.14",

],
extras_require={
"examples": [
Expand Down Expand Up @@ -64,7 +61,6 @@
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",

"Topic :: Software Development :: Libraries :: Python Modules",
],
keywords="yoti sdk 2FA multifactor authentication verification identity login register verify 2Factor",
Expand Down
5 changes: 4 additions & 1 deletion yoti_python_sdk/activity_details.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,10 @@ def ensure_postal_address(self):
][config.KEY_FORMATTED_ADDRESS]

def __iter__(self):
yield "user_id", self.__remember_me_id # Using the private member directly to avoid a deprecation warning
yield (
"user_id",
self.__remember_me_id,
) # Using the private member directly to avoid a deprecation warning
yield "parent_remember_me_id", self.parent_remember_me_id
yield "outcome", self.outcome
yield "receipt_id", self.receipt_id
Expand Down
1 change: 0 additions & 1 deletion yoti_python_sdk/date_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import logging
import re


ERROR_PARSING_DATE = "Error parsing date"


Expand Down
6 changes: 6 additions & 0 deletions yoti_python_sdk/doc_scan/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
from .session.create.notification_config import NotificationConfigBuilder
from .session.create.sdk_config import SdkConfigBuilder
from .session.create.session_spec import SessionSpecBuilder
from .session.create.session_overview_pdf_request import (
SessionOverviewPdfRequest,
SessionOverviewPdfRequestBuilder,
)
from .client import DocScanClient

__all__ = [
Expand All @@ -25,5 +29,7 @@
"SessionSpecBuilder",
"NotificationConfigBuilder",
"SdkConfigBuilder",
"SessionOverviewPdfRequest",
"SessionOverviewPdfRequestBuilder",
"DocScanClient",
]
42 changes: 42 additions & 0 deletions yoti_python_sdk/doc_scan/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,3 +196,45 @@ def get_supported_documents(self):

parsed = json.loads(response.text)
return SupportedDocumentsResponse(parsed)

def create_session_overview_pdf(self, session_id, pdf_request=None):
"""
Generates a session overview PDF for the specified session.

Issues a POST to /idverify/v1/sessions/{sessionId}/pdf and returns
the raw PDF bytes from the 201 response. The flags in
``pdf_request`` are sent nested under an ``"options"`` key, as the
endpoint expects (``{"options": {"include_images": true, ...}}``).
All flag fields are optional; any flag that was not explicitly set
is omitted from ``options`` entirely — it is not sent as ``false`` or
``null``. When ``pdf_request`` is omitted, ``{"options": {}}`` is
sent. Validation of the flags is deferred to the backend.

:param session_id: the session ID
:type session_id: str
:param pdf_request: optional request body containing boolean flags
:type pdf_request: SessionOverviewPdfRequest or None
:return: raw binary PDF bytes
:rtype: bytes
:raises DocScanException: if the server returns a non-201 status code
"""
options = pdf_request if pdf_request is not None else {}
payload = json.dumps({"options": options}, cls=YotiEncoder).encode("utf-8")

request = (
SignedRequest.builder()
.with_post()
.with_pem_file(self.__key)
.with_base_url(self.__api_url)
.with_endpoint(Endpoint.create_session_overview_pdf_path(session_id))
.with_param("sdkId", self.__sdk_id)
.with_payload(payload)
.with_header("Content-Type", "application/json")
.build()
)
Comment on lines +233 to +234
response = request.execute()

if response.status_code != 201:
raise DocScanException("Failed to create session overview PDF", response)

return response.content
4 changes: 4 additions & 0 deletions yoti_python_sdk/doc_scan/endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,7 @@ def delete_media_path(session_id, media_id):
@staticmethod
def get_supported_documents_path():
return "/supported-documents"

@staticmethod
def create_session_overview_pdf_path(session_id):
return "/sessions/{sessionId}/pdf".format(sessionId=session_id)
1 change: 0 additions & 1 deletion yoti_python_sdk/doc_scan/session/create/check/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from .liveness import RequestedLivenessCheckBuilder
from .watchlist_screen import WatchlistScreeningCheckBuilder


__all__ = [
"RequestedDocumentAuthenticityCheckBuilder",
"RequestedIDDocumentComparisonCheckBuilder",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,5 +100,7 @@ def with_manual_check(self, manual_check):
return self

def build(self):
config = WatchlistScreeningCheckConfig(self.__manual_check, self.__categories or [])
config = WatchlistScreeningCheckConfig(
self.__manual_check, self.__categories or []
)
return WatchlistScreeningCheck(config)
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ def to_json(self):


class OrthogonalRestrictionsFilter(DocumentFilter):
def __init__(self, country_restriction, type_restriction, allow_non_latin_documents=None):
def __init__(
self, country_restriction, type_restriction, allow_non_latin_documents=None
):
DocumentFilter.__init__(self, filter_type=ORTHOGONAL_RESTRICTIONS)

self.__country_restriction = country_restriction
Expand Down Expand Up @@ -213,5 +215,7 @@ def build(self):
:rtype: OrthogonalRestrictionsFilter
"""
return OrthogonalRestrictionsFilter(
self.__country_restriction, self.__type_restriction, self.__allow_non_latin_documents
self.__country_restriction,
self.__type_restriction,
self.__allow_non_latin_documents,
)
Original file line number Diff line number Diff line change
Expand Up @@ -195,4 +195,6 @@ def build(self):
:return: the build notification config
:rtype: NotificationConfig
"""
return NotificationConfig(self.__auth_token, self.__endpoint, self.__topics, self.__auth_type)
return NotificationConfig(
self.__auth_token, self.__endpoint, self.__topics, self.__auth_type
)
10 changes: 8 additions & 2 deletions yoti_python_sdk/doc_scan/session/create/sdk_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,9 @@ def with_suppressed_screens(self, suppressed_screens):
:return: the builder
:rtype: SdkConfigBuilder
"""
self.__suppressed_screens = list(suppressed_screens) if suppressed_screens is not None else None
self.__suppressed_screens = (
list(suppressed_screens) if suppressed_screens is not None else None
)
return self

def with_suppressed_screen(self, screen):
Expand Down Expand Up @@ -409,5 +411,9 @@ def build(self):
self.__allow_handoff,
self.__privacy_policy_url,
self.__brand_id,
list(self.__suppressed_screens) if self.__suppressed_screens is not None else None,
(
list(self.__suppressed_screens)
if self.__suppressed_screens is not None
else None
),
)
Loading
Loading