Telegram Bot Payment API 无法处理 pre_checkout_query
支付老李
结论
pre_checkout_query 没有响应,是因为 preCheckoutHandler() 只是普通函数,没有注册为 Telegram 的更新处理器。successful_payment 的处理器也写反了:装饰器放在函数体内,因此没有绑定回调。
Telegram 发出 pre_checkout_query 后,机器人必须及时调用 answer_pre_checkout_query()。如果没有响应,客户端会因等待超时而提示付款处理失败。
正确写法
import telebot
from telebot import types
tb = telebot.TeleBot('BOT_TOKEN')
PRICE = 10000 # 100.00 RUB,金额使用最小货币单位
@tb.message_handler(commands=['pay'])
def pay(message):
prices = [
types.LabeledPrice(label='Test product', amount=PRICE)
]
tb.send_invoice(
chat_id=message.chat.id,
title='Test title',
description='Test description',
invoice_payload='test',
provider_token='xxx:TEST:xxx',
currency='RUB',
prices=prices
)
@tb.pre_checkout_query_handler(func=lambda query: True)
def process_pre_checkout_query(pre_checkout_query):
# 实际项目中应在这里检查 payload、金额和货币类型
if pre_checkout_query.invoice_payload != 'test':
tb.answer_pre_checkout_query(
pre_checkout_query.id,
ok=False,
error_message='Invalid invoice payload.'
)
return
tb.answer_pre_checkout_query(
pre_checkout_query.id,
ok=True
)
@tb.message_handler(content_types=['successful_payment'])
def process_successful_payment(message):
payment = message.successful_payment
tb.send_message(
message.chat.id,
f'Payment completed: {payment.total_amount / 100:.2f} '
f'{payment.currency}'
)
tb.infinity_polling()
问题出在哪里
原代码中的函数:
def preCheckoutHandler(preCheckoutQuery: types.PreCheckoutQuery):
tb.answer_pre_checkout_query(preCheckoutQuery.id, ok=True)
没有通过 @tb.pre_checkout_query_handler(...) 注册。即使函数本身写得没问题,pyTelegramBotAPI 收到 pre_checkout_query 时也不会自动调用它。
successful_payment 的写法也有问题:
def successPayment(message):
tb.message_handler(content_types=['successful_payment'])
message_handler() 用于注册处理器,作为装饰器时应放在函数定义上方:
@tb.message_handler(content_types=['successful_payment'])
def successPayment(message):
pass
付款流程
正常的付款流程如下:
- 用户发送
/pay。 - 机器人调用
send_invoice()发送账单。 - 用户确认付款信息。
- Telegram 向机器人发送
pre_checkout_query。 - 机器人检查订单,然后调用
answer_pre_checkout_query(query.id, ok=True)。 - 付款成功后,Telegram 发送带有
successful_payment的消息。 - 机器人读取
message.successful_payment,更新订单并向用户确认。
第 5 步需要及时完成。机器人收到 pre_checkout_query 后应尽快响应,通常要控制在 10 秒以内。不要在响应之前执行耗时操作。
注意事项
LabeledPrice.amount使用货币的最小单位。对于 RUB,10000表示100.00 RUB,并非10000 RUB。- 收到
successful_payment后不要直接发货。应保存并校验invoice_payload、金额、货币类型以及 Telegram 提供的支付标识,避免订单错配或重复处理。 - 如果订单无法履行,应返回
ok=False,并提供用户能够理解的error_message。 - 确保机器人正在通过 polling 或 webhook 接收更新。否则,即使处理器注册正确,也不会被执行。
provider_token必须与当前支付环境一致。测试令牌只能用于对应的测试流程。- 不同版本的
pyTelegramBotAPI在类型标注或部分方法参数上可能略有差异,但处理方式相同:注册pre_checkout_query_handler,并在回调中调用answer_pre_checkout_query()。
备注:内容仅供参考。
