You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Stripe Webhooks中传递Telegram ChatID作为客户端参考ID遇400错误

解决Stripe Webhook无法传递Telegram ChatID(client_reference_id)及400错误问题

问题核心

  • 没法把Telegram ChatID作为client_reference_id通过Stripe Webhooks传过来,导致同步Stripe数据时拿不到ChatID存入数据库
  • 当前Webhook接口返回400 Bad Request错误

问题排查和解决办法

1. 先搞定400错误

Webhook返回400基本就两个原因:

  • Payload解析错误:要么Stripe发送的payload格式异常,要么代码读取时修改了原始数据
  • 签名验证失败:endpoint_secret和Stripe后台配置的不一致,或者payload被中间件篡改

先核对这几点:

  • 确认Stripe Dashboard里的Webhook密钥(endpoint_secret)和代码里的完全一致,别多空格少字符
  • 代码里request.get_data(as_text=True)要确保没修改原始payload,Stripe签名验证必须用原封不动的payload
  • 检查服务器有没有自动解析JSON的中间件,这类中间件会破坏原始payload,导致验证失败

2. client_reference_id传不过来的关键原因

你Webhook代码里直接从session['client_reference_id']取值,但问题大概率出在创建Stripe Checkout Session的时候根本没设置这个字段——你提供的代码里没有创建Session的逻辑,这才是核心遗漏点。

正确设置client_reference_id的示例(加到你的Telegram机器人逻辑里)

用户触发订阅时,生成Checkout Session必须把client_reference_id设为用户的ChatID,注意要转成字符串(Stripe要求字符串类型):

# 示例:机器人处理订阅请求的逻辑
stripe.api_key = stripe_api_key

def create_subscription_session(chat_id):
    try:
        session = stripe.checkout.Session.create(
            payment_method_types=['card'],
            line_items=[{
                'price': 'price_xxxxxx', # 替换成你自己的Stripe价格ID
                'quantity': 1,
            }],
            mode='subscription',
            success_url='https://你的域名.com/success',
            cancel_url='https://你的域名.com/cancel',
            client_reference_id=str(chat_id)  # 必须加这一行,把ChatID转成字符串
        )
        return session.url
    except stripe.error.StripeError as e:
        logging.error(f"创建Stripe会话出错:{e}")
        return None

3. Webhook代码的优化点

处理client_reference_id为空的情况

要是创建Session时没设这个字段,Webhook里直接取值会报错,加个判断避免崩溃:

if event['type'] == 'checkout.session.completed':
    session = event['data']['object']
    chat_id = session.get('client_reference_id')  # 用get方法,避免KeyError
    if not chat_id:
        logging.error("Checkout Session里没找到client_reference_id")
        return jsonify(success=True), 200  # 别返回400,不然Stripe会重复发事件
    
    amount_total = session['amount_total']
    # 后面的逻辑不变...

修复数据库更新的问题

如果user_subscription表里没有这个ChatID的记录,UPDATE语句不会生效,改成INSERT ... ON CONFLICT既能新增又能更新:

cursor.execute(
    """
    INSERT INTO user_subscription (chat_id, subscription_plan)
    VALUES (%s, %s)
    ON CONFLICT (chat_id) DO UPDATE SET subscription_plan = EXCLUDED.subscription_plan
    """,
    (chat_id, subscription_plan)
)

4. 测试步骤

  1. 用Stripe CLI测试Webhook:
    stripe listen --forward-to localhost:4242/webhook
    
    然后触发一个测试的checkout.session.completed事件,看日志能不能拿到client_reference_id
  2. 去Stripe Dashboard的Checkout Sessions列表里,查看创建的Session详情,确认client_reference_id确实存在

修正后的Webhook代码片段

# 处理Stripe Webhook事件
@app.route('/webhook', methods=['POST'])
def webhook():
    payload = request.get_data(as_text=True)
    sig_header = request.headers.get('Stripe-Signature')

    try:
        event = stripe.Webhook.construct_event(payload, sig_header, endpoint_secret)
    except ValueError as e:
        logging.error(f"无效payload:{e}")
        return jsonify(success=False), 400
    except stripe.error.SignatureVerificationError as e:
        logging.error(f"签名验证失败:{e}")
        return jsonify(success=False), 400

    # 处理会话完成事件
    if event['type'] == 'checkout.session.completed':
        session = event['data']['object']
        chat_id = session.get('client_reference_id')
        
        if not chat_id:
            logging.error("Checkout Session中未找到client_reference_id")
            return jsonify(success=True), 200
            
        amount_total = session['amount_total']
        
        # 根据金额判断订阅计划
        subscription_plan = 'Weekly' if amount_total == 695 else 'Monthly' if amount_total == 1995 else 'Unknown'

        # 更新数据库
        conn = get_db_connection()
        if conn is not None:
            cursor = conn.cursor()
            try:
                cursor.execute(
                    """
                    INSERT INTO user_subscription (chat_id, subscription_plan)
                    VALUES (%s, %s)
                    ON CONFLICT (chat_id) DO UPDATE SET subscription_plan = EXCLUDED.subscription_plan
                    """,
                    (chat_id, subscription_plan)
                )
                conn.commit()
                logging.info(f"已更新chat_id {chat_id}的订阅计划")
            except Exception as e:
                logging.error(f"数据库错误:{e}")
            finally:
                cursor.close()
                conn.close()
        else:
            logging.error("数据库连接失败")

    return jsonify(success=True), 200

内容的提问来源于stack exchange,提问作者Rafay Imran

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 22:32:05