Python SDK for Bank of Maldives Connect API with synchronous and asynchronous support
Project description
BML Connect Python SDK
Python SDK for Bank of Maldives Connect API with synchronous and asynchronous support.
Compatible with all Python frameworks including Django, Flask, FastAPI, and Sanic.
Features
- ๐ Sync/Async Support: Choose your preferred programming style
- ๐ฏ Full API Coverage: Create, retrieve, cancel transactions; webhook signature verification
- ๐ Type Annotations: Full type hint support for better development experience
- ๐ก๏ธ Error Handling: Comprehensive error hierarchy for easy debugging
- ๐ Framework Agnostic: Works with any Python web framework
- ๐ Context Manager Support: Automatic resource cleanup with
with/async with - ๐ MIT Licensed: Open source and free to use
Installation
pip install bml-connect-python
Quick Start
Synchronous Usage
from bml_connect import BMLConnect, Environment
# Use as a context manager for automatic cleanup
with BMLConnect(
api_key="your_api_key",
app_id="your_app_id",
environment=Environment.SANDBOX
) as client:
transaction = client.transactions.create_transaction({
"amount": 1500, # 15.00 MVR
"currency": "MVR",
"provider": "alipay",
"redirectUrl": "https://yourstore.com/success",
"localId": "order_123",
"customerReference": "Customer #456"
})
print(f"Transaction ID: {transaction.transaction_id}")
print(f"Payment URL: {transaction.url}")
Asynchronous Usage
import asyncio
from bml_connect import BMLConnect, Environment
async def main():
async with BMLConnect(
api_key="your_api_key",
app_id="your_app_id",
environment=Environment.SANDBOX,
async_mode=True
) as client:
transaction = await client.transactions.create_transaction({
"amount": 2000,
"currency": "MVR",
"provider": "wechat",
"redirectUrl": "https://yourstore.com/success"
})
print(f"Transaction ID: {transaction.transaction_id}")
asyncio.run(main())
Framework Integration Examples
Flask Integration
from flask import Flask, request, jsonify
from bml_connect import BMLConnect
app = Flask(__name__)
client = BMLConnect(api_key="your_api_key", app_id="your_app_id")
@app.route('/webhook', methods=['POST'])
def webhook():
payload = request.get_json()
signature = payload.get('signature')
if client.verify_webhook_signature(payload, signature):
# Process webhook
return jsonify({"status": "success"}), 200
else:
return jsonify({"error": "Invalid signature"}), 403
FastAPI Integration
from fastapi import FastAPI, Request, HTTPException
from bml_connect import BMLConnect
app = FastAPI()
client = BMLConnect(api_key="your_api_key", app_id="your_app_id")
@app.post("/webhook")
async def handle_webhook(request: Request):
payload = await request.json()
signature = payload.get("signature")
if client.verify_webhook_signature(payload, signature):
return {"status": "success"}
else:
raise HTTPException(403, "Invalid signature")
Sanic Integration
from sanic import Sanic, response
from bml_connect import BMLConnect
app = Sanic("BMLWebhook")
client = BMLConnect(api_key="your_api_key", app_id="your_app_id")
@app.post('/webhook')
async def webhook(request):
payload = request.json
signature = payload.get('signature')
if client.verify_webhook_signature(payload, signature):
return response.json({"status": "success"})
else:
return response.json({"error": "Invalid signature"}, status=403)
API Reference
BMLConnect(api_key, app_id, environment, async_mode, timeout)
Main entry point for the SDK.
| Parameter | Type | Default | Description |
|---|---|---|---|
api_key |
str |
required | Your API key from the BML merchant portal |
app_id |
str |
required | Your application ID from the BML merchant portal |
environment |
Environment or str |
PRODUCTION |
Environment.SANDBOX or Environment.PRODUCTION |
async_mode |
bool |
False |
Set True to use async methods |
timeout |
int |
30 |
Request timeout in seconds |
Transaction Methods
| Method | Sync | Async | Description |
|---|---|---|---|
create_transaction(data) |
โ | โ | Create a new payment transaction |
get_transaction(id) |
โ | โ | Retrieve a transaction by ID |
cancel_transaction(id) |
โ | โ | Cancel a transaction by ID |
list_transactions(...) |
โ | โ | List transactions with optional filters |
list_transactions Parameters
client.transactions.list_transactions(
page=1,
per_page=20,
state="CONFIRMED", # Filter by TransactionState value
provider="alipay", # Filter by provider
start_date="2026-01-01", # Filter from date
end_date="2026-02-01", # Filter to date
)
Core Classes
BMLConnect: Main entry point for the SDKTransaction: Typed transaction object returned by all transaction methodsQRCode: QR code details attached to a transactionPaginatedResponse: Wraps paginated transaction list resultsEnvironment:SANDBOXorPRODUCTIONSignMethod:SHA1(default) orMD5TransactionState:CREATED,QR_CODE_GENERATED,CONFIRMED,CANCELLED,FAILED,EXPIRED,REFUND_REQUESTED,REFUNDED
Exception Hierarchy
BMLConnectError
โโโ AuthenticationError (401)
โโโ ValidationError (400)
โโโ NotFoundError (404)
โโโ ServerError (5xx)
โโโ RateLimitError (429)
Signature Utilities
from bml_connect import SignatureUtils
# Generate a signature manually
signature = SignatureUtils.generate_signature(data, api_key, method)
# Verify a signature with constant-time comparison
is_valid = SignatureUtils.verify_signature(data, signature, api_key, method)
Advanced Usage
Transaction Management
with BMLConnect(api_key="your_api_key", app_id="your_app_id") as client:
# Create
transaction = client.transactions.create_transaction({
"amount": 5000,
"currency": "MVR",
"provider": "alipay",
"redirectUrl": "https://yourstore.com/success",
"localId": "order_456"
})
# Retrieve
details = client.transactions.get_transaction(transaction.transaction_id)
print(f"State: {details.state}")
# Cancel
cancelled = client.transactions.cancel_transaction(transaction.transaction_id)
# List with filters
results = client.transactions.list_transactions(
page=1,
per_page=10,
state="CONFIRMED"
)
for t in results.items:
print(t.transaction_id, t.amount, t.state)
Webhook Handling
@app.route('/webhook', methods=['POST'])
def handle_webhook():
payload = request.get_json()
if not client.verify_webhook_signature(payload, payload.get('signature')):
return {"error": "Invalid signature"}, 403
state = payload.get('state')
if state == 'CONFIRMED':
pass # fulfil the order
elif state == 'REFUND_REQUESTED':
pass # initiate refund flow
elif state == 'REFUNDED':
pass # mark order as refunded
return {"status": "success"}
Custom Timeout
# For slow network environments or large payloads
client = BMLConnect(
api_key="your_api_key",
app_id="your_app_id",
timeout=60
)
Requirements
- Python 3.9+
requestsaiohttp
Development
Setup
git clone https://github.com/quillfires/bml-connect-python.git
cd bml-connect-python
pip install -e .[dev]
Running Tests
pytest
Code Quality
black .
flake8 .
mypy .
Project Structure
bml-connect-python/
โโโ src/bml_connect/ # Source code
โโโ tests/ # Test files
โโโ examples/ # Usage examples
โโโ pyproject.toml # Build configuration
โโโ requirements.txt # Runtime dependencies
โโโ requirements-dev.txt # Development dependencies
โโโ README.md # This file
Contributing
Contributions are welcome. Please see CONTRIBUTING.md for details.
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
License
MIT License โ see LICENSE for details.
Support
- ๐ Documentation
- ๐ Issue Tracker
- ๐ฌ Discussions
Changelog
See CHANGELOG.md for a full history of changes.
Security
If you discover a security issue, please email fayaz.quill@gmail.com instead of opening a public issue.
Made with โค๏ธ for the Maldivian developer community
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file bml_connect_python-1.2.0.tar.gz.
File metadata
- Download URL: bml_connect_python-1.2.0.tar.gz
- Upload date:
- Size: 13.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cae3f35930535d7cab0dba8d438957de7ec23578ec1c87b651f66329dbaf818f
|
|
| MD5 |
1c0af5d6ec8eb4806651506a5fc8daea
|
|
| BLAKE2b-256 |
cfc8d3b75191de78c3fb53b7ce294cda817033478b93ab8c6c81f5f2431c6b28
|
Provenance
The following attestation bundles were made for bml_connect_python-1.2.0.tar.gz:
Publisher:
release.yml on quillfires/bml-connect-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
bml_connect_python-1.2.0.tar.gz -
Subject digest:
cae3f35930535d7cab0dba8d438957de7ec23578ec1c87b651f66329dbaf818f - Sigstore transparency entry: 1001475191
- Sigstore integration time:
-
Permalink:
quillfires/bml-connect-python@e9dad7080be1a33e6d193dd4c56d0a6521c8c40a -
Branch / Tag:
refs/tags/v1.2.0 - Owner: https://github.com/quillfires
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e9dad7080be1a33e6d193dd4c56d0a6521c8c40a -
Trigger Event:
push
-
Statement type:
File details
Details for the file bml_connect_python-1.2.0-py3-none-any.whl.
File metadata
- Download URL: bml_connect_python-1.2.0-py3-none-any.whl
- Upload date:
- Size: 11.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c1e6d2dc3a6aca3f61df6fa4de377ccb7a61755ae7087167121c83b07af5fde3
|
|
| MD5 |
bb72a268b04c23e7f43a4577d613a34d
|
|
| BLAKE2b-256 |
e45aec20e26322d077668cef842e2428f407ee676f01c57f8fe50610c46a20ad
|
Provenance
The following attestation bundles were made for bml_connect_python-1.2.0-py3-none-any.whl:
Publisher:
release.yml on quillfires/bml-connect-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
bml_connect_python-1.2.0-py3-none-any.whl -
Subject digest:
c1e6d2dc3a6aca3f61df6fa4de377ccb7a61755ae7087167121c83b07af5fde3 - Sigstore transparency entry: 1001475218
- Sigstore integration time:
-
Permalink:
quillfires/bml-connect-python@e9dad7080be1a33e6d193dd4c56d0a6521c8c40a -
Branch / Tag:
refs/tags/v1.2.0 - Owner: https://github.com/quillfires
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e9dad7080be1a33e6d193dd4c56d0a6521c8c40a -
Trigger Event:
push
-
Statement type: