Async Python SDK for the WATA payment system
Project description
aiowata
Async Python SDK for the WATA payment system.
Built on top of aiohttp, provides full coverage of the WATA H2H Payment API: payment links, transactions, refunds, and webhook verification.
Features
- Async/await — built on
aiohttp, uses a single reusable session per client - Payment links — create, retrieve, and search one-time or multi-use payment links
- Transactions — retrieve and search payment transactions
- Refunds — full or partial refund for paid transactions
- Webhook verification — RSA-SHA512 signature validation
- Sandbox support — switch between production and sandbox with a single flag
- Typed — full type annotations,
py.typedmarker for PEP 561
Installation
pip install aiowata
Quick Start
import asyncio
from aiowata import WataClient, Currency
async def main():
async with WataClient(token="your-access-token") as client:
link = await client.create_link(
amount=1500.00,
currency=Currency.RUB,
order_id="order-001",
description="Premium subscription",
)
print(f"Payment URL: {link.url}")
asyncio.run(main())
Usage
Payment Links
from aiowata import WataClient, Currency, LinkType, LinkStatus
async with WataClient(token="...") as client:
# One-time link (default)
link = await client.create_link(
amount=1000.00,
currency=Currency.RUB,
order_id="order-123",
)
# Multi-use link with custom expiry
link = await client.create_link(
amount=500.00,
currency=Currency.RUB,
type=LinkType.MANY_TIME,
expiration_date_time="2025-12-31T23:59:59Z",
)
# Retrieve link by ID
link = await client.get_link("3fa85f64-5717-4562-b3fc-2c963f66afa6")
# Search links
result = await client.search_links(
currencies=[Currency.RUB],
statuses=[LinkStatus.OPENED],
amount_from=100,
max_result_count=50,
)
for item in result.items:
print(item.id, item.amount, item.status)
print(f"Total: {result.total_count}")
Transactions
from aiowata import WataClient, TransactionStatus
async with WataClient(token="...") as client:
# Get transaction by ID
tx = await client.get_transaction("3a16a4f0-27b0-09d1-16da-ba8d5c63eae3")
print(tx.status, tx.amount)
# Search paid transactions
result = await client.search_transactions(
statuses=[TransactionStatus.PAID],
amount_from=500,
max_result_count=100,
)
for tx in result.items:
print(tx.id, tx.amount, tx.currency)
Refunds
async with WataClient(token="...") as client:
refund = await client.create_refund(
original_transaction_id="3a16a4f0-27b0-09d1-16da-ba8d5c63eae3",
amount=500.00,
)
print(refund.transaction_id, refund.transaction_status)
The final refund status (Paid / Declined) is delivered asynchronously via webhook.
Webhook Verification
from aiowata import WataClient, verify_webhook_signature, parse_webhook
# Fetch the public key once and cache it
async with WataClient(token="...") as client:
public_key = await client.get_public_key()
# Inside your webhook handler (e.g. aiohttp / FastAPI / Django)
async def handle_webhook(request):
body: bytes = await request.read()
signature: str = request.headers["X-Signature"]
if not verify_webhook_signature(body, signature, public_key):
return web.Response(status=400)
payload = parse_webhook(body)
print(payload.transaction_status, payload.amount, payload.order_id)
# IMPORTANT: return 200 so WATA does not retry
return web.Response(status=200)
Sandbox
Pass sandbox=True to use the WATA test environment:
client = WataClient(token="sandbox-token", sandbox=True)
Test cards (sandbox only):
| Type | Number | Result |
|---|---|---|
| MIR 3DS | 2200 0000 0000 0004 |
Success |
| MIR no 3DS | 2200 0000 2222 2222 |
Success |
| VISA 3DS | 4242 4242 4242 4242 |
Success |
| VISA no 3DS | 4000 0000 0000 3055 |
Success |
| VISA 3DS | 4012 8888 8888 1881 |
Declined |
| VISA no 3DS | 4111 1111 1111 1111 |
Declined |
Error Handling
All API errors inherit from WataError:
from aiowata import (
WataError,
WataValidationError,
WataAuthError,
WataRateLimitError,
)
try:
link = await client.create_link(amount=-1, currency="RUB")
except WataValidationError as e:
print(f"Validation: {e}")
for err in e.validation_errors:
print(f" {err.members}: {err.message}")
except WataAuthError:
print("Invalid or expired token")
except WataRateLimitError as e:
print(f"Rate limited, retry after {e.retry_after}s")
except WataError as e:
print(f"API error: {e}")
Exception hierarchy:
WataError
└── WataAPIError
├── WataValidationError (400)
├── WataAuthError (401)
├── WataForbiddenError (403)
├── WataRateLimitError (429)
└── WataServerError (5xx)
Custom Session
You can pass your own aiohttp.ClientSession for connection pooling or proxy configuration:
import aiohttp
async with aiohttp.ClientSession() as session:
client = WataClient(token="...", session=session)
link = await client.create_link(amount=100, currency="RUB")
# session lifecycle is managed by you
License
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 aiowata-0.1.0.tar.gz.
File metadata
- Download URL: aiowata-0.1.0.tar.gz
- Upload date:
- Size: 11.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
53aad1da75e09a815192b788d2fc03b3da26d33a4c6457d67e7901fb1da546ea
|
|
| MD5 |
c056e2bba6ad951f545f8eb98ac79631
|
|
| BLAKE2b-256 |
8ec83f6b9e2d75b12b5f2546f6acaec29fd96ee1f5d179f938497cedac1583d0
|
File details
Details for the file aiowata-0.1.0-py3-none-any.whl.
File metadata
- Download URL: aiowata-0.1.0-py3-none-any.whl
- Upload date:
- Size: 14.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2922c87ce8e7ec3b8d8a04dbc7909b7a8cd628eedd834417e6d4e48888a1107d
|
|
| MD5 |
f55c678b0c767dc3446dc7dc4edccb51
|
|
| BLAKE2b-256 |
bfba0f967cc4c5b0159e74df9987b5d1aebe690fb63c47085ed60b1c20ae556e
|