tingee-sdk 0.4.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.
@@ -0,0 +1,146 @@
1
+ Metadata-Version: 2.4
2
+ Name: tingee-sdk
3
+ Version: 0.4.0
4
+ Summary: Tingee Open API SDK for Python
5
+ Author-email: Tingee <developer@heno.vn>
6
+ License: ISC
7
+ Project-URL: Homepage, https://developers.tingee.vn
8
+ Project-URL: Documentation, https://developers.tingee.vn/docs/sdk
9
+ Project-URL: Repository, https://github.com/tingeehub/tingee-python
10
+ Project-URL: Issues, https://github.com/tingeehub/tingee-python/issues
11
+ Keywords: tingee,sdk,payment,api
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Typing :: Typed
15
+ Requires-Python: >=3.8
16
+ Description-Content-Type: text/markdown
17
+
18
+ # tingee-sdk (Python)
19
+
20
+ > SDK chính thức tích hợp **Tingee Open API** cho Python
21
+
22
+ Python ≥ 3.8, không phụ thuộc thư viện ngoài.
23
+
24
+ ---
25
+
26
+ ## Cài đặt
27
+
28
+ ```bash
29
+ pip install tingee-sdk
30
+ ```
31
+
32
+ ---
33
+
34
+ ## Bắt đầu nhanh
35
+
36
+ ```python
37
+ import os
38
+ from tingee_sdk import TingeeClient, is_success_response
39
+
40
+ client = TingeeClient(
41
+ secret_key=os.environ["TINGEE_SECRET_KEY"],
42
+ client_id=os.environ["TINGEE_CLIENT_ID"],
43
+ environment="uat", # hoặc "production" (mặc định)
44
+ )
45
+
46
+ result = client.bank.get_banks()
47
+ if is_success_response(result):
48
+ print(result["data"])
49
+
50
+ result = client.bank.generate_viet_qr({
51
+ "bankName": "OCB",
52
+ "accountNumber": "0123456789",
53
+ "amount": 100000,
54
+ "content": "Thanh toan don hang",
55
+ })
56
+ ```
57
+
58
+ - Method được nhóm theo path: `/v1/bank/create-va` → `client.bank.create_va(body)`, `/v1/deep-link/generate` → `client.deep_link.generate(body)`.
59
+ - Body và query truyền dạng `dict`, tên field giữ nguyên như API (camelCase). Kiểu DTO (TypedDict) nằm trong `tingee_sdk.models`.
60
+ - SDK tự ký request (`x-signature`, `x-request-timestamp`, `x-client-id`).
61
+
62
+ > **Lưu ý:** SDK trả về `dict` có `code` và `message`. Kiểm tra `code == "00"` hoặc `is_success_response(result)` để xác định thành công — SDK **không tự raise** khi `code != "00"`. Lỗi HTTP (non-2xx), lỗi mạng và timeout raise `TingeeHttpError`.
63
+
64
+ ---
65
+
66
+ ## Xác thực Webhook
67
+
68
+ ```python
69
+ # Flask
70
+ from flask import Flask, request
71
+
72
+ app = Flask(__name__)
73
+
74
+ @app.post("/webhook/tingee")
75
+ def tingee_webhook():
76
+ result = client.verify_webhook_signature(
77
+ request.headers.get("x-signature"),
78
+ request.headers.get("x-request-timestamp"),
79
+ request.get_data(), # raw body
80
+ )
81
+ if result["code"] != "00":
82
+ return {"error": result["message"]}, 401
83
+
84
+ body = request.get_json()
85
+ # Xử lý giao dịch...
86
+ return {"received": True}
87
+ ```
88
+
89
+ Hoặc dùng hàm `verify_webhook_signature(secret_token, signature, timestamp, body)` với secret token riêng.
90
+
91
+ ---
92
+
93
+ ## Xác thực URL webhook
94
+
95
+ Khi lưu URL webhook trên Tingee, hệ thống gửi một request xác thực vào URL đó:
96
+
97
+ ```json
98
+ { "event": "url-validation", "plainToken": "<chuỗi ngẫu nhiên>", "timestamp": 1730000000000 }
99
+ ```
100
+
101
+ Endpoint phải trả về HTTP 200 với `{ "plainToken": "...", "encryptedToken": "..." }`, trong đó `encryptedToken = HMAC-SHA256(secretToken, plainToken)` dạng hex. `secretToken` là secret của **API Key loại Xác thực webhook** đang bật (tạo trong mục API Credentials).
102
+
103
+ SDK làm sẵn bước này: hàm trả về body cần phản hồi, hoặc `None` nếu không phải request xác thực. Gọi **trước** khi verify chữ ký, vì body xác thực không có các trường giao dịch.
104
+
105
+ ```python
106
+ from tingee_sdk import create_url_validation_response
107
+
108
+ @app.post("/webhook/tingee")
109
+ def tingee_webhook():
110
+ validation = create_url_validation_response(os.environ["TINGEE_WEBHOOK_SECRET"], request.get_data())
111
+ if validation is not None:
112
+ return validation
113
+
114
+ # Verify chữ ký & xử lý giao dịch như bình thường...
115
+ ```
116
+
117
+ Nếu dùng chung khóa với client: `client.create_url_validation_response(request.get_data())`.
118
+
119
+ ---
120
+
121
+ ## Bank Constants
122
+
123
+ ```python
124
+ from tingee_sdk import BankNameEnum, get_bank_code, get_bank_short_name, get_bank_bin
125
+
126
+ get_bank_code("970436") # BankNameEnum.VCB
127
+ get_bank_short_name("970436") # 'Vietcombank'
128
+ get_bank_bin(BankNameEnum.VCB) # '970436'
129
+ ```
130
+
131
+ ---
132
+
133
+ ## Liên hệ & Hỗ trợ
134
+
135
+ 📧 **Email:** [developer@heno.vn](mailto:developer@heno.vn)
136
+ 💬 **Telegram:** [@tingeesupport](https://t.me/tingeesupport)
137
+ 🌐 **Website:** [tingee.vn](https://tingee.vn)
138
+
139
+ ---
140
+
141
+ ## Xem thêm
142
+
143
+ - [CHANGELOG](./CHANGELOG.md)
144
+ - [Tài liệu Tingee Open API](https://open-api.tingee.vn)
145
+ - [Tài liệu Tingee Developer](https://developers.tingee.vn)
146
+ - [Trang chủ Tingee](https://tingee.vn)
@@ -0,0 +1,129 @@
1
+ # tingee-sdk (Python)
2
+
3
+ > SDK chính thức tích hợp **Tingee Open API** cho Python
4
+
5
+ Python ≥ 3.8, không phụ thuộc thư viện ngoài.
6
+
7
+ ---
8
+
9
+ ## Cài đặt
10
+
11
+ ```bash
12
+ pip install tingee-sdk
13
+ ```
14
+
15
+ ---
16
+
17
+ ## Bắt đầu nhanh
18
+
19
+ ```python
20
+ import os
21
+ from tingee_sdk import TingeeClient, is_success_response
22
+
23
+ client = TingeeClient(
24
+ secret_key=os.environ["TINGEE_SECRET_KEY"],
25
+ client_id=os.environ["TINGEE_CLIENT_ID"],
26
+ environment="uat", # hoặc "production" (mặc định)
27
+ )
28
+
29
+ result = client.bank.get_banks()
30
+ if is_success_response(result):
31
+ print(result["data"])
32
+
33
+ result = client.bank.generate_viet_qr({
34
+ "bankName": "OCB",
35
+ "accountNumber": "0123456789",
36
+ "amount": 100000,
37
+ "content": "Thanh toan don hang",
38
+ })
39
+ ```
40
+
41
+ - Method được nhóm theo path: `/v1/bank/create-va` → `client.bank.create_va(body)`, `/v1/deep-link/generate` → `client.deep_link.generate(body)`.
42
+ - Body và query truyền dạng `dict`, tên field giữ nguyên như API (camelCase). Kiểu DTO (TypedDict) nằm trong `tingee_sdk.models`.
43
+ - SDK tự ký request (`x-signature`, `x-request-timestamp`, `x-client-id`).
44
+
45
+ > **Lưu ý:** SDK trả về `dict` có `code` và `message`. Kiểm tra `code == "00"` hoặc `is_success_response(result)` để xác định thành công — SDK **không tự raise** khi `code != "00"`. Lỗi HTTP (non-2xx), lỗi mạng và timeout raise `TingeeHttpError`.
46
+
47
+ ---
48
+
49
+ ## Xác thực Webhook
50
+
51
+ ```python
52
+ # Flask
53
+ from flask import Flask, request
54
+
55
+ app = Flask(__name__)
56
+
57
+ @app.post("/webhook/tingee")
58
+ def tingee_webhook():
59
+ result = client.verify_webhook_signature(
60
+ request.headers.get("x-signature"),
61
+ request.headers.get("x-request-timestamp"),
62
+ request.get_data(), # raw body
63
+ )
64
+ if result["code"] != "00":
65
+ return {"error": result["message"]}, 401
66
+
67
+ body = request.get_json()
68
+ # Xử lý giao dịch...
69
+ return {"received": True}
70
+ ```
71
+
72
+ Hoặc dùng hàm `verify_webhook_signature(secret_token, signature, timestamp, body)` với secret token riêng.
73
+
74
+ ---
75
+
76
+ ## Xác thực URL webhook
77
+
78
+ Khi lưu URL webhook trên Tingee, hệ thống gửi một request xác thực vào URL đó:
79
+
80
+ ```json
81
+ { "event": "url-validation", "plainToken": "<chuỗi ngẫu nhiên>", "timestamp": 1730000000000 }
82
+ ```
83
+
84
+ Endpoint phải trả về HTTP 200 với `{ "plainToken": "...", "encryptedToken": "..." }`, trong đó `encryptedToken = HMAC-SHA256(secretToken, plainToken)` dạng hex. `secretToken` là secret của **API Key loại Xác thực webhook** đang bật (tạo trong mục API Credentials).
85
+
86
+ SDK làm sẵn bước này: hàm trả về body cần phản hồi, hoặc `None` nếu không phải request xác thực. Gọi **trước** khi verify chữ ký, vì body xác thực không có các trường giao dịch.
87
+
88
+ ```python
89
+ from tingee_sdk import create_url_validation_response
90
+
91
+ @app.post("/webhook/tingee")
92
+ def tingee_webhook():
93
+ validation = create_url_validation_response(os.environ["TINGEE_WEBHOOK_SECRET"], request.get_data())
94
+ if validation is not None:
95
+ return validation
96
+
97
+ # Verify chữ ký & xử lý giao dịch như bình thường...
98
+ ```
99
+
100
+ Nếu dùng chung khóa với client: `client.create_url_validation_response(request.get_data())`.
101
+
102
+ ---
103
+
104
+ ## Bank Constants
105
+
106
+ ```python
107
+ from tingee_sdk import BankNameEnum, get_bank_code, get_bank_short_name, get_bank_bin
108
+
109
+ get_bank_code("970436") # BankNameEnum.VCB
110
+ get_bank_short_name("970436") # 'Vietcombank'
111
+ get_bank_bin(BankNameEnum.VCB) # '970436'
112
+ ```
113
+
114
+ ---
115
+
116
+ ## Liên hệ & Hỗ trợ
117
+
118
+ 📧 **Email:** [developer@heno.vn](mailto:developer@heno.vn)
119
+ 💬 **Telegram:** [@tingeesupport](https://t.me/tingeesupport)
120
+ 🌐 **Website:** [tingee.vn](https://tingee.vn)
121
+
122
+ ---
123
+
124
+ ## Xem thêm
125
+
126
+ - [CHANGELOG](./CHANGELOG.md)
127
+ - [Tài liệu Tingee Open API](https://open-api.tingee.vn)
128
+ - [Tài liệu Tingee Developer](https://developers.tingee.vn)
129
+ - [Trang chủ Tingee](https://tingee.vn)
@@ -0,0 +1,31 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "tingee-sdk"
7
+ version = "0.4.0"
8
+ description = "Tingee Open API SDK for Python"
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = { text = "ISC" }
12
+ authors = [{ name = "Tingee", email = "developer@heno.vn" }]
13
+ keywords = ["tingee", "sdk", "payment", "api"]
14
+ dependencies = []
15
+ classifiers = [
16
+ "Programming Language :: Python :: 3",
17
+ "Operating System :: OS Independent",
18
+ "Typing :: Typed",
19
+ ]
20
+
21
+ [project.urls]
22
+ Homepage = "https://developers.tingee.vn"
23
+ Documentation = "https://developers.tingee.vn/docs/sdk"
24
+ Repository = "https://github.com/tingeehub/tingee-python"
25
+ Issues = "https://github.com/tingeehub/tingee-python/issues"
26
+
27
+ [tool.setuptools.packages.find]
28
+ where = ["src"]
29
+
30
+ [tool.setuptools.package-data]
31
+ tingee_sdk = ["py.typed"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,53 @@
1
+ """
2
+ Tingee SDK for Python
3
+
4
+ Official SDK for Tingee Open API. DTO types are in ``tingee_sdk.models``.
5
+ """
6
+
7
+ from .bank_constants import (
8
+ BANKS,
9
+ BANKS_OBJECT,
10
+ BankAccountTypeEnum,
11
+ BankBinEnum,
12
+ BankNameEnum,
13
+ get_bank_bin,
14
+ get_bank_code,
15
+ get_bank_short_name,
16
+ )
17
+ from .client import ENVIRONMENT_URLS, TingeeClient, TingeeEnvironment
18
+ from .http import TingeeHttpError
19
+ from .response import TingeeApiResponse, is_error_response, is_success_response
20
+ from .signature import (
21
+ URL_VALIDATION_EVENT,
22
+ create_url_validation_response,
23
+ format_timestamp,
24
+ generate_signature,
25
+ verify_webhook_signature,
26
+ )
27
+ from .version import SDK_VERSION
28
+
29
+ __version__ = SDK_VERSION
30
+
31
+ __all__ = [
32
+ "TingeeClient",
33
+ "TingeeEnvironment",
34
+ "ENVIRONMENT_URLS",
35
+ "TingeeHttpError",
36
+ "TingeeApiResponse",
37
+ "is_success_response",
38
+ "is_error_response",
39
+ "generate_signature",
40
+ "format_timestamp",
41
+ "verify_webhook_signature",
42
+ "create_url_validation_response",
43
+ "URL_VALIDATION_EVENT",
44
+ "BankNameEnum",
45
+ "BankBinEnum",
46
+ "BankAccountTypeEnum",
47
+ "BANKS",
48
+ "BANKS_OBJECT",
49
+ "get_bank_code",
50
+ "get_bank_short_name",
51
+ "get_bank_bin",
52
+ "SDK_VERSION",
53
+ ]
@@ -0,0 +1,92 @@
1
+ """Bank constants (same as the other SDKs)."""
2
+
3
+ from enum import Enum
4
+ from typing import Dict, List, Optional, TypedDict
5
+
6
+ __all__ = [
7
+ "BankNameEnum",
8
+ "BankBinEnum",
9
+ "BankAccountTypeEnum",
10
+ "BankInfo",
11
+ "BANKS_OBJECT",
12
+ "BANKS",
13
+ "get_bank_code",
14
+ "get_bank_short_name",
15
+ "get_bank_bin",
16
+ ]
17
+
18
+
19
+ class BankNameEnum(str, Enum):
20
+ OCB = "OCB"
21
+ BIDV = "BIDV"
22
+ MBB = "MBB"
23
+ ACB = "ACB"
24
+ VPB = "VPB"
25
+ PGB = "PGB"
26
+ VIB = "VIB"
27
+ STB = "STB"
28
+ CTG = "CTG"
29
+ VCB = "VCB"
30
+ AGRIBANK = "AGRIBANK"
31
+ SHINHAN = "SHINHAN"
32
+ COB = "COB"
33
+
34
+
35
+ class BankBinEnum(str, Enum):
36
+ OCB = "970448"
37
+ BIDV = "970418"
38
+ MBB = "970422"
39
+ ACB = "970416"
40
+ VPB = "970432"
41
+ PGB = "970430"
42
+ VIB = "970441"
43
+ STB = "970403"
44
+ CTG = "970415"
45
+ VCB = "970436"
46
+ AGRIBANK = "970405"
47
+ SHINHAN = "970424"
48
+ COB = "970446"
49
+
50
+
51
+ class BankAccountTypeEnum(str, Enum):
52
+ PersonalAccount = "personal-account"
53
+ BusinessAccount = "business-account"
54
+ BusinessHouseHoldAccount = "business-household-account"
55
+
56
+
57
+ class BankInfo(TypedDict):
58
+ code: BankNameEnum
59
+ name: str
60
+ shortName: str
61
+ bin: str
62
+
63
+
64
+ BANKS_OBJECT: Dict[BankNameEnum, BankInfo] = {
65
+ BankNameEnum.VCB: {"code": BankNameEnum.VCB, "name": "Ngân hàng TMCP Ngoại Thương Việt Nam", "bin": "970436", "shortName": "Vietcombank"},
66
+ BankNameEnum.CTG: {"code": BankNameEnum.CTG, "name": "Ngân hàng TMCP Công thương Việt Nam", "bin": "970415", "shortName": "VietinBank"},
67
+ BankNameEnum.BIDV: {"code": BankNameEnum.BIDV, "name": "Ngân hàng TMCP Đầu tư và Phát triển Việt Nam", "bin": "970418", "shortName": "BIDV"},
68
+ BankNameEnum.AGRIBANK: {"code": BankNameEnum.AGRIBANK, "name": "Ngân hàng Nông nghiệp và Phát triển Nông thôn Việt Nam", "bin": "970405", "shortName": "Agribank"},
69
+ BankNameEnum.MBB: {"code": BankNameEnum.MBB, "name": "Ngân hàng TMCP Quân đội", "bin": "970422", "shortName": "MB Bank"},
70
+ BankNameEnum.ACB: {"code": BankNameEnum.ACB, "name": "Ngân hàng TMCP Á Châu", "bin": "970416", "shortName": "ACB"},
71
+ BankNameEnum.OCB: {"code": BankNameEnum.OCB, "name": "Ngân hàng TMCP Phương Đông", "bin": "970448", "shortName": "OCB Bank"},
72
+ BankNameEnum.VPB: {"code": BankNameEnum.VPB, "name": "Ngân hàng TMCP Việt Nam Thịnh Vượng", "bin": "970432", "shortName": "VPBank"},
73
+ BankNameEnum.STB: {"code": BankNameEnum.STB, "name": "Ngân hàng TMCP Sài Gòn Thương Tín", "bin": "970403", "shortName": "Sacombank"},
74
+ BankNameEnum.VIB: {"code": BankNameEnum.VIB, "name": "Ngân hàng TMCP Quốc tế Việt Nam", "bin": "970441", "shortName": "VIB"},
75
+ BankNameEnum.PGB: {"code": BankNameEnum.PGB, "name": "Ngân Hàng TMCP Thịnh vượng và Phát triển", "bin": "970430", "shortName": "PGBank"},
76
+ BankNameEnum.SHINHAN: {"code": BankNameEnum.SHINHAN, "name": "Ngân hàng TNHH MTV Shinhan Việt Nam", "bin": "970424", "shortName": "Shinhan Bank"},
77
+ BankNameEnum.COB: {"code": BankNameEnum.COB, "name": "Ngân hàng Hợp tác Xã Việt Nam", "bin": "970446", "shortName": "Co-opBank"},
78
+ }
79
+
80
+ BANKS: List[BankInfo] = list(BANKS_OBJECT.values())
81
+
82
+
83
+ def get_bank_code(bank_bin: str) -> Optional[BankNameEnum]:
84
+ return next((x["code"] for x in BANKS if x["bin"] == bank_bin), None)
85
+
86
+
87
+ def get_bank_short_name(bank_bin: str) -> Optional[str]:
88
+ return next((x["shortName"] for x in BANKS if x["bin"] == bank_bin), None)
89
+
90
+
91
+ def get_bank_bin(bank_name: BankNameEnum) -> Optional[str]:
92
+ return next((x["bin"] for x in BANKS if x["code"] == bank_name), None)
@@ -0,0 +1,76 @@
1
+ from typing import Any, Dict, Literal, Optional, Union
2
+
3
+ from .generated_methods import GeneratedMethods
4
+ from .http import TingeeHttpClient
5
+ from .signature import create_url_validation_response, verify_webhook_signature
6
+
7
+ __all__ = ["TingeeClient", "TingeeEnvironment", "ENVIRONMENT_URLS"]
8
+
9
+ TingeeEnvironment = Literal["production", "uat"]
10
+
11
+ ENVIRONMENT_URLS: Dict[str, str] = {
12
+ "production": "https://open-api.tingee.vn",
13
+ "uat": "https://uat-open-api.tingee.vn",
14
+ }
15
+
16
+
17
+ class TingeeClient(GeneratedMethods):
18
+ """
19
+ Tingee Open API client.
20
+
21
+ Example::
22
+
23
+ client = TingeeClient(secret_key="...", client_id="...", environment="uat")
24
+ result = client.bank.get_banks()
25
+ if result["code"] == "00":
26
+ print(result["data"])
27
+ """
28
+
29
+ def __init__(
30
+ self,
31
+ secret_key: str,
32
+ client_id: str,
33
+ environment: TingeeEnvironment = "production",
34
+ timeout: float = 90,
35
+ base_url: Optional[str] = None,
36
+ ) -> None:
37
+ """
38
+ :param secret_key: Secret key for generating request signature
39
+ :param client_id: Client ID for API authentication
40
+ :param environment: ``production`` (default) or ``uat``
41
+ :param timeout: Request timeout in seconds (default 90)
42
+ :param base_url: Custom base URL (overrides environment)
43
+ """
44
+ if not secret_key:
45
+ raise ValueError("secret_key is required")
46
+ if not client_id:
47
+ raise ValueError("client_id is required")
48
+ if environment not in ENVIRONMENT_URLS and not base_url:
49
+ raise ValueError("environment must be one of: {0}".format(", ".join(ENVIRONMENT_URLS)))
50
+
51
+ self._secret_key = secret_key
52
+ self._base_url = base_url or ENVIRONMENT_URLS[environment]
53
+ super().__init__(TingeeHttpClient(self._base_url, secret_key, client_id, timeout))
54
+
55
+ def get_base_url(self) -> str:
56
+ return self._base_url
57
+
58
+ def verify_webhook_signature(
59
+ self,
60
+ signature: Optional[str],
61
+ timestamp: Optional[str],
62
+ body: Union[Dict[str, Any], str, bytes, None],
63
+ ) -> Dict[str, str]:
64
+ """Verify an incoming Tingee webhook signature using this client's secret_key."""
65
+ return verify_webhook_signature(self._secret_key, signature, timestamp, body)
66
+
67
+ def create_url_validation_response(
68
+ self,
69
+ body: Union[Dict[str, Any], str, bytes, None],
70
+ secret_token: Optional[str] = None,
71
+ ) -> Optional[Dict[str, str]]:
72
+ """
73
+ Build the response for a webhook URL validation request, ``None`` if body is not one.
74
+ Pass ``secret_token`` when your "Webhook verification" credential differs from this client's.
75
+ """
76
+ return create_url_validation_response(secret_token or self._secret_key, body)