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

本地FastAPI环境下Stripe Webhook超时及支付状态不一致问题

本地测试Stripe Webhook:支付页面无响应超时,后台流程却正常完成

问题现象

支付后Stripe的checkout.session.completed事件能被立即接收,调试器确认已进入return JSONResponse(status_code=200, content={"message": "Success"})代码段,但Stripe支付页面无任何反应,一段时间后超时跳转到预设的success URL,且后台业务流程已正确处理完成。

同时发现同一会话在Stripe仪表盘的Events标签显示payment_status为"paid",但Logs中显示为"unpaid",存在状态不一致情况。

Webhook代码

async def my_webhook_view(request: Request, db: Session = Depends(get_db)):
    payload = await request.body()
    sig_header = request.headers.get('stripe-signature')
    event = None
    try:
        event = stripe.Webhook.construct_event(
            payload, sig_header, ENDPOINT_SECRET
        )
    except ValueError as e:
        # Invalid payload
        raise HTTPException(status_code=400, detail="Invalid payload")
    except stripe.error.SignatureVerificationError as e:
        # Invalid signature
        raise HTTPException(status_code=400, detail="Invalid signature")

    if event['type'] == 'checkout.session.completed':
        #email = event['data']['object']['customer_details']['email']
        # Check if the event has already been processed
        event_id = event['id']
        if db.query(ProcessedEvent).filter_by(id=event_id).first():
            return {"message": "Event already processed"}

        # Process the event
        session = event['data']['object']
        db_event = ProcessedEvent(id=event_id)
        db.add(db_event)
        db.commit()
        msg = fulfill_checkout(event['data']['object']['id'])
        if msg == "Event received":
            print("Event received")
            return JSONResponse(status_code=200, content={"message": "Success"})
        else:
            print("Event unprocessed")
            return JSONResponse(status_code=200, content={"message": "Cancel"})

Checkout会话创建代码

#Stripe Payment
@app.post('/create-checkout-session')
async def create_checkout_session(price_id: str = Form(...)):
    try:
        checkout_session = stripe.checkout.Session.create(
            line_items=[
                {
                    # Provide the exact Price ID (for example, pr_1234) of the product you want to sell
                    'price': price_id,
                    'quantity': 1,
                },
            ],
            mode='payment',
            success_url= f'{DOMAIN}/success',
            cancel_url= f'{DOMAIN}/cancel',
            billing_address_collection = 'required'
        )
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))
    
    return RedirectResponse(checkout_session.url,303)

额外细节

  • 本地Webhook仅监听checkout.session.completed事件,通过命令 stripe listen --events checkout.session.completed --forward-to localhost:8000/webhook 转发请求
  • Stripe仪表盘Webhook配置正确

问题原因及解决办法

1. Webhook响应超时(核心原因)

Stripe要求Webhook端点必须在10秒内返回响应,如果你的fulfill_checkout函数执行耗时较长(比如复杂数据库操作、外部API调用),会导致Stripe无法及时收到响应,进而触发支付页面超时跳转。

解决办法:
将业务处理逻辑放入后台任务,先快速返回200响应给Stripe,再异步处理业务:

from fastapi import BackgroundTasks

async def my_webhook_view(request: Request, db: Session = Depends(get_db), background_tasks: BackgroundTasks = Depends()):
    # 签名验证等代码保持不变

    if event['type'] == 'checkout.session.completed':
        event_id = event['id']
        if db.query(ProcessedEvent).filter_by(id=event_id).first():
            return {"message": "Event already processed"}

        # 先记录事件已接收并提交数据库
        db_event = ProcessedEvent(id=event_id)
        db.add(db_event)
        db.commit()
        # 将业务处理放入后台任务
        background_tasks.add_task(fulfill_checkout, event['data']['object']['id'])
        # 立即返回200响应
        print("Event received, processing in background")
        return JSONResponse(status_code=200, content={"message": "Success"})

2. 响应格式简化

Stripe对Webhook响应的内容要求极低,仅需200状态码即可。可以简化响应内容,避免因格式或编码问题导致Stripe判定响应异常:

from fastapi import Response

# 替换原返回语句
return Response(status_code=200)

3. 本地转发工具排查

检查stripe listen的运行日志,确认请求转发是否存在延迟;同时用curl测试本地Webhook端点的响应时间:

curl -X POST localhost:8000/webhook -d '{}' -w "%{time_total}\n"

确保响应时间在1秒以内。

4. 会话状态不一致问题

Events标签显示的是会话最终状态,Logs中显示的是Webhook触发时的快照状态。解决Webhook响应超时问题后,Stripe会及时更新会话状态,这个不一致现象会自动消失。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 09:52:03