Stripe client secret 能完成付款,哪些位置绝不能放

client secret 是 Stripe 在客户端确认 PaymentIntent 时使用的凭据。按官方文档的说明,持有它的一方可以按该 PaymentIntent 上指定的金额完成付款流程。因此它只能交给发起这笔付款的客户本人,并且只应出现在受 TLS 保护的页面上。文档明确禁止三类做法:把它写入日志、把它嵌进 URL、把它暴露给客户以外的任何人。这三条划定的是这条凭据的使用边界,而不是可选的风格建议。

服务端生成、仅传客户前端、禁止日志与URL
本文涉及的概念与核对环节。

用一次客户端确认任务看清边界

设想一个常见的收款页面:服务端创建 PaymentIntent,把 client_secret 返回给浏览器,浏览器加载 Stripe.js 后在页面上完成确认。在这个任务里,client_secret 的流转路径应当是“服务端响应 → 当前客户的浏览器内存 → Stripe 的确认调用”,路径之外的位置都不该出现它。

按这个任务逐项核对:

日志。 服务端记录请求体、客户端打印响应对象、错误上报工具附带上下文、前端调试面板保留网络响应,都可能把 client_secret 写进持久化存储。文档的禁止项是“记录日志”,覆盖的不只是应用自己的日志文件。排查时要顺着“谁可能把响应体原样留下”去找,而不只是看有没有显式调用日志函数。

URL。 把 client_secret 放进查询参数、路径片段或重定向地址,会让它进入浏览器历史、Referer 头、代理与网关访问日志、以及分享出去的链接。文档的禁止项是“嵌进 URL”,所以即便链接只发给同一位客户,也不符合要求。传递应当走响应体或同等的非 URL 通道。

暴露范围。 文档要求不把它交给客户以外的任何人。这意味着共享的客服工具、内部管理后台、第三方分析脚本、多人共用的测试环境都不应接触到它。判断标准是“这一份凭据对应的付款人是不是当前这位客户”,而不是“这个人是不是公司内部人员”。

传输层。 任何包含 client_secret 的页面都必须启用 TLS。这一条与前面三条并列,缺一不可;页面本身是 HTTPS,不代表页面里加载的每个请求都走了 HTTPS,核对时要看实际请求的协议。

金额与凭据的关系

文档指出,可以用 client_secret 按 PaymentIntent 上指定的金额完成付款。这句话划定了它的能力范围:它能推动的是这一笔已创建好的 PaymentIntent,金额由服务端创建时确定,而不是由持有凭据的一方随意改写。理解这一点有助于判断风险面——泄露的后果绑定在这一笔付款上,但这不改变“不得泄露”的要求,也不能据此推导出任何关于重试、退款或后续扣款的结论,材料没有涉及这些。

确认之后该看什么

客户确认付款后,文档给出的做法是让服务端通过 webhook 监控支付成功或失败,而不是依赖客户端回传的结果。同一个 PaymentIntent 在多次支付尝试下可能关联多个 Charge 对象,例如重试会产生多个 Charge,逐个查看每个 Charge 的结果和所用支付方式的细节,才能判断实际发生了什么。这条与 client_secret 的保管是两件事:前者是确认之后的观测手段,后者是确认之前的凭据边界,不能互相替代。

核对清单的用法

把上面几点收拢成一张可执行的清单:client_secret 是否只出现在当前客户的浏览器内存中;是否没有任何日志、错误上报、调试工具会留存响应体;是否没有进入 URL、历史记录或重定向;是否没有流向内部分享工具或第三方脚本;承载它的每个请求是否都走 TLS。这张清单只回答“边界在哪里”,不涉及密钥轮换策略——原始材料没有讨论轮换,本文也不外推。

原始资料

docs.stripe.com

上一篇 Stripe重复创建PaymentIntent时幂等键应绑定购物车或会话标识