| name | stripe-api-style |
| description | Stripe API 设计风格 Skill。蒸馏自 Stripe API 文档、Stripe Engineering Blog、
SDK 源码(stripe-python/stripe-node)、Patrick Collison 的 API 设计原则、
Stripe 工程师的技术演讲。
触发词:「Stripe 风格」「像 Stripe 一样设计 API」「stripe style」「REST API 最佳实践」。
适用:REST API 设计、SDK 开发、支付/金融系统 API、开发者体验设计。
|
Stripe API · 设计 DNA
"An API is a product. Its users are developers. Their experience matters." — Stripe Engineering
角色定义
此 Skill 激活后,你设计的 API 和 SDK 应该让开发者看 5 分钟文档就能上手,
而不是翻 3 天 StackOverflow。
Stripe 是 API 设计的行业标杆。 不是因为功能最多,而是因为每个细节都替开发者想到了。
这意味着:可预测、容错、开发者优先。
URL 设计 DNA
4 条直觉规则:
-
资源用复数名词,动作用 URL 后缀
# ✅ Stripe 风格
POST /v1/payment_intents # 创建
GET /v1/payment_intents/{id} # 获取
POST /v1/payment_intents/{id}/confirm # 动作(不用 PUT/PATCH 做动作)
POST /v1/payment_intents/{id}/cancel # 动作
# ❌ 常见错误
POST /v1/createPaymentIntent # 动词在 URL 里
PUT /v1/payment_intents/{id}/status # 用 HTTP 方法表达动作
-
嵌套资源最多一层
# ✅
GET /v1/customers/{customer_id}/sources
# ❌ 嵌套过深,URL 复杂化
GET /v1/customers/{id}/subscriptions/{sub_id}/items/{item_id}/discounts
-
资源 ID 带类型前缀,全局唯一
pi_3MtwBwLkdIwHu7ix28a3tqPa # PaymentIntent
cus_NffrFeUfNV2Hib # Customer
ch_3MmlLrLkdIwHu7ix0snN0B15 # Charge
这样通过 ID 就能知道是什么资源,debug 时省大力气。
-
版本在 URL 里,不在 header 里
# ✅ Stripe 风格 — 版本可见、可分享、可 bookmark
GET /v1/charges
# ❌ header 版本 — 不直觉,容易被代理丢掉
GET /charges
API-Version: 2024-04-10
请求/响应设计
请求:
POST /v1/payment_intents
Content-Type: application/x-www-form-urlencoded # Stripe 用 form encoding,不是 JSON
Authorization: Bearer sk_test_...
amount=2000¤cy=usd&payment_method=pm_xxx&confirm=true
- 用 form encoding(历史选择,但 Stripe 坚持保持一致性)
- 所有金额用最小货币单位(分),避免浮点数问题
响应结构(永远一致):
{
"id": "pi_3MtwBwLkdIwHu7ix28a3tqPa",
"object": "payment_intent",
"amount": 2000,
"currency": "usd",
"status": "succeeded",
"created": 1679090702,
"livemode": false,
"metadata": {}
}
object 字段是 Stripe 的杀手锏:
- 任何响应都有
object 字段标识类型
- Webhook、列表、嵌套对象统统如此
- 开发者不需要靠 URL 或 context 猜类型
错误处理设计
Stripe 的错误设计是业界范本:
{
"error": {
"type": "card_error",
"code": "card_declined",
"decline_code": "insufficient_funds",
"message": "Your card has insufficient funds.",
"param": "card",
"doc_url": "https://stripe.com/docs/error-codes/card-declined"
}
}
5 条错误设计规则:
- 错误类型分层:
type(大类)→ code(具体)→ decline_code(支付特有)
- HTTP 状态码语义化:
400 — 参数错误(你的锅)
401 — 认证失败
402 — 支付失败(Stripe 特有)
404 — 资源不存在
429 — 限流
500 — 我们的锅
- 永远提供 doc_url — 让开发者能自助
- param 字段 — 指出是哪个参数的问题,不让开发者猜
- 错误消息面向用户,不面向开发者 —
message 可以直接展示给终端用户
SDK 设计哲学
Stripe SDK 的设计原则(以 Python 为例):
import stripe
intent = stripe.PaymentIntent.create(
amount=2000,
currency="usd",
)
intent = stripe.PaymentIntent.modify(
"pi_xxx",
metadata={"order_id": "6735"},
)
intent = stripe.PaymentIntent.confirm("pi_xxx")
for customer in stripe.Customer.auto_paging_iter():
process(customer)
SDK 设计要点:
- 每个 API 资源对应一个 SDK class(
stripe.PaymentIntent、stripe.Customer)
- 类方法映射 HTTP 方法:
create → POST,retrieve → GET,modify → POST 更新,list → GET list
- 自动重试(指数退避)对 5xx 和网络错误
Stripe-Idempotency-Key 自动处理,POST 请求可重试
- 分页自动处理,
auto_paging_iter() 透明迭代
幂等性设计
Stripe 的幂等键是工程杰作:
POST /v1/payment_intents
Idempotency-Key: a0d92f5c-e5c0-4d3d-b5b3-7a1c9f8c6b2e
amount=2000¤cy=usd
- 同一个 key 在 24 小时内重复请求,返回同一个结果
- 网络超时后可以安全重试,不会重复扣款
- SDK 自动生成并附加幂等键(对开发者透明)
自己的 API 设计规则:
- 所有创建资源的操作必须支持幂等键
- 幂等键放 HTTP header,不放 body(避免 body 内容不一致时的歧义)
- 存储层面:key + 请求 hash → 缓存响应
Webhook 设计
{
"id": "evt_3MtwBwLkdIwHu7ix2sRetfHb",
"object": "event",
"type": "payment_intent.succeeded",
"created": 1679090712,
"livemode": false,
"data": {
"object": { }
},
"api_version": "2023-10-16"
}
Webhook 5 大原则:
type 格式:{resource}.{event}(payment_intent.succeeded)
data.object 包含完整资源快照,不需要再调 API 查
- 总是包含
api_version,subscriber 知道 payload 格式
- 签名验证:
Stripe-Signature header + 时间窗口防重放
- 重试:指数退避,最多 3 天
反模式(绝不这样写)
-
URL 里放动词
POST /api/createOrder # ❌
POST /api/orders # ✅
-
浮点数表示金额
{ "amount": 19.99 }
{ "amount": 1999 }
-
没有 object 类型字段 — 无法区分不同资源的响应
-
错误只有 HTTP 状态码 — 机器无法理解具体原因
-
分页用 offset/page — 数据变化时分页结果不稳定,用 cursor
# ❌
GET /orders?page=2&per_page=10
# ✅ cursor 分页,稳定
GET /orders?starting_after=ord_xxx&limit=10
-
破坏性变更不版本化 — 字段重命名必须引入新版本
-
Webhook payload 不含完整对象 — 不要只发 ID,让用户再查一次
校验测试
- 5 分钟测试:给一个没见过这个 API 的开发者,5 分钟内能成功调通第一个请求吗?
- 重试测试:同一个 POST 请求发两次,结果一致吗?资源只创建了一次吗?
- 错误测试:传一个错误参数,错误消息能告诉开发者具体哪里错了、怎么修吗?
来源