Skip to content

Commit

Permalink
start adding verify v2
Browse files Browse the repository at this point in the history
  • Loading branch information
maxkahan committed Apr 6, 2024
1 parent 2b68284 commit 20051fd
Show file tree
Hide file tree
Showing 11 changed files with 380 additions and 0 deletions.
16 changes: 16 additions & 0 deletions verify_v2/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
resource(name='pyproject', source='pyproject.toml')
file(name='readme', source='README.md')

files(sources=['tests/data/*'])

python_distribution(
name='vonage-verify-v2',
dependencies=[
':pyproject',
':readme',
'verify_v2/src/vonage_verify_v2',
],
provides=python_artifact(),
generate_setup=False,
repositories=['@pypi'],
)
2 changes: 2 additions & 0 deletions verify_v2/CHANGES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# 1.0.0
- Initial upload
64 changes: 64 additions & 0 deletions verify_v2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Vonage Verify V2 Package

This package contains the code to use [Vonage's Verify v2 API](https://developer.vonage.com/en/verify/overview) in Python. This package includes methods for working with 2-factor authentication (2FA) messages sent via SMS, Voice, WhatsApp and Email. You can also make Silent Authentication requests with Verify v2 to give your end user a more seamless experience.

## Usage

It is recommended to use this as part of the main `vonage` package. The examples below assume you've created an instance of the `vonage.Vonage` class called `vonage_client`.


######################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################

### Make a Verify Request

```python
from vonage_verify import VerifyRequest
params = {'number': '1234567890', 'brand': 'Acme Inc.'}
request = VerifyRequest(**params)
response = vonage_client.verify.start_verification(request)
```

### Make a PSD2 (Payment Services Directive v2) Request

```python
from vonage_verify import Psd2Request
params = {'number': '1234567890', 'payee': 'Acme Inc.', 'amount': 99.99}
request = VerifyRequest(**params)
response = vonage_client.verify.start_verification(request)
```

### Check a Verification Code

```python
vonage_client.verify.check_code(request_id='my_request_id', code='1234')
```

### Search Verification Requests

```python
# Search for single request
response = vonage_client.verify.search('my_request_id')

# Search for multiple requests
response = vonage_client.verify.search(['my_request_id_1', 'my_request_id_2'])
```

### Cancel a Verification

```python
response = vonage_client.verify.cancel_verification('my_request_id')
```

### Trigger the Next Workflow Event

```python
response = vonage_client.verify.trigger_next_event('my_request_id')
```

### Request a Network Unblock

Note: Network Unblock is switched off by default. Contact Sales to enable the Network Unblock API for your account.

```python
response = vonage_client.verify.request_network_unblock('23410')
```
29 changes: 29 additions & 0 deletions verify_v2/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
[project]
name = 'vonage-verify-v2'
version = '1.0.0'
description = 'Vonage verify v2 package'
readme = "README.md"
authors = [{ name = "Vonage", email = "devrel@vonage.com" }]
requires-python = ">=3.8"
dependencies = [
"vonage-http-client>=1.2.0",
"vonage-utils>=1.0.1",
"pydantic>=2.6.1",
]
classifiers = [
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"License :: OSI Approved :: Apache Software License",
]

[project.urls]
homepage = "https://github.com/Vonage/vonage-python-sdk"

[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"
1 change: 1 addition & 0 deletions verify_v2/src/vonage_verify_v2/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
python_sources()
28 changes: 28 additions & 0 deletions verify_v2/src/vonage_verify_v2/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from .errors import VerifyError
from .enums import VerifyChannel, VerifyLocale
from .requests import (
VerifyRequest,
SilentAuthWorkflow,
SmsWorkflow,
WhatsappWorkflow,
VoiceWorkflow,
EmailWorkflow,
)
from .responses import (
StartVerificationResponse,
)
from .verify_v2 import Verify

__all__ = [
'Verify',
'VerifyError',
'VerifyChannel',
'VerifyLocale',
'VerifyRequest',
'SilentAuthWorkflow',
'SmsWorkflow',
'WhatsappWorkflow',
'VoiceWorkflow',
'EmailWorkflow',
'StartVerificationResponse',
]
25 changes: 25 additions & 0 deletions verify_v2/src/vonage_verify_v2/enums.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from enum import Enum


class VerifyChannel(Enum):
SILENT_AUTH = 'silent_auth'
SMS = 'sms'
WHATSAPP = 'whatsapp'
VOICE = 'voice'
EMAIL = 'email'


class VerifyLocale(Enum):
EN_US = 'en-us'
EN_GB = 'en-gb'
ES_ES = 'es-es'
ES_MX = 'es-mx'
ES_US = 'es-us'
IT_IT = 'it-it'
FR_FR = 'fr-fr'
DE_DE = 'de-de'
RU_RU = 'ru-ru'
HI_IN = 'hi-in'
PT_BR = 'pt-br'
PT_PT = 'pt-pt'
ID_ID = 'id-id'
5 changes: 5 additions & 0 deletions verify_v2/src/vonage_verify_v2/errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from vonage_utils.errors import VonageError


class VerifyError(VonageError):
"""Indicates an error when using the Vonage Verify API."""
81 changes: 81 additions & 0 deletions verify_v2/src/vonage_verify_v2/requests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
from typing import List, Optional, Union
from re import search

from pydantic import (
BaseModel,
Field,
field_validator,
model_validator,
)
from vonage_utils.types.phone_number import PhoneNumber

from .enums import VerifyChannel, VerifyLocale
from .errors import VerifyError


class Workflow(BaseModel):
channel: VerifyChannel
to: PhoneNumber


class SilentAuthWorkflow(Workflow):
redirect_url: Optional[str] = None
sandbox: Optional[bool] = None


class SmsWorkflow(Workflow):
from_: Optional[Union[PhoneNumber, str]] = Field(None, serialization_alias='from')
entity_id: Optional[str] = Field(None, pattern=r'^[0-9]{1,20}$')
content_id: Optional[str] = Field(None, pattern=r'^[0-9]{1,20}$')
app_hash: Optional[str] = Field(None, min_length=11, max_length=11)

@field_validator('from_')
@classmethod
def check_valid_from_field(cls, v):
if (
v is not None
and type(v) is not PhoneNumber
and not search(r'^[a-zA-Z0-9]{1,15}$', v)
):
raise VerifyError(f'You must specify a valid "from" value if included.')


class WhatsappWorkflow(Workflow):
from_: Union[PhoneNumber, str] = Field(..., serialization_alias='from')

@field_validator('from_')
@classmethod
def check_valid_sender(cls, v):
if type(v) is not PhoneNumber and not search(r'^[a-zA-Z0-9]{1,15}$', v):
raise VerifyError(f'You must specify a valid "from" value.')


class VoiceWorkflow(Workflow):
@model_validator(mode='after')
def remove_from_field_from_voice(self):
self.from_ = None
return self


class EmailWorkflow(Workflow):
to: str
from_: Optional[str] = Field(None, serialization_alias='from')


class VerifyRequest(BaseModel):
brand: str = Field(..., min_length=1, max_length=16)
workflow: List[Workflow]
locale: Optional[VerifyLocale] = None
channel_timeout: Optional[int] = Field(None, ge=60, le=900)
client_ref: Optional[str] = Field(None, min_length=1, max_length=16)
code_length: Optional[int] = Field(None, ge=4, le=10)
code: Optional[str] = Field(None, pattern=r'^[a-zA-Z0-9]{4,10}$')

@model_validator(mode='after')
def remove_fields_if_only_silent_auth(self):
if len(self.workflow) == 1 and isinstance(self.workflow[0], SilentAuthWorkflow):
self.locale = None
self.client_ref = None
self.code_length = None
self.code = None
return self
8 changes: 8 additions & 0 deletions verify_v2/src/vonage_verify_v2/responses.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from typing import Optional

from pydantic import BaseModel


class StartVerificationResponse(BaseModel):
request_id: str
check_url: Optional[str] = None
121 changes: 121 additions & 0 deletions verify_v2/src/vonage_verify_v2/verify_v2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
from typing import List, Optional, Union

from pydantic import validate_call
from vonage_http_client.http_client import HttpClient

from .errors import VerifyError
from .requests import (
VerifyRequest,
SilentAuthWorkflow,
SmsWorkflow,
WhatsappWorkflow,
VoiceWorkflow,
EmailWorkflow,
)
from .responses import (
CheckCodeResponse,
StartVerificationResponse,
VerifyControlStatus,
VerifyStatus,
)


class Verify:
"""Calls Vonage's Verify V2 API."""

def __init__(self, http_client: HttpClient) -> None:
self._http_client = http_client

@validate_call
def start_verification(
self, verify_request: VerifyRequest
) -> StartVerificationResponse:
"""Start a verification process.
Args:
verify_request (VerifyRequest): The verification request object.
Returns:
StartVerificationResponse: The response object containing the `request_id`.
If requesting Silent Authentication, it will also contain a `check_url` field.
"""
response = self._http_client.post(
self._http_client.api_host,
'/v2/verify',
verify_request.model_dump(by_alias=True, exclude_none=True),
)

return StartVerificationResponse(**response)

####################################################################################################

####################################################################################################

####################################################################################################

####################################################################################################

@validate_call
def check_code(self, request_id: str, code: str) -> CheckCodeResponse:
"""Check a verification code.
Args:
request_id (str): The request ID.
code (str): The verification code.
Returns:
CheckCodeResponse: The response object containing the verification result.
"""
response = self._http_client.post(
self._http_client.api_host,
'/verify/check/json',
{'request_id': request_id, 'code': code},
self._auth_type,
self._sent_data_type,
)
self._check_for_error(response)
return CheckCodeResponse(**response)

@validate_call
def cancel_verification(self, request_id: str) -> VerifyControlStatus:
"""Cancel a verification request.
Args:
request_id (str): The request ID.
Returns:
VerifyControlStatus: The response object containing details of the submitted
verification control.
"""
response = self._http_client.post(
self._http_client.api_host,
'/verify/control/json',
{'request_id': request_id, 'cmd': 'cancel'},
self._auth_type,
self._sent_data_type,
)
self._check_for_error(response)

return VerifyControlStatus(**response)

@validate_call
def trigger_next_workflow(self, request_id: str) -> VerifyControlStatus:
"""Trigger the next workflow event in the verification process.
Args:
request_id (str): The request ID.
Returns:
VerifyControlStatus: The response object containing details of the submitted
verification control.
"""
response = self._http_client.post(
self._http_client.api_host,
'/verify/control/json',
{'request_id': request_id, 'cmd': 'trigger_next_event'},
self._auth_type,
self._sent_data_type,
)
self._check_for_error(response)

return VerifyControlStatus(**response)

0 comments on commit 20051fd

Please sign in to comment.