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

PayPal沙箱Webhook签名验证:部分事件失败求助

PayPal Webhook验证失败排查方案

问题描述

使用Python+FastAPI开发PayPal集成,两个Webhook接口逻辑如下:

  • /capture_payment 接收CHECKOUT.ORDER.APPROVED事件,验证通过后触发支付捕获;
  • 支付完成后,PayPal推送PAYMENT.CAPTURE.COMPLETED事件至/register_payment接口,验证后执行后续操作。

接口代码:

@app.post("/capture_payment") # 接收CHECKOUT.ORDER.APPROVED事件
async def handle_webhook(request: Request):
    headers = request.headers
    event = await request.json()
    payment_processor = PayPalPayment()
    verification = payment_processor.verify_webhook_signature(headers, event)['verification_status']
    if verification == 'SUCCESS':
        print('Validated!')
        order_id = event['resource']['id']
        result = payment_processor.capture_order(order_id) # 触发后续PAYMENT.CAPTURE.COMPLETED事件
        return {"code" : 200}
    else:
        return {"code": 400}

@app.post("/register_payment") # 接收PAYMENT.CAPTURE.COMPLETED事件
async def handle_webhook(request: Request):
    headers = request.headers
    event = await request.json()
    payment_processor = PayPalPayment()
    verification = payment_processor.verify_webhook_signature(headers, event)['verification_status']
    if verification == 'SUCCESS':
        print('Valid payment capture event received!')   
        # 后续业务操作
        return {'code' : 200}
    print('Webhook not verified!')
    return {'code' : 400} 

签名验证函数:

def verify_webhook_signature(self, webhook_headers, event):
    auth_algo = webhook_headers['PAYPAL-AUTH-ALGO']
    cert_url = webhook_headers['PAYPAL-CERT-URL']
    transmission_id = webhook_headers['PAYPAL-TRANSMISSION-ID']
    transmission_sig = webhook_headers['PAYPAL-TRANSMISSION-SIG']
    transmission_time = webhook_headers['PAYPAL-TRANSMISSION-TIME']
    webhook_id = get_webhook_id(event['event_type'])
    
    url = 'https://api-m.sandbox.paypal.com/v1/notifications/verify-webhook-signature'
    
    headers = {
        'Content-Type': 'application/json',
        'Authorization': f'Bearer {get_bearer_token()}',
    }

    data = {
        'auth_algo': auth_algo,
        'cert_url': cert_url,
        'transmission_id': transmission_id, 
        'transmission_sig': transmission_sig,
        'transmission_time': transmission_time,
        'webhook_id': webhook_id,
        'webhook_event': event
    }

    response = requests.post(url, headers=headers, json=data)
    
    return response.json()

def get_webhook_id(event_type : str):
    id = os.getenv(f'PAYPAL_WEBHOOK_ID_{event_type.upper()}')
    print(f'webhook id: {id}')
    return id

当前CHECKOUT.ORDER.APPROVED事件验证正常,但PAYMENT.CAPTURE.COMPLETED事件验证始终失败,Postman单独测试结果一致,需排查原因。


排查与解决方向

1. Webhook ID配置错误

  • 检查环境变量PAYPAL_WEBHOOK_ID_PAYMENT.CAPTURE.COMPLETED是否正确设置:注意事件类型中的.需完整保留,不能被替换或遗漏
  • 确认该Webhook ID是PayPal后台为PAYMENT.CAPTURE.COMPLETED事件配置的(可在沙箱商家账户的Webhook设置中查看),避免和CHECKOUT.ORDER.APPROVED的Webhook ID混用
  • 查看控制台打印的webhook id,确认其不为空或与PayPal后台配置一致

2. 沙箱Webhook订阅配置问题

  • 登录沙箱商家账户,确认Webhook已订阅PAYMENT.CAPTURE.COMPLETED事件,且回调地址指向/register_payment接口
  • 沙箱与生产环境的Webhook ID完全独立,确保使用的是沙箱环境的Webhook ID,而非生产环境的

3. 请求数据完整性问题

  • 检查FastAPI的request.headers是否完整获取了所有PAYPAL-*开头的头部字段,避免被反向代理、中间件修改或过滤
  • 确保webhook_event是原封不动的PayPal推送内容,未在接收时修改JSON结构(如字段排序、添加/删除内容),PayPal签名验证依赖事件内容的绝对完整性

4. Bearer Token权限问题

  • 确认生成Bearer Token的沙箱应用关联了正确的商家账户,且拥有notifications:webhooks:verify权限
  • 检查Token是否过期,或是否误用生产环境的Token验证沙箱事件

5. 时间同步问题

PayPal的签名验证会检查事件发送时间与服务器时间的差值(允许范围通常为5分钟),需:

  • 确保服务器系统时间与UTC时间同步,避免因时间偏差导致验证失败

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 07:54:54