Render部署Django服务调用Daraja API时JSON解码错误求助
解决Render部署MPesa Daraja API的JSONDecodeError及回调问题
一、修复JSONDecodeError(获取令牌失败)
这个错误说明response.json()尝试解析空内容或非JSON格式的响应,核心是部署环境的请求未得到有效JSON返回,按以下步骤排查:
1. 检查环境变量与配置正确性
- 核对Render上的
SAFARICOM_GET_ACCES_TOKEN_URL:注意变量名拼写(代码里写的ACCES少了一个s,应为ACCESS?如果本地配置正确,部署时可能变量名写错),确认地址是Daraja测试环境的https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials - 验证
MPESA_CONSUMER_KEY和MPESA_CONSUMER_SECRET:确保在Render的环境变量中没有多余空格、特殊字符未转义,且与Daraja开发者门户的密钥完全一致 - 确认Render网络连通性:部分云平台可能限制出站请求,可在Render的部署脚本中添加
curl https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials,查看日志确认能否访问端点
2. 强制输出关键日志(绕过Render免费版日志限制)
免费版Render默认不显示DEBUG级日志,修改代码提升日志级别并强制输出响应内容:
logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) # 改为INFO级别确保日志能被捕获 def get_mpesa_token(): url = os.getenv("SAFARICOM_GET_ACCES_TOKEN_URL") try: response = requests.get(url, auth=HTTPBasicAuth(settings.MPESA_CONSUMER_KEY, settings.MPESA_CONSUMER_SECRET)) # 强制输出到stdout,Render会捕获这些内容 print(f"Token request status code: {response.status_code}") print(f"Token request response content: {response.text}") if response.status_code != 200: logger.error(f"Token request failed with status {response.status_code}, content: {response.text}") raise ValueError(f"Token request failed: {response.status_code}") try: token_data = response.json() token = token_data.get('access_token') if token: return token else: logger.error("Access token missing in response") raise ValueError("Access token not found") except ValueError as e: logger.error(f"JSON decode failed: {e}, response content: {response.text}") raise except requests.RequestException as e: logger.error(f"Request error: {e}") raise
部署后查看Render的「Logs」标签,就能看到响应内容——如果返回的是HTML错误页面(比如401未授权、403禁止访问),说明密钥错误或IP未加入Daraja白名单。
3. 处理Daraja的IP白名单限制
Daraja测试环境默认只允许特定IP访问,若Render的出站IP不在白名单中,会返回非JSON的错误响应。解决方法:
- 在Daraja开发者门户的「My Apps」中,添加Render的出站IP(可通过
curl ifconfig.me在Render的部署脚本中获取) - 或使用合规的代理服务转发请求到Daraja API
二、解决回调接收失败问题
1. 确保回调地址符合要求
- 必须是HTTPS地址:Render免费版提供的
xxx.onrender.com域名自带HTTPS,符合要求 - 地址必须公开可访问:检查Render的服务是否处于「Live」状态,没有设置访问限制
- 路径拼写正确:核对Daraja配置的回调路径与代码中的路由完全一致
2. 优化回调路由的兼容性
- 不要设置严格的CORS规则:Daraja的服务器是第三方,需要允许跨域请求,示例路由:
from flask import request, jsonify @app.route('/mpesa-callback', methods=['POST']) def mpesa_callback(): # 直接接收请求,不需要CORS预检 data = request.get_json() print(f"Received callback: {data}") # 处理逻辑... return jsonify({"ResultCode": 0, "ResultDesc": "Success"}) - 支持多种请求格式:Daraja可能发送
application/x-www-form-urlencoded格式的请求,添加兼容处理:if request.is_json: data = request.get_json() else: data = request.form.to_dict()
3. 手动测试回调逻辑
用curl或Postman模拟Daraja的回调请求,验证服务能否正常接收:
curl -X POST https://your-render-url.com/mpesa-callback \ -H "Content-Type: application/json" \ -d '{ "MerchantRequestID": "test_merchant_id", "CheckoutRequestID": "test_checkout_id", "ResultCode": 0, "ResultDesc": "The service request is processed successfully." }'
查看Render日志,确认请求被接收并处理。
三、可靠的MPesa API测试方案
1. 使用Daraja官方测试模拟器
在Safaricom Daraja开发者门户中,找到「Simulator」工具:
- 可模拟支付发起、回调通知等全流程
- 不需要真实手机或SIM卡,适合本地和部署环境的逻辑测试
2. 替代ngrok的本地暴露方案
用Cloudflare Tunnel(免费且稳定)暴露本地服务:
- 安装Cloudflare CLI:
brew install cloudflare/cloudflare/cloudflared(Mac)或对应系统的安装包 - 登录Cloudflare:
cloudflared tunnel login - 启动隧道:
cloudflared tunnel --url http://localhost:8000 - 复制生成的HTTPS域名,配置为Daraja的回调地址
3. 完善日志与监控
- 在代码中输出所有关键请求/响应数据到stdout,Render会自动记录这些日志
- 对于回调请求,将接收的原始数据写入日志,方便排查格式问题
内容的提问来源于stack exchange,提问作者Michael Randiga
相关产品推荐
相关产品推荐

