lavepay
Асинхронный клиент к Lave Pay. Счета, выплаты, вебхуки. Если вебхук поднимать негде, статусы можно опрашивать самим.
pip install lavepay
Дополнительно, если нужно:
pip install "lavepay[fastapi]" # приём вебхука
pip install "lavepay[aiogram]" # кнопка оплаты в боте
pip install "lavepay[aiohttp]" # тот же вебхук на aiohttp
pip install "lavepay[qr]" # PNG со ссылкой
Ключ берётся при создании мерчанта, один раз. Переменная LAVEPAY_API_KEY, адрес по умолчанию https://api.laveshka.io/merchant/v1.
Суммы передавайте Decimal или строкой. float библиотека не примет.
Счёт
import asyncio
from decimal import Decimal
from lavepay import LavePay
async def main():
async with LavePay("LAVE-12-...") as lp:
invoice = await lp.invoices.create(
Decimal("12.34"),
order_id="ord-1042",
description="Набор стикеров",
)
print(invoice.pay_url)
asyncio.run(main())
Повтор с тем же order_id и той же суммой вернёт тот же счёт. Для выплат ключ свой, и его лучше собирать из заказа, а не случайно:
from lavepay import idempotency_key
key = idempotency_key("bonus", user_id, "2026-08-22")
await lp.transfers.create("@durov", "5.00", idempotency_key=key)
Перед тем как отдать товар, сверьте не только статус:
if invoice.matches(amount="12.34", order_id="ord-1042"):
...
Поллинг
Один обработчик и для вебхука, и для опроса. Схема как в aiosend: повесили функцию, поставили объект в очередь, запустили цикл.
from lavepay import LavePay
lp = LavePay("LAVE-12-...")
@lp.invoice_paid()
async def paid(invoice, message):
if invoice.matches(order_id=message.text):
await message.answer("оплачено")
async def checkout(message):
invoice = await lp.invoices.create("12.34", order_id=str(message.message_id))
invoice.poll(message=message)
# await lp.start_polling()
# lp.stop_polling()
discover=True сам смотрит открытые счета и выплаты, без poll() на каждый. Первый круг только запоминает, что уже висит, и старые оплаты заново не присылает.
await lp.start_polling(discover=True)
По умолчанию объект спрашивается, пока API само его не закроет. Свой предел — PollingConfig(timeout=3600), по нему придёт invoice_timeout. Частота запросов ограничена (max_rps=2), у ключа лимит 300 в минуту.
Один и тот же статус второй раз обработчику не отдаётся. Память процессная: после рестарта событие может прийти снова, matches от этого и нужен.
FastAPI
На ответ вебхуку есть 10 секунд, поэтому маршрут отвечает 200 сразу, а обработчики идут следом. Подпись считается по сырому телу. Если сначала вызвать request.json(), она не сойдётся.
from fastapi import FastAPI
from lavepay import LavePay
from lavepay.integrations.fastapi import install
app = FastAPI()
lp = LavePay("LAVE-12-...", webhook_secret="whsec_...")
@lp.invoice_paid()
async def paid(invoice):
...
install(app, lp) # POST /lavepay
Клиент ляжет в app.state.lavepay и закроется вместе с приложением.
aiogram
Это не Telegram Payments и не sendInvoice. Покупателю уходит ссылка в мини-приложение.
import asyncio
from aiogram import Bot, Dispatcher
from lavepay import LavePay
from lavepay.integrations.aiogram import install, pay_keyboard
lp = LavePay("LAVE-12-...")
bot = Bot("123:ABC")
dp = Dispatcher()
install(dp, lp) # поллинг стартует и гаснет вместе с ботом
@dp.message()
async def pay(message):
invoice = await lp.invoices.create("12.34", order_id=str(message.message_id))
invoice.poll(message=message)
await message.answer(
"К оплате",
reply_markup=pay_keyboard(invoice, check="Проверить оплату"),
)
@lp.invoice_paid()
async def paid(invoice, message):
await message.answer("оплачено")
«Проверить оплату» только говорит, ждёт счёт или уже оплачен. Товар по этой кнопке не выдавайте: его выдаёт invoice_paid, иначе заказ уйдёт дважды.
Если бот и так принимает апдейты вебхуком на aiohttp, туда же можно повесить Lave Pay, тогда опрос не нужен:
install(dp, lp, aiohttp_app=app)
Ошибки
Код исключения совпадает с error.code в ответе: InsufficientFundsError, RateChangedError, QuoteExpiredError и остальные. explain(error) — короткая фраза, её можно показать человеку. Текст сервера туда не попадает.
Выкладка
Собрать локально:
python -m build
На PyPI пакет уезжает тегом v0.1.0. Цифра должна совпасть с lavepay.__version__, иначе CI остановится до публикации. Workflow ci.yml гоняет тесты, собирает пакет и отдаёт его PyPI через trusted publisher, без токенов. На PyPI он указывается один раз: этот репозиторий, workflow ci.yml, окружение pypi.
Лицензия
MIT.
Metadata
Release files for lavepay 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| lavepay-0.1.0.tar.gz | 32.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| lavepay-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 68.5 kB
Release files / lavepay-0.1.0.tar.gz
| Download URL | lavepay-0.1.0.tar.gz |
|---|---|
| Size | 32.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
594ef7c80a952a866c50e1d7414c7526ad2c706144961551082710046729d6c5
|
|
BLAKE2b-256 checksum How to use checksums |
48e4099ccf07fb6d60645f3e3dc2ed136515736f745d32f14fb5770184f9ffdb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 7, 2026.
Transparency logRelease files / lavepay-0.1.0-py3-none-any.whl
| Download URL | lavepay-0.1.0-py3-none-any.whl |
|---|---|
| Size | 35.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5fe72f170b4e39d198928783426f2f994be5f4b15b5ef81541c169954cd8c3eb
|
|
BLAKE2b-256 checksum How to use checksums |
66e4ac7b9822a52e626df623730c2ad8011283ed3449da2ace7fa31c5fdf9535
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 7, 2026.
Transparency log