Django应用配置Shopify RecurringApplicationCharge遇403错误求助
Shopify应用配置RecurringApplicationCharge返回403 ForbiddenAccess错误的解决
问题背景
基于shopify_django_app搭建的应用,尝试通过shopify.RecurringApplicationCharge配置订阅计费功能,两种实现方式均返回403 ForbiddenAccess错误。
代码示例
第一种实现代码
@shop_login_required def buy_plan(request, id): plan = PlanTable.objects.get(id=id) context = {'plans': plan} charge = shopify.RecurringApplicationCharge() charge.name = plan.name charge.price = plan.price charge.test = True charge.return_url = reverse('accept') if charge.response_code == 201: confirmation_url = charge['recurring_application_charge']['confirmation_url'] return redirect(confirmation_url) else: return render(request, 'home/billing.html', context)
第二种实现代码(参考shopify_python_api文档)
@shop_login_required def buy_plan(request, id): plan = PlanTable.objects.get(id=id) context = {'plans': plan} charge = shopify.RecurringApplicationCharge.create({'name' : plan.name,'price' : plan.price, 'test' : True,'return_url' : reverse('accept')}) if charge.response_code == 201: confirmation_url = charge['recurring_application_charge']['confirmation_url'] return redirect(confirmation_url) else: return render(request, 'home/billing.html', context)
URLs配置
path('buy_plan/<str:id>', views.buy_plan, name='buy_plan'), path('accept', views.accept, name='accept'),
模板代码(billing.html)
{% extends "base.html" %} {% block content %} <h2>Plans</h2> {% if plans %} <div class="product-list"> {% for plan in plans %} <div class="product-card"> <div class="product-info"> <h1><b>Name: </b>{{plan.name}}</h1> <h2><b>Price: </b>{{plan.price}}</h2> <a href="{% url 'buy_plan' plan.id %}"> <button class="custom-btn btn-2">Activate</button> </a> </div> </div> {% endfor %} </div> {% else %} <em class="note">There are no plans.</em> {% endif %} {% endblock %}
错误信息
ForbiddenAccess at /buy_plan/1 Response(code=403, body="b''", headers={'Date': 'Fri, 18 Aug 2023 12:52:49 GMT', ...}, msg="Forbidden")
原因分析与解决办法
1. API权限缺失
- 检查Shopify后台应用的API权限,确保已勾选read_own_subscription_contracts和write_own_subscription_contracts权限,且应用安装时已获取到这些权限。
- 确认使用的Shopify API版本兼容性:旧版
RecurringApplicationCharge已逐步被Subscription API替代,若使用较新的API版本,需切换到新的订阅接口。
2. 请求未正确发起(第一种代码的致命错误)
第一种代码仅创建了RecurringApplicationCharge实例,但未调用charge.save()方法向Shopify API发起请求,直接判断response_code完全无效。正确做法是调用save()或使用第二种代码的create()方法。
3. Return URL配置错误
return_url必须是完整的绝对URL,且属于应用后台配置的授权域名。原代码中reverse('accept')返回的是相对路径,需拼接成绝对URL:return_url = request.build_absolute_uri(reverse('accept'))- 确认
accept路径在Shopify后台的"重定向URL"列表中已配置。
4. 测试模式限制
测试模式仅支持Shopify合作伙伴后台创建的开发店铺,普通店铺无法使用测试计费请求。确保当前使用的是开发店铺进行测试。
5. API客户端上下文失效
检查@shop_login_required装饰器是否正确初始化了Shopify API客户端,可在函数中添加调试代码确认:
print(shopify.ShopifyResource.site)
若输出的站点信息不包含有效访问令牌,需排查登录态维护逻辑。
修正后的代码示例
@shop_login_required def buy_plan(request, id): plan = PlanTable.objects.get(id=id) # 修复模板循环问题:将单个plan转为列表 context = {'plans': [plan]} try: # 生成绝对return_url return_url = request.build_absolute_uri(reverse('accept')) charge = shopify.RecurringApplicationCharge.create({ 'name': plan.name, 'price': plan.price, 'test': True, 'return_url': return_url }) # 直接通过属性获取确认URL,无需字典取值 if charge.confirmation_url: return redirect(charge.confirmation_url) else: print(f"Charge creation failed: {charge.errors}") return render(request, 'home/billing.html', context) except shopify.ForbiddenAccess as e: print(f"Forbidden error details: {e}") return render(request, 'home/billing.html', context)
内容的提问来源于stack exchange,提问作者royalsanga
相关产品推荐
相关产品推荐

