Stripe Checkout 会话中集成自定义税率与折扣


stripe checkout 会话中集成自定义税率与折扣

本文旨在提供一个全面的教程,指导开发者如何在Stripe Checkout会话中正确集成自定义税率和折扣。我们将深入探讨Stripe API中`TaxRate`和`Coupon`对象的创建与应用,纠正常见的参数使用错误,特别是`discounts`参数的错误用法,并提供一个结构化的Python代码示例,帮助您高效、准确地处理支付环节中的税务和优惠逻辑。

概述:Stripe Checkout中的税务与折扣处理

Stripe Checkout是一个预构建的、托管的支付页面,用于简化支付流程。在许多业务场景中,我们需要在Checkout会话中应用自定义的税率和折扣,以满足不同地区税务要求或营销活动需求。Stripe API提供了灵活的机制来管理这些元素,主要通过TaxRate对象和Coupon或PromotionCode对象来实现。

正确地将这些对象集成到stripe.checkout.Session.create调用中是关键。常见的错误包括参数名称或结构不正确,导致API返回InvalidRequestError。本教程将详细介绍如何避免这些问题,并提供一个实用的代码示例。

关键概念

在深入代码之前,理解以下Stripe对象至关重要:

  • Stripe Checkout Session: Stripe提供的一个托管支付页面实例,通过调用stripe.checkout.Session.create创建。
  • TaxRate (税率): 代表一个具体的税率,可以包含显示名称、百分比、描述、管辖区等信息。税率可以设置为包含或不包含在价格中(inclusive/exclusive)。
  • Coupon (优惠券): 代表一个折扣,可以是固定金额折扣(amount_off)或百分比折扣(percent_off)。优惠券通常与一个优惠活动相关联。
  • PromotionCode (促销码): 允许客户在Checkout页面输入代码来应用折扣。一个促销码可以关联一个或多个优惠券。

集成自定义税率

Stripe允许您在Checkout会话中应用一个或多个税率。这些税率可以是预先在Stripe Dashboard中创建的,也可以通过API动态创建。通常,为了效率和管理,建议预先创建常用税率。

1. 创建或获取TaxRate对象

如果您需要动态创建税率,可以使用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")

2. 将TaxRate应用于Checkout Session

税率可以应用于整个Checkout Session,也可以应用于会话中的单个line_item。当应用于整个会话时,Stripe会将这些税率应用于所有商品行。

在stripe.checkout.Session.create中,通过tax_rates参数传递一个税率ID列表:

美图云修 美图云修

商业级AI影像处理工具

美图云修 52 查看详情 美图云修
# ... (在您的视图或服务函数中)
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则允许客户通过输入代码来应用这些优惠券。

1. 创建或获取Coupon对象

与税率类似,优惠券也可以通过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%

2. 将折扣应用于Checkout Session

在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)

注意事项与最佳实践

  1. Stripe API Key: 确保您的Stripe secret key已正确配置在Django settings.py 中,并被stripe.api_key引用。
  2. TaxRate 和 Coupon 的生命周期: 在生产环境中,TaxRate和Coupon对象通常是一次性创建的(例如,在Stripe Dashboard中,或通过管理界面/脚本)。然后,您只需在数据库中存储它们的Stripe ID,并在创建Checkout Session时引用这些ID。动态创建(如示例中所示)虽然可行,但在每次用户结账时都调用Stripe API创建新对象会增加延迟和不必要的API请求。
  3. 错误处理: 务必在API调用周围添加try-except块来捕获stripe.error.StripeError,以便优雅地处理网络问题、无效参数或其他Stripe API返回的错误。
  4. 货币单位: Stripe API处理金额时通常使用最小货币单位(例如,美元使用美分)。确保在传递unit_amount和amount_off时进行正确的转换(乘以100)。
  5. **税率的包含性 (

以上就是Stripe Checkout 会话中集成自定义税率与折扣的详细内容,更多请关注其它相关文章!


# js  # 是一个  # 应用于  # 自定义  # 您的  #   # 网络问题  # cos  # api调用  # ai  # session  # app  # go  # json  # git  # python  # django  # 建设工地招人网站大全  # 安徽专业网站建设服务  # 潍坊互联网seo查询  # 视频网站内容优化软件  # 金溪网站优化推广  # 花茶市场营销推广方案  # 邯郸百度网站优化费用  # 安徽省网站优化排名  # 如何运行seo  # 正规的福州seo平台  # 多个  # 提供一个  # 每次都  # 即使是  # 可以通过  # 美图 


相关栏目: 【 Google疑问12 】 【 Facebook疑问10 】 【 优化推广96088 】 【 技术知识133117 】 【 IDC资讯59369 】 【 网络运营7196 】 【 IT资讯61894


相关推荐: iPhone17Pro如何连接蓝牙耳机_iPhone17Pro蓝牙设备配对与连接方法介绍  国际经济与贸易就业方向解析  晓晓优选app支付宝绑定方法  Pandas中基于动态偏移量实现DataFrame列值位移的策略  美发店速赢秘籍  AO3中文入口稳定分享_AO3官网HTTPS看文详解  Go语言中方法与接收器:指针和值类型的调用机制详解  泰拉瑞亚水晶无法放置问题  《真我》申请退款方法  《盗墓笔记手游》技能介绍  Apple Music无故扣费引质疑  快手网页版官方访问 快手网页版页面在线打开  偃武诸葛亮阵容搭配推荐  VS Code的时间线(Timeline)视图:您的代码时光机  微信如何设置字体大小_微信字体设置的阅读舒适  苹果手机如何清理系统缓存数据 iPhone非越狱清理垃圾文件的技巧【系统优化】  Mac hosts文件在哪里_Mac修改hosts文件详细教程  如何发挥新媒体矩阵作用?新媒体矩阵怎么搭建?  QQ阅读小说搜索入口地址_QQ阅读小说搜索入口地址搜索在线阅读  Golang如何使用gRPC拦截器实现日志收集_Golang gRPC拦截器日志收集实践  《淘票票》添加到苹果钱包教程  b站如何剪辑视频_b站必剪app使用教程  MacBook Pro词典使用指南  谷歌浏览器如何查找和删除恶意软件 谷歌浏览器内置安全清理工具使用教程  《随手记》启用语音备注方法  解决CSS容器溢出问题:使用calc()实现精确布局与边距控制  《桃源记2》资源采集攻略  向日葵客户端怎么进行语音通话_向日葵客户端语音通话功能使用方法  Django模型动态关联检查:高效管理复杂关系  C++怎么实现一个红黑树_C++高级数据结构与平衡二叉搜索树  Python中深度嵌套字典与列表的数据提取与条件过滤指南  海棠书屋官方在线书籍入口 海棠书屋文学作品浏览官网链接  win11资源管理器标签页怎么用 Win11文件管理器多标签高效操作【新功能】  键盘测试软件哪个好_键盘故障检测工具推荐  word页码灰色不能用如何解决  《星露谷物语》克林特好感度事件介绍  《随手记》关闭首页消息推送方法  荣耀magicv5怎么上手测评  如何在CSS中清除浮动解决背景颜色不包裹内容问题_clear after技巧  如何在CSS中使用过渡制作按钮边框渐变_border-color transition实现  免费占卜在线神算_免费占卜手机神算  PHP安全加载非公开目录图片与动态内容类型处理指南  《米姆米姆哈》米姆获取及技能攻略  知乎APP怎么查看自己被邀请的问题_知乎APP邀请回答记录查看与参与方法  mail.qq.com登录入口 QQ邮箱网页版直达  《procreate》绘制渐变效果教程  ExcelSCAN与LAMBDA如何创建自定义移动平均函数_SCAN实现任意窗口期移动平均计算  汽水音乐网页版登录 汽水音乐网页端官方入口  为什么XML解析器对大小写敏感? 理解XML规范中的大小写规则与最佳实践  在Peewee中处理PostgreSQL记录重复:一站式数据摄取教程 

 2025-12-13

了解您产品搜索量及市场趋势,制定营销计划

同行竞争及网站分析保障您的广告效果

点击免费数据支持

提交您的需求,1小时内享受我们的专业解答。

运城市盐湖区信雨科技有限公司


运城市盐湖区信雨科技有限公司

运城市盐湖区信雨科技有限公司是一家深耕海外推广领域十年的专业服务商,作为谷歌推广与Facebook广告全球合作伙伴,聚焦外贸企业出海痛点,以数字化营销为核心,提供一站式海外营销解决方案。公司凭借十年行业沉淀与平台官方资源加持,打破传统外贸获客壁垒,助力企业高效开拓全球市场,成为中小企业出海的可靠合作伙伴。

 8156699

 13765294890

 8156699@qq.com

Notice

We and selected third parties use cookies or similar technologies for technical purposes and, with your consent, for other purposes as specified in the cookie policy.
You can consent to the use of such technologies by closing this notice, by interacting with any link or button outside of this notice or by continuing to browse otherwise.