Skip to main content

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.typed marker 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

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

aiowata-0.1.0.tar.gz (11.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

aiowata-0.1.0-py3-none-any.whl (14.2 kB view details)

Uploaded Python 3

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

Hashes for aiowata-0.1.0.tar.gz
Algorithm Hash digest
SHA256 53aad1da75e09a815192b788d2fc03b3da26d33a4c6457d67e7901fb1da546ea
MD5 c056e2bba6ad951f545f8eb98ac79631
BLAKE2b-256 8ec83f6b9e2d75b12b5f2546f6acaec29fd96ee1f5d179f938497cedac1583d0

See more details on using hashes here.

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

Hashes for aiowata-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2922c87ce8e7ec3b8d8a04dbc7909b7a8cd628eedd834417e6d4e48888a1107d
MD5 f55c678b0c767dc3446dc7dc4edccb51
BLAKE2b-256 bfba0f967cc4c5b0159e74df9987b5d1aebe690fb63c47085ed60b1c20ae556e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page