本文旨在提供一个全面的教程,指导开发者如何在Stripe Checkout会话中正确集成自定义税率和折扣。我们将深入探讨Stripe API中`TaxRate`和`Coupon`对象的创建与应用,纠正常见的参数使用错误,特别是`discounts`参数的错误用法,并提供一个结构化的Python代码示例,帮助您高效、准确地处理支付环节中的税务和优惠逻辑。
Stripe Checkout是一个预构建的、托管的支付页面,用于简化支付流程。在许多业务场景中,我们需要在Checkout会话中应用自定义的税率和折扣,以满足不同地区税务要求或营销活动需求。Stripe API提供了灵活的机制来管理这些元素,主要通过TaxRate对象和Coupon或PromotionCode对象来实现。
正确地将这些对象集成到stripe.checkout.Session.create调用中是关键。常见的错误包括参数名称或结构不正确,导致API返回InvalidRequestError。本教程将详细介绍如何避免这些问题,并提供一个实用的代码示例。
在深入代码之前,理解以下Stripe对象至关重要:
Stripe允许您在Checkout会话中应用一个或多个税率。这些税率可以是预先在Stripe Dashboard中创建的,也可以通过API动态创建。通常,为了效率和管理,建议预先创建常用税率。
如果您需要动态创建税率,可以使用stripe.TaxRate.create()。如果税率是固定的,可以直接使用其ID。
import stripe
# 示例:动态创建税率(通常只在税率变动或首次设置时执行)
def create_or_get_tax_rate(display_name, percentage, jurisdiction, inclusive=False):
# 检查是否已存在同名/同参数的税率,避免重复创建
# 实际生产中,更好的做法是查询现有税率或从数据库加载预设ID
try:
# 尝试检索现有税率,这里简化处理,实际可能需要更复杂的查询逻辑
# 例如,通过metadata存储自定义ID,或遍历检索
# 为了演示,我们假设每次都创建,但建议在生产环境中避免
tax_rate = stripe.TaxRate.create(
display_name=display_name,
description=f"{display_name} Tax",
percentage=percentage,
jurisdiction=jurisdiction,
inclusive=inclusive,
)
return tax_rate.id
except stripe.error.StripeError as e:
print(f"Error creating tax rate: {e}")
# 如果是已存在错误,可以尝试检索
# 简化处理,直接返回None或抛出异常
return None
# 假设您的订单模型中存储了税率信息
# order.tax.all() 返回一个包含税率信息的集合
# For example:
# class Tax(models.Model):
# name = models.CharField(max_length=100)
# rate = models.DecimalField(max_digits=5, decimal_places=2) # e.g., 5.00 for 5%
# jurisdiction = models.CharField(max_length=100, default="US")税率可以应用于整个Checkout Session,也可以应用于会话中的单个line_item。当应用于整个会话时,Stripe会将这些税率应用于所有商品行。
在stripe.checkout.Session.create中,通过tax_rates参数传递一个税率ID列表:
# ... (在您的视图或服务函数中)
tax_rate_ids = []
for tax_obj in order.tax.all(): # 假设 order.tax.all() 返回您的自定义税率对象
# 在生产环境中,您应该从数据库加载预先创建的Stripe TaxRate ID
# 这里为了演示,我们假设 tax_obj 包含创建Stripe TaxRate所需的信息
# 并且我们动态创建或获取其ID
tax_rate_id = create_or_get_tax_rate(
display_name=tax_obj.name,
percentage=float(tax_obj.rate), # 确保是浮点数
jurisdiction=tax_obj.jurisdiction,
inclusive=False # 根据您的业务逻辑设置
)
if tax_rate_id:
tax_rate_ids.append(tax_rate_id)
# ... 在 stripe.checkout.Session.create 中使用
# session = stripe.checkout.Session.create(
# # ... 其他参数
# tax_rates=tax_rate_ids,
# # ...
# )折扣可以通过Stripe的Coupon或PromotionCode实现。Coupon定义了折扣的具体内容(如减免金额或百分比),而PromotionCode则允许客户通过输入代码来应用这些优惠券。
与税率类似,优惠券也可以通过API动态创建或预先创建。
# 示例:动态创建优惠券(通常只在优惠活动设置时执行)
def create_or_get_coupon(name, amount_off_cents=None, percent_off=None, currency='usd', duration='once'):
try:
if amount_off_cents:
coupon = stripe.Coupon.create(
amount_off=amount_off_cents, # 以最小货币单位表示,例如美分
duration=duration, # once, forever, or repeating
currency=currency,
name=name,
)
elif percent_off:
coupon = stripe.Coupon.create(
percent_off=percent_off,
duration=duration,
currency=currency, # 即使是百分比折扣,也需要货币类型
name=name,
)
else:
raise ValueError("Must provide either amount_off_cents or percent_off")
return coupon.id
except stripe.error.StripeError as e:
print(f"Error creating coupon: {e}")
return None
# 假设您的订单模型中存储了折扣信息
# order.discount.all() 返回一个包含折扣信息的集合
# For example:
# class Discount(models.Model):
# name = models.CharField(max_length=100)
# amount = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True) # e.g., 10.00 for $10
# percentage = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True) # e.g., 10.00 for 10%在stripe.checkout.Session.create中,通过discounts参数传递一个包含优惠券ID或促销码ID的列表。请注意,这里的参数结构非常重要,也是原始问题中出错的地方。
正确的discounts参数应该是一个字典列表,每个字典包含一个coupon键或一个promotion_code键。
# ... (在您的视图或服务函数中)
discount_items = []
for discount_obj in order.discount.all(): # 假设 order.discount.all() 返回您的自定义折扣对象
# 在生产环境中,您应该从数据库加载预先创建的Stripe Coupon ID
# 这里为了演示,我们假设 discount_obj 包含创建Stripe Coupon所需的信息
# 并且我们动态创建或获取其ID
coupon_id = None
if discount_obj.amount:
coupon_id = create_or_get_coupon(
name=discount_obj.name,
amount_off_cents=int(float(discount_obj.amount) * 100), # 转换为美分
currency='usd' # 根据您的货币设置
)
elif discount_obj.percentage:
coupon_id = create_or_get_coupon(
name=discount_obj.name,
percent_off=float(discount_obj.percentage),
currency='usd' # 即使是百分比折扣,也需要货币类型
)
if coupon_id:
discount_items.append({"coupon": coupon_id}) # 正确的参数结构!
# ... 在 stripe.checkout.Session.create 中使用
# session = stripe.checkout.Session.create(
# # ... 其他参数
# discounts=discount_items,
# # ...
# )原始错误分析: 原始代码中使用了discounts=[{"discounts": '{{COUPON_ID}}'}]。Stripe API期望的是discounts列表中的每个元素是一个字典,该字典直接包含"coupon"或"promotion_code"键,而不是再次嵌套一个"discounts"键。因此,Received unknown parameter: discounts[0][discounts]这个错误提示非常准确。
以下是一个结合了税率和折扣的Django视图示例,基于您提供的原始代码进行修正和优化。
import stripe
from django.conf import settings
from django.http import JsonResponse
from django.views import View
# from .models import Order, Tax, Discount # 假设您有这些模型
# 配置Stripe API密钥
stripe.api_key = settings.STRIPE_SECRET_KEY # 确保在settings.py中配置
# 辅助函数:创建或获取Stripe TaxRate
def create_or_get_stripe_tax_rate(display_name, percentage, jurisdiction, inclusive=False):
"""
创建一个Stripe TaxRate或返回现有TaxRate的ID。
在生产环境中,建议预先创建TaxRate并存储其ID,而不是每次都动态创建。
"""
# 实际应用中,您可能会查询数据库中存储的Stripe TaxRate ID
# 这里为演示目的,简化为直接创建
try:
tax_rate = stripe.TaxRate.create(
display_name=display_name,
description=f"{display_name} Tax",
percentage=percentage,
jurisdiction=jurisdiction,
inclusive=inclusive,
)
return tax_rate.id
except stripe.error.StripeError as e:
# 更健壮的错误处理,例如记录日志
print(f"Error creating Stripe TaxRate: {e}")
return None
# 辅助函数:创建或获取Stripe Coupon
def create_or_get_stripe_coupon(name, amount_off_cents=None, percent_off=None, currency='usd', duration='once'):
"""
创建一个Stripe Coupon或返回现有Coupon的ID。
在生产环境中,建议预先创建Coupon并存储其ID,而不是每次都动态创建。
"""
try:
if amount_off_cents is not None:
coupon = stripe.Coupon.create(
amount_off=amount_off_cents,
duration=duration,
currency=currency,
name=name,
)
elif percent_off is not None:
coupon = stripe.Coupon.create(
percent_off=percent_off,
duration=duration,
currency=currency,
name=name,
)
else:
raise ValueError("Must provide either amount_off_cents or percent_off")
return coupon.id
except stripe.error.StripeError as e:
print(f"Error creating Stripe Coupon: {e}")
return None
class CreateCheckoutSessionOrderView(View):
def get(self, request, *args, **kwargs):
order_id = self.kwargs["order_id"]
DOMAIN: str = 'http://127.0.0.1:8000' # 生产环境应使用您的实际域名
# 假设 Order, Tax, Discount 模型已定义并可访问
# from your_app.models import Order, Tax, Discount
try:
# 替换为您的实际订单获取逻辑
# order = Order.objects.get(id=order_id)
# 模拟订单数据
class MockTax:
def __init__(self, name, rate, jurisdiction):
self.name = name
self.rate = rate
self.jurisdiction = jurisdiction
class MockDiscount:
def __init__(self, name, amount=None, percentage=None):
self.name = name
self.amount = amount
self.percentage = percentage
class MockOrder:
def __init__(self, id, total_cost, name, taxes, discounts):
self.id = id
self._total_cost = total_cost
self._name = name
self._taxes = taxes
self._discounts = discounts
def get_total_cost(self):
return self._total_cost
def __str__(self):
return self._name
def tax(self):
# 模拟ManyToManyField
class TaxManager:
def all(self):
return self._taxes
return TaxManager()
def discount(self):
# 模拟ManyToManyField
class DiscountManager:
def all(self):
return self._discounts
return DiscountManager()
# 模拟订单数据
order = MockOrder(
id=order_id,
total_cost=100.00, # 示例总价
name=f"Order #{order_id}",
taxes=[
MockTax(name="Sales Tax", rate=7.5, jurisdiction="US"),
# MockTax(name="VAT", rate=20.0, jurisdiction="GB"),
],
discounts=[
MockDiscount(name="Welcome Discount", amount=10.00), # 10美元折扣
# MockDiscount(name="Promo 5%", percentage=5.0), # 5%折扣
]
)
except Exception as e:
return JsonResponse({'error': str(e)}, status=404)
# --- 处理税率 ---
tax_rate_ids = []
for tax_obj in order.tax().all():
tax_rate_id = create_or_get_stripe_tax_rate(
display_name=tax_obj.name,
percentage=float(tax_obj.rate),
jurisdiction=tax_obj.jurisdiction,
inclusive=False, # 根据您的业务逻辑调整
)
if tax_rate_id:
tax_rate_ids.append(tax_rate_id)
# --- 处理折扣 ---
discount_items = []
for discount_obj in order.discount().all():
coupon_id = None
if discount_obj.amount is not None:
coupon_id = create_or_get_stripe_coupon(
name=discount_obj.name,
amount_off_cents=int(float(discount_obj.amount) * 100),
currency='usd',
)
elif discount_obj.percentage is not None:
coupon_id = create_or_get_stripe_coupon(
name=discount_obj.name,
percent_off=float(discount_obj.percentage),
currency='usd', # 即使是百分比折扣,也需要货币类型
)
if coupon_id:
discount_items.append({"coupon": coupon_id}) # 正确的参数结构
try:
session = stripe.checkout.Session.create(
payment_method_types=['card'],
line_items=[
{
'price_data': {
'currency': 'usd',
'unit_amount': int(order.get_total_cost() * 100), # 总价转换为美分
'product_data': {
'name': order.__str__(),
'description': f"Purchase for order {order.id}",
},
},
'quantity': 1,
# 如果需要对特定商品行应用税率,可以在这里添加 'tax_rates': [...]
# 但通常在会话级别应用更常见
},
],
payment_intent_data={
'metadata': {
'order_id': order.id,
},
},
mode='payment',
success_url=DOMAIN + '/success/',
cancel_url=DOMAIN + '/cancel/',
tax_rates=tax_rate_ids, # 应用税率
discounts=discount_items, # 应用折扣
)
return JsonResponse({'id': session.id})
except stripe.error.StripeError as e:
# 捕获Stripe API错误
return JsonResponse({'error': str(e)}, status=500)
except Exception as e:
# 捕获其他未知错误
return JsonResponse({'error': f"An unexpected error occurred: {e}"}, status=500)
# python
# js
# git
# json
# go
# app
# session
# ai
# django
# api调用
# cos
# 网络问题
# 币
相关文章:
武汉网站如何制作,黄黄高铁武穴北站途经哪些村庄?
c++怎么使用类型萃取type_traits_c++ 模板元编程类型判断【方法】
网站制作难吗安全吗,做一个网站需要多久时间?
SAX解析器是什么,它与DOM在处理大型XML文件时有何不同?
如何选择靠谱的建站公司加盟品牌?
弹幕视频网站制作教程下载,弹幕视频网站是什么意思?
打鱼网站制作软件,波克捕鱼官方号怎么注册?
免费网站制作模板下载,除了易企秀之外还有什么H5平台可以制作H5长页面,最好是免费的?
深圳网站制作费用多少钱,读秀,深圳文献港这样的网站很多只提供网上试读,但有些人只要提供试读的文章就能全篇下载,这个是怎么弄的?
如何快速建站并高效导出源代码?
如何在新浪SAE免费搭建个人博客?
如何在建站主机中优化服务器配置?
建站主机空间推荐 高性价比配置与快速部署方案解析
湖北网站制作公司有哪些,湖北清能集团官网?
网站制作软件免费下载安装,有哪些免费下载的软件网站?
建站之星代理平台如何选择最佳方案?
官网自助建站系统:SEO优化+多语言支持,快速搭建专业网站
建站之星如何保障用户数据免受黑客入侵?
网站制作外包价格怎么算,招聘网站上写的“外包”是什么意思?
长沙做网站要多少钱,长沙国安网络怎么样?
如何在万网开始建站?分步指南解析
如何基于PHP生成高效IDC网络公司建站源码?
深圳网站制作公司好吗,在深圳找工作哪个网站最好啊?
免费制作海报的网站,哪位做平面的朋友告诉我用什么软件做海报比较好?ps还是cd还是ai这几个软件我都会些我是做网页的?
网站制作服务平台,有什么网站可以发布本地服务信息?
建站之星如何快速更换网站模板?
公司门户网站制作流程,华为官网怎么做?
模具网站制作流程,如何找模具客户?
建站之星CMS五站合一模板配置与SEO优化指南
如何快速查询域名建站关键信息?
南阳网站制作公司推荐,小学电子版试卷去哪里找资源好?
合肥做个网站多少钱,合肥本地有没有比较靠谱的交友平台?
建站之星如何助力网站排名飙升?揭秘高效技巧
建站三合一如何选?哪家性价比更高?
威客平台建站流程解析:高效搭建教程与设计优化方案
装修招标网站设计制作流程,装修招标流程?
简单实现Android验证码
天河区网站制作公司,广州天河区如何办理身份证?需要什么资料有预约的网站吗?
导航网站建站方案与优化指南:一站式高效搭建技巧解析
在线制作视频网站免费,都有哪些好的动漫网站?
css网站制作参考文献有哪些,易聊怎么注册?
建站之星IIS配置教程:代码生成技巧与站点搭建指南
5种Android数据存储方式汇总
清除minerd进程的简单方法
建站之星客服服务时间及联系方式如何?
智能起名网站制作软件有哪些,制作logo的软件?
C++中的Pimpl idiom是什么,有什么好处?(隐藏实现)
如何确保西部建站助手FTP传输的安全性?
学校建站服务器如何选型才能满足性能需求?
建站之星如何助力企业快速打造五合一网站?
*请认真填写需求信息,我们会在24小时内与您取得联系。