bml-connect-python 1.0.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.
- bml_connect_python-1.0.0/LICENSE +9 -0
- bml_connect_python-1.0.0/MANIFEST.in +12 -0
- bml_connect_python-1.0.0/PKG-INFO +279 -0
- bml_connect_python-1.0.0/README.md +230 -0
- bml_connect_python-1.0.0/examples/basic_async.py +52 -0
- bml_connect_python-1.0.0/examples/basic_sync.py +50 -0
- bml_connect_python-1.0.0/examples/webhook_fastapi.py +43 -0
- bml_connect_python-1.0.0/examples/webhook_flask.py +33 -0
- bml_connect_python-1.0.0/examples/webhook_plain.py +27 -0
- bml_connect_python-1.0.0/examples/webhook_sanic.py +27 -0
- bml_connect_python-1.0.0/pyproject.toml +95 -0
- bml_connect_python-1.0.0/requirements-dev.txt +10 -0
- bml_connect_python-1.0.0/requirements.txt +3 -0
- bml_connect_python-1.0.0/setup.cfg +4 -0
- bml_connect_python-1.0.0/src/bml_connect/__init__.py +79 -0
- bml_connect_python-1.0.0/src/bml_connect/client.py +536 -0
- bml_connect_python-1.0.0/src/bml_connect_python.egg-info/PKG-INFO +279 -0
- bml_connect_python-1.0.0/src/bml_connect_python.egg-info/SOURCES.txt +21 -0
- bml_connect_python-1.0.0/src/bml_connect_python.egg-info/dependency_links.txt +1 -0
- bml_connect_python-1.0.0/src/bml_connect_python.egg-info/requires.txt +25 -0
- bml_connect_python-1.0.0/src/bml_connect_python.egg-info/top_level.txt +1 -0
- bml_connect_python-1.0.0/tests/test_client.py +8 -0
- bml_connect_python-1.0.0/tests/test_sdk.py +26 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Ali Fayaz (Quill) (quillfires)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
include README.md
|
|
2
|
+
include LICENSE
|
|
3
|
+
include requirements.txt
|
|
4
|
+
include requirements-dev.txt
|
|
5
|
+
include pyproject.toml
|
|
6
|
+
|
|
7
|
+
recursive-include src *.py
|
|
8
|
+
recursive-include tests *.py
|
|
9
|
+
recursive-include examples *.py
|
|
10
|
+
|
|
11
|
+
recursive-exclude * __pycache__
|
|
12
|
+
recursive-exclude * *.py[co]
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bml-connect-python
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Python SDK for Bank of Maldives Connect API with sync/async support
|
|
5
|
+
Author-email: Ali Fayaz <fayaz.quill@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/quillfires/bml-connect-python
|
|
8
|
+
Project-URL: Documentation, https://bml-connect-python.readthedocs.io
|
|
9
|
+
Project-URL: Repository, https://github.com/quillfires/bml-connect-python
|
|
10
|
+
Project-URL: Issues, https://github.com/quillfires/bml-connect-python/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/quillfires/bml-connect-python/releases
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Classifier: Topic :: Office/Business :: Financial :: Point-Of-Sale
|
|
24
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
25
|
+
Requires-Python: >=3.7
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Requires-Dist: requests>=2.28.0
|
|
29
|
+
Requires-Dist: aiohttp>=3.8.0
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
32
|
+
Requires-Dist: pytest-asyncio>=0.20.0; extra == "dev"
|
|
33
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
34
|
+
Requires-Dist: black>=22.0.0; extra == "dev"
|
|
35
|
+
Requires-Dist: flake8>=5.0.0; extra == "dev"
|
|
36
|
+
Requires-Dist: mypy>=0.990; extra == "dev"
|
|
37
|
+
Requires-Dist: pre-commit>=2.20.0; extra == "dev"
|
|
38
|
+
Requires-Dist: types-requests>=2.28.0; extra == "dev"
|
|
39
|
+
Provides-Extra: django
|
|
40
|
+
Requires-Dist: django>=3.2; extra == "django"
|
|
41
|
+
Provides-Extra: flask
|
|
42
|
+
Requires-Dist: flask>=2.0; extra == "flask"
|
|
43
|
+
Provides-Extra: fastapi
|
|
44
|
+
Requires-Dist: fastapi>=0.68.0; extra == "fastapi"
|
|
45
|
+
Requires-Dist: uvicorn>=0.15.0; extra == "fastapi"
|
|
46
|
+
Provides-Extra: sanic
|
|
47
|
+
Requires-Dist: sanic>=22.0.0; extra == "sanic"
|
|
48
|
+
Dynamic: license-file
|
|
49
|
+
|
|
50
|
+
# BML Connect Python SDK
|
|
51
|
+
|
|
52
|
+
[](https://pypi.org/project/bml-connect-python/)
|
|
53
|
+
[](https://pypi.org/project/bml-connect-python/)
|
|
54
|
+
[](https://opensource.org/licenses/MIT)
|
|
55
|
+
|
|
56
|
+
Python SDK for Bank of Maldives Connect API with synchronous and asynchronous support.
|
|
57
|
+
Compatible with all Python frameworks including Django, Flask, FastAPI, and Sanic.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Features
|
|
62
|
+
|
|
63
|
+
- **Sync/Async Support:** Choose your preferred programming style
|
|
64
|
+
- **Full API Coverage:** Transactions, webhooks, and signature verification
|
|
65
|
+
- **Type Annotations:** Full type hint support for better development experience
|
|
66
|
+
- **Error Handling:** Comprehensive error hierarchy for easy debugging
|
|
67
|
+
- **Framework Agnostic:** Works with any Python web framework
|
|
68
|
+
- **MIT Licensed:** Open source and free to use
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Installation
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
pip install bml-connect-python
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Quick Start
|
|
81
|
+
|
|
82
|
+
### Synchronous Client
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
from bml_connect import BMLConnect, Environment
|
|
86
|
+
|
|
87
|
+
client = BMLConnect(
|
|
88
|
+
api_key="your_api_key",
|
|
89
|
+
app_id="your_app_id",
|
|
90
|
+
environment=Environment.SANDBOX
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
try:
|
|
94
|
+
transaction = client.transactions.create_transaction({
|
|
95
|
+
"amount": 1500, # 15.00 MVR
|
|
96
|
+
"currency": "MVR",
|
|
97
|
+
"provider": "alipay",
|
|
98
|
+
"redirectUrl": "https://yourstore.com/success",
|
|
99
|
+
"localId": "order_123",
|
|
100
|
+
"customerReference": "Customer #456"
|
|
101
|
+
})
|
|
102
|
+
print(f"Transaction ID: {transaction.transaction_id}")
|
|
103
|
+
print(f"Payment URL: {transaction.url}")
|
|
104
|
+
except Exception as e:
|
|
105
|
+
print(f"Error: {e}")
|
|
106
|
+
finally:
|
|
107
|
+
client.close()
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Asynchronous Client
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
import asyncio
|
|
114
|
+
from bml_connect import BMLConnect, Environment
|
|
115
|
+
|
|
116
|
+
async def main():
|
|
117
|
+
client = BMLConnect(
|
|
118
|
+
api_key="your_api_key",
|
|
119
|
+
app_id="your_app_id",
|
|
120
|
+
environment=Environment.SANDBOX,
|
|
121
|
+
async_mode=True
|
|
122
|
+
)
|
|
123
|
+
try:
|
|
124
|
+
transaction = await client.transactions.create_transaction({
|
|
125
|
+
"amount": 2000,
|
|
126
|
+
"currency": "MVR",
|
|
127
|
+
"provider": "wechat",
|
|
128
|
+
"redirectUrl": "https://yourstore.com/success"
|
|
129
|
+
})
|
|
130
|
+
print(f"Transaction ID: {transaction.transaction_id}")
|
|
131
|
+
finally:
|
|
132
|
+
await client.aclose()
|
|
133
|
+
|
|
134
|
+
asyncio.run(main())
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Webhook Verification
|
|
140
|
+
|
|
141
|
+
### Flask Example
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
from flask import Flask, request, jsonify
|
|
145
|
+
from bml_connect import BMLConnect
|
|
146
|
+
|
|
147
|
+
app = Flask(__name__)
|
|
148
|
+
client = BMLConnect(api_key="your_api_key", app_id="your_app_id")
|
|
149
|
+
|
|
150
|
+
@app.route('/webhook', methods=['POST'])
|
|
151
|
+
def webhook():
|
|
152
|
+
payload = request.get_json()
|
|
153
|
+
signature = payload.get('signature')
|
|
154
|
+
if client.verify_webhook_signature(payload, signature):
|
|
155
|
+
# Process webhook
|
|
156
|
+
return jsonify({"status": "success"}), 200
|
|
157
|
+
else:
|
|
158
|
+
return jsonify({"error": "Invalid signature"}), 403
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### FastAPI Example
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
from fastapi import FastAPI, Request, HTTPException
|
|
165
|
+
from bml_connect import BMLConnect
|
|
166
|
+
|
|
167
|
+
app = FastAPI()
|
|
168
|
+
client = BMLConnect(api_key="your_api_key", app_id="your_app_id")
|
|
169
|
+
|
|
170
|
+
@app.post("/webhook")
|
|
171
|
+
async def handle_webhook(request: Request):
|
|
172
|
+
payload = await request.json()
|
|
173
|
+
signature = payload.get("signature")
|
|
174
|
+
if client.verify_webhook_signature(payload, signature):
|
|
175
|
+
return {"status": "success"}
|
|
176
|
+
else:
|
|
177
|
+
raise HTTPException(403, "Invalid signature")
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Sanic Example
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
from sanic import Sanic, response
|
|
184
|
+
from bml_connect import BMLConnect
|
|
185
|
+
|
|
186
|
+
app = Sanic("BMLWebhook")
|
|
187
|
+
client = BMLConnect(api_key="your_api_key", app_id="your_app_id")
|
|
188
|
+
|
|
189
|
+
@app.post('/webhook')
|
|
190
|
+
async def webhook(request):
|
|
191
|
+
payload = request.json
|
|
192
|
+
signature = payload.get('signature')
|
|
193
|
+
if client.verify_webhook_signature(payload, signature):
|
|
194
|
+
return response.json({"status": "success"})
|
|
195
|
+
else:
|
|
196
|
+
return response.json({"error": "Invalid signature"}, status=403)
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## API Reference
|
|
202
|
+
|
|
203
|
+
### Main Classes
|
|
204
|
+
|
|
205
|
+
- `BMLConnect`: Main entry point for the SDK.
|
|
206
|
+
- `Transaction`: Transaction object.
|
|
207
|
+
- `QRCode`: QR code details.
|
|
208
|
+
- `PaginatedResponse`: For paginated transaction lists.
|
|
209
|
+
- `Environment`: Enum for `SANDBOX` and `PRODUCTION`.
|
|
210
|
+
- `SignMethod`: Enum for signature methods.
|
|
211
|
+
- `TransactionState`: Enum for transaction states.
|
|
212
|
+
|
|
213
|
+
### Error Classes
|
|
214
|
+
|
|
215
|
+
- `BMLConnectError`
|
|
216
|
+
- `AuthenticationError`
|
|
217
|
+
- `ValidationError`
|
|
218
|
+
- `NotFoundError`
|
|
219
|
+
- `ServerError`
|
|
220
|
+
- `RateLimitError`
|
|
221
|
+
|
|
222
|
+
### Utilities
|
|
223
|
+
|
|
224
|
+
- `SignatureUtils.generate_signature(data, api_key, method)`
|
|
225
|
+
- `SignatureUtils.verify_signature(data, signature, api_key, method)`
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## Development
|
|
230
|
+
|
|
231
|
+
### Requirements
|
|
232
|
+
|
|
233
|
+
- Python 3.7+
|
|
234
|
+
- See `requirements.txt` and `requirements-dev.txt` for dependencies.
|
|
235
|
+
|
|
236
|
+
### Testing
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
pip install -e .[dev]
|
|
240
|
+
pytest
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Formatting & Linting
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
black .
|
|
247
|
+
flake8 .
|
|
248
|
+
mypy .
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## Packaging
|
|
254
|
+
|
|
255
|
+
- Uses `pyproject.toml` for build configuration.
|
|
256
|
+
- Source code is in `src/bml_connect/`.
|
|
257
|
+
- Examples in `examples/`.
|
|
258
|
+
- Tests in `tests/`.
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## License
|
|
263
|
+
|
|
264
|
+
MIT License. See `LICENSE` for details.
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## Links
|
|
269
|
+
|
|
270
|
+
- [Homepage](https://github.com/bankofmaldives/bml-connect-python)
|
|
271
|
+
- [Documentation](https://bml-connect-python.readthedocs.io)
|
|
272
|
+
- [API Reference](docs/api_reference.md)
|
|
273
|
+
- [Changelog](https://github.com/bankofmaldives/bml-connect-python/releases)
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## Contributing
|
|
278
|
+
|
|
279
|
+
Pull requests and issues are welcome! See the documentation for guidelines.
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# BML Connect Python SDK
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/bml-connect-python/)
|
|
4
|
+
[](https://pypi.org/project/bml-connect-python/)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
Python SDK for Bank of Maldives Connect API with synchronous and asynchronous support.
|
|
8
|
+
Compatible with all Python frameworks including Django, Flask, FastAPI, and Sanic.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
- **Sync/Async Support:** Choose your preferred programming style
|
|
15
|
+
- **Full API Coverage:** Transactions, webhooks, and signature verification
|
|
16
|
+
- **Type Annotations:** Full type hint support for better development experience
|
|
17
|
+
- **Error Handling:** Comprehensive error hierarchy for easy debugging
|
|
18
|
+
- **Framework Agnostic:** Works with any Python web framework
|
|
19
|
+
- **MIT Licensed:** Open source and free to use
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install bml-connect-python
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Quick Start
|
|
32
|
+
|
|
33
|
+
### Synchronous Client
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
from bml_connect import BMLConnect, Environment
|
|
37
|
+
|
|
38
|
+
client = BMLConnect(
|
|
39
|
+
api_key="your_api_key",
|
|
40
|
+
app_id="your_app_id",
|
|
41
|
+
environment=Environment.SANDBOX
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
try:
|
|
45
|
+
transaction = client.transactions.create_transaction({
|
|
46
|
+
"amount": 1500, # 15.00 MVR
|
|
47
|
+
"currency": "MVR",
|
|
48
|
+
"provider": "alipay",
|
|
49
|
+
"redirectUrl": "https://yourstore.com/success",
|
|
50
|
+
"localId": "order_123",
|
|
51
|
+
"customerReference": "Customer #456"
|
|
52
|
+
})
|
|
53
|
+
print(f"Transaction ID: {transaction.transaction_id}")
|
|
54
|
+
print(f"Payment URL: {transaction.url}")
|
|
55
|
+
except Exception as e:
|
|
56
|
+
print(f"Error: {e}")
|
|
57
|
+
finally:
|
|
58
|
+
client.close()
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Asynchronous Client
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
import asyncio
|
|
65
|
+
from bml_connect import BMLConnect, Environment
|
|
66
|
+
|
|
67
|
+
async def main():
|
|
68
|
+
client = BMLConnect(
|
|
69
|
+
api_key="your_api_key",
|
|
70
|
+
app_id="your_app_id",
|
|
71
|
+
environment=Environment.SANDBOX,
|
|
72
|
+
async_mode=True
|
|
73
|
+
)
|
|
74
|
+
try:
|
|
75
|
+
transaction = await client.transactions.create_transaction({
|
|
76
|
+
"amount": 2000,
|
|
77
|
+
"currency": "MVR",
|
|
78
|
+
"provider": "wechat",
|
|
79
|
+
"redirectUrl": "https://yourstore.com/success"
|
|
80
|
+
})
|
|
81
|
+
print(f"Transaction ID: {transaction.transaction_id}")
|
|
82
|
+
finally:
|
|
83
|
+
await client.aclose()
|
|
84
|
+
|
|
85
|
+
asyncio.run(main())
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Webhook Verification
|
|
91
|
+
|
|
92
|
+
### Flask Example
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
from flask import Flask, request, jsonify
|
|
96
|
+
from bml_connect import BMLConnect
|
|
97
|
+
|
|
98
|
+
app = Flask(__name__)
|
|
99
|
+
client = BMLConnect(api_key="your_api_key", app_id="your_app_id")
|
|
100
|
+
|
|
101
|
+
@app.route('/webhook', methods=['POST'])
|
|
102
|
+
def webhook():
|
|
103
|
+
payload = request.get_json()
|
|
104
|
+
signature = payload.get('signature')
|
|
105
|
+
if client.verify_webhook_signature(payload, signature):
|
|
106
|
+
# Process webhook
|
|
107
|
+
return jsonify({"status": "success"}), 200
|
|
108
|
+
else:
|
|
109
|
+
return jsonify({"error": "Invalid signature"}), 403
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### FastAPI Example
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
from fastapi import FastAPI, Request, HTTPException
|
|
116
|
+
from bml_connect import BMLConnect
|
|
117
|
+
|
|
118
|
+
app = FastAPI()
|
|
119
|
+
client = BMLConnect(api_key="your_api_key", app_id="your_app_id")
|
|
120
|
+
|
|
121
|
+
@app.post("/webhook")
|
|
122
|
+
async def handle_webhook(request: Request):
|
|
123
|
+
payload = await request.json()
|
|
124
|
+
signature = payload.get("signature")
|
|
125
|
+
if client.verify_webhook_signature(payload, signature):
|
|
126
|
+
return {"status": "success"}
|
|
127
|
+
else:
|
|
128
|
+
raise HTTPException(403, "Invalid signature")
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Sanic Example
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
from sanic import Sanic, response
|
|
135
|
+
from bml_connect import BMLConnect
|
|
136
|
+
|
|
137
|
+
app = Sanic("BMLWebhook")
|
|
138
|
+
client = BMLConnect(api_key="your_api_key", app_id="your_app_id")
|
|
139
|
+
|
|
140
|
+
@app.post('/webhook')
|
|
141
|
+
async def webhook(request):
|
|
142
|
+
payload = request.json
|
|
143
|
+
signature = payload.get('signature')
|
|
144
|
+
if client.verify_webhook_signature(payload, signature):
|
|
145
|
+
return response.json({"status": "success"})
|
|
146
|
+
else:
|
|
147
|
+
return response.json({"error": "Invalid signature"}, status=403)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## API Reference
|
|
153
|
+
|
|
154
|
+
### Main Classes
|
|
155
|
+
|
|
156
|
+
- `BMLConnect`: Main entry point for the SDK.
|
|
157
|
+
- `Transaction`: Transaction object.
|
|
158
|
+
- `QRCode`: QR code details.
|
|
159
|
+
- `PaginatedResponse`: For paginated transaction lists.
|
|
160
|
+
- `Environment`: Enum for `SANDBOX` and `PRODUCTION`.
|
|
161
|
+
- `SignMethod`: Enum for signature methods.
|
|
162
|
+
- `TransactionState`: Enum for transaction states.
|
|
163
|
+
|
|
164
|
+
### Error Classes
|
|
165
|
+
|
|
166
|
+
- `BMLConnectError`
|
|
167
|
+
- `AuthenticationError`
|
|
168
|
+
- `ValidationError`
|
|
169
|
+
- `NotFoundError`
|
|
170
|
+
- `ServerError`
|
|
171
|
+
- `RateLimitError`
|
|
172
|
+
|
|
173
|
+
### Utilities
|
|
174
|
+
|
|
175
|
+
- `SignatureUtils.generate_signature(data, api_key, method)`
|
|
176
|
+
- `SignatureUtils.verify_signature(data, signature, api_key, method)`
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Development
|
|
181
|
+
|
|
182
|
+
### Requirements
|
|
183
|
+
|
|
184
|
+
- Python 3.7+
|
|
185
|
+
- See `requirements.txt` and `requirements-dev.txt` for dependencies.
|
|
186
|
+
|
|
187
|
+
### Testing
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
pip install -e .[dev]
|
|
191
|
+
pytest
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Formatting & Linting
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
black .
|
|
198
|
+
flake8 .
|
|
199
|
+
mypy .
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## Packaging
|
|
205
|
+
|
|
206
|
+
- Uses `pyproject.toml` for build configuration.
|
|
207
|
+
- Source code is in `src/bml_connect/`.
|
|
208
|
+
- Examples in `examples/`.
|
|
209
|
+
- Tests in `tests/`.
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## License
|
|
214
|
+
|
|
215
|
+
MIT License. See `LICENSE` for details.
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Links
|
|
220
|
+
|
|
221
|
+
- [Homepage](https://github.com/bankofmaldives/bml-connect-python)
|
|
222
|
+
- [Documentation](https://bml-connect-python.readthedocs.io)
|
|
223
|
+
- [API Reference](docs/api_reference.md)
|
|
224
|
+
- [Changelog](https://github.com/bankofmaldives/bml-connect-python/releases)
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## Contributing
|
|
229
|
+
|
|
230
|
+
Pull requests and issues are welcome! See the documentation for guidelines.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Basic Asynchronous Usage Example
|
|
3
|
+
===============================
|
|
4
|
+
|
|
5
|
+
This example shows how to use the BML Connect SDK in asynchronous mode.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
from bml_connect import BMLConnect, Environment
|
|
10
|
+
|
|
11
|
+
async def main():
|
|
12
|
+
# Initialize async client
|
|
13
|
+
client = BMLConnect(
|
|
14
|
+
api_key="your_api_key_here",
|
|
15
|
+
app_id="your_app_id_here",
|
|
16
|
+
environment=Environment.SANDBOX,
|
|
17
|
+
async_mode=True
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
try:
|
|
21
|
+
# Create a transaction
|
|
22
|
+
transaction = await client.transactions.create_transaction({
|
|
23
|
+
"amount": 2000, # 20.00 MVR
|
|
24
|
+
"currency": "MVR",
|
|
25
|
+
"provider": "wechat",
|
|
26
|
+
"redirectUrl": "https://yourstore.com/success"
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
print(f"Created transaction: {transaction.transaction_id}")
|
|
30
|
+
print(f"QR Code URL: {transaction.qr_code.url if transaction.qr_code else 'N/A'}")
|
|
31
|
+
|
|
32
|
+
# Get transaction details
|
|
33
|
+
details = await client.transactions.get_transaction(transaction.transaction_id)
|
|
34
|
+
print(f"Current status: {details.state.value if details.state else 'N/A'}")
|
|
35
|
+
|
|
36
|
+
# List transactions with filters
|
|
37
|
+
print("\nListing confirmed transactions...")
|
|
38
|
+
transactions = await client.transactions.list_transactions(
|
|
39
|
+
page=1,
|
|
40
|
+
per_page=5,
|
|
41
|
+
state="CONFIRMED"
|
|
42
|
+
)
|
|
43
|
+
print(f"Found {transactions.count} confirmed transactions")
|
|
44
|
+
|
|
45
|
+
except Exception as e:
|
|
46
|
+
print(f"Error: {str(e)}")
|
|
47
|
+
finally:
|
|
48
|
+
# Clean up async resources
|
|
49
|
+
await client.aclose()
|
|
50
|
+
|
|
51
|
+
if __name__ == "__main__":
|
|
52
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Basic Synchronous Usage Example
|
|
3
|
+
===============================
|
|
4
|
+
|
|
5
|
+
This example shows how to use the BML Connect SDK in synchronous mode.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from bml_connect import BMLConnect, Environment
|
|
9
|
+
|
|
10
|
+
# Initialize client
|
|
11
|
+
client = BMLConnect(
|
|
12
|
+
api_key="your_api_key_here",
|
|
13
|
+
app_id="your_app_id_here",
|
|
14
|
+
environment=Environment.SANDBOX
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
try:
|
|
18
|
+
# Create a transaction
|
|
19
|
+
transaction = client.transactions.create_transaction({
|
|
20
|
+
"amount": 1500, # 15.00 MVR
|
|
21
|
+
"currency": "MVR",
|
|
22
|
+
"provider": "alipay",
|
|
23
|
+
"redirectUrl": "https://yourstore.com/success",
|
|
24
|
+
"localId": "order_12345",
|
|
25
|
+
"customerReference": "Customer #789"
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
print("Transaction created successfully!")
|
|
29
|
+
print(f"ID: {transaction.transaction_id}")
|
|
30
|
+
print(f"Amount: {transaction.amount / 100:.2f} {transaction.currency}")
|
|
31
|
+
print(f"Status: {transaction.state.value if transaction.state else 'N/A'}")
|
|
32
|
+
print(f"Payment URL: {transaction.url}")
|
|
33
|
+
|
|
34
|
+
# Retrieve the same transaction
|
|
35
|
+
print("\nFetching transaction details...")
|
|
36
|
+
fetched = client.transactions.get_transaction(transaction.transaction_id)
|
|
37
|
+
print(f"Fetched status: {fetched.state.value if fetched.state else 'N/A'}")
|
|
38
|
+
|
|
39
|
+
# List recent transactions
|
|
40
|
+
print("\nListing recent transactions...")
|
|
41
|
+
transactions = client.transactions.list_transactions(page=1, per_page=3)
|
|
42
|
+
print(f"Found {transactions.count} transactions (showing first 3):")
|
|
43
|
+
for txn in transactions.items:
|
|
44
|
+
print(f"- {txn.transaction_id}: {txn.amount/100:.2f} {txn.currency} ({txn.state.value})")
|
|
45
|
+
|
|
46
|
+
except Exception as e:
|
|
47
|
+
print(f"Error: {str(e)}")
|
|
48
|
+
finally:
|
|
49
|
+
# Clean up resources
|
|
50
|
+
client.close()
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""
|
|
2
|
+
FastAPI Webhook Handler Example
|
|
3
|
+
===============================
|
|
4
|
+
|
|
5
|
+
This example shows how to handle BML Connect webhooks with FastAPI.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from fastapi import FastAPI, Request, HTTPException
|
|
9
|
+
from bml_connect import BMLConnect
|
|
10
|
+
import uvicorn
|
|
11
|
+
|
|
12
|
+
app = FastAPI(title="BML Connect Webhook Handler")
|
|
13
|
+
client = BMLConnect(api_key="your_api_key_here", app_id="your_app_id_here")
|
|
14
|
+
|
|
15
|
+
@app.post("/webhook")
|
|
16
|
+
async def handle_webhook(request: Request):
|
|
17
|
+
try:
|
|
18
|
+
payload = await request.json()
|
|
19
|
+
signature = payload.get("signature")
|
|
20
|
+
|
|
21
|
+
if not signature:
|
|
22
|
+
raise HTTPException(400, "Missing signature header")
|
|
23
|
+
|
|
24
|
+
if client.verify_webhook_signature(payload, signature):
|
|
25
|
+
# Process verified webhook
|
|
26
|
+
transaction_id = payload.get("transactionId")
|
|
27
|
+
status = payload.get("status")
|
|
28
|
+
amount = payload.get("amount", 0) / 100
|
|
29
|
+
currency = payload.get("currency", "MVR")
|
|
30
|
+
|
|
31
|
+
print(f"Received webhook for transaction {transaction_id}")
|
|
32
|
+
print(f"Status: {status}, Amount: {amount:.2f} {currency}")
|
|
33
|
+
|
|
34
|
+
# Add your business logic here (update database, send notification, etc.)
|
|
35
|
+
|
|
36
|
+
return {"status": "success"}
|
|
37
|
+
else:
|
|
38
|
+
raise HTTPException(403, "Invalid signature")
|
|
39
|
+
except Exception as e:
|
|
40
|
+
raise HTTPException(400, str(e))
|
|
41
|
+
|
|
42
|
+
if __name__ == "__main__":
|
|
43
|
+
uvicorn.run(app, host="0.0.0.0", port=8000)
|