PAYATHON 2026

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

付款流程

正常的付款流程如下:

  1. 用户发送 /pay。
  2. 机器人调用 send_invoice() 发送账单。
  3. 用户确认付款信息。
  4. Telegram 向机器人发送 pre_checkout_query。
  5. 机器人检查订单,然后调用 answer_pre_checkout_query(query.id, ok=True)。
  6. 付款成功后,Telegram 发送带有 successful_payment 的消息。
  7. 机器人读取 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()。

备注:内容仅供参考。