Stripe Go SDK初始化须用secret key且严禁前端暴露,应从环境变量加载;创建PaymentIntent需显式设confirm=true和capture_method=manual以支持延迟扣款;Webhook处理必须校验签名并用event.ID幂等去重;错误处理需区分stripe.Error类型精准响应。

Stripe Go SDK 初始化必须用 secret key,且不能暴露在前端
Stripe 的 secret_key 是服务端唯一合法凭证,任何将其拼进前端 URL、写进 JS 或通过 API 返回给客户端的行为,都会导致密钥泄露和资金风险。Golang 服务初始化时应从环境变量加载:
stripe.Key = os.Getenv("STRIPE_SECRET_KEY")。别用 .env 文件硬编码——Docker/K8s 环境下必须走 Secret 挂载或 Vault 注入。如果本地调试时临时写死,务必加 // DEV ONLY 注释并 gitignore 掉相关配置文件。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
创建 PaymentIntent 要显式设 confirm=true 且 capture_method=manual 才能延迟扣款
单笔扣款场景下,多数业务需要「先冻结再确认」(比如酒店预授权、订单风控拦截后人工审核)。直接调 stripe.PaymentIntent.New() 默认是自动捕获(capture_method=automatic),钱秒扣,无法撤回。正确做法是:
params := &stripe.PaymentIntentParams{
Amount: stripe.Int64(1999),
Currency: stripe.String("usd"),
Confirm: stripe.Bool(true),
CaptureMethod: stripe.String("manual"),
PaymentMethodTypes: stripe.StringSlice([]string{"card"}),
}。注意 Confirm: true 表示立即触发 3D Secure 验证流程;若要分两步(先 create 再 confirm),就得设 Confirm: false,后续调 stripe.PaymentIntent.Confirm() 并传入 PaymentMethodID。
Webhook 处理必须校验签名,且重放攻击防护靠 unique event.id + 幂等存储
Stripe 发来的 payment_intent.succeeded 事件不是“发一次就完事”,网络抖动或重试机制会导致重复投递。只靠数据库 INSERT ... ON CONFLICT DO NOTHING 不够:Stripe 的签名验证失败时,你根本不该处理事件体。必须用官方库的 stripe.Webhook.ConstructEvent() 校验 Stripe-Signature header:
event, err := stripe.Webhook.ConstructEvent(payload, sigHeader, os.Getenv("STRIPE_WEBHOOK_SECRET"))。校验通过后,立刻用 event.ID 查表判断是否已处理过——这个 ID 全局唯一,比时间戳或金额更可靠。别用订单号做幂等键,因为一个订单可能对应多个 PaymentIntent(比如部分退款重试)。
错误处理要区分 StripeError 类型,尤其 network_timeout 和 card_declined
Go SDK 抛出的 stripe.Error 包含 Type、Code、HTTPStatusCode 三重信息,不能只看 status code。比如 HTTPStatusCode == 402 可能是 card_declined(用户卡被拒),也可能是 insufficient_funds(余额不足),业务提示语要不同;而 network_timeout 属于可重试错误,应加 backoff 后重发 PaymentIntent.Confirm。常见误操作是把所有 err != nil 统一返回 “支付失败,请重试”,实际该让用户看到 “您的银行卡不支持国际交易” 这类具体原因。检查方式:
if serr, ok := err.(*stripe.Error); ok {
switch serr.Code {
case "card_declined":
// 引导换卡
case "expired_card":
// 提示过期
case "network_timeout":
// 记日志 + 重试
}
}
Stripe 的坑不在 API 调用本身,而在状态机对齐——你的 Order 状态、PaymentIntent 状态、Webhook 处理状态,三者必须严格同步。漏掉任意一个 Webhook 或跳过签名校验,都可能造成资损。

















