书籍目录 展开 / 收起

商品、价格与订单:建立清晰的交易模型

把上一节的报价变成系统能识别的数据。本篇的成品是一个测试商品、一条9美元一次性价格和一份完整订单样例。付款接入时,浏览器只需告诉API“我要10次包”,金额和额度由服务器找到。

需要你已能进入Stripe账户的独立测试环境。若主体申请尚未明确,先完成这里的本地订单示例,控制台步骤等测试权限具备后再做;不要为练习填不真实的申请资料。本篇没有代你创建任何支付账户或商品。

先读懂这份完成的交易

ClearCopy第一次卖的是10次成功报告,使用期按前节练习设为付款后30天。用下面这组教学标识串起来:

{
  "orderId": "demo-order-001",
  "userId": "demo-user-a",
  "offerCode": "clearcopy-10-v1",
  "offerTitle": "ClearCopy · 10 checks",
  "amountMinor": 900,
  "currency": "usd",
  "credits": 10,
  "validDays": 30,
  "status": "pending",
  "provider": "stripe",
  "providerPriceId": "REPLACE_WITH_SANDBOX_PRICE_ID",
  "checkoutSessionId": null,
  "fulfilledAt": null
}

amountMinor使用美元的分,因此900表示9美元。offerCode是你自己稳定的套餐代码,providerPriceId是Stripe生成的价格标识,二者用途不同。复制此例时保留套餐代码,用你的测试价格ID替换大写占位值;其他demo-标识仅用于演练。

这份订单把购买当时承诺的价格、次数和有效期保留下来。将来套餐改成12美元,不会改变这笔9美元订单。产品名称可以修正文案,订单里已经成交的金额和权益仍按原快照处理。

在测试环境创建对应商品

打开Stripe产品与价格说明,观察官方将商品解释为卖什么,将价格解释为收多少及多久收一次。配图中的商品、价格关系正好对应本节的两层数据。

  1. 在Dashboard的账户切换器中进入独立Sandbox,确认页面显示测试环境。
  2. 打开 Product catalog,选择 Create product,名称输入 ClearCopy · 10 checks。
  3. 在描述中填写 10 completed English app-description checks.。保存前确认这里说的报告与你实际准备实现的内容一致。
  4. 添加一次性价格,币种选 USD,金额输入 9.00,不选择月度或年度重复扣款。
  5. 保存商品,打开价格详情,复制它的price_标识到本地测试配置。控制台生成值每个账户都不同,不要复制教程中的虚构ID。

完成后,你应看见商品下有一条9美元一次性价格。如果显示/month,回到商品创建一条正确的一次性价格并用于练习;不要为了修展示文字继续沿用错误的周期价格。

Stripe官方产品与价格说明:Product描述提供什么,Price描述金额与收费频率,二者不要混成一个展示名称
Stripe官方产品与价格说明:Product描述提供什么,Price描述金额与收费频率,二者不要混成一个展示名称。

让服务端回答“这包多少钱”

把下列配置保存在API侧,而不是Astro的公开脚本里。这是数据结构示例,尚未绑定真实项目环境变量:

const offers = {
  'clearcopy-10-v1': {
    amountMinor: 900,
    currency: 'usd',
    credits: 10,
    validDays: 30,
    priceId: process.env.STRIPE_PRICE_10_CHECKS
  }
};

浏览器提交的购买意图只保留套餐代码:

{"offerCode":"clearcopy-10-v1"}

API从已登录会话取得userId,查询这份配置,生成订单。不要接收浏览器自报的userId、credits或amountMinor来决定发给谁和发多少。实际请求创建Checkout时,把本地订单号放入服务端创建的会话关联字段,返回的会话ID再写回本地订单。

这是一次明确的数据转换:收到套餐代码,得到前面的pending订单。它还没有付款,因此额度仍是0。以后查看交易,不必靠邮件地址猜哪个用户买过东西。

处理用户连点两次的那几秒

假设用户点击购买后网络超时,又点了一次。若API每次都新建订单和Checkout,会出现两个可付款页面。给一次购买尝试创建一个随机请求标识,同一次重试继续用它;用户明确重新发起另一笔购买时再换标识。

Stripe幂等请求说明同一个幂等键可用于安全重试。你的数据库也要给(userId, purchaseAttemptId)加唯一约束:重复请求先查回原订单,再复用已有会话;创建Stripe会话时使用由订单号派生的稳定幂等键。这样平台侧和应用侧都能认出同一次尝试。

在纸上连续输入两次同一请求,完成后的记录应是:

操作 本地订单数 对应Checkout会话数 可用次数
第一次请求 1 1 0
同次尝试超时后重试 1 1 0
经服务端确认已付并完成发放 1 1 10

发放还需要下一节的付款核对。Stripe履约文档明确要求同一个会话多次、甚至并发调用时只履约一次,因此数据库还应为付款会话建立唯一关联,为发放建立唯一记录。这里不把“已创建会话”改成“已付款”。

带着三项值离开这一页

现在把你的测试price_标识填回订单例,核对金额为900、币种为usd,状态仍为pending;保留尚未付款的0次余额。若你把一条订阅价格填进一次性套餐,或把9当成9美分,这一步就能发现。

保存商品ID、价格ID和已填的订单JSON到独立教学项目中,凭据只放环境配置。到这一步,报价中的“9美元10次”已经有了稳定套餐代码、平台价格和购买快照,付款回调能准确找到要完成的那笔订单。

登录后可保存阅读进度

下一篇:支付闭环:结账、回调验证与权益发放登录保存进度