easyid-python 1.0.2__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.
- easyid_python-1.0.2/.github/workflows/publish-python-pypi.yml +40 -0
- easyid_python-1.0.2/LICENSE +21 -0
- easyid_python-1.0.2/PKG-INFO +89 -0
- easyid_python-1.0.2/README.md +61 -0
- easyid_python-1.0.2/README.zh-CN.md +79 -0
- easyid_python-1.0.2/pyproject.toml +47 -0
- easyid_python-1.0.2/src/easyid/__init__.py +39 -0
- easyid_python-1.0.2/src/easyid/bank.py +48 -0
- easyid_python-1.0.2/src/easyid/billing.py +62 -0
- easyid_python-1.0.2/src/easyid/client.py +56 -0
- easyid_python-1.0.2/src/easyid/error.py +20 -0
- easyid_python-1.0.2/src/easyid/face.py +85 -0
- easyid_python-1.0.2/src/easyid/idcard.py +77 -0
- easyid_python-1.0.2/src/easyid/phone.py +44 -0
- easyid_python-1.0.2/src/easyid/py.typed +1 -0
- easyid_python-1.0.2/src/easyid/risk.py +82 -0
- easyid_python-1.0.2/src/easyid/signer.py +24 -0
- easyid_python-1.0.2/src/easyid/transport.py +202 -0
- easyid_python-1.0.2/test_easyid.py +275 -0
- easyid_python-1.0.2/tests/test_easyid.py +275 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
name: Publish Python Package
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
id-token: write
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
publish:
|
|
15
|
+
if: github.repository == 'easyid-com-cn/easyid-python'
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- name: Checkout
|
|
20
|
+
uses: actions/checkout@v4
|
|
21
|
+
|
|
22
|
+
- name: Setup Python
|
|
23
|
+
uses: actions/setup-python@v5
|
|
24
|
+
with:
|
|
25
|
+
python-version: "3.12"
|
|
26
|
+
|
|
27
|
+
- name: Install build tooling
|
|
28
|
+
run: python -m pip install --upgrade build
|
|
29
|
+
|
|
30
|
+
- name: Install package with test dependencies
|
|
31
|
+
run: python -m pip install -e .[test]
|
|
32
|
+
|
|
33
|
+
- name: Run tests
|
|
34
|
+
run: pytest
|
|
35
|
+
|
|
36
|
+
- name: Build distributions
|
|
37
|
+
run: python -m build
|
|
38
|
+
|
|
39
|
+
- name: Publish to PyPI
|
|
40
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 EasyID Team
|
|
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,89 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: easyid-python
|
|
3
|
+
Version: 1.0.2
|
|
4
|
+
Summary: Official Python SDK for the EasyID identity verification API.
|
|
5
|
+
Project-URL: Homepage, https://github.com/easyid-com-cn/easyid-python
|
|
6
|
+
Project-URL: Repository, https://github.com/easyid-com-cn/easyid-python
|
|
7
|
+
Project-URL: Documentation, https://www.easyid.com.cn
|
|
8
|
+
Author: EasyID Team
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: easyid,identity,kyc,risk,verification
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Requires-Python: >=3.9
|
|
24
|
+
Requires-Dist: requests<3,>=2.31.0
|
|
25
|
+
Provides-Extra: test
|
|
26
|
+
Requires-Dist: pytest<9,>=8; extra == 'test'
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# EasyID Python SDK
|
|
30
|
+
|
|
31
|
+
Official Python SDK for the EasyID identity verification API.
|
|
32
|
+
|
|
33
|
+
EasyID 易验云 focuses on identity verification and security risk control APIs, including real-name verification, liveness detection, face recognition, phone verification, and fraud-risk related capabilities.
|
|
34
|
+
|
|
35
|
+
中文文档: [README.zh-CN.md](README.zh-CN.md)
|
|
36
|
+
|
|
37
|
+
## Install
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install easyid-python
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Quick Start
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
from easyid import EasyID
|
|
47
|
+
|
|
48
|
+
client = EasyID("ak_xxx", "sk_xxx")
|
|
49
|
+
result = client.idcard.verify2(name="张三", id_number="110101199001011234")
|
|
50
|
+
|
|
51
|
+
print(result.match)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Supported APIs
|
|
55
|
+
|
|
56
|
+
- IDCard: `verify2`, `verify3`, `ocr`
|
|
57
|
+
- Phone: `status`, `verify3`
|
|
58
|
+
- Face: `liveness`, `compare`, `verify`
|
|
59
|
+
- Bank: `verify4`
|
|
60
|
+
- Risk: `score`, `store_fingerprint`
|
|
61
|
+
- Billing: `balance`, `records`
|
|
62
|
+
|
|
63
|
+
## Configuration
|
|
64
|
+
|
|
65
|
+
- `base_url`
|
|
66
|
+
- `timeout`
|
|
67
|
+
- `session`
|
|
68
|
+
|
|
69
|
+
## Error Handling
|
|
70
|
+
|
|
71
|
+
Service-side business errors raise `APIError`.
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
from easyid import APIError
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
client.phone.status(phone="13800138000")
|
|
78
|
+
except APIError as exc:
|
|
79
|
+
print(exc.code, exc.message, exc.request_id)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Security Notice
|
|
83
|
+
|
|
84
|
+
This is a server-side SDK. Never expose `secret` in browsers, mobile apps, or other untrusted clients.
|
|
85
|
+
|
|
86
|
+
## Official Resources
|
|
87
|
+
|
|
88
|
+
- Official website: `https://www.easyid.com.cn/`
|
|
89
|
+
- GitHub organization: `https://github.com/easyid-com-cn/`
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# EasyID Python SDK
|
|
2
|
+
|
|
3
|
+
Official Python SDK for the EasyID identity verification API.
|
|
4
|
+
|
|
5
|
+
EasyID 易验云 focuses on identity verification and security risk control APIs, including real-name verification, liveness detection, face recognition, phone verification, and fraud-risk related capabilities.
|
|
6
|
+
|
|
7
|
+
中文文档: [README.zh-CN.md](README.zh-CN.md)
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install easyid-python
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
from easyid import EasyID
|
|
19
|
+
|
|
20
|
+
client = EasyID("ak_xxx", "sk_xxx")
|
|
21
|
+
result = client.idcard.verify2(name="张三", id_number="110101199001011234")
|
|
22
|
+
|
|
23
|
+
print(result.match)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Supported APIs
|
|
27
|
+
|
|
28
|
+
- IDCard: `verify2`, `verify3`, `ocr`
|
|
29
|
+
- Phone: `status`, `verify3`
|
|
30
|
+
- Face: `liveness`, `compare`, `verify`
|
|
31
|
+
- Bank: `verify4`
|
|
32
|
+
- Risk: `score`, `store_fingerprint`
|
|
33
|
+
- Billing: `balance`, `records`
|
|
34
|
+
|
|
35
|
+
## Configuration
|
|
36
|
+
|
|
37
|
+
- `base_url`
|
|
38
|
+
- `timeout`
|
|
39
|
+
- `session`
|
|
40
|
+
|
|
41
|
+
## Error Handling
|
|
42
|
+
|
|
43
|
+
Service-side business errors raise `APIError`.
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
from easyid import APIError
|
|
47
|
+
|
|
48
|
+
try:
|
|
49
|
+
client.phone.status(phone="13800138000")
|
|
50
|
+
except APIError as exc:
|
|
51
|
+
print(exc.code, exc.message, exc.request_id)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Security Notice
|
|
55
|
+
|
|
56
|
+
This is a server-side SDK. Never expose `secret` in browsers, mobile apps, or other untrusted clients.
|
|
57
|
+
|
|
58
|
+
## Official Resources
|
|
59
|
+
|
|
60
|
+
- Official website: `https://www.easyid.com.cn/`
|
|
61
|
+
- GitHub organization: `https://github.com/easyid-com-cn/`
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# EasyID Python SDK
|
|
2
|
+
|
|
3
|
+
EasyID Python SDK 是易验云身份验证 API 的官方 Python 客户端。
|
|
4
|
+
|
|
5
|
+
English README: [README.md](README.md)
|
|
6
|
+
|
|
7
|
+
EasyID 提供身份证核验、手机号核验、人脸识别、银行卡核验、风控评分等能力。本 SDK 适用于服务端应用,自动处理认证头、签名和统一响应解析。
|
|
8
|
+
|
|
9
|
+
## 安装
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install easyid-python
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
如果是开发调试当前仓库,也可以在 `python/` 目录下执行:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install -e .
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 快速开始
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
from easyid import EasyID, APIError
|
|
25
|
+
|
|
26
|
+
client = EasyID("ak_xxx", "sk_xxx")
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
result = client.idcard.verify2(name="张三", id_number="110101199001011234")
|
|
30
|
+
print("是否匹配:", result.match)
|
|
31
|
+
except APIError as exc:
|
|
32
|
+
print(exc.code, exc.message, exc.request_id)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## 已支持接口
|
|
36
|
+
|
|
37
|
+
- `client.idcard.verify2()`:身份证二要素核验
|
|
38
|
+
- `client.idcard.verify3()`:身份证三要素核验
|
|
39
|
+
- `client.idcard.ocr()`:身份证 OCR
|
|
40
|
+
- `client.phone.status()`:手机号状态查询
|
|
41
|
+
- `client.phone.verify3()`:手机号三要素核验
|
|
42
|
+
- `client.face.liveness()`:人脸活体检测
|
|
43
|
+
- `client.face.compare()`:人脸比对
|
|
44
|
+
- `client.face.verify()`:人脸核验
|
|
45
|
+
- `client.bank.verify4()`:银行卡四要素核验
|
|
46
|
+
- `client.risk.score()`:风控评分
|
|
47
|
+
- `client.risk.store_fingerprint()`:存储设备指纹
|
|
48
|
+
- `client.billing.balance()`:查询账户余额
|
|
49
|
+
- `client.billing.records()`:查询账单记录
|
|
50
|
+
|
|
51
|
+
## 配置项
|
|
52
|
+
|
|
53
|
+
- `base_url`:自定义 API 地址
|
|
54
|
+
- `timeout`:请求超时,单位秒
|
|
55
|
+
- `session`:自定义 `requests.Session`
|
|
56
|
+
|
|
57
|
+
## 错误处理
|
|
58
|
+
|
|
59
|
+
服务端业务错误会抛出 `APIError`。
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from easyid import APIError
|
|
63
|
+
|
|
64
|
+
try:
|
|
65
|
+
client.phone.status(phone="13800138000")
|
|
66
|
+
except APIError as exc:
|
|
67
|
+
print(exc.code, exc.message, exc.request_id)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## 安全说明
|
|
71
|
+
|
|
72
|
+
- 这是服务端 SDK,不要在浏览器、移动端或其他不可信环境暴露 `secret`
|
|
73
|
+
- `key_id` 必须符合 `ak_[0-9a-f]+`
|
|
74
|
+
- SDK 会自动处理 HMAC-SHA256 签名和认证请求头
|
|
75
|
+
|
|
76
|
+
## 官方资源
|
|
77
|
+
|
|
78
|
+
- 官网:`https://www.easyid.com.cn/`
|
|
79
|
+
- GitHub:`https://github.com/easyid-com-cn/`
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.25.0"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "easyid-python"
|
|
7
|
+
version = "1.0.2"
|
|
8
|
+
description = "Official Python SDK for the EasyID identity verification API."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "EasyID Team" }
|
|
14
|
+
]
|
|
15
|
+
keywords = ["easyid", "identity", "verification", "kyc", "risk"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
22
|
+
"Programming Language :: Python :: 3.9",
|
|
23
|
+
"Programming Language :: Python :: 3.10",
|
|
24
|
+
"Programming Language :: Python :: 3.11",
|
|
25
|
+
"Programming Language :: Python :: 3.12",
|
|
26
|
+
"Programming Language :: Python :: 3.13",
|
|
27
|
+
"Topic :: Software Development :: Libraries :: Python Modules"
|
|
28
|
+
]
|
|
29
|
+
dependencies = [
|
|
30
|
+
"requests>=2.31.0,<3"
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.optional-dependencies]
|
|
34
|
+
test = [
|
|
35
|
+
"pytest>=8,<9"
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[project.urls]
|
|
39
|
+
Homepage = "https://github.com/easyid-com-cn/easyid-python"
|
|
40
|
+
Repository = "https://github.com/easyid-com-cn/easyid-python"
|
|
41
|
+
Documentation = "https://www.easyid.com.cn"
|
|
42
|
+
|
|
43
|
+
[tool.hatch.build.targets.wheel]
|
|
44
|
+
packages = ["src/easyid"]
|
|
45
|
+
|
|
46
|
+
[tool.pytest.ini_options]
|
|
47
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""EasyID Python SDK."""
|
|
2
|
+
|
|
3
|
+
from .bank import BankService, BankVerify4Result
|
|
4
|
+
from .billing import BillingRecord, BillingRecordsResult, BillingService, BillingBalanceResult
|
|
5
|
+
from .client import EasyID
|
|
6
|
+
from .error import APIError, is_api_error
|
|
7
|
+
from .face import CompareResult, FaceService, FaceVerifyResult, LivenessResult
|
|
8
|
+
from .idcard import IDCardOCRResult, IDCardService, IDCardVerifyResult
|
|
9
|
+
from .phone import PhoneService, PhoneStatusResult, PhoneVerify3Result
|
|
10
|
+
from .risk import RiskDetails, RiskScoreResult, RiskService, StoreFingerprintResult
|
|
11
|
+
|
|
12
|
+
__version__ = EasyID.version
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"APIError",
|
|
16
|
+
"BankService",
|
|
17
|
+
"BankVerify4Result",
|
|
18
|
+
"BillingBalanceResult",
|
|
19
|
+
"BillingRecord",
|
|
20
|
+
"BillingRecordsResult",
|
|
21
|
+
"BillingService",
|
|
22
|
+
"CompareResult",
|
|
23
|
+
"EasyID",
|
|
24
|
+
"FaceService",
|
|
25
|
+
"FaceVerifyResult",
|
|
26
|
+
"IDCardOCRResult",
|
|
27
|
+
"IDCardService",
|
|
28
|
+
"IDCardVerifyResult",
|
|
29
|
+
"LivenessResult",
|
|
30
|
+
"PhoneService",
|
|
31
|
+
"PhoneStatusResult",
|
|
32
|
+
"PhoneVerify3Result",
|
|
33
|
+
"RiskDetails",
|
|
34
|
+
"RiskScoreResult",
|
|
35
|
+
"RiskService",
|
|
36
|
+
"StoreFingerprintResult",
|
|
37
|
+
"__version__",
|
|
38
|
+
"is_api_error",
|
|
39
|
+
]
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Bank APIs."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
from .transport import Transport
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True)
|
|
12
|
+
class BankVerify4Result:
|
|
13
|
+
result: bool
|
|
14
|
+
match: bool
|
|
15
|
+
bank_name: str
|
|
16
|
+
supplier: str
|
|
17
|
+
score: float
|
|
18
|
+
masked_bank_card: str
|
|
19
|
+
card_type: str
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class BankService:
|
|
23
|
+
def __init__(self, transport: Transport) -> None:
|
|
24
|
+
self._transport = transport
|
|
25
|
+
|
|
26
|
+
def verify4(
|
|
27
|
+
self,
|
|
28
|
+
*,
|
|
29
|
+
name: str,
|
|
30
|
+
id_number: str,
|
|
31
|
+
bank_card: str,
|
|
32
|
+
mobile: Optional[str] = None,
|
|
33
|
+
trace_id: Optional[str] = None,
|
|
34
|
+
) -> BankVerify4Result:
|
|
35
|
+
body = {
|
|
36
|
+
"name": name,
|
|
37
|
+
"id_number": id_number,
|
|
38
|
+
"bank_card": bank_card,
|
|
39
|
+
"mobile": mobile,
|
|
40
|
+
"trace_id": trace_id,
|
|
41
|
+
}
|
|
42
|
+
data = self._transport.request_json(
|
|
43
|
+
"POST",
|
|
44
|
+
"/v1/bank/verify4",
|
|
45
|
+
body={key: value for key, value in body.items() if value is not None},
|
|
46
|
+
)
|
|
47
|
+
return BankVerify4Result(**data)
|
|
48
|
+
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""Billing APIs."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import List
|
|
7
|
+
|
|
8
|
+
from .transport import Transport
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True)
|
|
12
|
+
class BillingBalanceResult:
|
|
13
|
+
app_id: str
|
|
14
|
+
available_cents: int
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True)
|
|
18
|
+
class BillingRecord:
|
|
19
|
+
id: int
|
|
20
|
+
app_id: str
|
|
21
|
+
request_id: str
|
|
22
|
+
change_cents: int
|
|
23
|
+
balance_before: int
|
|
24
|
+
balance_after: int
|
|
25
|
+
reason: str
|
|
26
|
+
operator: str
|
|
27
|
+
created_at: int
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(frozen=True)
|
|
31
|
+
class BillingRecordsResult:
|
|
32
|
+
total: int
|
|
33
|
+
page: int
|
|
34
|
+
records: List[BillingRecord]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class BillingService:
|
|
38
|
+
def __init__(self, transport: Transport) -> None:
|
|
39
|
+
self._transport = transport
|
|
40
|
+
|
|
41
|
+
def balance(self, *, app_id: str) -> BillingBalanceResult:
|
|
42
|
+
data = self._transport.request_json(
|
|
43
|
+
"GET",
|
|
44
|
+
"/v1/billing/balance",
|
|
45
|
+
query={"app_id": app_id},
|
|
46
|
+
)
|
|
47
|
+
return BillingBalanceResult(**data)
|
|
48
|
+
|
|
49
|
+
def records(self, *, app_id: str, page: int = 1, page_size: int = 20) -> BillingRecordsResult:
|
|
50
|
+
if page <= 0:
|
|
51
|
+
page = 1
|
|
52
|
+
if page_size <= 0:
|
|
53
|
+
page_size = 20
|
|
54
|
+
else:
|
|
55
|
+
page_size = min(page_size, 100)
|
|
56
|
+
data = self._transport.request_json(
|
|
57
|
+
"GET",
|
|
58
|
+
"/v1/billing/records",
|
|
59
|
+
query={"app_id": app_id, "page": page, "page_size": page_size},
|
|
60
|
+
)
|
|
61
|
+
records = [BillingRecord(**record) for record in data.get("records", [])]
|
|
62
|
+
return BillingRecordsResult(total=data["total"], page=data["page"], records=records)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""Public client entrypoint for the EasyID SDK."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
import requests
|
|
9
|
+
|
|
10
|
+
from .bank import BankService
|
|
11
|
+
from .billing import BillingService
|
|
12
|
+
from .face import FaceService
|
|
13
|
+
from .idcard import IDCardService
|
|
14
|
+
from .phone import PhoneService
|
|
15
|
+
from .risk import RiskService
|
|
16
|
+
from .transport import DEFAULT_BASE_URL, SDK_VERSION, Transport
|
|
17
|
+
|
|
18
|
+
_KEY_ID_RE = re.compile(r"^ak_[0-9a-f]+$")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class EasyID:
|
|
22
|
+
"""EasyID API client.
|
|
23
|
+
|
|
24
|
+
The client is safe to reuse across requests.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
version = SDK_VERSION
|
|
28
|
+
|
|
29
|
+
def __init__(
|
|
30
|
+
self,
|
|
31
|
+
key_id: str,
|
|
32
|
+
secret: str,
|
|
33
|
+
*,
|
|
34
|
+
base_url: str = DEFAULT_BASE_URL,
|
|
35
|
+
timeout: float = 30.0,
|
|
36
|
+
session: Optional[requests.Session] = None,
|
|
37
|
+
) -> None:
|
|
38
|
+
if not _KEY_ID_RE.fullmatch(key_id):
|
|
39
|
+
raise ValueError(f"easyid: key_id must match ak_<hex>, got: {key_id}")
|
|
40
|
+
if not secret:
|
|
41
|
+
raise ValueError("easyid: secret must not be empty")
|
|
42
|
+
|
|
43
|
+
self._transport = Transport(
|
|
44
|
+
key_id=key_id,
|
|
45
|
+
secret=secret,
|
|
46
|
+
base_url=base_url,
|
|
47
|
+
timeout=timeout,
|
|
48
|
+
session=session,
|
|
49
|
+
)
|
|
50
|
+
self.idcard = IDCardService(self._transport)
|
|
51
|
+
self.phone = PhoneService(self._transport)
|
|
52
|
+
self.face = FaceService(self._transport)
|
|
53
|
+
self.bank = BankService(self._transport)
|
|
54
|
+
self.risk = RiskService(self._transport)
|
|
55
|
+
self.billing = BillingService(self._transport)
|
|
56
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""Error types for the EasyID SDK."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class APIError(Exception):
|
|
7
|
+
"""Business error returned by the EasyID API."""
|
|
8
|
+
|
|
9
|
+
def __init__(self, code: int, message: str, request_id: str) -> None:
|
|
10
|
+
self.code = code
|
|
11
|
+
self.message = message
|
|
12
|
+
self.request_id = request_id
|
|
13
|
+
super().__init__(f"easyid: code={code} message={message} request_id={request_id}")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def is_api_error(exc: BaseException) -> bool:
|
|
17
|
+
"""Return True when *exc* is an :class:`APIError`."""
|
|
18
|
+
|
|
19
|
+
return isinstance(exc, APIError)
|
|
20
|
+
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""Face APIs."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
from .transport import FileInput, Transport, make_multipart_part
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True)
|
|
12
|
+
class LivenessResult:
|
|
13
|
+
liveness: bool
|
|
14
|
+
score: float
|
|
15
|
+
method: str
|
|
16
|
+
frames_analyzed: int
|
|
17
|
+
attack_type: Optional[str]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass(frozen=True)
|
|
21
|
+
class CompareResult:
|
|
22
|
+
match: bool
|
|
23
|
+
score: float
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass(frozen=True)
|
|
27
|
+
class FaceVerifyResult:
|
|
28
|
+
result: bool
|
|
29
|
+
supplier: str
|
|
30
|
+
score: float
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class FaceService:
|
|
34
|
+
def __init__(self, transport: Transport) -> None:
|
|
35
|
+
self._transport = transport
|
|
36
|
+
|
|
37
|
+
def liveness(
|
|
38
|
+
self,
|
|
39
|
+
*,
|
|
40
|
+
media: FileInput,
|
|
41
|
+
mode: Optional[str] = None,
|
|
42
|
+
filename: Optional[str] = None,
|
|
43
|
+
) -> LivenessResult:
|
|
44
|
+
fields = {}
|
|
45
|
+
if mode is not None:
|
|
46
|
+
fields["mode"] = mode
|
|
47
|
+
data = self._transport.request_multipart(
|
|
48
|
+
"/v1/face/liveness",
|
|
49
|
+
fields=fields,
|
|
50
|
+
files=[make_multipart_part("media", media, filename)],
|
|
51
|
+
)
|
|
52
|
+
return LivenessResult(**data)
|
|
53
|
+
|
|
54
|
+
def compare(
|
|
55
|
+
self,
|
|
56
|
+
*,
|
|
57
|
+
image1: FileInput,
|
|
58
|
+
image2: FileInput,
|
|
59
|
+
filename1: Optional[str] = None,
|
|
60
|
+
filename2: Optional[str] = None,
|
|
61
|
+
) -> CompareResult:
|
|
62
|
+
data = self._transport.request_multipart(
|
|
63
|
+
"/v1/face/compare",
|
|
64
|
+
files=[
|
|
65
|
+
make_multipart_part("image1", image1, filename1),
|
|
66
|
+
make_multipart_part("image2", image2, filename2),
|
|
67
|
+
],
|
|
68
|
+
)
|
|
69
|
+
return CompareResult(**data)
|
|
70
|
+
|
|
71
|
+
def verify(
|
|
72
|
+
self,
|
|
73
|
+
*,
|
|
74
|
+
id_number: str,
|
|
75
|
+
media_key: Optional[str] = None,
|
|
76
|
+
callback_url: Optional[str] = None,
|
|
77
|
+
) -> FaceVerifyResult:
|
|
78
|
+
body = {"id_number": id_number}
|
|
79
|
+
if media_key is not None:
|
|
80
|
+
body["media_key"] = media_key
|
|
81
|
+
if callback_url is not None:
|
|
82
|
+
body["callback_url"] = callback_url
|
|
83
|
+
data = self._transport.request_json("POST", "/v1/face/verify", body=body)
|
|
84
|
+
return FaceVerifyResult(**data)
|
|
85
|
+
|