- 后端
- 即时通讯
- API设计
【免费下载链接】aiogram
aiogram is a modern and fully asynchronous framework for Telegram Bot API written in Python using asyncio
导读
预结算查询(Pre-Checkout Query)是 Telegram Bot API 支付流程中,用户在确认支付与配送信息之后、真正扣款之前发送给机器人的最后一个校验节点。本文基于 aiogram 官方文档 pre_checkout_query.rst 的核心内容,围绕PreCheckoutQueryHandler这一基于类的处理器展开:先说明其注册方式与继承自BaseHandler的扩展能力,再结合仓库源码与测试用例,深入讲解事件对象PreCheckoutQuery的字段结构、应答方法answer()以及与之配套的函数式写法,帮助你在机器人支付链路中可靠地完成库存校验与订单确认。
一、什么是 PreCheckoutQueryHandler
PreCheckoutQueryHandler是 aiogram 为预结算查询事件提供的基于类的处理器基类,定义于 aiogram/handlers/pre_checkout_query.py:
from abc import ABC from aiogram.handlers import BaseHandler from aiogram.types import PreCheckoutQuery, User class PreCheckoutQueryHandler(BaseHandler[PreCheckoutQuery], ABC): """ Base class for pre-checkout handlers """ @property def from_user(self) -> User: return self.event.from_user从源码结构可以看到三个关键信息:
- 它是一个泛型子类,泛型参数为
PreCheckoutQuery,因此self.event被类型标注为PreCheckoutQuery对象; - 它被声明为
ABC(抽象基类),继承者必须实现async def handle()方法才能实例化使用; - 它通过
from_user属性对self.event.from_user做了别名,这是文档中提到的“扩展”(Extension)之一。
该处理器与 ShippingQueryHandler 的实现模式完全一致(ShippingQueryHandler同样只增加了from_user别名),可见这是一组为支付类事件统一设计的快捷属性。
二、简单用法:三步注册一个预结算处理器
官方文档给出了最精简的用法:
from aiogram.handlers import PreCheckoutQueryHandler ... @router.pre_checkout_query() class MyHandler(PreCheckoutQueryHandler): async def handle(self) -> Any: ...这里@router.pre_checkout_query()是类装饰器用法。结合源码 aiogram/dispatcher/router.py 可以看到,Router在初始化时便创建了名为pre_checkout_query的TelegramEventObserver:
self.pre_checkout_query = TelegramEventObserver( router=self, event_name="pre_checkout_query", )因此router.pre_checkout_query直接对应 Bot API 更新中的pre_checkout_query事件字段。当被装饰的类实例通过__await__协议把执行委托给handle()方法(见 base.py),事件分发引擎便可以在过滤器全部通过后直接await这个类实例。
实际运行时,TelegramEventObserver.trigger()会对每个已注册 handler 依次执行过滤器检查、包裹中间件并调用处理逻辑(见 telegram.py),命中后立即返回、停止继续传播。
三、Extension:继承 BaseHandler 后自带的能力
官方文档明确指出:PreCheckoutQueryHandler是BaseHandler的子类,并带有扩展属性self.from_user。这一节把这两点拆开讲透。
3.1 BaseHandler 提供的通用基础设施
BaseHandler定义在 aiogram/handlers/base.py,它是所有基于类处理器的共同基类(详见 class_based_handlers/base.rst),默认构造器会自动接收事件与全部上下文数据,无需覆写:
def __init__(self, event: T, **kwargs: Any) -> None: self.event: T = event self.data: dict[str, Any] = kwargs由此,每个子类实例都会获得以下成员:
self.event:当前分发进来的PreCheckoutQuery事件对象;self.data:携带的上下文字典(包含bot、update等键);self.bot:从上下文中解析出的Bot实例(若不存在则抛出RuntimeError);self.update:当前完整Update对象;async def handle():必须由子类实现的抽象方法,返回值即处理器结果。
测试用例 tests/test_handler/test_base.py 对上述行为逐一做了验证,例如handler.event == event、handler.data["key"] == 42、await handler == 42,以及 bot 缺失时抛出RuntimeError等。
3.2 from_user 属性别名
PreCheckoutQueryHandler扩展的from_user是self.event.from_user的类型化快捷方式,其正确性由仓库测试 tests/test_handler/test_pre_checkout_query.py 直接印证:
class MyHandler(PreCheckoutQueryHandler): async def handle(self) -> Any: assert self.event == event assert self.from_user == self.event.from_user return True assert await MyHandler(event)因此,在处理逻辑中你可以直接用self.from_user拿到发起支付的用户,而不必每次写self.event.from_user。
四、深入事件对象:PreCheckoutQuery 的关键字段
要正确实现支付确认逻辑,需要清楚self.event(即PreCheckoutQuery)携带了哪些数据。其完整字段定义在 aiogram/types/pre_checkout_query.py,核心字段如下:
| 字段 | 类型 | 说明 |
|---|---|---|
id | str | 预结算查询的唯一标识符 |
from_user | User | 发起支付的用户(序列化别名from) |
currency | str | 三位 ISO 4217 货币代码;Telegram Stars 支付时为"XTR" |
total_amount | int | 以货币最小单位表示的订单总价(整数,非浮点)。例如US$ 1.45应传145 |
invoice_payload | str | 机器人下发票时自定义的负载字符串,用于关联订单 |
shipping_option_id | str \| None | 可选,用户选择的配送选项标识符(有配送时才有) |
order_info | OrderInfo \| None | 可选,用户提交的订单信息 |
from_user通过 pydantic 的Field(..., alias="from")映射 Bot API 中的from字段,因此在构造或测试时以from_user为参数名,序列化时会自动转换为 Telegram 要求的from。
五、应答预结算查询:answer() 方法
收到预结算查询后,机器人必须在10 秒内调用AnswerPreCheckoutQuery方法给出应答,否则支付流程会失败。PreCheckoutQuery对象内置了便捷方法answer()(源码见 pre_checkout_query.py),它会自动填充pre_checkout_query_id:
def answer( self, ok: bool, error_message: str | None = None, **kwargs: Any, ) -> AnswerPreCheckoutQuery: return AnswerPreCheckoutQuery( pre_checkout_query_id=self.id, ok=ok, error_message=error_message, **kwargs, ).as_(self._bot)ok=True:一切正常(商品有货等),机器人准备继续处理订单;ok=False:存在任何问题,此时必须提供error_message,Telegram 会将该人类可读的错误原因展示给用户(例如“商品已售罄,请更换颜色或款式”)。
该方法返回的是AnswerPreCheckoutQuery方法对象(定义于 aiogram/methods/answer_pre_checkout_query.py),通过.as_(self._bot)绑定到当前 bot 后即可await执行。仓库测试 tests/test_api/test_types/test_pre_checkout_query.py 验证了answer()生成的AnswerPreCheckoutQuery实例确实携带了pre_checkout_query_id及透传的ok、error_message;而 tests/test_api/test_methods/test_answer_pre_checkout_query.py 则验证了bot.answer_pre_checkout_query(...)底层调用的正确性。
六、完整实践:基于类的支付确认处理器
结合上面所有要点,一个完整的基于类实现的预结算处理器如下:
from typing import Any from aiogram import Router from aiogram.handlers import PreCheckoutQueryHandler router = Router() # 模拟库存,实际项目中应查询数据库 GOODS = {"demo": 1, "another": 0} @router.pre_checkout_query() class PreCheckoutHandler(PreCheckoutQueryHandler): async def handle(self) -> Any: stock = GOODS.get(self.event.invoice_payload, 0) if stock > 0: await self.event.answer(ok=True) # 确认可以扣款 return True # 拒绝支付并给出对用户友好的原因 await self.event.answer(ok=False, error_message="商品已售罄,请稍后再试") return False要点回顾:
- 用
@router.pre_checkout_query()装饰类即可完成注册,类在分发时会被当作可等待对象执行handle(); - 通过
self.event.invoice_payload关联到下单时的业务负载,做库存/订单校验; - 通过
self.event.answer(ok=...)在 10 秒窗口内给出最终确认或拒绝; - 校验失败必须附带
error_message,便于用户理解失败原因。
七、补充:函数式写法的对照
基于类的处理器与 aiogram 的函数式注册是可以并存的两种风格。仓库自带的支付示例 examples/stars_invoice.py 给出了函数式写法作为对照:
@invoices_router.pre_checkout_query(F.invoice_payload == "demo") async def pre_checkout_query(query: PreCheckoutQuery) -> None: # if your product is available for sale, # confirm that you are ready to accept payment if GOODS.get(query.invoice_payload) > 0: await query.answer(ok=True) else: await query.answer(ok=False, error_message="The product is out of stock")该示例还展示了完整支付链路的其他环节:用message.answer_invoice(...)发起发票(货币为"XTR"时即 Telegram Stars 支付)、用F.invoice_payload过滤器只处理特定订单、以及支付成功后通过bot.refund_star_payment(...)退款。无论函数式还是基于类,query.answer()/self.event.answer()的应答语义完全一致。
八、小结
PreCheckoutQueryHandler是 aiogram 为支付类事件提供的基于类处理器基类,使用流程可以概括为:
- 注册:
@router.pre_checkout_query()装饰自定义类; - 继承与扩展:继承
BaseHandler获得event、data、bot、update等基础设施,并额外获得from_user别名(见 pre_checkout_query.py); - 业务校验:读取
PreCheckoutQuery的invoice_payload、total_amount、currency等字段确认订单; - 应答:在 10 秒内调用
self.event.answer(ok=...)确认或拒绝支付。
对于需要复用支付逻辑、组合继承与 mixin 的复杂项目,基于类的写法能显著提升代码的组织性与可测试性;仓库中 tests/test_handler/test_pre_checkout_query.py 的测试模式也可以直接借鉴到自己的测试套件中。
- 后端
- 即时通讯
- API设计
【免费下载链接】aiogram
aiogram is a modern and fully asynchronous framework for Telegram Bot API written in Python using asyncio
相关推荐
aiogram 类处理器之 PollHandler:基于类的轮询(Poll)事件处理完整指南
aiogram 类处理器之 PollHandler:基于类的轮询(Poll)事件处理完整指南 在 aiogram 中,除了函数式处理器( async def 回
后端即时通讯API设计aiogram 基于类的处理器(Class-based Handlers)全面指南:从 BaseHandler 到可复用的事件处理架构
aiogram 基于类的处理器(Class based Handlers)全面指南:从 BaseHandler 到可复用的事件处理架构 在 aiogram 中,
后端即时通讯API设计aiogram 类式错误处理器 ErrorHandler:从装饰器到 handle() 的完整异常处理方案
aiogram 类式错误处理器 ErrorHandler:从装饰器到 handle 的完整异常处理方案 导读 本文围绕 docs/dispatcher/clas
后端即时通讯API设计
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考