Webhooks
Recommended Structure
We recommend keeping your webhook handlers thin by moving all business logic into a dedicated service class — PaymeService. This keeps your handlers clean and your logic testable.
PaymeService
# services/payme.py
from aiopayme.exceptions import Errors
from aiopayme.utils import time_to_payme
from aiopayme.types import (
CheckPerformTransactionCtx,
CreateTransactionCtx,
PerformTransactionCtx,
CancelTransactionCtx,
CheckTransactionCtx,
GetStatementCtx,
)
class PaymeService:
def __init__(self, db: AsyncSession):
self.db = db
async def check_perform(self, ctx: CheckPerformTransactionCtx):
order = await self.get_order(ctx.account.order_id)
if not order:
raise Errors.invalid_account()
if order.amount * 100 != ctx.amount:
raise Errors.invalid_amount()
return ctx.ok(allow=True)
async def create_transaction(self, ctx: CreateTransactionCtx):
tx = await self.get_transaction(ctx.payme_id)
order = await self.get_order(ctx.account.order_id)
if not order:
raise Errors.invalid_account()
if order.amount * 100 != ctx.amount:
raise Errors.invalid_amount()
if tx:
if tx.state == -1:
raise Errors.unable_to_perform()
return ctx.ok(transaction_id=tx.payme_id, create_time=tx.create_time)
if order.status == OrderStatus.PAID:
raise Errors.invalid_account()
existing_tx = await self.get_active_transaction(order.id)
if existing_tx:
rejected = PaymeTransaction(
payme_id=ctx.payme_id,
order_id=order.id,
amount=ctx.amount,
create_time=ctx.time,
state=-1,
cancel_time=time_to_payme(),
reason=3,
)
self.db.add(rejected)
await self.db.commit()
raise Errors.unable_to_perform()
tx = PaymeTransaction(
payme_id=ctx.payme_id,
order_id=order.id,
amount=ctx.amount,
create_time=ctx.time,
state=1,
)
self.db.add(tx)
await self.db.commit()
return ctx.ok(transaction_id=tx.payme_id, create_time=tx.create_time)
async def perform_transaction(self, ctx: PerformTransactionCtx):
tx = await self.get_transaction(ctx.transaction_id)
if not tx:
raise Errors.transaction_not_found()
if tx.state == 2:
return ctx.ok(transaction_id=tx.payme_id, perform_time=tx.perform_time, state=2)
tx.state = 2
tx.perform_time = time_to_payme()
await self.db.commit()
return ctx.ok(transaction_id=tx.payme_id, perform_time=tx.perform_time, state=2)
async def cancel_transaction(self, ctx: CancelTransactionCtx):
tx = await self.get_transaction(ctx.transaction_id)
if not tx:
raise Errors.transaction_not_found()
if tx.state in (-1, -2):
return ctx.ok(
transaction=tx.payme_id,
cancel_time=tx.cancel_time,
state=tx.state,
reason=tx.reason,
)
tx.state = -2 if tx.state == 2 else -1
tx.cancel_time = time_to_payme()
tx.reason = ctx.reason
await self.db.commit()
return ctx.ok(
transaction=tx.payme_id,
state=tx.state,
cancel_time=tx.cancel_time,
reason=tx.reason,
)
async def check_transaction(self, ctx: CheckTransactionCtx):
tx = await self.get_transaction(ctx.transaction_id)
if not tx:
raise Errors.transaction_not_found()
return ctx.ok(
state=tx.state,
create_time=tx.create_time,
perform_time=tx.perform_time,
cancel_time=tx.cancel_time,
reason=tx.reason,
)
async def get_statement(self, ctx: GetStatementCtx):
from_time = ctx.from_time
to_time = ctx.to_time
if from_time > to_time:
from_time, to_time = to_time, from_time
txs = await self.get_transactions(from_time, to_time)
return ctx.ok(transactions=[
{
"id": tx.payme_id,
"time": tx.create_time,
"amount": tx.amount,
"account": {"order_id": tx.order_id},
"state": tx.state,
"create_time": tx.create_time,
"perform_time": tx.perform_time or 0,
"cancel_time": tx.cancel_time or 0,
"reason": tx.reason,
}
for tx in txs
])
Handlers
# handlers/payme.py
from aiopayme import Router
from aiopayme.types import *
from sqlalchemy.ext.asyncio import AsyncSession
from app.services.payme import PaymeService
router = Router()
@router.check_perform_transaction()
async def check_perform(ctx: CheckPerformTransactionCtx, db: AsyncSession):
return await PaymeService(db).check_perform(ctx)
@router.create_transaction()
async def create_transaction(ctx: CreateTransactionCtx, db: AsyncSession):
return await PaymeService(db).create_transaction(ctx)
@router.perform_transaction()
async def perform_transaction(ctx: PerformTransactionCtx, db: AsyncSession):
return await PaymeService(db).perform_transaction(ctx)
@router.cancel_transaction()
async def cancel_transaction(ctx: CancelTransactionCtx, db: AsyncSession):
return await PaymeService(db).cancel_transaction(ctx)
@router.check_transaction()
async def check_transaction(ctx: CheckTransactionCtx, db: AsyncSession):
return await PaymeService(db).check_transaction(ctx)
@router.get_statement()
async def get_statement(ctx: GetStatementCtx, db: AsyncSession):
return await PaymeService(db).get_statement(ctx)