Qiwi payments for humans(for healthy humans)
Supports most of qiwi apis: qiwi-maps, bills, wallet
pip install aioqiwi
Library | Description |
---|---|
aiohttp | default http server |
pydantic | schema validation |
However aioqiwi is highly customizable. Example of switching json modules:
pip install orjson
from aioqiwi import Wallet
from aioqiwi.core.tooling import json
wallet = Wallet()
wallet.tools.json_module = json.JSONModule("orjson")
import asyncio
from aioqiwi import Wallet
async def qiwi():
async with Wallet("TOKEN from https://qiwi.com/api") as w:
w.phone_number = '+7878787878' # phone number is not required by default, but some methods need it
balance = await w.balance()
print("ACCOUNTS:")
for acc in balance.accounts:
print(acc.alias, acc.balance)
asyncio.run(qiwi())
aioqiwi provides user-friendly web-hooks handler
import asyncio
from aioqiwi.wallet import WebHook, Wallet
wallet = Wallet("...")
@wallet.hm(lambda event: ...)
async def payments_handler(hook: WebHook):
print(f"{hook.payment.account} sent you {event.payment}")
@wallet.hm()
async def secret_payments_handler(event: WebHook):
await something(event.payment.commission.amount)
wallet.idle(port=8090)
When you do Wallet::idle, aioqiwi adds connector closing to aiohttp.web.Application::on_shutdown to make sure connector closes, however if you want to avoid this behaviour pass close_connector_ate=False to Wallet::idle
Handler manager QiwiClient.handler_manager or qiwi_client.hm is responsible for event-handlers registering and filtering/delivering updates to them. There're currently two event processing strategies: 1. core.handler.EventProcessStrategy.ORDERED - sequential filter-check. has O(n) amplitude 2. core.handler.EventProcessStrategy.MILKSHAKE - as receives update, will shuffle existing handlers list. has O(n) amplitude
Note
Filters results are not currently cached.
Note
Some users don't want mess with web-hooks, for those fellas aioqiwi has history_polling [wip] in aioqiwi.contrib. Different approach for dealing with payment events. Find usage example in examples/ directory.
import asyncio
from aioqiwi import QiwiKassa
async def test_kassa():
async with QiwiKassa("SECRET KEY from p2p.qiwi.com or kassa.qiwi.com") as kassa:
sent_invoice = await kassa.new_bill(14.88, lifetime=44)
# setting lifetime to 44 ahead today [default is 10] 45 - is max
print("Url to pay:", sent_invoice.pay_url)
await kassa.close()
asyncio.run(test_kassa())
sent_invoice.pay_url
will redirect us to something like:
from aioqiwi.kassa import QiwiKassa, Notification
kassa = QiwiKassa('PRIVATE_KEY')
@kassa.hm(lambda bill: bill.bill.amount.currency == 'RUB')
async def my_shiny_rubles_handler(bill_update: Notification):
# do something
pass
kassa.idle()
aioqiwi covers qiwi's MAPS api in aioqiwi.terminals module
QiwiClient.connector is responsible for making http requests. Current available request managers are located in aioqiwi.core.connectors
Default connector is aioqiwi.core.connectors.asyncio, but if it's no suit for you, you can easily switch to another
Example:
from aioqiwi import Wallet
from aioqiwi.core.connectors.aiohttp import AiohttpConnector
wallet = Wallet("auth")
# switch with read-to-use connector-like instance implementing
wallet.connector = AiohttpConnector(timeout, {"user-agent": "opeka/02"})
# or switch with aioqiwi.core.connectors.abstract.Connector compatible class
wallet.connector = AiohttpConnector
You can easily implement your own http client(connector), subclassing from aioqiwi.core.connectors.abstract.AbstractConnector. Take a look at "out of the box" aiohttp or asyncio sessions for the start.
Consider we have a aioqiwi.wallet.Wallet instance with a named reference wallet to it. Known error when we cannot ask server for more than 50 rows in wallet.history. To handle that error, we simply:
from aioqiwi.exceptions import AioqiwiError
from aioqiwi.errors import ErrorInfo
try:
await wallet.history(2 ** 6) # pass rows=64, whilst constraint is 0<rows<51
except AioqiwiError as exc:
if exc.err: # this feature is experimental
exc.err: ErrorInfo = exc.err # cast to aioqiwi.Wallet's error info
print(exc.err.error_message)
This is slight different error and aioqiwi should not be really responsible for it. It's usually server-side error which makes exception that should be raised connector-specific. asyncio.TimeoutError is exception that is produced by asyncio connector. In aiohttp or other connectors it may differ.
aioqiwi's server.BaseWebHookView and requests.Requests support "return policy", it means you can get response/update in the form that suits your needs. There're currently 5 return policies.
- NOTHING - returns nothing(note: None is python's implicit return), :note: returning nothing does not mean doing nothing, validation is done anyway
- READ_DATA - raw return once stream is read
- JSON - raw return once read data was deserialized
- MODEL - complex return once json deserialized and new model instantiated
- LIST_OF_MODELS - complex return once json deserialized as an iterable list with new instantiated models of json objects
You can find examples in examples/
directory in github repository. For start examples above should be enough.
- Tests/CI/CD
- Implement all qiwi wallet API methods
- history_polling needs to be tested
- implement wallet web-hook payment verification
My group